Commit b040b513 authored by Xiaocheng Hu's avatar Xiaocheng Hu Committed by Commit Bot

[LayoutNG] Convert some offset mapping APIs to take Position

This patch changes three APIs to take Position parameters to better support
mapping non-text anchored positions to LayoutNG canonical text:
- IsBeforeNonCollapsedCharacter
- IsAfterNonCollapsedCharacter
- GetCharacterBefore

By changing the API, we are also allowed to consolidate some overridden
functions in LayoutTextFragment.

Bug: 699017
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_layout_tests_layout_ng
Change-Id: Ied30ef2e26f74342b68e8b27b385436aa7afc33e
Reviewed-on: https://chromium-review.googlesource.com/742689
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Cr-Commit-Position: refs/heads/master@{#512956}
parent 47e8a219
......@@ -65,15 +65,14 @@ bool EditCommand::IsRenderedCharacter(const Position& position) {
if (!layout_object || !layout_object->IsText())
return false;
const int offset_in_node = position.OffsetInContainerNode();
// Use NG offset mapping when LayoutNG is enabled.
if (auto* mapping = NGOffsetMapping::GetFor(position)) {
return mapping->IsBeforeNonCollapsedCharacter(node, offset_in_node);
return mapping->IsBeforeNonCollapsedContent(position);
}
// TODO(editing-dev): This doesn't handle first-letter correctly. Fix it.
const LayoutText* layout_text = ToLayoutText(layout_object);
const int offset_in_node = position.OffsetInContainerNode();
for (InlineTextBox* box : InlineTextBoxesOf(*layout_text)) {
if (offset_in_node < static_cast<int>(box->Start()) &&
!layout_text->ContainsReversedText()) {
......
......@@ -2038,16 +2038,17 @@ bool LayoutText::HasNonCollapsedText() const {
bool LayoutText::ContainsCaretOffset(int text_offset) const {
DCHECK_GE(text_offset, 0);
if (auto* mapping = GetNGOffsetMapping()) {
// ::first-letter handling should be done by LayoutTextFragment override.
DCHECK(!IsTextFragment());
if (!GetNode())
if (text_offset > static_cast<int>(TextLength()))
return false;
if (mapping->IsBeforeNonCollapsedCharacter(*GetNode(), text_offset))
const Position position = PositionForCaretOffset(text_offset);
if (position.IsNull())
return false;
if (text_offset < static_cast<int>(TextLength()) &&
mapping->IsBeforeNonCollapsedContent(position))
return true;
if (!mapping->IsAfterNonCollapsedCharacter(*GetNode(), text_offset))
if (!text_offset || !mapping->IsAfterNonCollapsedContent(position))
return false;
return *mapping->GetCharacterBefore(*GetNode(), text_offset) !=
kNewlineCharacter;
return *mapping->GetCharacterBefore(position) != kNewlineCharacter;
}
for (InlineTextBox* box : InlineTextBoxesOf(*this)) {
......@@ -2100,11 +2101,12 @@ static bool DoesContinueOnNextLine(const LayoutText& text_layout_object,
bool LayoutText::IsBeforeNonCollapsedCharacter(unsigned text_offset) const {
if (auto* mapping = GetNGOffsetMapping()) {
// ::first-letter handling should be done by LayoutTextFragment override.
DCHECK(!IsTextFragment());
if (!GetNode())
if (text_offset >= TextLength())
return false;
const Position position = PositionForCaretOffset(text_offset);
if (position.IsNull())
return false;
return mapping->IsBeforeNonCollapsedCharacter(*GetNode(), text_offset);
return mapping->IsBeforeNonCollapsedContent(position);
}
InlineTextBox* const last_text_box = LastTextBox();
......@@ -2132,11 +2134,12 @@ bool LayoutText::IsBeforeNonCollapsedCharacter(unsigned text_offset) const {
bool LayoutText::IsAfterNonCollapsedCharacter(unsigned text_offset) const {
if (auto* mapping = GetNGOffsetMapping()) {
// ::first-letter handling should be done by LayoutTextFragment override.
DCHECK(!IsTextFragment());
if (!GetNode())
if (!text_offset)
return false;
const Position position = PositionForCaretOffset(text_offset);
if (position.IsNull())
return false;
return mapping->IsAfterNonCollapsedCharacter(*GetNode(), text_offset);
return mapping->IsAfterNonCollapsedContent(position);
}
InlineTextBox* const last_text_box = LastTextBox();
......
......@@ -216,12 +216,12 @@ class CORE_EXPORT LayoutText : public LayoutObject {
// or segment break in node with style white-space: pre/pre-line/pre-wrap).
// TODO(editing-dev): The behavior is introduced by crrev.com/e3eb4e in
// InlineTextBox::ContainsCaretOffset(). Try to understand it.
virtual bool ContainsCaretOffset(int) const;
bool ContainsCaretOffset(int) const;
// Return true if the offset (0-based in the |text_| string) is before/after a
// non-collapsed character in this LayoutText, respectively.
virtual bool IsBeforeNonCollapsedCharacter(unsigned) const;
virtual bool IsAfterNonCollapsedCharacter(unsigned) const;
bool IsBeforeNonCollapsedCharacter(unsigned) const;
bool IsAfterNonCollapsedCharacter(unsigned) const;
int CaretMinOffset() const override;
int CaretMaxOffset() const override;
......
......@@ -196,55 +196,4 @@ Optional<unsigned> LayoutTextFragment::CaretOffsetForPosition(
return dom_offset - Start();
}
bool LayoutTextFragment::ContainsCaretOffset(int text_offset) const {
auto* mapping = GetNGOffsetMapping();
if (!mapping)
return LayoutText::ContainsCaretOffset(text_offset);
DCHECK_GE(text_offset, 0);
if (text_offset > static_cast<int>(FragmentLength()))
return false;
const Node* node = AssociatedTextNode();
if (!node)
return false;
const unsigned dom_offset = text_offset + Start();
if (mapping->IsBeforeNonCollapsedCharacter(*node, dom_offset))
return true;
if (text_offset == 0)
return false;
if (!mapping->IsAfterNonCollapsedCharacter(*node, dom_offset))
return false;
return *mapping->GetCharacterBefore(*node, dom_offset) != kNewlineCharacter;
}
bool LayoutTextFragment::IsBeforeNonCollapsedCharacter(
unsigned text_offset) const {
auto* mapping = GetNGOffsetMapping();
if (!mapping)
return LayoutText::IsBeforeNonCollapsedCharacter(text_offset);
if (text_offset >= FragmentLength())
return false;
const Node* node = AssociatedTextNode();
if (!node)
return false;
const unsigned dom_offset = text_offset + Start();
return mapping->IsBeforeNonCollapsedCharacter(*node, dom_offset);
}
bool LayoutTextFragment::IsAfterNonCollapsedCharacter(
unsigned text_offset) const {
auto* mapping = GetNGOffsetMapping();
if (!mapping)
return LayoutText::IsAfterNonCollapsedCharacter(text_offset);
if (!text_offset)
return false;
const Node* node = AssociatedTextNode();
if (!node)
return false;
const unsigned dom_offset = text_offset + Start();
return mapping->IsAfterNonCollapsedCharacter(*node, dom_offset);
}
} // namespace blink
......@@ -53,9 +53,6 @@ class CORE_EXPORT LayoutTextFragment final : public LayoutText {
Position PositionForCaretOffset(unsigned) const override;
Optional<unsigned> CaretOffsetForPosition(const Position&) const override;
bool ContainsCaretOffset(int) const override;
bool IsBeforeNonCollapsedCharacter(unsigned) const override;
bool IsAfterNonCollapsedCharacter(unsigned) const override;
unsigned Start() const { return start_; }
unsigned FragmentLength() const { return fragment_length_; }
......
......@@ -257,16 +257,21 @@ Position NGOffsetMapping::EndOfLastNonCollapsedContent(
return Position();
}
bool NGOffsetMapping::IsBeforeNonCollapsedCharacter(const Node& node,
unsigned offset) const {
const NGOffsetMappingUnit* unit =
GetMappingUnitForPosition(CreatePositionForOffsetMapping(node, offset));
bool NGOffsetMapping::IsBeforeNonCollapsedContent(
const Position& position) const {
DCHECK(NGOffsetMapping::AcceptsPosition(position));
const NGOffsetMappingUnit* unit = GetMappingUnitForPosition(position);
const unsigned offset = ToNodeOffsetPair(position).second;
return unit && offset < unit->DOMEnd() &&
unit->GetType() != NGOffsetMappingUnitType::kCollapsed;
}
bool NGOffsetMapping::IsAfterNonCollapsedCharacter(const Node& node,
unsigned offset) const {
bool NGOffsetMapping::IsAfterNonCollapsedContent(
const Position& position) const {
DCHECK(NGOffsetMapping::AcceptsPosition(position));
const auto node_and_offset = ToNodeOffsetPair(position);
const Node& node = node_and_offset.first;
const unsigned offset = node_and_offset.second;
if (!offset)
return false;
// In case we have one unit ending at |offset| and another starting at
......@@ -277,10 +282,10 @@ bool NGOffsetMapping::IsAfterNonCollapsedCharacter(const Node& node,
unit->GetType() != NGOffsetMappingUnitType::kCollapsed;
}
Optional<UChar> NGOffsetMapping::GetCharacterBefore(const Node& node,
unsigned offset) const {
Optional<unsigned> text_content_offset =
GetTextContentOffset(CreatePositionForOffsetMapping(node, offset));
Optional<UChar> NGOffsetMapping::GetCharacterBefore(
const Position& position) const {
DCHECK(NGOffsetMapping::AcceptsPosition(position));
Optional<unsigned> text_content_offset = GetTextContentOffset(position);
if (!text_content_offset || !*text_content_offset)
return WTF::nullopt;
return text_[*text_content_offset - 1];
......
......@@ -144,17 +144,14 @@ class CORE_EXPORT NGOffsetMapping {
Position StartOfNextNonCollapsedContent(const Position&) const;
Position EndOfLastNonCollapsedContent(const Position&) const;
// Returns true if the offset is right before a non-collapsed character. If
// the offset is at the end of the node, returns false.
bool IsBeforeNonCollapsedCharacter(const Node&, unsigned offset) const;
// Returns true if the position is right before/after non-collapsed content.
// Note that false is returned if the position is already at node end/start.
bool IsBeforeNonCollapsedContent(const Position&) const;
bool IsAfterNonCollapsedContent(const Position&) const;
// Returns true if the offset is right after a non-collapsed character. If the
// offset is at the beginning of the node, returns false.
bool IsAfterNonCollapsedCharacter(const Node&, unsigned offset) const;
// Maps the given DOM offset to text content, and then returns the text
// Maps the given position to text content, and then returns the text
// content character before the offset. Returns nullopt if it does not exist.
Optional<UChar> GetCharacterBefore(const Node&, unsigned offset) const;
Optional<UChar> GetCharacterBefore(const Position&) const;
// ------ Mapping APIs from text content to DOM ------
......
......@@ -63,12 +63,12 @@ class NGOffsetMappingTest : public NGLayoutTest {
return GetOffsetMapping().EndOfLastNonCollapsedContent(position);
}
bool IsBeforeNonCollapsedCharacter(const Node& node, unsigned offset) const {
return GetOffsetMapping().IsBeforeNonCollapsedCharacter(node, offset);
bool IsBeforeNonCollapsedContent(const Position& position) const {
return GetOffsetMapping().IsBeforeNonCollapsedContent(position);
}
bool IsAfterNonCollapsedCharacter(const Node& node, unsigned offset) const {
return GetOffsetMapping().IsAfterNonCollapsedCharacter(node, offset);
bool IsAfterNonCollapsedContent(const Position& position) const {
return GetOffsetMapping().IsAfterNonCollapsedContent(position);
}
Position GetFirstPosition(unsigned offset) const {
......@@ -156,17 +156,17 @@ TEST_F(NGOffsetMappingTest, OneTextNode) {
EXPECT_EQ(Position(foo_node, 3),
EndOfLastNonCollapsedContent(Position(foo_node, 3)));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*foo_node, 0));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*foo_node, 1));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*foo_node, 2));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(foo_node, 0)));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(foo_node, 1)));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(foo_node, 2)));
EXPECT_FALSE(
IsBeforeNonCollapsedCharacter(*foo_node, 3)); // false at node end
IsBeforeNonCollapsedContent(Position(foo_node, 3))); // false at node end
// false at node start
EXPECT_FALSE(IsAfterNonCollapsedCharacter(*foo_node, 0));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*foo_node, 1));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*foo_node, 2));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*foo_node, 3));
EXPECT_FALSE(IsAfterNonCollapsedContent(Position(foo_node, 0)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(foo_node, 1)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(foo_node, 2)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(foo_node, 3)));
}
TEST_F(NGOffsetMappingTest, TwoTextNodes) {
......@@ -210,29 +210,29 @@ TEST_F(NGOffsetMappingTest, TwoTextNodes) {
EXPECT_EQ(Position(foo_node, 3), GetFirstPosition(3));
EXPECT_EQ(Position(bar_node, 0), GetLastPosition(3));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*foo_node, 0));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*foo_node, 1));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*foo_node, 2));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(foo_node, 0)));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(foo_node, 1)));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(foo_node, 2)));
EXPECT_FALSE(
IsBeforeNonCollapsedCharacter(*foo_node, 3)); // false at node end
IsBeforeNonCollapsedContent(Position(foo_node, 3))); // false at node end
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*bar_node, 0));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*bar_node, 1));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*bar_node, 2));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(bar_node, 0)));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(bar_node, 1)));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(bar_node, 2)));
EXPECT_FALSE(
IsBeforeNonCollapsedCharacter(*bar_node, 3)); // false at node end
IsBeforeNonCollapsedContent(Position(bar_node, 3))); // false at node end
// false at node start
EXPECT_FALSE(IsAfterNonCollapsedCharacter(*foo_node, 0));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*foo_node, 1));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*foo_node, 2));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*foo_node, 3));
EXPECT_FALSE(IsAfterNonCollapsedContent(Position(foo_node, 0)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(foo_node, 1)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(foo_node, 2)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(foo_node, 3)));
// false at node start
EXPECT_FALSE(IsAfterNonCollapsedCharacter(*bar_node, 0));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*bar_node, 1));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*bar_node, 2));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*bar_node, 3));
EXPECT_FALSE(IsAfterNonCollapsedContent(Position(bar_node, 0)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(bar_node, 1)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(bar_node, 2)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(bar_node, 3)));
}
TEST_F(NGOffsetMappingTest, BRBetweenTextNodes) {
......@@ -342,25 +342,25 @@ TEST_F(NGOffsetMappingTest, OneTextNodeWithCollapsedSpace) {
EXPECT_EQ(Position(node, 4), EndOfLastNonCollapsedContent(Position(node, 4)));
EXPECT_EQ(Position(node, 4), EndOfLastNonCollapsedContent(Position(node, 5)));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*node, 0));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*node, 1));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*node, 2));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*node, 3));
EXPECT_FALSE(IsBeforeNonCollapsedCharacter(*node, 4));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*node, 5));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*node, 6));
EXPECT_TRUE(IsBeforeNonCollapsedCharacter(*node, 7));
EXPECT_FALSE(IsBeforeNonCollapsedCharacter(*node, 8));
EXPECT_FALSE(IsAfterNonCollapsedCharacter(*node, 0));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*node, 1));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*node, 2));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*node, 3));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*node, 4));
EXPECT_FALSE(IsAfterNonCollapsedCharacter(*node, 5));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*node, 6));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*node, 7));
EXPECT_TRUE(IsAfterNonCollapsedCharacter(*node, 8));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(node, 0)));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(node, 1)));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(node, 2)));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(node, 3)));
EXPECT_FALSE(IsBeforeNonCollapsedContent(Position(node, 4)));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(node, 5)));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(node, 6)));
EXPECT_TRUE(IsBeforeNonCollapsedContent(Position(node, 7)));
EXPECT_FALSE(IsBeforeNonCollapsedContent(Position(node, 8)));
EXPECT_FALSE(IsAfterNonCollapsedContent(Position(node, 0)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(node, 1)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(node, 2)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(node, 3)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(node, 4)));
EXPECT_FALSE(IsAfterNonCollapsedContent(Position(node, 5)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(node, 6)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(node, 7)));
EXPECT_TRUE(IsAfterNonCollapsedContent(Position(node, 8)));
}
TEST_F(NGOffsetMappingTest, FullyCollapsedWhiteSpaceNode) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment