Commit 113628e8 authored by Martin Robinson's avatar Martin Robinson Committed by Commit Bot

Assign ATK active state to dialogs when children have focus

When children of dialogs have focus, explicitly assign the active state
to the dialog. This will allow Orca to properly announce dialogs. We
need to do something special here, because in Chromium dialogs do not
have their own toplevel windows.

Bug: 1023208
Change-Id: I02a0a89781d88477238728b8b516b7a2606d6af4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1917224Reviewed-by: default avatarJoanmarie Diggs <jdiggs@igalia.com>
Commit-Queue: Martin Robinson <mrobinson@igalia.com>
Cr-Commit-Position: refs/heads/master@{#716019}
parent f716a004
...@@ -133,6 +133,8 @@ AXPlatformNodeAuraLinux* g_current_selected = nullptr; ...@@ -133,6 +133,8 @@ AXPlatformNodeAuraLinux* g_current_selected = nullptr;
// null if if the AtkObject is destroyed. // null if if the AtkObject is destroyed.
AtkObject* g_active_top_level_frame = nullptr; AtkObject* g_active_top_level_frame = nullptr;
AtkObject* g_active_views_dialog = nullptr;
#if defined(ATK_216) #if defined(ATK_216)
constexpr AtkRole kStaticRole = ATK_ROLE_STATIC; constexpr AtkRole kStaticRole = ATK_ROLE_STATIC;
constexpr AtkRole kSubscriptRole = ATK_ROLE_SUBSCRIPT; constexpr AtkRole kSubscriptRole = ATK_ROLE_SUBSCRIPT;
...@@ -2676,6 +2678,9 @@ void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) { ...@@ -2676,6 +2678,9 @@ void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) {
FindAtkObjectParentFrame(GetActiveMenus().back()) == atk_object_) FindAtkObjectParentFrame(GetActiveMenus().back()) == atk_object_)
atk_state_set_add_state(atk_state_set, ATK_STATE_ACTIVE); atk_state_set_add_state(atk_state_set, ATK_STATE_ACTIVE);
if (atk_object_ && atk_object_ == g_active_views_dialog)
atk_state_set_add_state(atk_state_set, ATK_STATE_ACTIVE);
bool is_minimized = delegate_->IsMinimized(); bool is_minimized = delegate_->IsMinimized();
if (is_minimized && data.role == ax::mojom::Role::kWindow) if (is_minimized && data.role == ax::mojom::Role::kWindow)
atk_state_set_add_state(atk_state_set, ATK_STATE_ICONIFIED); atk_state_set_add_state(atk_state_set, ATK_STATE_ICONIFIED);
...@@ -3164,6 +3169,31 @@ void AXPlatformNodeAuraLinux::OnScrolledToAnchor() { ...@@ -3164,6 +3169,31 @@ void AXPlatformNodeAuraLinux::OnScrolledToAnchor() {
g_signal_emit_by_name(atk_object, "text-caret-moved", 0); g_signal_emit_by_name(atk_object, "text-caret-moved", 0);
} }
void AXPlatformNodeAuraLinux::SetActiveViewsDialog() {
AtkObject* old_views_dialog = g_active_views_dialog;
AtkObject* new_views_dialog = nullptr;
AtkObject* parent = GetOrCreateAtkObject();
if (!GetDelegate()->IsWebContent()) {
while (parent) {
if (atk_object_get_role(parent) == ATK_ROLE_DIALOG) {
new_views_dialog = parent;
break;
}
parent = atk_object_get_parent(parent);
}
}
if (old_views_dialog == new_views_dialog)
return;
SetWeakGPtrToAtkObject(&g_active_views_dialog, new_views_dialog);
if (old_views_dialog)
atk_object_notify_state_change(old_views_dialog, ATK_STATE_ACTIVE, FALSE);
if (new_views_dialog)
atk_object_notify_state_change(new_views_dialog, ATK_STATE_ACTIVE, TRUE);
}
void AXPlatformNodeAuraLinux::OnFocused() { void AXPlatformNodeAuraLinux::OnFocused() {
AtkObject* atk_object = GetOrCreateAtkObject(); AtkObject* atk_object = GetOrCreateAtkObject();
...@@ -3175,6 +3205,8 @@ void AXPlatformNodeAuraLinux::OnFocused() { ...@@ -3175,6 +3205,8 @@ void AXPlatformNodeAuraLinux::OnFocused() {
if (atk_object == g_current_focused) if (atk_object == g_current_focused)
return; return;
SetActiveViewsDialog();
if (g_current_focused) { if (g_current_focused) {
g_signal_emit_by_name(g_current_focused, "focus-event", false); g_signal_emit_by_name(g_current_focused, "focus-event", false);
atk_object_notify_state_change(ATK_OBJECT(g_current_focused), atk_object_notify_state_change(ATK_OBJECT(g_current_focused),
......
...@@ -171,6 +171,7 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase { ...@@ -171,6 +171,7 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase {
void ResendFocusSignalsForCurrentlyFocusedNode(); void ResendFocusSignalsForCurrentlyFocusedNode();
bool SupportsSelectionWithAtkSelection(); bool SupportsSelectionWithAtkSelection();
bool SelectionAndFocusAreTheSame(); bool SelectionAndFocusAreTheSame();
void SetActiveViewsDialog();
// AXPlatformNode overrides. // AXPlatformNode overrides.
// This has a side effect of creating the AtkObject if one does not already // This has a side effect of creating the AtkObject if one does not already
......
...@@ -178,6 +178,14 @@ static void TestAtkObjectBoolAttribute( ...@@ -178,6 +178,14 @@ static void TestAtkObjectBoolAttribute(
EnsureAtkObjectHasAttributeWithValue(atk_object, attribute_name, "false"); EnsureAtkObjectHasAttributeWithValue(atk_object, attribute_name, "false");
} }
static bool AtkObjectHasState(AtkObject* atk_object, AtkStateType state) {
AtkStateSet* state_set = atk_object_ref_state_set(atk_object);
EXPECT_TRUE(ATK_IS_STATE_SET(state_set));
bool in_state_set = atk_state_set_contains_state(state_set, state);
g_object_unref(state_set);
return in_state_set;
}
// //
// AtkObject tests // AtkObject tests
// //
...@@ -1486,12 +1494,7 @@ class ActivationTester { ...@@ -1486,12 +1494,7 @@ class ActivationTester {
} }
bool IsActivatedInStateSet() { bool IsActivatedInStateSet() {
AtkStateSet* state_set = atk_object_ref_state_set(target_); return AtkObjectHasState(target_, ATK_STATE_ACTIVE);
EXPECT_TRUE(ATK_IS_STATE_SET(state_set));
bool in_state_set =
atk_state_set_contains_state(state_set, ATK_STATE_ACTIVE);
g_object_unref(state_set);
return in_state_set;
} }
void Reset() { void Reset() {
...@@ -1590,18 +1593,10 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkWindowMinimized) { ...@@ -1590,18 +1593,10 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkWindowMinimized) {
g_object_ref(root_atk_object); g_object_ref(root_atk_object);
EXPECT_TRUE(ATK_IS_WINDOW(root_atk_object)); EXPECT_TRUE(ATK_IS_WINDOW(root_atk_object));
EXPECT_FALSE(AtkObjectHasState(root_atk_object, ATK_STATE_ICONIFIED));
AtkStateSet* state_set = atk_object_ref_state_set(root_atk_object);
EXPECT_TRUE(ATK_IS_STATE_SET(state_set));
EXPECT_FALSE(atk_state_set_contains_state(state_set, ATK_STATE_ICONIFIED));
g_object_unref(state_set);
GetRootWrapper()->set_minimized(true); GetRootWrapper()->set_minimized(true);
EXPECT_TRUE(AtkObjectHasState(root_atk_object, ATK_STATE_ICONIFIED));
state_set = atk_object_ref_state_set(root_atk_object);
EXPECT_TRUE(ATK_IS_STATE_SET(state_set));
EXPECT_TRUE(atk_state_set_contains_state(state_set, ATK_STATE_ICONIFIED));
g_object_unref(state_set);
bool saw_state_change = false; bool saw_state_change = false;
g_signal_connect(root_atk_object, "state-change", g_signal_connect(root_atk_object, "state-change",
...@@ -2369,4 +2364,55 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestScrolledToAnchorEvent) { ...@@ -2369,4 +2364,55 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestScrolledToAnchorEvent) {
ASSERT_TRUE(saw_caret_moved); ASSERT_TRUE(saw_caret_moved);
} }
TEST_F(AXPlatformNodeAuraLinuxTest, TestDialogActiveWhenChildFocused) {
AXNodeData root;
root.id = 1;
root.role = ax::mojom::Role::kWindow;
root.child_ids.push_back(2);
root.child_ids.push_back(3);
AXNodeData dialog;
dialog.id = 2;
dialog.role = ax::mojom::Role::kDialog;
dialog.child_ids.push_back(4);
AXNodeData node_outside_dialog;
node_outside_dialog.id = 3;
node_outside_dialog.role = ax::mojom::Role::kTextField;
AXNodeData entry;
entry.id = 4;
entry.role = ax::mojom::Role::kTextField;
Init(root, dialog, node_outside_dialog, entry);
AtkObject* root_atk_object(GetRootAtkObject());
EXPECT_TRUE(ATK_IS_OBJECT(root_atk_object));
AXNode* dialog_node = GetRootNode()->children()[0];
AtkObject* dialog_obj = AtkObjectFromNode(dialog_node);
bool saw_active_state_change = false;
g_signal_connect(dialog_obj, "state-change",
G_CALLBACK(+[](AtkObject* atkobject, gchar* state_changed,
gboolean new_value, bool* flag) {
if (!g_strcmp0(state_changed, "active"))
*flag = true;
}),
&saw_active_state_change);
AXNode* entry_node = dialog_node->children()[0];
GetPlatformNode(entry_node)
->NotifyAccessibilityEvent(ax::mojom::Event::kFocus);
EXPECT_TRUE(saw_active_state_change);
EXPECT_TRUE(AtkObjectHasState(dialog_obj, ATK_STATE_ACTIVE));
saw_active_state_change = false;
AXNode* outside_node = GetRootNode()->children()[1];
GetPlatformNode(outside_node)
->NotifyAccessibilityEvent(ax::mojom::Event::kFocus);
EXPECT_TRUE(saw_active_state_change);
EXPECT_FALSE(AtkObjectHasState(dialog_obj, ATK_STATE_ACTIVE));
}
} // namespace ui } // namespace ui
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