Commit a938b4d7 authored by Martin Robinson's avatar Martin Robinson Committed by Commit Bot

Fire AtkText text-selection-change signals on common ancestor

Instead of firing the signal on the node which contains the selection
focus, fire it on the first common ancestor of the anchor and focus
nodes. This means that if the AT does a tree dive to get the full
selection it won't first have to climb to find the full selection
extents.

Also, along with this new way of firing events we also take into account
the anchor and focus nodes when deciding whether or not to avoid firing
duplicate caret-changed events.

Bug: 1003359
Change-Id: Ia4ac03be3e0548e9b435d6e21512c3c22459e6ea
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1816474Reviewed-by: default avatarJoanmarie Diggs <jdiggs@igalia.com>
Commit-Queue: Martin Robinson <mrobinson@igalia.com>
Cr-Commit-Position: refs/heads/master@{#701039}
parent eb9ba3fa
...@@ -824,6 +824,90 @@ IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest, ...@@ -824,6 +824,90 @@ IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest,
g_object_unref(div); g_object_unref(div);
} }
IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest,
TestTextSelectionAcrossElements) {
LoadInitialAccessibilityTreeFromHtml(std::string(
R"HTML(<!DOCTYPE html>
<html>
<body>
<div id="parent" contenteditable="true">
<div id="child1">Child 1</div>
<div id="child2">Child 2</div>
</div>
</body>
</html>)HTML"));
AtkObject* document = GetRendererAccessible();
EXPECT_EQ(1, atk_object_get_n_accessible_children(document));
AtkText* parent = ATK_TEXT(atk_object_ref_accessible_child(document, 0));
EXPECT_EQ(2, atk_object_get_n_accessible_children(ATK_OBJECT(parent)));
AtkText* child1 =
ATK_TEXT(atk_object_ref_accessible_child(ATK_OBJECT(parent), 0));
AtkText* child2 =
ATK_TEXT(atk_object_ref_accessible_child(ATK_OBJECT(parent), 1));
EXPECT_NE(nullptr, child1);
EXPECT_NE(nullptr, child2);
auto callback = G_CALLBACK(+[](AtkText*, bool* flag) { *flag = true; });
bool saw_selection_change_in_parent = false;
g_signal_connect(parent, "text-selection-changed", callback,
&saw_selection_change_in_parent);
bool saw_selection_change_in_child1 = false;
g_signal_connect(child1, "text-selection-changed", callback,
&saw_selection_change_in_child1);
bool saw_selection_change_in_child2 = false;
g_signal_connect(child2, "text-selection-changed", callback,
&saw_selection_change_in_child2);
AccessibilityNotificationWaiter selection_waiter(
shell()->web_contents(), ui::kAXModeComplete,
ax::mojom::Event::kTextSelectionChanged);
ExecuteScript(
base::UTF8ToUTF16("let parent = document.getElementById('parent');"
"let child1 = document.getElementById('child1');"
"let child2 = document.getElementById('child2');"
"let range = document.createRange();"
"range.setStart(child1.firstChild, 3);"
"range.setEnd(child1.firstChild, 5);"
"parent.focus();"
"document.getSelection().removeAllRanges();"
"document.getSelection().addRange(range);"));
selection_waiter.WaitForNotification();
EXPECT_FALSE(saw_selection_change_in_parent);
EXPECT_TRUE(saw_selection_change_in_child1);
EXPECT_FALSE(saw_selection_change_in_child2);
saw_selection_change_in_parent = false;
saw_selection_change_in_child1 = false;
saw_selection_change_in_child2 = false;
EXPECT_TRUE(atk_text_remove_selection(parent, 0));
selection_waiter.WaitForNotification();
EXPECT_FALSE(saw_selection_change_in_parent);
EXPECT_TRUE(saw_selection_change_in_child1);
EXPECT_FALSE(saw_selection_change_in_child2);
saw_selection_change_in_parent = false;
saw_selection_change_in_child1 = false;
saw_selection_change_in_child2 = false;
ExecuteScript(
base::UTF8ToUTF16("let range2 = document.createRange();"
"range2.setStart(child1.firstChild, 0);"
"range2.setEnd(child2.firstChild, 3);"
"parent.focus();"
"document.getSelection().removeAllRanges();"
"document.getSelection().addRange(range2);"));
selection_waiter.WaitForNotification();
EXPECT_TRUE(saw_selection_change_in_parent);
EXPECT_FALSE(saw_selection_change_in_child1);
EXPECT_FALSE(saw_selection_change_in_child2);
}
// TODO(crbug.com/981913): This flakes on linux. // TODO(crbug.com/981913): This flakes on linux.
IN_PROC_BROWSER_TEST_F( IN_PROC_BROWSER_TEST_F(
AccessibilityAuraLinuxBrowserTest, AccessibilityAuraLinuxBrowserTest,
...@@ -982,6 +1066,8 @@ IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest, ...@@ -982,6 +1066,8 @@ IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest,
saw_caret_move_in_div = false; saw_caret_move_in_div = false;
atk_text_set_caret_offset(anonymous_block, 3); atk_text_set_caret_offset(anonymous_block, 3);
selection_waiter.WaitForNotification();
EXPECT_FALSE(saw_caret_move_in_div); EXPECT_FALSE(saw_caret_move_in_div);
EXPECT_FALSE(saw_caret_move_in_text); EXPECT_FALSE(saw_caret_move_in_text);
EXPECT_FALSE(saw_caret_move_in_anonymous_block); EXPECT_FALSE(saw_caret_move_in_anonymous_block);
......
...@@ -25,6 +25,9 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAuraLinux ...@@ -25,6 +25,9 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAuraLinux
~BrowserAccessibilityManagerAuraLinux() override; ~BrowserAccessibilityManagerAuraLinux() override;
static ui::AXTreeUpdate GetEmptyDocument(); static ui::AXTreeUpdate GetEmptyDocument();
static BrowserAccessibility* FindCommonAncestor(
BrowserAccessibility* object1,
BrowserAccessibility* object2);
// Implementation of BrowserAccessibilityManager methods. // Implementation of BrowserAccessibilityManager methods.
void FireFocusEvent(BrowserAccessibility* node) override; void FireFocusEvent(BrowserAccessibility* node) override;
......
...@@ -191,21 +191,6 @@ AtkObject* FindAtkObjectToplevelParentDocument(AtkObject* atk_object) { ...@@ -191,21 +191,6 @@ AtkObject* FindAtkObjectToplevelParentDocument(AtkObject* atk_object) {
return toplevel_document; return toplevel_document;
} }
bool EmitsAtkTextEvents(AtkObject* atk_object) {
// If this node is not a static text node, it supports the full AtkText
// interface.
AtkRole role = atk_object_get_role(atk_object);
if (role != ATK_ROLE_TEXT)
return true;
// If this node is not a static text leaf node, it supports the full AtkText
// interface.
if (atk_object_get_n_accessible_children(atk_object))
return true;
return false;
}
bool IsFrameAncestorOfAtkObject(AtkObject* frame, AtkObject* atk_object) { bool IsFrameAncestorOfAtkObject(AtkObject* frame, AtkObject* atk_object) {
AtkObject* current_frame = FindAtkObjectParentFrame(atk_object); AtkObject* current_frame = FindAtkObjectParentFrame(atk_object);
while (current_frame) { while (current_frame) {
...@@ -353,10 +338,6 @@ AtkObject* GetActiveDescendantOfCurrentFocused() { ...@@ -353,10 +338,6 @@ AtkObject* GetActiveDescendantOfCurrentFocused() {
return nullptr; return nullptr;
} }
bool SelectionOffsetsIndicateSelection(const std::pair<int, int>& offsets) {
return offsets.first >= 0 && offsets.second >= 0 &&
offsets.first != offsets.second;
}
void PrependTextAttributeToSet(const AtkTextAttribute attribute, void PrependTextAttributeToSet(const AtkTextAttribute attribute,
const std::string& value, const std::string& value,
...@@ -2286,7 +2267,7 @@ void AXPlatformNodeAuraLinux::StaticInitialize() { ...@@ -2286,7 +2267,7 @@ void AXPlatformNodeAuraLinux::StaticInitialize() {
AtkUtilAuraLinux::GetInstance()->InitializeAsync(); AtkUtilAuraLinux::GetInstance()->InitializeAsync();
} }
AtkRole AXPlatformNodeAuraLinux::GetAtkRole() { AtkRole AXPlatformNodeAuraLinux::GetAtkRole() const {
switch (GetData().role) { switch (GetData().role) {
case ax::mojom::Role::kAlert: case ax::mojom::Role::kAlert:
return ATK_ROLE_ALERT; return ATK_ROLE_ALERT;
...@@ -3224,35 +3205,139 @@ bool AXPlatformNodeAuraLinux::SelectionAndFocusAreTheSame() { ...@@ -3224,35 +3205,139 @@ bool AXPlatformNodeAuraLinux::SelectionAndFocusAreTheSame() {
return false; return false;
} }
void AXPlatformNodeAuraLinux::OnTextSelectionChanged() { bool AXPlatformNodeAuraLinux::EmitsAtkTextEvents() const {
AtkObject* atk_object = GetOrCreateAtkObject(); // If this node is not a static text node, it supports the full AtkText
if (!EmitsAtkTextEvents(atk_object)) { // interface.
if (GetAtkRole() != ATK_ROLE_TEXT)
return true;
// If this node has children it is not a static text leaf node and supports
// the full AtkText interface.
if (GetChildCount())
return true;
return false;
}
void AXPlatformNodeAuraLinux::GetFullSelection(int32_t* anchor_node_id,
int* anchor_offset,
int32_t* focus_node_id,
int* focus_offset) {
DCHECK(anchor_node_id);
DCHECK(anchor_offset);
DCHECK(focus_node_id);
DCHECK(focus_offset);
if (IsPlainTextField() &&
GetIntAttribute(ax::mojom::IntAttribute::kTextSelStart, anchor_offset) &&
GetIntAttribute(ax::mojom::IntAttribute::kTextSelEnd, focus_offset)) {
int32_t node_id = GetData().id != -1 ? GetData().id : GetUniqueId();
*anchor_node_id = *focus_node_id = node_id;
return;
}
ui::AXTree::Selection selection = GetDelegate()->GetUnignoredSelection();
*anchor_node_id = selection.anchor_object_id;
*anchor_offset = selection.anchor_offset;
*focus_node_id = selection.focus_object_id;
*focus_offset = selection.focus_offset;
}
AXPlatformNodeAuraLinux& AXPlatformNodeAuraLinux::FindEditableRootOrDocument() {
if (GetAtkRole() == ATK_ROLE_DOCUMENT_WEB)
return *this;
if (GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot))
return *this;
if (auto* parent = AtkObjectToAXPlatformNodeAuraLinux(GetParent()))
return parent->FindEditableRootOrDocument();
return *this;
}
AXPlatformNodeAuraLinux* AXPlatformNodeAuraLinux::FindCommonAncestor(
AXPlatformNodeAuraLinux* other) {
if (this == other || other->IsDescendantOf(this))
return this;
if (auto* parent = AtkObjectToAXPlatformNodeAuraLinux(GetParent()))
return parent->FindCommonAncestor(other);
return nullptr;
}
void AXPlatformNodeAuraLinux::UpdateSelectionInformation(int32_t anchor_node_id,
int anchor_offset,
int32_t focus_node_id,
int focus_offset) {
had_nonzero_width_selection =
focus_node_id != anchor_node_id || focus_offset != anchor_offset;
current_caret_ = std::make_pair(focus_node_id, focus_offset);
}
void AXPlatformNodeAuraLinux::EmitSelectionChangedSignal(bool had_selection) {
if (!EmitsAtkTextEvents()) {
if (auto* parent = AtkObjectToAXPlatformNodeAuraLinux(GetParent())) if (auto* parent = AtkObjectToAXPlatformNodeAuraLinux(GetParent()))
parent->OnTextSelectionChanged(); parent->EmitSelectionChangedSignal(had_selection);
return; return;
} }
AtkObject* atk_object = GetOrCreateAtkObject();
DCHECK(ATK_IS_TEXT(atk_object)); DCHECK(ATK_IS_TEXT(atk_object));
std::pair<int, int> selection;
std::pair<int, int> new_selection; GetSelectionOffsets(&selection.first, &selection.second);
GetSelectionOffsets(&new_selection.first, &new_selection.second);
std::pair<int, int> old_selection = text_selection_;
text_selection_ = new_selection;
// ATK does not consider a collapsed selection a selection, so // ATK does not consider a collapsed selection a selection, so
// when the collapsed selection changes (caret movement), we should // when the collapsed selection changes (caret movement), we should
// avoid sending text-selection-changed events. // avoid sending text-selection-changed events.
bool has_selection = SelectionOffsetsIndicateSelection(new_selection); if (HasSelection() || had_selection)
bool had_selection = SelectionOffsetsIndicateSelection(old_selection);
if (has_selection != had_selection ||
(has_selection && new_selection != old_selection)) {
g_signal_emit_by_name(atk_object, "text-selection-changed"); g_signal_emit_by_name(atk_object, "text-selection-changed");
}
void AXPlatformNodeAuraLinux::EmitCaretChangedSignal() {
if (!EmitsAtkTextEvents()) {
if (auto* parent = AtkObjectToAXPlatformNodeAuraLinux(GetParent()))
parent->EmitCaretChangedSignal();
return;
} }
if (HasCaret() && new_selection.second != old_selection.second) { DCHECK(HasCaret());
g_signal_emit_by_name(atk_object, "text-caret-moved", std::pair<int, int> selection;
UTF16ToUnicodeOffsetInText(new_selection.second)); GetSelectionOffsets(&selection.first, &selection.second);
AtkObject* atk_object = GetOrCreateAtkObject();
DCHECK(ATK_IS_TEXT(atk_object));
g_signal_emit_by_name(atk_object, "text-caret-moved",
UTF16ToUnicodeOffsetInText(selection.second));
}
void AXPlatformNodeAuraLinux::OnTextSelectionChanged() {
int32_t anchor_node_id, focus_node_id;
int anchor_offset, focus_offset;
GetFullSelection(&anchor_node_id, &anchor_offset, &focus_node_id,
&focus_offset);
auto* anchor_node = static_cast<AXPlatformNodeAuraLinux*>(
GetDelegate()->GetFromNodeID(anchor_node_id));
auto* focus_node = static_cast<AXPlatformNodeAuraLinux*>(
GetDelegate()->GetFromNodeID(focus_node_id));
if (!anchor_node || !focus_node)
return;
AXPlatformNodeAuraLinux& editable_root = FindEditableRootOrDocument();
AXPlatformNodeAuraLinux* common_ancestor =
focus_node->FindCommonAncestor(anchor_node);
if (common_ancestor) {
common_ancestor->EmitSelectionChangedSignal(
editable_root.HadNonZeroWidthSelection());
} }
// It's possible for the selection to change and for the caret to stay in
// place. This might happen if the selection is totally reset with a
// different anchor node, but the same focus node. We should avoid sending a
// caret changed signal in that case.
std::pair<int32_t, int> prev_caret = editable_root.GetCurrentCaret();
if (prev_caret.first != focus_node_id || prev_caret.second != focus_offset)
focus_node->EmitCaretChangedSignal();
editable_root.UpdateSelectionInformation(anchor_node_id, anchor_offset,
focus_node_id, focus_offset);
} }
bool AXPlatformNodeAuraLinux::SupportsSelectionWithAtkSelection() { bool AXPlatformNodeAuraLinux::SupportsSelectionWithAtkSelection() {
...@@ -3485,7 +3570,7 @@ void AXPlatformNodeAuraLinux::UpdateHypertext() { ...@@ -3485,7 +3570,7 @@ void AXPlatformNodeAuraLinux::UpdateHypertext() {
AtkObject* atk_object = GetOrCreateAtkObject(); AtkObject* atk_object = GetOrCreateAtkObject();
DCHECK(ATK_IS_TEXT(atk_object)); DCHECK(ATK_IS_TEXT(atk_object));
if (!EmitsAtkTextEvents(atk_object)) if (!EmitsAtkTextEvents())
return; return;
if (old_len > 0) { if (old_len > 0) {
...@@ -3902,7 +3987,6 @@ bool AXPlatformNodeAuraLinux::SetCaretOffset(int offset) { ...@@ -3902,7 +3987,6 @@ bool AXPlatformNodeAuraLinux::SetCaretOffset(int offset) {
if (!SetHypertextSelection(offset, offset)) if (!SetHypertextSelection(offset, offset))
return false; return false;
OnTextSelectionChanged();
return true; return true;
} }
...@@ -3932,14 +4016,14 @@ bool AXPlatformNodeAuraLinux::SetTextSelectionForAtkText(int start_offset, ...@@ -3932,14 +4016,14 @@ bool AXPlatformNodeAuraLinux::SetTextSelectionForAtkText(int start_offset,
if (!SetHypertextSelection(start_offset, end_offset)) if (!SetHypertextSelection(start_offset, end_offset))
return false; return false;
OnTextSelectionChanged();
return true; return true;
} }
bool AXPlatformNodeAuraLinux::HasSelection() { bool AXPlatformNodeAuraLinux::HasSelection() {
std::pair<int, int> selection; std::pair<int, int> selection;
GetSelectionOffsets(&selection.first, &selection.second); GetSelectionOffsets(&selection.first, &selection.second);
return SelectionOffsetsIndicateSelection(selection); return selection.first >= 0 && selection.second >= 0 &&
selection.first != selection.second;
} }
void AXPlatformNodeAuraLinux::GetSelectionExtents(int* start_offset, void AXPlatformNodeAuraLinux::GetSelectionExtents(int* start_offset,
...@@ -4169,7 +4253,7 @@ void AXPlatformNodeAuraLinux::ActivateFindInPageResult(int start_offset, ...@@ -4169,7 +4253,7 @@ void AXPlatformNodeAuraLinux::ActivateFindInPageResult(int start_offset,
AtkObject* atk_object = GetOrCreateAtkObject(); AtkObject* atk_object = GetOrCreateAtkObject();
DCHECK(ATK_IS_TEXT(atk_object)); DCHECK(ATK_IS_TEXT(atk_object));
if (!EmitsAtkTextEvents(atk_object)) { if (!EmitsAtkTextEvents()) {
ActivateFindInPageInParent(start_offset, end_offset); ActivateFindInPageInParent(start_offset, end_offset);
return; return;
} }
......
...@@ -106,7 +106,7 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase { ...@@ -106,7 +106,7 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase {
void EnsureAtkObjectIsValid(); void EnsureAtkObjectIsValid();
void Destroy() override; void Destroy() override;
AtkRole GetAtkRole(); AtkRole GetAtkRole() const;
void GetAtkState(AtkStateSet* state_set); void GetAtkState(AtkStateSet* state_set);
AtkRelationSet* GetAtkRelations(); AtkRelationSet* GetAtkRelations();
void GetExtents(gint* x, gint* y, gint* width, gint* height, void GetExtents(gint* x, gint* y, gint* width, gint* height,
...@@ -197,6 +197,7 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase { ...@@ -197,6 +197,7 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase {
bool SetCaretOffset(int offset); bool SetCaretOffset(int offset);
bool SetTextSelectionForAtkText(int start_offset, int end_offset); bool SetTextSelectionForAtkText(int start_offset, int end_offset);
bool HasSelection(); bool HasSelection();
void GetSelectionExtents(int* start_offset, int* end_offset); void GetSelectionExtents(int* start_offset, int* end_offset);
gchar* GetSelectionWithText(int* start_offset, int* end_offset); gchar* GetSelectionWithText(int* start_offset, int* end_offset);
...@@ -289,6 +290,41 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase { ...@@ -289,6 +290,41 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase {
// Find the first child which is a document containing web content. // Find the first child which is a document containing web content.
AtkObject* FindFirstWebContentDocument(); AtkObject* FindFirstWebContentDocument();
// If a selection that intersects this node get the full selection
// including start and end node ids.
void GetFullSelection(int32_t* anchor_node_id,
int* anchor_offset,
int32_t* focus_node_id,
int* focus_offset);
// Returns true if this node's AtkObject is suitable for emitting AtkText
// signals. ATs don't expect static text objects to emit AtkText signals.
bool EmitsAtkTextEvents() const;
// Find the first ancestor which is an editable root or a document. This node
// will be one which contains a single selection.
AXPlatformNodeAuraLinux& FindEditableRootOrDocument();
// Find the first common ancestor between this node and a given node.
AXPlatformNodeAuraLinux* FindCommonAncestor(AXPlatformNodeAuraLinux* other);
// Update the selection information stored in this node. This should be
// called on the editable root, the root node of the accessibility tree, or
// the document (ie the node returned by FindEditableRootOrDocument()).
void UpdateSelectionInformation(int32_t anchor_node_id,
int anchor_offset,
int32_t focus_node_id,
int focus_offset);
// Emit a GObject signal indicating a selection change.
void EmitSelectionChangedSignal(bool had_selection);
// Emit a GObject signal indicating that the caret has moved.
void EmitCaretChangedSignal();
bool HadNonZeroWidthSelection() const { return had_nonzero_width_selection; }
std::pair<int32_t, int> GetCurrentCaret() const { return current_caret_; }
// If the given argument can be found as a child of this node, return its // If the given argument can be found as a child of this node, return its
// hypertext extents, otherwise return base::nullopt; // hypertext extents, otherwise return base::nullopt;
base::Optional<std::pair<int, int>> GetHypertextExtentsOfChild( base::Optional<std::pair<int, int>> GetHypertextExtentsOfChild(
...@@ -312,10 +348,17 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase { ...@@ -312,10 +348,17 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase {
// minimized the last time it's visibility changed. // minimized the last time it's visibility changed.
bool was_minimized_ = false; bool was_minimized_ = false;
// The previously observed text selection for this node. We store // Information about the selection meant to be stored on the return value of
// this in order to avoid sending duplicate text-selection-changed // FindEditableRootOrDocument().
// and text-caret-moved events. //
std::pair<int, int> text_selection_ = std::make_pair(-1, -1); // Whether or not we previously had a selection where the anchor and focus
// were not equal. This is what ATK consider a "selection."
bool had_nonzero_width_selection = false;
// Information about the current caret location (a node id and an offset).
// This is used to track when the caret actually moves during a selection
// change.
std::pair<int32_t, int> current_caret_ = {-1, -1};
// A map which converts between an offset in the node's hypertext and the // A map which converts between an offset in the node's hypertext and the
// ATK text attributes at that offset. // ATK text attributes at that offset.
......
...@@ -1399,34 +1399,52 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextCaretMoved) { ...@@ -1399,34 +1399,52 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextCaretMoved) {
&caret_position_from_event); &caret_position_from_event);
atk_text_set_caret_offset(atk_text, 4); atk_text_set_caret_offset(atk_text, 4);
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
ASSERT_EQ(atk_text_get_caret_offset(atk_text), 4); ASSERT_EQ(atk_text_get_caret_offset(atk_text), 4);
ASSERT_EQ(caret_position_from_event, 4); ASSERT_EQ(caret_position_from_event, 4);
// Setting the same position should not trigger another event. // Setting the same position should not trigger another event.
caret_position_from_event = -1; caret_position_from_event = -1;
atk_text_set_caret_offset(atk_text, 4); atk_text_set_caret_offset(atk_text, 4);
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
ASSERT_EQ(atk_text_get_caret_offset(atk_text), 4); ASSERT_EQ(atk_text_get_caret_offset(atk_text), 4);
ASSERT_EQ(caret_position_from_event, -1); ASSERT_EQ(caret_position_from_event, -1);
int character_count = atk_text_get_character_count(atk_text); int character_count = atk_text_get_character_count(atk_text);
atk_text_set_caret_offset(atk_text, -1); atk_text_set_caret_offset(atk_text, -1);
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
ASSERT_EQ(atk_text_get_caret_offset(atk_text), character_count); ASSERT_EQ(atk_text_get_caret_offset(atk_text), character_count);
ASSERT_EQ(caret_position_from_event, character_count); ASSERT_EQ(caret_position_from_event, character_count);
atk_text_set_caret_offset(atk_text, 0); // Reset position. atk_text_set_caret_offset(atk_text, 0); // Reset position.
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
caret_position_from_event = -1; caret_position_from_event = -1;
atk_text_set_caret_offset(atk_text, -1000); atk_text_set_caret_offset(atk_text, -1000);
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
ASSERT_EQ(atk_text_get_caret_offset(atk_text), character_count); ASSERT_EQ(atk_text_get_caret_offset(atk_text), character_count);
ASSERT_EQ(caret_position_from_event, character_count); ASSERT_EQ(caret_position_from_event, character_count);
atk_text_set_caret_offset(atk_text, 0); // Reset position. atk_text_set_caret_offset(atk_text, 0); // Reset position.
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
caret_position_from_event = -1; caret_position_from_event = -1;
atk_text_set_caret_offset(atk_text, 1000); atk_text_set_caret_offset(atk_text, 1000);
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
ASSERT_EQ(atk_text_get_caret_offset(atk_text), character_count); ASSERT_EQ(atk_text_get_caret_offset(atk_text), character_count);
ASSERT_EQ(caret_position_from_event, character_count); ASSERT_EQ(caret_position_from_event, character_count);
caret_position_from_event = -1; caret_position_from_event = -1;
atk_text_set_caret_offset(atk_text, character_count - 1); atk_text_set_caret_offset(atk_text, character_count - 1);
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
ASSERT_EQ(atk_text_get_caret_offset(atk_text), character_count - 1); ASSERT_EQ(atk_text_get_caret_offset(atk_text), character_count - 1);
ASSERT_EQ(caret_position_from_event, character_count - 1); ASSERT_EQ(caret_position_from_event, character_count - 1);
...@@ -2096,6 +2114,8 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextTextFieldSetSelection) { ...@@ -2096,6 +2114,8 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextTextFieldSetSelection) {
int selection_start, selection_end; int selection_start, selection_end;
EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 0, 1)); EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 0, 1));
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
EXPECT_TRUE(saw_selection_change); EXPECT_TRUE(saw_selection_change);
g_free(atk_text_get_selection(atk_text, 0, &selection_start, &selection_end)); g_free(atk_text_get_selection(atk_text, 0, &selection_start, &selection_end));
EXPECT_EQ(selection_start, 0); EXPECT_EQ(selection_start, 0);
...@@ -2103,25 +2123,22 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextTextFieldSetSelection) { ...@@ -2103,25 +2123,22 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextTextFieldSetSelection) {
// Reset position. // Reset position.
EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 0, 0)); EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 0, 0));
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
saw_selection_change = false; saw_selection_change = false;
EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 1, 0)); EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 1, 0));
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
EXPECT_TRUE(saw_selection_change); EXPECT_TRUE(saw_selection_change);
g_free(atk_text_get_selection(atk_text, 0, &selection_start, &selection_end)); g_free(atk_text_get_selection(atk_text, 0, &selection_start, &selection_end));
EXPECT_EQ(selection_start, 0); EXPECT_EQ(selection_start, 0);
EXPECT_EQ(selection_end, 1); EXPECT_EQ(selection_end, 1);
// Setting the selection to the same location should not trigger
// another event.
saw_selection_change = false;
EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 1, 0));
EXPECT_FALSE(saw_selection_change);
g_free(atk_text_get_selection(atk_text, 0, &selection_start, &selection_end));
EXPECT_EQ(selection_start, 0);
EXPECT_EQ(selection_end, 1);
saw_selection_change = false; saw_selection_change = false;
EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 2, 4)); EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 2, 4));
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
EXPECT_TRUE(saw_selection_change); EXPECT_TRUE(saw_selection_change);
g_free(atk_text_get_selection(atk_text, 0, &selection_start, &selection_end)); g_free(atk_text_get_selection(atk_text, 0, &selection_start, &selection_end));
EXPECT_EQ(selection_start, 2); EXPECT_EQ(selection_start, 2);
...@@ -2136,11 +2153,12 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextTextFieldSetSelection) { ...@@ -2136,11 +2153,12 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextTextFieldSetSelection) {
saw_selection_change = false; saw_selection_change = false;
EXPECT_FALSE(atk_text_set_selection(atk_text, 0, 0, 50)); EXPECT_FALSE(atk_text_set_selection(atk_text, 0, 0, 50));
EXPECT_FALSE(saw_selection_change);
saw_selection_change = false; saw_selection_change = false;
int n_characters = atk_text_get_character_count(atk_text); int n_characters = atk_text_get_character_count(atk_text);
EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 0, -1)); EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 0, -1));
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
EXPECT_TRUE(saw_selection_change); EXPECT_TRUE(saw_selection_change);
g_free(atk_text_get_selection(atk_text, 0, &selection_start, &selection_end)); g_free(atk_text_get_selection(atk_text, 0, &selection_start, &selection_end));
EXPECT_EQ(selection_start, 0); EXPECT_EQ(selection_start, 0);
...@@ -2148,6 +2166,8 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextTextFieldSetSelection) { ...@@ -2148,6 +2166,8 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextTextFieldSetSelection) {
saw_selection_change = false; saw_selection_change = false;
EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 0, 1)); EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 0, 1));
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
EXPECT_EQ(1, atk_text_get_n_selections(atk_text)); EXPECT_EQ(1, atk_text_get_n_selections(atk_text));
EXPECT_TRUE(atk_text_remove_selection(atk_text, 0)); EXPECT_TRUE(atk_text_remove_selection(atk_text, 0));
EXPECT_TRUE(saw_selection_change); EXPECT_TRUE(saw_selection_change);
...@@ -2155,9 +2175,13 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextTextFieldSetSelection) { ...@@ -2155,9 +2175,13 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextTextFieldSetSelection) {
// Reset position. // Reset position.
EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 0, 0)); EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 0, 0));
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
saw_selection_change = false; saw_selection_change = false;
EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 0, 1)); EXPECT_TRUE(atk_text_set_selection(atk_text, 0, 0, 1));
GetRootPlatformNode()->NotifyAccessibilityEvent(
ax::mojom::Event::kTextSelectionChanged);
EXPECT_EQ(1, atk_text_get_n_selections(atk_text)); EXPECT_EQ(1, atk_text_get_n_selections(atk_text));
EXPECT_FALSE(atk_text_remove_selection(atk_text, 1)); EXPECT_FALSE(atk_text_remove_selection(atk_text, 1));
EXPECT_TRUE(saw_selection_change); EXPECT_TRUE(saw_selection_change);
......
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