summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/editing/text_offset_mapping.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/core/editing/text_offset_mapping.cc')
-rw-r--r--chromium/third_party/blink/renderer/core/editing/text_offset_mapping.cc156
1 files changed, 119 insertions, 37 deletions
diff --git a/chromium/third_party/blink/renderer/core/editing/text_offset_mapping.cc b/chromium/third_party/blink/renderer/core/editing/text_offset_mapping.cc
index b507f7be8cd..3e9cdcbe154 100644
--- a/chromium/third_party/blink/renderer/core/editing/text_offset_mapping.cc
+++ b/chromium/third_party/blink/renderer/core/editing/text_offset_mapping.cc
@@ -10,6 +10,7 @@
#include "third_party/blink/renderer/core/editing/iterators/character_iterator.h"
#include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
#include "third_party/blink/renderer/core/editing/position.h"
+#include "third_party/blink/renderer/core/html/forms/text_control_element.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
namespace blink {
@@ -103,9 +104,10 @@ const LayoutBlockFlow* ComputeInlineContentsAsBlockFlow(
block_flow.IsFloatingOrOutOfFlowPositioned()) {
const LayoutBlockFlow& root_block_flow =
RootInlineContentsContainerOf(block_flow);
- DCHECK(CanBeInlineContentsContainer(root_block_flow))
- << layout_object << " block_flow=" << block_flow
- << " root_block_flow=" << root_block_flow;
+ // Skip |root_block_flow| if it's an anonymous wrapper created for
+ // pseudo elements. See test AnonymousBlockFlowWrapperForFloatPseudo.
+ if (!CanBeInlineContentsContainer(root_block_flow))
+ return nullptr;
return &root_block_flow;
}
if (!CanBeInlineContentsContainer(block_flow))
@@ -232,10 +234,11 @@ TextOffsetMapping::ForwardRange TextOffsetMapping::ForwardRangeOf(
}
// static
-TextOffsetMapping::InlineContents TextOffsetMapping::FindBackwardInlineContents(
- const PositionInFlatTree& position) {
- for (const Node* node = position.NodeAsRangeLastNode(); node;
- node = FlatTreeTraversal::Previous(*node)) {
+template <typename Traverser>
+TextOffsetMapping::InlineContents TextOffsetMapping::FindInlineContentsInternal(
+ const Node* start_node,
+ Traverser traverser) {
+ for (const Node* node = start_node; node; node = traverser(*node)) {
const InlineContents inline_contents = ComputeInlineContentsFromNode(*node);
if (inline_contents.IsNotNull())
return inline_contents;
@@ -244,17 +247,78 @@ TextOffsetMapping::InlineContents TextOffsetMapping::FindBackwardInlineContents(
}
// static
+TextOffsetMapping::InlineContents TextOffsetMapping::FindBackwardInlineContents(
+ const PositionInFlatTree& position) {
+ const Node* previous_node = position.NodeAsRangeLastNode();
+ if (!previous_node)
+ return InlineContents();
+
+ if (const TextControlElement* enclosing_text_control =
+ EnclosingTextControl(position)) {
+ if (!FlatTreeTraversal::IsDescendantOf(*previous_node,
+ *enclosing_text_control)) {
+ // The first position in a text control reaches here.
+ return InlineContents();
+ }
+
+ return TextOffsetMapping::FindInlineContentsInternal(
+ previous_node, [enclosing_text_control](const Node& node) {
+ return FlatTreeTraversal::Previous(node, enclosing_text_control);
+ });
+ }
+
+ auto previous_skipping_text_control = [](const Node& node) -> const Node* {
+ DCHECK(!EnclosingTextControl(&node));
+ const Node* previous = FlatTreeTraversal::Previous(node);
+ const TextControlElement* previous_text_control =
+ EnclosingTextControl(previous);
+ if (!previous_text_control)
+ return previous;
+ return previous_text_control;
+ };
+
+ if (const TextControlElement* last_enclosing_text_control =
+ EnclosingTextControl(previous_node)) {
+ // Example, <input value=foo><span>bar</span>, span@beforeAnchor
+ return TextOffsetMapping::FindInlineContentsInternal(
+ last_enclosing_text_control, previous_skipping_text_control);
+ }
+ return TextOffsetMapping::FindInlineContentsInternal(
+ previous_node, previous_skipping_text_control);
+}
+
+// static
// Note: "doubleclick-whitespace-img-crash.html" call |NextWordPosition())
// with AfterNode(IMG) for <body><img></body>
TextOffsetMapping::InlineContents TextOffsetMapping::FindForwardInlineContents(
const PositionInFlatTree& position) {
- for (const Node* node = position.NodeAsRangeFirstNode(); node;
- node = FlatTreeTraversal::Next(*node)) {
- const InlineContents inline_contents = ComputeInlineContentsFromNode(*node);
- if (inline_contents.IsNotNull())
- return inline_contents;
+ const Node* next_node = position.NodeAsRangeFirstNode();
+ if (!next_node)
+ return InlineContents();
+
+ if (const TextControlElement* enclosing_text_control =
+ EnclosingTextControl(position)) {
+ if (!FlatTreeTraversal::IsDescendantOf(*next_node,
+ *enclosing_text_control)) {
+ // The last position in a text control reaches here.
+ return InlineContents();
+ }
+
+ return TextOffsetMapping::FindInlineContentsInternal(
+ next_node, [enclosing_text_control](const Node& node) {
+ return FlatTreeTraversal::Next(node, enclosing_text_control);
+ });
}
- return InlineContents();
+
+ auto next_skipping_text_control = [](const Node& node) {
+ DCHECK(!EnclosingTextControl(&node));
+ if (IsTextControl(node))
+ return FlatTreeTraversal::NextSkippingChildren(node);
+ return FlatTreeTraversal::Next(node);
+ };
+ DCHECK(!EnclosingTextControl(next_node));
+ return TextOffsetMapping::FindInlineContentsInternal(
+ next_node, next_skipping_text_control);
}
// ----
@@ -322,38 +386,56 @@ EphemeralRangeInFlatTree TextOffsetMapping::InlineContents::GetRange() const {
: PositionInFlatTree::AfterNode(last_node));
}
+PositionInFlatTree
+TextOffsetMapping::InlineContents::LastPositionBeforeBlockFlow() const {
+ DCHECK(block_flow_);
+ if (const Node* node = block_flow_->NonPseudoNode()) {
+ if (!FlatTreeTraversal::Parent(*node)) {
+ // Reached start of document.
+ return PositionInFlatTree();
+ }
+ return PositionInFlatTree::BeforeNode(*node);
+ }
+ DCHECK(first_);
+ DCHECK(first_->NonPseudoNode());
+ DCHECK(FlatTreeTraversal::Parent(*first_->NonPseudoNode()));
+ return PositionInFlatTree::BeforeNode(*first_->NonPseudoNode());
+}
+
+PositionInFlatTree
+TextOffsetMapping::InlineContents::FirstPositionAfterBlockFlow() const {
+ DCHECK(block_flow_);
+ if (const Node* node = block_flow_->NonPseudoNode()) {
+ if (!FlatTreeTraversal::Parent(*node)) {
+ // Reached end of document.
+ return PositionInFlatTree();
+ }
+ return PositionInFlatTree::AfterNode(*node);
+ }
+ DCHECK(last_);
+ DCHECK(last_->NonPseudoNode());
+ DCHECK(FlatTreeTraversal::Parent(*last_->NonPseudoNode()));
+ return PositionInFlatTree::AfterNode(*last_->NonPseudoNode());
+}
+
// static
TextOffsetMapping::InlineContents TextOffsetMapping::InlineContents::NextOf(
const InlineContents& inline_contents) {
- for (LayoutObject* runner =
- inline_contents.block_flow_->NextInPreOrderAfterChildren();
- runner; runner = runner->NextInPreOrder()) {
- if (!CanBeInlineContentsContainer(*runner))
- continue;
- const LayoutBlockFlow& block_flow = ToLayoutBlockFlow(*runner);
- if (block_flow.IsFloatingOrOutOfFlowPositioned())
- continue;
- DCHECK(!block_flow.IsAtomicInlineLevel()) << block_flow;
- return CreateInlineContentsFromBlockFlow(block_flow);
- }
- return InlineContents();
+ const PositionInFlatTree position_after =
+ inline_contents.FirstPositionAfterBlockFlow();
+ if (position_after.IsNull())
+ return InlineContents();
+ return TextOffsetMapping::FindForwardInlineContents(position_after);
}
// static
TextOffsetMapping::InlineContents TextOffsetMapping::InlineContents::PreviousOf(
const InlineContents& inline_contents) {
- for (LayoutObject* runner = inline_contents.block_flow_->PreviousInPreOrder();
- runner; runner = runner->PreviousInPreOrder()) {
- const LayoutBlockFlow* const block_flow =
- ComputeInlineContentsAsBlockFlow(*runner);
- if (!block_flow || block_flow->IsFloatingOrOutOfFlowPositioned())
- continue;
- DCHECK(!block_flow->IsDescendantOf(inline_contents.block_flow_))
- << block_flow;
- DCHECK(!block_flow->IsAtomicInlineLevel()) << block_flow;
- return CreateInlineContentsFromBlockFlow(*block_flow);
- }
- return InlineContents();
+ const PositionInFlatTree position_before =
+ inline_contents.LastPositionBeforeBlockFlow();
+ if (position_before.IsNull())
+ return InlineContents();
+ return TextOffsetMapping::FindBackwardInlineContents(position_before);
}
std::ostream& operator<<(