| Index: third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc
|
| diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc
|
| index ff37b1366728acbe1f301ac1383d425065e56654..d5f9f1ccea3d804e423c6e8903317c884d2b7b43 100644
|
| --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc
|
| +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc
|
| @@ -8,6 +8,7 @@
|
| #include "core/layout/LayoutBlockFlow.h"
|
| #include "core/layout/LayoutObject.h"
|
| #include "core/layout/LayoutText.h"
|
| +#include "core/layout/LayoutTextFragment.h"
|
| #include "core/layout/api/LineLayoutAPIShim.h"
|
| #include "core/layout/line/LineInfo.h"
|
| #include "core/layout/line/RootInlineBox.h"
|
| @@ -18,6 +19,7 @@
|
| #include "core/layout/ng/inline/ng_inline_layout_algorithm.h"
|
| #include "core/layout/ng/inline/ng_line_box_fragment.h"
|
| #include "core/layout/ng/inline/ng_line_breaker.h"
|
| +#include "core/layout/ng/inline/ng_offset_mapping_result.h"
|
| #include "core/layout/ng/inline/ng_physical_line_box_fragment.h"
|
| #include "core/layout/ng/inline/ng_physical_text_fragment.h"
|
| #include "core/layout/ng/inline/ng_text_fragment.h"
|
| @@ -174,16 +176,105 @@ unsigned PlaceInlineBoxChildren(
|
| return text_index;
|
| }
|
|
|
| -LayoutBox* CollectInlinesInternal(LayoutBlockFlow* block,
|
| - NGInlineItemsBuilder* builder) {
|
| +// Templated helper function for CollectInlinesInternal().
|
| +template <typename OffsetMappingBuilder>
|
| +void ClearNeedsLayoutIfUpdatingLayout(LayoutObject* node) {
|
| + node->ClearNeedsLayout();
|
| +}
|
| +
|
| +template <>
|
| +void ClearNeedsLayoutIfUpdatingLayout<NGOffsetMappingBuilder>(LayoutObject*) {}
|
| +
|
| +// Templated helper function for CollectInlinesInternal().
|
| +template <typename OffsetMappingBuilder>
|
| +String GetTextForInlineCollection(const LayoutText& node) {
|
| + return node.GetText();
|
| +}
|
| +
|
| +// This function is a workaround of writing the whitespace-collapsed string back
|
| +// to LayoutText after inline collection, so that we can still recover the
|
| +// original text for building offset mapping.
|
| +// TODO(xiaochengh): Remove this function once we can:
|
| +// - paint inlines directly from the fragment tree, or
|
| +// - perform inline collection directly from DOM instead of LayoutText
|
| +template <>
|
| +String GetTextForInlineCollection<NGOffsetMappingBuilder>(
|
| + const LayoutText& layout_text) {
|
| + if (layout_text.Style()->TextSecurity() != ETextSecurity::kNone)
|
| + return layout_text.GetText();
|
| +
|
| + // TODO(xiaochengh): Return the text-transformed string instead of DOM data
|
| + // string.
|
| +
|
| + // Special handling for first-letter.
|
| + if (layout_text.IsTextFragment()) {
|
| + const LayoutTextFragment& text_fragment = ToLayoutTextFragment(layout_text);
|
| + Text* node = text_fragment.AssociatedTextNode();
|
| + if (!node) {
|
| + // Reaches here if the LayoutTextFragment is due to a LayoutQuote.
|
| + return layout_text.GetText();
|
| + }
|
| + unsigned first_letter_length = node->GetLayoutObject()->TextStartOffset();
|
| + if (text_fragment.IsRemainingTextLayoutObject())
|
| + return node->data().Substring(first_letter_length);
|
| + return node->data().Substring(0, first_letter_length);
|
| + }
|
| +
|
| + Node* node = layout_text.GetNode();
|
| + if (!node || !node->IsTextNode())
|
| + return layout_text.GetText();
|
| + return ToText(node)->data();
|
| +}
|
| +
|
| +// Templated helper function for CollectInlinesInternal().
|
| +template <typename OffsetMappingBuilder>
|
| +void AppendTextTransformedOffsetMapping(OffsetMappingBuilder*,
|
| + const LayoutText*,
|
| + const String&) {}
|
| +
|
| +template <>
|
| +void AppendTextTransformedOffsetMapping<NGOffsetMappingBuilder>(
|
| + NGOffsetMappingBuilder* concatenated_mapping_builder,
|
| + const LayoutText* node,
|
| + const String& text_transformed_string) {
|
| + // TODO(xiaochengh): We are assuming that DOM data string and text-transformed
|
| + // strings have the same length, which is incorrect.
|
| + NGOffsetMappingBuilder text_transformed_mapping_builder;
|
| + text_transformed_mapping_builder.AppendIdentityMapping(
|
| + text_transformed_string.length());
|
| + text_transformed_mapping_builder.Annotate(node);
|
| + concatenated_mapping_builder->Concatenate(text_transformed_mapping_builder);
|
| +}
|
| +
|
| +// The function is templated to indicate the purpose of collected inlines:
|
| +// - With EmptyOffsetMappingBuilder: updating layout;
|
| +// - With NGOffsetMappingBuilder: building offset mapping on clean layout.
|
| +//
|
| +// This allows code sharing between the two purposes with slightly different
|
| +// behaviors. For example, we clear a LayoutObject's need layout flags when
|
| +// updating layout, but don't do that when building offset mapping.
|
| +//
|
| +// There are also performance considerations, since template saves the overhead
|
| +// for condition checking and branching.
|
| +template <typename OffsetMappingBuilder>
|
| +LayoutBox* CollectInlinesInternal(
|
| + LayoutBlockFlow* block,
|
| + NGInlineItemsBuilderTemplate<OffsetMappingBuilder>* builder) {
|
| builder->EnterBlock(block->Style());
|
| LayoutObject* node = block->FirstChild();
|
| LayoutBox* next_box = nullptr;
|
| while (node) {
|
| if (node->IsText()) {
|
| builder->SetIsSVGText(node->IsSVGInlineText());
|
| - builder->Append(ToLayoutText(node)->GetText(), node->Style(), node);
|
| - node->ClearNeedsLayout();
|
| +
|
| + LayoutText* layout_text = ToLayoutText(node);
|
| + const String& text =
|
| + GetTextForInlineCollection<OffsetMappingBuilder>(*layout_text);
|
| + builder->Append(text, node->Style(), layout_text);
|
| + ClearNeedsLayoutIfUpdatingLayout<OffsetMappingBuilder>(layout_text);
|
| +
|
| + AppendTextTransformedOffsetMapping(
|
| + &builder->GetConcatenatedOffsetMappingBuilder(), layout_text, text);
|
|
|
| } else if (node->IsFloating()) {
|
| // Add floats and positioned objects in the same way as atomic inlines.
|
| @@ -215,7 +306,7 @@ LayoutBox* CollectInlinesInternal(LayoutBlockFlow* block,
|
|
|
| } else {
|
| // An empty inline node.
|
| - node->ClearNeedsLayout();
|
| + ClearNeedsLayoutIfUpdatingLayout<OffsetMappingBuilder>(node);
|
| }
|
|
|
| builder->ExitInline(node);
|
| @@ -235,7 +326,7 @@ LayoutBox* CollectInlinesInternal(LayoutBlockFlow* block,
|
| }
|
| DCHECK(node->IsInline());
|
| builder->ExitInline(node);
|
| - node->ClearNeedsLayout();
|
| + ClearNeedsLayoutIfUpdatingLayout<OffsetMappingBuilder>(node);
|
| }
|
| }
|
| builder->ExitBlock();
|
| @@ -269,6 +360,25 @@ void NGInlineNode::PrepareLayout() {
|
| ShapeText();
|
| }
|
|
|
| +NGOffsetMappingResult NGInlineNode::BuildOffsetMapping() const {
|
| + DCHECK(!GetLayoutBlockFlow()->GetDocument().NeedsLayoutTreeUpdate());
|
| +
|
| + // TODO(xiaochengh): BuildOffsetMapping() discards the NGInlineItems and
|
| + // text content built by |builder|, because they are already there in
|
| + // NGInlineNodeData. For efficiency, we should make |builder| not construct
|
| + // items and text content.
|
| + Vector<NGInlineItem> items;
|
| + NGInlineItemsBuilderForOffsetMapping builder(&items);
|
| + CollectInlinesInternal(GetLayoutBlockFlow(), &builder);
|
| + builder.ToString();
|
| +
|
| + NGOffsetMappingBuilder& mapping_builder =
|
| + builder.GetConcatenatedOffsetMappingBuilder();
|
| + mapping_builder.Composite(builder.GetOffsetMappingBuilder());
|
| +
|
| + return mapping_builder.Build();
|
| +}
|
| +
|
| // Depth-first-scan of all LayoutInline and LayoutText nodes that make up this
|
| // NGInlineNode object. Collects LayoutText items, merging them up into the
|
| // parent LayoutInline where possible, and joining all text content in a single
|
|
|