Commit 9c2f1b4b authored by Nektarios Paisios's avatar Nektarios Paisios Committed by Commit Bot

Implements AXLineForTextMarker and corrects the implementation of...

Implements AXLineForTextMarker and corrects the implementation of AXTextMarkerRangeForLine and insertionPointLineNumber

1. NSAccessibilityLineForTextMarker should return the line number within the object
it is called on where the given text marker is located.

2. NSAccessibilityTextMarkerRangeForLineParameterizedAttribute was not implemented correctly.
It should return a text marker range spanning the given line within
the object it is called on.
It doesn't receive a text marker as a parameter but an integer representing the
line number.

3. Per Safari, "insertionPointLineNumber" should return nil when the selection is not collapsed
or when called on an object that does not contain the caret.

4. Took the opportunity to re-write the line calculation logic using C++ algorithms
and switch away from using the deprecated sel_start and sel_end attributes.

R=dmazzoni@chromium.org

Bug: 990931
Change-Id: I96d7aaf56c1603c13f58827bb58b8bf467d91994
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1812100Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Commit-Queue: Nektarios Paisios <nektar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#699334}
parent 7ba14412
...@@ -50,7 +50,7 @@ class AXRange { ...@@ -50,7 +50,7 @@ class AXRange {
focus_.swap(other.focus_); focus_.swap(other.focus_);
} }
virtual ~AXRange() {} virtual ~AXRange() = default;
AXPositionType* anchor() const { AXPositionType* anchor() const {
DCHECK(anchor_); DCHECK(anchor_);
...@@ -62,15 +62,10 @@ class AXRange { ...@@ -62,15 +62,10 @@ class AXRange {
return focus_.get(); return focus_.get();
} }
bool IsNull() const {
DCHECK(anchor_ && focus_);
return anchor_->IsNullPosition() || focus_->IsNullPosition();
}
AXRange& operator=(const AXRange& other) = delete; AXRange& operator=(const AXRange& other) = delete;
AXRange& operator=(const AXRange&& other) { AXRange& operator=(AXRange&& other) {
if (this != other) { if (this != &other) {
anchor_ = AXPositionType::CreateNullPosition(); anchor_ = AXPositionType::CreateNullPosition();
focus_ = AXPositionType::CreateNullPosition(); focus_ = AXPositionType::CreateNullPosition();
anchor_.swap(other.anchor_); anchor_.swap(other.anchor_);
...@@ -88,7 +83,7 @@ class AXRange { ...@@ -88,7 +83,7 @@ class AXRange {
bool operator!=(const AXRange& other) const { return !(*this == other); } bool operator!=(const AXRange& other) const { return !(*this == other); }
AXRange GetForwardRange() const { AXRange AsForwardRange() const {
// When we have a range with an empty text representation, its endpoints // When we have a range with an empty text representation, its endpoints
// would be considered equal as text positions, but they could be located in // would be considered equal as text positions, but they could be located in
// different anchors of the AXTree. Compare them as tree positions first to // different anchors of the AXTree. Compare them as tree positions first to
...@@ -102,6 +97,8 @@ class AXRange { ...@@ -102,6 +97,8 @@ class AXRange {
: AXRange(anchor_->Clone(), focus_->Clone()); : AXRange(anchor_->Clone(), focus_->Clone());
} }
bool IsCollapsed() const { return !IsNull() && *anchor_ == *focus_; }
// We define a "leaf text range" as an AXRange whose endpoints are leaf text // We define a "leaf text range" as an AXRange whose endpoints are leaf text
// positions located within the same anchor of the AXTree. // positions located within the same anchor of the AXTree.
bool IsLeafTextRange() const { bool IsLeafTextRange() const {
...@@ -109,6 +106,11 @@ class AXRange { ...@@ -109,6 +106,11 @@ class AXRange {
anchor_->IsLeafTextPosition() && focus_->IsLeafTextPosition(); anchor_->IsLeafTextPosition() && focus_->IsLeafTextPosition();
} }
bool IsNull() const {
DCHECK(anchor_ && focus_);
return anchor_->IsNullPosition() || focus_->IsNullPosition();
}
// We can decompose any given AXRange into multiple "leaf text ranges". // We can decompose any given AXRange into multiple "leaf text ranges".
// As an example, consider the following HTML code: // As an example, consider the following HTML code:
// //
...@@ -208,13 +210,13 @@ class AXRange { ...@@ -208,13 +210,13 @@ class AXRange {
}; };
Iterator begin() const { Iterator begin() const {
AXRange forward_range = GetForwardRange(); AXRange forward_range = AsForwardRange();
return Iterator(std::move(forward_range.anchor_), return Iterator(std::move(forward_range.anchor_),
std::move(forward_range.focus_)); std::move(forward_range.focus_));
} }
Iterator end() const { Iterator end() const {
AXRange forward_range = GetForwardRange(); AXRange forward_range = AsForwardRange();
return Iterator(nullptr, std::move(forward_range.focus_)); return Iterator(nullptr, std::move(forward_range.focus_));
} }
......
...@@ -377,6 +377,100 @@ TEST_F(AXRangeTest, EqualityOperators) { ...@@ -377,6 +377,100 @@ TEST_F(AXRangeTest, EqualityOperators) {
EXPECT_EQ(test_positions_1_and_2, test_positions_1_and_3); EXPECT_EQ(test_positions_1_and_2, test_positions_1_and_3);
} }
TEST_F(AXRangeTest, AsForwardRange) {
TestPositionRange null_range(AXNodePosition::CreateNullPosition(),
AXNodePosition::CreateNullPosition());
null_range = null_range.AsForwardRange();
EXPECT_TRUE(null_range.IsNull());
TestPositionInstance tree_position = AXNodePosition::CreateTreePosition(
tree_->data().tree_id, button_.id, 0 /* child_index */);
TestPositionInstance text_position1 = AXNodePosition::CreateTextPosition(
tree_->data().tree_id, line_break1_.id, 1 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
TestPositionInstance text_position2 = AXNodePosition::CreateTextPosition(
tree_->data().tree_id, inline_box2_.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
TestPositionRange tree_to_text_range(text_position1->Clone(),
tree_position->Clone());
tree_to_text_range = tree_to_text_range.AsForwardRange();
EXPECT_EQ(*tree_position, *tree_to_text_range.anchor());
EXPECT_EQ(*text_position1, *tree_to_text_range.focus());
TestPositionRange text_to_text_range(text_position2->Clone(),
text_position1->Clone());
text_to_text_range = text_to_text_range.AsForwardRange();
EXPECT_EQ(*text_position1, *text_to_text_range.anchor());
EXPECT_EQ(*text_position2, *text_to_text_range.focus());
}
TEST_F(AXRangeTest, IsCollapsed) {
TestPositionRange null_range(AXNodePosition::CreateNullPosition(),
AXNodePosition::CreateNullPosition());
null_range = null_range.AsForwardRange();
EXPECT_FALSE(null_range.IsCollapsed());
TestPositionInstance tree_position1 = AXNodePosition::CreateTreePosition(
tree_->data().tree_id, text_field_.id, 0 /* child_index */);
// Since there are no children in inline_box1_, the following is essentially
// an "after text" position which should not compare as equivalent to the
// above tree position which is a "before text" position inside the text
// field.
TestPositionInstance tree_position2 = AXNodePosition::CreateTreePosition(
tree_->data().tree_id, inline_box1_.id, 0 /* child_index */);
TestPositionInstance text_position1 = AXNodePosition::CreateTextPosition(
tree_->data().tree_id, static_text1_.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
TestPositionInstance text_position2 = AXNodePosition::CreateTextPosition(
tree_->data().tree_id, inline_box1_.id, 0 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
TestPositionInstance text_position3 = AXNodePosition::CreateTextPosition(
tree_->data().tree_id, inline_box2_.id, 1 /* text_offset */,
ax::mojom::TextAffinity::kDownstream);
TestPositionRange tree_to_null_range(tree_position1->Clone(),
AXNodePosition::CreateNullPosition());
EXPECT_TRUE(tree_to_null_range.IsNull());
EXPECT_FALSE(tree_to_null_range.IsCollapsed());
TestPositionRange null_to_text_range(AXNodePosition::CreateNullPosition(),
text_position1->Clone());
EXPECT_TRUE(null_to_text_range.IsNull());
EXPECT_FALSE(null_to_text_range.IsCollapsed());
TestPositionRange tree_to_tree_range(tree_position2->Clone(),
tree_position1->Clone());
EXPECT_TRUE(tree_to_tree_range.IsCollapsed());
// A tree and a text position that essentially point to the same text offset
// are equivalent, even if they are anchored to a different node.
TestPositionRange tree_to_text_range(tree_position1->Clone(),
text_position1->Clone());
EXPECT_TRUE(tree_to_text_range.IsCollapsed());
// The following positions are not equivalent since tree_position2 is an
// "after text" position.
tree_to_text_range =
TestPositionRange(tree_position2->Clone(), text_position2->Clone());
EXPECT_FALSE(tree_to_text_range.IsCollapsed());
TestPositionRange text_to_text_range(text_position1->Clone(),
text_position1->Clone());
EXPECT_TRUE(text_to_text_range.IsCollapsed());
// Two text positions that essentially point to the same text offset are
// equivalent, even if they are anchored to a different node.
text_to_text_range =
TestPositionRange(text_position1->Clone(), text_position2->Clone());
EXPECT_TRUE(text_to_text_range.IsCollapsed());
text_to_text_range =
TestPositionRange(text_position1->Clone(), text_position3->Clone());
EXPECT_FALSE(text_to_text_range.IsCollapsed());
}
TEST_F(AXRangeTest, BeginAndEndIterators) { TEST_F(AXRangeTest, BeginAndEndIterators) {
TestPositionInstance null_position = AXNodePosition::CreateNullPosition(); TestPositionInstance null_position = AXNodePosition::CreateNullPosition();
TestPositionInstance test_position1 = AXNodePosition::CreateTextPosition( TestPositionInstance test_position1 = AXNodePosition::CreateTextPosition(
......
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