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;
// null if if the AtkObject is destroyed.
AtkObject* g_active_top_level_frame = nullptr;
AtkObject* g_active_views_dialog = nullptr;
#if defined(ATK_216)
constexpr AtkRole kStaticRole = ATK_ROLE_STATIC;
constexpr AtkRole kSubscriptRole = ATK_ROLE_SUBSCRIPT;
......@@ -2676,6 +2678,9 @@ void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) {
FindAtkObjectParentFrame(GetActiveMenus().back()) == atk_object_)
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();
if (is_minimized && data.role == ax::mojom::Role::kWindow)
atk_state_set_add_state(atk_state_set, ATK_STATE_ICONIFIED);
......@@ -3164,6 +3169,31 @@ void AXPlatformNodeAuraLinux::OnScrolledToAnchor() {
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() {
AtkObject* atk_object = GetOrCreateAtkObject();
......@@ -3175,6 +3205,8 @@ void AXPlatformNodeAuraLinux::OnFocused() {
if (atk_object == g_current_focused)
return;
SetActiveViewsDialog();
if (g_current_focused) {
g_signal_emit_by_name(g_current_focused, "focus-event", false);
atk_object_notify_state_change(ATK_OBJECT(g_current_focused),
......
......@@ -171,6 +171,7 @@ class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase {
void ResendFocusSignalsForCurrentlyFocusedNode();
bool SupportsSelectionWithAtkSelection();
bool SelectionAndFocusAreTheSame();
void SetActiveViewsDialog();
// AXPlatformNode overrides.
// This has a side effect of creating the AtkObject if one does not already
......
......@@ -178,6 +178,14 @@ static void TestAtkObjectBoolAttribute(
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
//
......@@ -1486,12 +1494,7 @@ class ActivationTester {
}
bool IsActivatedInStateSet() {
AtkStateSet* state_set = atk_object_ref_state_set(target_);
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;
return AtkObjectHasState(target_, ATK_STATE_ACTIVE);
}
void Reset() {
......@@ -1590,18 +1593,10 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkWindowMinimized) {
g_object_ref(root_atk_object);
EXPECT_TRUE(ATK_IS_WINDOW(root_atk_object));
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);
EXPECT_FALSE(AtkObjectHasState(root_atk_object, ATK_STATE_ICONIFIED));
GetRootWrapper()->set_minimized(true);
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);
EXPECT_TRUE(AtkObjectHasState(root_atk_object, ATK_STATE_ICONIFIED));
bool saw_state_change = false;
g_signal_connect(root_atk_object, "state-change",
......@@ -2369,4 +2364,55 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestScrolledToAnchorEvent) {
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
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