From 80d3c912259746251bb90d3035079dc71030f82d Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Thu, 15 Jun 2017 21:29:03 +0900 Subject: [PATCH 01/16] Add NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE mode The "GetNeighbor" method returns an accessible object which has same bus with root object. But the returned recurse value is 1. The following is the case. If embedded object has highlight, and there is no more previous object, then it returns PROXY object which has embedding side bus information. In this case, the "GetNeighbor" should find previous sibling object in embedding process. The "GetNeighbor" is using NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE for this case. Change-Id: Icae2709b2746bd970643034c2d66b1c196c65ca9 --- atspi/atspi-accessible.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index bafe114..40053f2 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -583,6 +583,7 @@ typedef enum { NEIGHBOR_SEARCH_MODE_NORMAL = 0, NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT = 1, NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING = 2, + NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE = 3, } GetNeighborSearchMode; /** * atspi_accessible_get_neighbor: @@ -630,10 +631,17 @@ atspi_accessible_get_neighbor (AtspiAccessible *root, // thus we're recursing. should the recurse failed to find anything it will end with if (ret && recurse) { g_object_unref(G_OBJECT(start)); - g_queue_push_tail(children_root_stack, ret); start = ret; g_object_ref(start); - search_mode = NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT; + if (are_objects_on_the_same_bus(root, ret)) + { + search_mode = NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE; + } + else + { + g_queue_push_tail(children_root_stack, ret); + search_mode = NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT; + } continue; } // found the one we've been looking for -- 2.7.4 From 7b0103f7c7775e7b0f50cb0939c891fbf0a0bcff Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Fri, 30 Jun 2017 15:50:52 +0900 Subject: [PATCH 02/16] atspi_action_do_action_name: change parameter type The name is not changed in atspi_action_do_action_name. So the type of name should be const gchar* Change-Id: I6a9329ea34ef566dd444cae5112dead99ef4a9ce --- atspi/atspi-action.c | 2 +- atspi/atspi-action.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/atspi/atspi-action.c b/atspi/atspi-action.c index d4e963e..ccf05bd 100644 --- a/atspi/atspi-action.c +++ b/atspi/atspi-action.c @@ -224,7 +224,7 @@ atspi_action_do_action (AtspiAction *obj, gint i, GError **error) * Returns: #TRUE if the action is successfully invoked, otherwise #FALSE. **/ gboolean -atspi_action_do_action_name (AtspiAction *obj, gchar *name, GError **error) +atspi_action_do_action_name (AtspiAction *obj, const gchar *name, GError **error) { const char *action_name = name; dbus_bool_t retval = FALSE; diff --git a/atspi/atspi-action.h b/atspi/atspi-action.h index 00f3a2e..d30395e 100644 --- a/atspi/atspi-action.h +++ b/atspi/atspi-action.h @@ -57,7 +57,7 @@ gchar * atspi_action_get_key_binding (AtspiAction *obj, gint i, GError **error); gchar * atspi_action_get_localized_name (AtspiAction *obj, gint i, GError **error); gboolean atspi_action_do_action (AtspiAction *obj, gint i, GError **error); -gboolean atspi_action_do_action_name (AtspiAction *obj, gchar *name, GError **error); +gboolean atspi_action_do_action_name (AtspiAction *obj, const gchar *name, GError **error); #ifndef ATSPI_DISABLE_DEPRECATED gchar * atspi_action_get_description (AtspiAction *obj, gint i, GError **error); -- 2.7.4 From 23b49ea6ed68e056d1d8de4756293ca026ec09ce Mon Sep 17 00:00:00 2001 From: Pawel Kurowski Date: Thu, 1 Jun 2017 12:45:08 +0200 Subject: [PATCH 03/16] Add relation-dump option to at_spi2_tool Flags -d and -c now inform when object has relations to list. Change-Id: I0c9a03c8d18ddf965c72ceac2bb804742c4540bf --- test/at_spi2_tool.c | 155 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 110 insertions(+), 45 deletions(-) diff --git a/test/at_spi2_tool.c b/test/at_spi2_tool.c index dfbc051..15adca5 100644 --- a/test/at_spi2_tool.c +++ b/test/at_spi2_tool.c @@ -5,6 +5,7 @@ #include #include #include +#include #define ERROR_STATE -1 #define SAFE_BUFFER_SIZE 2048 @@ -18,6 +19,8 @@ #define DUMP 0 #define CHECK 1 #define FIRST_MATCH 2 +#define RELATION_TABLE_COLUMN_COUNT 7 +#define RELATION_COLUMN_WIDTH 20 #define VERSION "1.1" static unsigned indent_width = 2; @@ -80,12 +83,6 @@ typedef struct _Box_Size { char *width; } Box_Size; -char *_strdup(const char *c) -{ - char *result = (char *)calloc(1, strlen(c) + 1); - return result ? strcpy(result, c) : result; -} - static char *_multiply_string(char c, unsigned number) { char *result = (char *)calloc(1, number + 1); @@ -100,7 +97,7 @@ static char *_multiply_string(char c, unsigned number) static void _print_module_legend() { - printf("\n[[node],[node role name],[attributes list],[x,y,width,height],[node name],[states list][flow to node, flow from node]\n\n"); + printf("\n[[node],[node role name],[attributes list],[x,y,width,height],[node name],[states list][relations]\n\n"); } static void _print_atspi_states_legend() @@ -251,7 +248,7 @@ static char *_get_states(AtspiAccessible *node, int length_limit) state_type = g_array_index(states, AtspiStateType, i); char node_state_str[8]; - sprintf(node_state_str, "(%d)", state_type); + snprintf(node_state_str, 8, "(%d)", state_type); _combine_strings(&state_string, node_state_str); } @@ -289,32 +286,6 @@ static char *_get_attributes(AtspiAccessible *node, int length_limit) return result; } -static char *_get_object_in_relation(AtspiAccessible *source, AtspiRelationType search_type) -{ - if (source == NULL) - return ""; - - GArray *relations = atspi_accessible_get_relation_set(source, NULL); - - if (relations == NULL) - return ""; - - AtspiAccessible *ret = NULL; - for (int i = 0; i < relations->len; ++i) { - AtspiRelation *relation = g_array_index(relations, AtspiRelation *, i); - AtspiRelationType type = atspi_relation_get_relation_type(relation); - - if (type == search_type) { - ret = atspi_relation_get_target(relation, 0); - break; - } - } - - g_array_free(relations, TRUE); - - return ret ? atspi_accessible_get_unique_id(ret, NULL) : g_strdup(""); -} - static char *_get_info(AtspiAccessible *node, int length_limit) { if (!node) @@ -328,11 +299,10 @@ static char *_get_info(AtspiAccessible *node, int length_limit) Box_Size *box_size = _get_box_size(node); char *states = _get_states(node, length_limit); - char *flows_to = _get_object_in_relation(node, ATSPI_RELATION_FLOWS_TO); - char *flows_from = _get_object_in_relation(node, ATSPI_RELATION_FLOWS_FROM); + GArray *relations = atspi_accessible_get_relation_set(node, NULL); char result[SAFE_BUFFER_SIZE]; - int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s],[%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s,%s]]", + int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s],[%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s]]", unique_id, node_role_name, attributes, @@ -342,8 +312,7 @@ static char *_get_info(AtspiAccessible *node, int length_limit) box_size->height, node_name, states, - flows_to, - flows_from); + (relations && relations->len) ? "*" : ""); if (ret >= SAFE_BUFFER_SIZE) fprintf(stderr, "\n%s, %s %d: generated string is too long. Buffer overflow\n", __FILE__, __FUNCTION__, __LINE__); @@ -358,10 +327,9 @@ static char *_get_info(AtspiAccessible *node, int length_limit) free(box_size); } free(states); - free(flows_to); - free(flows_from); + g_array_free(relations, TRUE); - return _strdup(result); + return g_strdup(result); } static void _test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessible *parent_candidate, int parent_candidate_index) @@ -374,7 +342,7 @@ static void _test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessi char parent_status[NUMBER_WIDTH]; if (parent == NULL) - strcpy(parent_status, "_"); + strncpy(parent_status, "_", NUMBER_WIDTH); else snprintf(parent_status, NUMBER_WIDTH, "%d", parent_index); @@ -441,11 +409,105 @@ void _print_help() printf("\tat_spi2_tool -i1 -c starter\n"); printf("\t show AT-SPI tree with integrity test for node \"starter\" using one-space indentation\n"); } + void _print_version() { printf("AT-SPI2-CORE-UTIL v%s\n", VERSION); } +static void _print_horizontal_line_in_relation_table() { + for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) { + for (int j = 0; j < RELATION_COLUMN_WIDTH; j++) + printf("-"); + printf("+"); + } + printf("\n"); +} + +static void _print_legend_for_relation_table() { + char *table[] = {"OBJECT", "CONTROLLER_FOR", "CONTROLLED_BY", "FLOWS_TO", "FLOWS_FROM", "DESCRIBED_BY", "OTHER RELATION [ID]"}; + assert(ARRAY_SIZE(table) == RELATION_TABLE_COLUMN_COUNT); + for(int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) + printf("%*s|", RELATION_COLUMN_WIDTH , table[i]); + printf("\n"); + _print_horizontal_line_in_relation_table(); +} + +static char *_get_unique_id_of_object_in_relation(AtspiRelation *relation) { + if (!relation) + return NULL; + + gint last_index = atspi_relation_get_n_targets(relation) - 1; + AtspiAccessible *target = atspi_relation_get_target(relation, last_index); + return atspi_accessible_get_unique_id(target, NULL); +} + +static void _print_relations_for_object(AtspiAccessible *node) { + GArray *relations = atspi_accessible_get_relation_set(node, NULL); + if (!relations) + return; + + if (!relations->len) { + g_array_free(relations, FALSE); + return; + } + + char **table = calloc(RELATION_TABLE_COLUMN_COUNT, sizeof(char *)); + if (!table) { + fprintf(stderr, "Calloc failed. Can't alloc memory for object relations string\n"); + return; + } + + table[0] = atspi_accessible_get_unique_id(node, NULL); + for (int i = 0; i < relations->len; i++) { + AtspiRelation *relation = g_array_index(relations, AtspiRelation *, i); + AtspiRelationType type = atspi_relation_get_relation_type(relation); + int idx; + switch (type) { + case ATSPI_RELATION_CONTROLLER_FOR: idx = 1; break; + case ATSPI_RELATION_CONTROLLED_BY: idx = 2; break; + case ATSPI_RELATION_FLOWS_TO: idx = 3; break; + case ATSPI_RELATION_FLOWS_FROM: idx = 4; break; + case ATSPI_RELATION_DESCRIBED_BY: idx = 5; break; + default: idx = 0; + } + + if (idx > 0) + table[idx] = _get_unique_id_of_object_in_relation(relation); + else { + char buf[16]; + snprintf(buf, sizeof(buf), " [%d]", type); + _combine_strings(&table[RELATION_TABLE_COLUMN_COUNT - 1], buf); + } + } + + for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) { + printf("%*s|", RELATION_COLUMN_WIDTH, table[i] ? table[i] : ""); + free(table[i]); + } + free(table); + + printf("\n"); + _print_horizontal_line_in_relation_table(); + g_array_free(relations, TRUE); +} + +static void _print_relations_for_objects_in_tree(AtspiAccessible *node) { + _print_relations_for_object(node); + + int count = atspi_accessible_get_child_count(node, NULL); + for (int i = 0; i < count; i++) { + AtspiAccessible *child = atspi_accessible_get_child_at_index(node, i, NULL); + if (child) + _print_relations_for_objects_in_tree(child); + } +} + +static void _print_relations_table(AtspiAccessible *node) { + _print_legend_for_relation_table(); + _print_relations_for_objects_in_tree(node); +} + static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bool first_match, int length_limit) { @@ -460,7 +522,7 @@ static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bo for (int i = 0; i < count; i++) { AtspiAccessible *child = atspi_accessible_get_child_at_index(desktop, i, NULL); if (child == NULL) { - fprintf(stderr, "\n%s, %s %d: Null child occured. Results may be misleading.\n", __FILE__, __FUNCTION__, __LINE__); + fprintf(stderr, "\n%s, %s %d: Null application occured. Results may be misleading.\n", __FILE__, __FUNCTION__, __LINE__); continue; } @@ -478,12 +540,15 @@ static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bo _test_atspi_parent_child_relation(child, desktop, i); _print_atspi_tree_verify_maybe_r(0, child, check, length_limit); + printf("\n"); + _print_relations_table(child); if (first_match) { free(name); break; - } else + } else { printf("\n"); + } free(name); } -- 2.7.4 From 73e22e1f4da152c9b77ecd2f5a0face5692c2a51 Mon Sep 17 00:00:00 2001 From: Pawel Kurowski Date: Mon, 17 Jul 2017 22:15:35 +0200 Subject: [PATCH 04/16] add attributes table to at_spi2_tool atributes table will be printed, when atributes string is too long add relations_found flag which tells if relation table legend should be printed Change-Id: I2a0b71f5e8b439881f5b83e9b7f1a17556f9e713 --- test/at_spi2_tool.c | 161 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 127 insertions(+), 34 deletions(-) diff --git a/test/at_spi2_tool.c b/test/at_spi2_tool.c index 15adca5..08c2af5 100644 --- a/test/at_spi2_tool.c +++ b/test/at_spi2_tool.c @@ -20,7 +20,9 @@ #define CHECK 1 #define FIRST_MATCH 2 #define RELATION_TABLE_COLUMN_COUNT 7 -#define RELATION_COLUMN_WIDTH 20 +#define ATTRIBUTE_TABLE_COLUMN_COUNT 3 +#define RELATION_TABLE_COLUMN_WIDTH 20 +#define ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH 20 #define VERSION "1.1" static unsigned indent_width = 2; @@ -260,7 +262,7 @@ static char *_get_states(AtspiAccessible *node, int length_limit) return state_string; } -static char *_get_attributes(AtspiAccessible *node, int length_limit) +static char *_get_attributes(AtspiAccessible *node, int length_limit, bool *attributes_are_too_long) { GHashTable *attributes = atspi_accessible_get_attributes(node, NULL); @@ -273,20 +275,24 @@ static char *_get_attributes(AtspiAccessible *node, int length_limit) g_hash_table_iter_init (&attributes_iter, attributes); char *result = NULL; while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) { - char attributes_string[ATTR_WIDTH]; - int ret = snprintf(attributes_string, ATTR_WIDTH, "(%s=%s)", (char *)attr_key, (char *)attr_value); - if (ret >= ATTR_WIDTH) - fprintf(stderr, "\n_get_attributes: generated string is too long. Buffer overflow\n"); + char attributes_string[SAFE_BUFFER_SIZE]; + int ret = snprintf(attributes_string, SAFE_BUFFER_SIZE, "(%s=%s)", (char *)attr_key, (char *)attr_value); + if (ret >= SAFE_BUFFER_SIZE) + fprintf(stderr, "\n%s, %s %d: generated string is too long. Buffer overflow\n", __FILE__, __FUNCTION__, __LINE__); _combine_strings(&result, attributes_string); } g_hash_table_unref(attributes); - _truncate_string(result, length_limit); + int real_truncate_size = (length_limit < ATTR_WIDTH && length_limit > MINIMAL_MODULE_WIDTH) ? length_limit : ATTR_WIDTH; + if (result && attributes_are_too_long && strlen(result) > real_truncate_size) + *attributes_are_too_long = true; + + _truncate_string(result, real_truncate_size); return result; } -static char *_get_info(AtspiAccessible *node, int length_limit) +static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes_are_too_long, bool *app_has_relations) { if (!node) return NULL; @@ -295,11 +301,12 @@ static char *_get_info(AtspiAccessible *node, int length_limit) char *node_role_name = atspi_accessible_get_role_name(node, NULL); char *unique_id = atspi_accessible_get_unique_id(node, NULL); - char *attributes = _get_attributes(node, length_limit); + char *attributes = _get_attributes(node, length_limit, attributes_are_too_long); Box_Size *box_size = _get_box_size(node); char *states = _get_states(node, length_limit); GArray *relations = atspi_accessible_get_relation_set(node, NULL); + bool current_node_has_relations = (relations && relations->len); char result[SAFE_BUFFER_SIZE]; int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s],[%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s]]", @@ -312,11 +319,14 @@ static char *_get_info(AtspiAccessible *node, int length_limit) box_size->height, node_name, states, - (relations && relations->len) ? "*" : ""); + current_node_has_relations ? "*" : ""); if (ret >= SAFE_BUFFER_SIZE) fprintf(stderr, "\n%s, %s %d: generated string is too long. Buffer overflow\n", __FILE__, __FUNCTION__, __LINE__); + if (current_node_has_relations) + *app_has_relations = true; + free(node_name); free(node_role_name); free(unique_id); @@ -360,7 +370,8 @@ static void _test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessi printf("%-*s\t", CHECK_OUTPUT_WIDTH, output); } -static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible *object, bool check_integrity, int length_limit) +static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible *object, bool check_integrity, int length_limit, + bool *attributes_are_too_long, bool *app_has_relations) { char *indent = _multiply_string(' ', indent_number*indent_width); if (indent != NULL) { @@ -368,7 +379,7 @@ static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible * free(indent); } - char *node_info = _get_info(object, length_limit); + char *node_info = _get_info(object, length_limit, attributes_are_too_long, app_has_relations); if (node_info != NULL) { printf("%s\n", node_info); free(node_info); @@ -381,7 +392,7 @@ static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible * if (check_integrity) _test_atspi_parent_child_relation(child, object, i); - _print_atspi_tree_verify_maybe_r(indent_number + 1, child, check_integrity, length_limit); + _print_atspi_tree_verify_maybe_r(indent_number + 1, child, check_integrity, length_limit, attributes_are_too_long, app_has_relations); } } return 0; @@ -415,24 +426,15 @@ void _print_version() printf("AT-SPI2-CORE-UTIL v%s\n", VERSION); } -static void _print_horizontal_line_in_relation_table() { +static void _print_horizontal_line_in_relations_table() { for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) { - for (int j = 0; j < RELATION_COLUMN_WIDTH; j++) + for (int j = 0; j < RELATION_TABLE_COLUMN_WIDTH; j++) printf("-"); printf("+"); } printf("\n"); } -static void _print_legend_for_relation_table() { - char *table[] = {"OBJECT", "CONTROLLER_FOR", "CONTROLLED_BY", "FLOWS_TO", "FLOWS_FROM", "DESCRIBED_BY", "OTHER RELATION [ID]"}; - assert(ARRAY_SIZE(table) == RELATION_TABLE_COLUMN_COUNT); - for(int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) - printf("%*s|", RELATION_COLUMN_WIDTH , table[i]); - printf("\n"); - _print_horizontal_line_in_relation_table(); -} - static char *_get_unique_id_of_object_in_relation(AtspiRelation *relation) { if (!relation) return NULL; @@ -482,32 +484,116 @@ static void _print_relations_for_object(AtspiAccessible *node) { } for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) { - printf("%*s|", RELATION_COLUMN_WIDTH, table[i] ? table[i] : ""); + printf("%*s|", RELATION_TABLE_COLUMN_WIDTH, table[i] ? table[i] : ""); free(table[i]); } free(table); printf("\n"); - _print_horizontal_line_in_relation_table(); + _print_horizontal_line_in_relations_table(); g_array_free(relations, TRUE); } -static void _print_relations_for_objects_in_tree(AtspiAccessible *node) { - _print_relations_for_object(node); +typedef void (*print_information_about_object_function)(AtspiAccessible *); + +static void _iterate_over_tree(print_information_about_object_function func, AtspiAccessible *node) { + func(node); int count = atspi_accessible_get_child_count(node, NULL); for (int i = 0; i < count; i++) { AtspiAccessible *child = atspi_accessible_get_child_at_index(node, i, NULL); if (child) - _print_relations_for_objects_in_tree(child); + _iterate_over_tree(func, child); } } +static void _print_relations_for_objects_in_tree(AtspiAccessible *node) { + _iterate_over_tree(_print_relations_for_object, node); +} + +static void _print_header_for_relation_table() { + char *table[] = {"OBJECT", "CONTROLLER_FOR", "CONTROLLED_BY", "FLOWS_TO", "FLOWS_FROM", "DESCRIBED_BY", "OTHER RELATION [ID]"}; + assert(ARRAY_SIZE(table) == RELATION_TABLE_COLUMN_COUNT); + + _print_horizontal_line_in_relations_table(); + + for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) + printf("%*s|", RELATION_TABLE_COLUMN_WIDTH , table[i]); + + printf("\n"); + _print_horizontal_line_in_relations_table(); +} + static void _print_relations_table(AtspiAccessible *node) { - _print_legend_for_relation_table(); + printf("\nRELATIONS TABLE\n"); + _print_header_for_relation_table(); _print_relations_for_objects_in_tree(node); } +static void _print_horizontal_line_in_attributes_table() { + int size_factor = 1; + for (int i = 0; i < ATTRIBUTE_TABLE_COLUMN_COUNT; i++) { + if (i == ATTRIBUTE_TABLE_COLUMN_COUNT - 1) + size_factor = 4; + for (int j = 0; j < ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * size_factor; j++) + printf("-"); + printf("+"); + } + printf("\n"); +} + +static void _print_attributes_for_object(AtspiAccessible *node) { + GHashTable *attributes = atspi_accessible_get_attributes(node, NULL); + + if (!attributes) + return; + + char *unique_id = atspi_accessible_get_unique_id(node, NULL); + GHashTableIter attributes_iter; + gpointer attr_key; + gpointer attr_value; + + g_hash_table_iter_init (&attributes_iter, attributes); + while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) { + printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH, unique_id ? unique_id : ""); + printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH, attr_key ? (char *) attr_key : ""); + printf("%*s|\n", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * 4, attr_value ? (char *) attr_value : ""); + } + + if (g_hash_table_size (attributes)) + _print_horizontal_line_in_attributes_table(); + + g_hash_table_unref(attributes); + free(unique_id); +} + +static void _print_attributes_for_objects_in_tree(AtspiAccessible *node) { + _iterate_over_tree(_print_attributes_for_object, node); +} + +static void _print_header_for_attributes_table() { + char *table[] = {"OBJECT", "ATTRIBUTE KEY", "ATTRIBUTE VALUE"}; + assert(ARRAY_SIZE(table) == ATTRIBUTE_TABLE_COLUMN_COUNT); + + _print_horizontal_line_in_attributes_table(); + + int size_factor = 1; + for (int i = 0; i < ATTRIBUTE_TABLE_COLUMN_COUNT; i++) { + if (i == ATTRIBUTE_TABLE_COLUMN_COUNT - 1) + size_factor = 4; + printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * size_factor , table[i]); + } + + printf("\n"); + _print_horizontal_line_in_attributes_table(); +} + +static void _print_attributes_table(AtspiAccessible *node) { + printf("\nATTRIBUTES TABLE\n"); + _print_header_for_attributes_table(); + _print_attributes_for_objects_in_tree(node); +} + static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bool first_match, int length_limit) { @@ -519,6 +605,7 @@ static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bo int count = atspi_accessible_get_child_count(desktop, NULL); bool app_name_matched = false; + for (int i = 0; i < count; i++) { AtspiAccessible *child = atspi_accessible_get_child_at_index(desktop, i, NULL); if (child == NULL) { @@ -527,6 +614,8 @@ static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bo } char *name = atspi_accessible_get_name(child, NULL); + bool attributes_are_too_long = false; + bool app_has_relations = false; if (!dump && !check) printf("%s\n", name); @@ -539,9 +628,13 @@ static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bo if (check) _test_atspi_parent_child_relation(child, desktop, i); - _print_atspi_tree_verify_maybe_r(0, child, check, length_limit); - printf("\n"); - _print_relations_table(child); + _print_atspi_tree_verify_maybe_r(0, child, check, length_limit, &attributes_are_too_long, &app_has_relations); + + if (app_has_relations) + _print_relations_table(child); + + if (attributes_are_too_long) + _print_attributes_table(child); if (first_match) { free(name); @@ -647,7 +740,7 @@ static void _run_command(int argc, char *argv[]) case 'a': enable_at_spi_client = TRUE; - if(optarg[0] == 'f' || optarg[0] == '0') + if (optarg[0] == 'f' || optarg[0] == '0') enable_at_spi_client = FALSE; _at_spi_client_enable(enable_at_spi_client); -- 2.7.4 From 2c0f9a891f49d36cb5d133dd0f54d96e77283cf2 Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Wed, 19 Jul 2017 15:15:28 +0900 Subject: [PATCH 05/16] bus: follow D-bus policy Tizen D-bus policy does not support Smack rule. The following patch set of D-bus probably affects dbus-daemon which is used for accessibility. https://review.tizen.org/gerrit/#/c/133270/ It was not possilbe to launch dbus-daemon with following error message. org.a11y.Bus[2457]: Failed to start message bus: Attribute "smack" is invalid on element in this context org.a11y.Bus[2457]: Failed to launch bus: Bus exited with code 1 Change-Id: I67435b1a27f91193348d1e699805b6d09be8fddc --- bus/accessibility.conf.in | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/bus/accessibility.conf.in b/bus/accessibility.conf.in index 77f12f8..9336ad7 100644 --- a/bus/accessibility.conf.in +++ b/bus/accessibility.conf.in @@ -8,28 +8,11 @@ unix:tmpdir=/tmp - owner - - - - - - - + - - - - - - - - - - -- 2.7.4 From 87ea2e308098bb8fb316fc5fc7fd39141dfbc51d Mon Sep 17 00:00:00 2001 From: Mariusz Wachowicz Date: Thu, 10 Aug 2017 11:29:48 +0200 Subject: [PATCH 06/16] move ref_accessible function to public scope this function is needed outside at-spi library (other projects, e.g. universal-switch), to get AtspiAccessible object after it has been received as application name and path name Change-Id: I51e72af3f191d4a14174cd0b4819522154e3e1ba --- atspi/atspi-misc.c | 10 +++++----- atspi/atspi-misc.h | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/atspi/atspi-misc.c b/atspi/atspi-misc.c index 193257b..02eab8c 100644 --- a/atspi/atspi-misc.c +++ b/atspi/atspi-misc.c @@ -260,7 +260,7 @@ get_application (const char *bus_name) return app; } -static AtspiAccessible * +AtspiAccessible * ref_accessible (const char *app_name, const char *path) { AtspiApplication *app; @@ -1149,11 +1149,12 @@ out: process_deferred_messages (); if (dbus_error_is_set (&err)) { - /* TODO: Set gerror */ + g_set_error_literal(error, ATSPI_ERROR, ATSPI_ERROR_IPC, err.message); dbus_error_free (&err); + if (reply) + dbus_message_unref(reply); } - - if (reply && dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR) + else if (reply && dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { const char *err_str = NULL; dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &err_str, DBUS_TYPE_INVALID); @@ -1162,7 +1163,6 @@ out: dbus_message_unref (reply); return NULL; } - return reply; } diff --git a/atspi/atspi-misc.h b/atspi/atspi-misc.h index f13596f..e39d7b8 100644 --- a/atspi/atspi-misc.h +++ b/atspi/atspi-misc.h @@ -47,6 +47,9 @@ void atspi_set_main_context (GMainContext *cnx); gchar * atspi_role_get_name (AtspiRole role); + +AtspiAccessible *ref_accessible(const char *app_name, const char *path); + G_END_DECLS #endif /* _ATSPI_MISC_H_ */ -- 2.7.4 From 9dc6aaf0f801e52d145783e15125148efec16bae Mon Sep 17 00:00:00 2001 From: Bowon Ryu Date: Fri, 11 Aug 2017 16:10:56 +0900 Subject: [PATCH 07/16] at_spi2_tool: Apply ASLR to executable files. Change-Id: I86b9dd51723548d1b888eed7847668ca9e318110 Signed-off-by: Bowon Ryu --- test/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Makefile.am b/test/Makefile.am index 828f126..bb9acb9 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -3,8 +3,8 @@ LDADD = $(top_builddir)/atspi/libatspi.la bin_PROGRAMS = at_spi2_tool at_spi2_tool_SOURCES = at_spi2_tool.c at_spi2_tool_CPPFLAGS = -I$(top_srcdir) -I$(top_builddir) -I$(top_builddir)/atspi -at_spi2_tool_CFLAGS = $(GIO_CFLAGS) $(GLIB_CFLAGS) --std=c99 -Wall $(GOBJ_LIBS) $(DBUS_CFLAGS) -at_spi2_tool_LDFLAGS = $(GIO_LIBS) +at_spi2_tool_CFLAGS = $(GIO_CFLAGS) $(GLIB_CFLAGS) --std=c99 -Wall -fPIE $(GOBJ_LIBS) $(DBUS_CFLAGS) +at_spi2_tool_LDFLAGS = $(GIO_LIBS) -pie noinst_PROGRAMS = memory -- 2.7.4 From adf4836bec89de9e7a93a6988c7ae6ea7870936c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Pawe=C5=82=20Stawicki?= Date: Mon, 5 Jun 2017 17:40:47 +0200 Subject: [PATCH 08/16] at-spi-bus-launcher: launch universal-switch Change-Id: I16bff1e30b2c72c812be767f1b21f88a65ee1787 --- bus/at-spi-bus-launcher.c | 197 +++++++++++++++++++++++++++++----------------- 1 file changed, 125 insertions(+), 72 deletions(-) diff --git a/bus/at-spi-bus-launcher.c b/bus/at-spi-bus-launcher.c index bb6f9f9..7948d1b 100644 --- a/bus/at-spi-bus-launcher.c +++ b/bus/at-spi-bus-launcher.c @@ -35,7 +35,11 @@ #include #endif +//TODO: move to vconf/vconf-internal-setting-keys.h? +#define VCONFKEY_SETAPPL_ACCESSIBILITY_UNIVERSAL_SWITCH "db/setting/accessibility/universal-switch" + #define APP_CONTROL_OPERATION_SCREEN_READ "http://tizen.org/appcontrol/operation/read_screen" +#define APP_CONTROL_OPERATION_UNIVERSAL_SWITCH "http://tizen.org/appcontrol/operation/universal_switch" #include #include @@ -64,7 +68,7 @@ FILE *log_file; #define LOGD(arg...) do {if (log_file) {fprintf(log_file, ##arg);fprintf(log_file, "\n"); fflush(log_file);}} while(0) #endif -static gboolean _launch_screen_reader_repeat_until_success(gpointer user_data); +static gboolean _launch_process_repeat_until_success(gpointer user_data); typedef enum { A11Y_BUS_STATE_IDLE = 0, @@ -74,6 +78,14 @@ typedef enum { } A11yBusState; typedef struct { + const char * name; + const char * app_control_operation; + const char * vconf_key; + int launch_repeats; + int pid; +} A11yBusClient; + +typedef struct { GMainLoop *loop; gboolean launch_immediately; gboolean a11y_enabled; @@ -83,9 +95,8 @@ typedef struct { GSettings *a11y_schema; GSettings *interface_schema; - int launch_screen_reader_repeats; - gboolean screen_reader_needed; - int pid; + A11yBusClient screen_reader; + A11yBusClient universal_switch; A11yBusState state; @@ -662,24 +673,43 @@ gsettings_key_changed (GSettings *gsettings, const gchar *key, void *user_data) } static int -_screen_reader_dead_tracker (int pid, void *data) +_process_dead_tracker (int pid, void *data) { A11yBusLauncher *app = data; - if (app->pid > 0 && pid == app->pid) + if (app->screen_reader.pid > 0 && pid == app->screen_reader.pid) { LOGE("screen reader is dead, pid: %d, restarting", pid); - app->pid = 0; - g_timeout_add_seconds (2, _launch_screen_reader_repeat_until_success, app); + app->screen_reader.pid = 0; + g_timeout_add_seconds (2, _launch_process_repeat_until_success, &app->screen_reader); + } + + if (app->universal_switch.pid > 0 && pid == app->universal_switch.pid) + { + LOGE("universal switch is dead, pid: %d, restarting", pid); + app->universal_switch.pid = 0; + g_timeout_add_seconds (2, _launch_process_repeat_until_success, &app->universal_switch); } return 0; } +static void +_register_process_dead_tracker () +{ + if(_global_app->screen_reader.pid > 0 || _global_app->universal_switch.pid > 0) { + LOGD("registering process dead tracker"); + aul_listen_app_dead_signal(_process_dead_tracker, _global_app); + } else { + LOGD("unregistering process dead tracker"); + aul_listen_app_dead_signal(NULL, NULL); + } +} + + static gboolean -_launch_screen_reader(gpointer user_data, gboolean by_vconf_change) +_launch_client(A11yBusClient *client, gboolean by_vconf_change) { - A11yBusLauncher *bl = user_data; - LOGD("Launching screen reader"); + LOGD("Launching %s", client->name); bundle *kb = NULL; gboolean ret = FALSE; @@ -700,120 +730,147 @@ _launch_screen_reader(gpointer user_data, gboolean by_vconf_change) } } - int operation_error = appsvc_set_operation(kb, APP_CONTROL_OPERATION_SCREEN_READ); + int operation_error = appsvc_set_operation(kb, client->app_control_operation); LOGD("appsvc_set_operation: %i", operation_error); - bl->pid = appsvc_run_service(kb, 0, NULL, NULL); + client->pid = appsvc_run_service(kb, 0, NULL, NULL); - if (bl->pid > 0) + if (client->pid > 0) { - LOGD("Screen reader launched with pid: %i", bl->pid); - LOGD("registering screen reader dead tracker"); - aul_listen_app_dead_signal(_screen_reader_dead_tracker, bl); + LOGD("Process launched with pid: %i", client->pid); + _register_process_dead_tracker(); ret = TRUE; } else { - LOGD("Can't start screen-reader - error code: %i", bl->pid); + LOGD("Can't start %s - error code: %i", client->name, client->pid); } - bundle_free(kb); return ret; } static gboolean -_launch_screen_reader_repeat_until_success(gpointer user_data) { - A11yBusLauncher *bl = user_data; +_launch_process_repeat_until_success(gpointer user_data) { + A11yBusClient *client = user_data; - if (bl->launch_screen_reader_repeats > 100 || bl->pid > 0) + if (client->launch_repeats > 100 || client->pid > 0) { //do not try anymore return FALSE; } - gboolean ret = _launch_screen_reader(user_data, FALSE); + gboolean ret = _launch_client(client, FALSE); if (ret) { //we managed to - bl->launch_screen_reader_repeats = 0; + client->launch_repeats = 0; return FALSE; } + client->launch_repeats++; //try again return TRUE; } static gboolean -_terminate_screen_reader(A11yBusLauncher *bl) +_terminate_process(int pid) { - LOGD("Terminating screen reader"); int ret; int ret_aul; - if (bl->pid <= 0) + if (pid <= 0) return FALSE; - - LOGD("unregistering screen reader dead tracker"); - aul_listen_app_dead_signal(NULL, NULL); - - int status = aul_app_get_status_bypid(bl->pid); + int status = aul_app_get_status_bypid(pid); if (status < 0) { - LOGD("App with pid %d already terminated", bl->pid); - bl->pid = 0; + LOGD("App with pid %d already terminated", pid); return TRUE; } - LOGD("terminate process with pid %d", bl->pid); - ret_aul = aul_terminate_pid(bl->pid); + LOGD("terminate process with pid %d", pid); + ret_aul = aul_terminate_pid(pid); if (ret_aul >= 0) { LOGD("Terminating with aul_terminate_pid: return is %d", ret_aul); - bl->pid = 0; return TRUE; } else LOGD("aul_terminate_pid failed: return is %d", ret_aul); LOGD("Unable to terminate process using aul api. Sending SIGTERM signal"); - ret = kill(bl->pid, SIGTERM); + ret = kill(pid, SIGTERM); if (!ret) { - bl->pid = 0; return TRUE; } - LOGD("Unable to terminate process: %d with api or signal.", bl->pid); + LOGD("Unable to terminate process: %d with api or signal.", pid); return FALSE; } -void screen_reader_cb(keynode_t *node, void *user_data) +static gboolean +_terminate_client(A11yBusClient *client) { - A11yBusLauncher *bl = user_data; - int ret; + LOGD("Terminating %s", client->name); + int pid = client->pid; + client->pid = 0; + _register_process_dead_tracker(); + gboolean ret = _terminate_process(pid); + return ret; +} - ret = vconf_keynode_get_bool(node); - LOGD("vconf_keynode_get_bool(node): %i", ret); - if (ret < 0) +void vconf_client_cb(keynode_t *node, void *user_data) +{ + A11yBusClient *client = user_data; + int client_needed = vconf_keynode_get_bool(node); + LOGD("vconf_keynode_get_bool(node): %i", client_needed); + if (client_needed < 0) return; //check if process really exists (e.g didn't crash) - if (bl->pid > 0) + if (client->pid > 0) { - int err = kill(bl->pid,0); + int err = kill(client->pid,0); //process doesn't exist if (err == ESRCH) - bl->pid = 0; + client->pid = 0; } - bl->screen_reader_needed = ret; - LOGD("bl->screen_reader_needed: %i, bl->pid: %i", ret, bl->pid); - if (!bl->screen_reader_needed && (bl->pid > 0)) - _terminate_screen_reader(bl); - else if (bl->screen_reader_needed && (bl->pid <= 0)) - _launch_screen_reader(bl, TRUE); + LOGD("client_needed: %i, client->pid: %i", client_needed, client->pid); + if (!client_needed && (client->pid > 0)) + _terminate_client(client); + else if (client_needed && (client->pid <= 0)) + _launch_client(client, TRUE); +} + + +static gboolean register_client(A11yBusClient *client) +{ + gboolean client_needed = FALSE; + + if(!client->vconf_key) { + LOGE("Vconf_key missing for client: %s \n", client->vconf_key); + return FALSE; + } + + int ret = vconf_get_bool(client->vconf_key, &client_needed); + if (ret != 0) + { + LOGD("Could not read %s key value.\n", client->vconf_key); + return FALSE; + } + ret = vconf_notify_key_changed(client->vconf_key, vconf_client_cb, client); + if(ret != 0) + { + LOGD("Could not add information level callback\n"); + return FALSE; + } + + if (client_needed) + g_timeout_add_seconds(2,_launch_process_repeat_until_success, client); + return TRUE; } int @@ -825,7 +882,6 @@ main (int argc, #endif LOGD("Starting atspi bus launcher"); - int name_owner_id; gboolean a11y_set = FALSE; gboolean screen_reader_set = FALSE; gint i; @@ -838,9 +894,16 @@ main (int argc, _global_app = g_slice_new0 (A11yBusLauncher); _global_app->loop = g_main_loop_new (NULL, FALSE); - _global_app->launch_screen_reader_repeats = 0; _global_app->client_watcher_id = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + _global_app->screen_reader.name = "screen-reader"; + _global_app->screen_reader.app_control_operation = APP_CONTROL_OPERATION_SCREEN_READ; + _global_app->screen_reader.vconf_key = VCONFKEY_SETAPPL_ACCESSIBILITY_TTS; + + _global_app->universal_switch.name = "universal-switch"; + _global_app->universal_switch.app_control_operation = APP_CONTROL_OPERATION_UNIVERSAL_SWITCH; + _global_app->universal_switch.vconf_key = VCONFKEY_SETAPPL_ACCESSIBILITY_UNIVERSAL_SWITCH; + for (i = 1; i < argc; i++) { if (!strcmp (argv[i], "--launch-immediately")) @@ -886,7 +949,7 @@ main (int argc, introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); g_assert (introspection_data != NULL); - name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + g_bus_own_name (G_BUS_TYPE_SESSION, "org.a11y.Bus", G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, on_bus_acquired, @@ -895,20 +958,10 @@ main (int argc, _global_app, NULL); - int ret = vconf_get_bool(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, &_global_app->screen_reader_needed); - if (ret != 0) - { - LOGD("Could not read VCONFKEY_SETAPPL_ACCESSIBILITY_TTS key value.\n"); - return FALSE; - } - ret = vconf_notify_key_changed(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, screen_reader_cb, _global_app); - if(ret != 0) - { - LOGD("Could not add information level callback\n"); - return FALSE; - } - if (_global_app->screen_reader_needed) - g_timeout_add_seconds(2,_launch_screen_reader_repeat_until_success, _global_app); + if(!register_client(&_global_app->screen_reader)) + return FALSE; + if(!register_client(&_global_app->universal_switch)) + return FALSE; g_main_loop_run (_global_app->loop); -- 2.7.4 From f0a290a4980997257594fd44f0ba4b71689edb7b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Pawe=C5=82=20Stawicki?= Date: Thu, 7 Sep 2017 12:37:38 +0200 Subject: [PATCH 09/16] bugfix: at-spi-bus-launcher terminates when unviersal-switch vconf key not defined Change-Id: I4a891f300aeb4261938a9e5dbdf4cdf5749c7d05 --- bus/at-spi-bus-launcher.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bus/at-spi-bus-launcher.c b/bus/at-spi-bus-launcher.c index 7948d1b..d180a15 100644 --- a/bus/at-spi-bus-launcher.c +++ b/bus/at-spi-bus-launcher.c @@ -958,10 +958,8 @@ main (int argc, _global_app, NULL); - if(!register_client(&_global_app->screen_reader)) - return FALSE; - if(!register_client(&_global_app->universal_switch)) - return FALSE; + register_client (&_global_app->screen_reader); + register_client (&_global_app->universal_switch); g_main_loop_run (_global_app->loop); -- 2.7.4 From 5e6b82550dd2635f9a18eb82ab72fced8e3e3fcc Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Tue, 12 Sep 2017 20:37:56 +0900 Subject: [PATCH 10/16] check reply message of "GetNeighbor" The dbus_message_iter_init makes a crash if a message(reply) is NULL. The message(reply) is NULL with following error message. error message: Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken. Change-Id: I64c9f468f26299af86ee52dff3714b03112d49e3 --- atspi/atspi-accessible.c | 1 + 1 file changed, 1 insertion(+) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index 40053f2..752001c 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -617,6 +617,7 @@ atspi_accessible_get_neighbor (AtspiAccessible *root, const char *path = are_objects_on_the_same_bus(root, start) ? root_path : ""; DBusMessage *reply = _atspi_dbus_call_partial (start, atspi_interface_accessible, "GetNeighbor", error, "sii", path, (int)direction, (int)search_mode); + _ATSPI_DBUS_CHECK_SIG (reply, "(so)y", error, NULL); dbus_message_iter_init (reply, &iter); AtspiAccessible *ret = _atspi_dbus_return_accessible_from_iter (&iter); -- 2.7.4 From 616b87d69d8938f13ded9c6298c9f8b5256d617a Mon Sep 17 00:00:00 2001 From: Radoslaw Cybulski Date: Tue, 12 Sep 2017 14:45:33 +0200 Subject: [PATCH 11/16] Fix crash, when dbus call in GetNeighbor fails Failed dbus call would cause a crash by trying to get an iterator to a reply message, which is null. This patch cleans up _atspi_dbus_call_partial function to always return NULL on failure and fixes NULL reply handling in atspi_accessible_get_neighbor and atspi_accessible_get_navigable_at_point functions. Change-Id: Ie158a79f8f1452c9a5685137c9b42104563dc717 --- atspi/atspi-accessible.c | 10 ++++++++-- atspi/atspi-misc.c | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index 752001c..7e889e9 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -346,7 +346,12 @@ atspi_accessible_get_navigable_at_point (AtspiAccessible *root, g_return_val_if_fail (root != NULL, NULL); do { reply = _atspi_dbus_call_partial (root, atspi_interface_accessible, "GetNavigableAtPoint", error, "iiu", d_x, d_y, d_ctype); - + // call failed, error is set, so we bail out + if (!reply) { + if (deputy) g_object_unref(deputy); + if (return_value) g_object_unref(return_value); + return NULL; + } _ATSPI_DBUS_CHECK_SIG (reply, "(so)y(so)", NULL, NULL); dbus_message_iter_init (reply, &iter); @@ -616,6 +621,8 @@ atspi_accessible_get_neighbor (AtspiAccessible *root, while(1) { const char *path = are_objects_on_the_same_bus(root, start) ? root_path : ""; DBusMessage *reply = _atspi_dbus_call_partial (start, atspi_interface_accessible, "GetNeighbor", error, "sii", path, (int)direction, (int)search_mode); + // call failed, error is set, so we bail out + if (!reply) break; _ATSPI_DBUS_CHECK_SIG (reply, "(so)y", error, NULL); dbus_message_iter_init (reply, &iter); @@ -684,7 +691,6 @@ atspi_accessible_get_neighbor (AtspiAccessible *root, // nothing found g_object_unref(start); - return_value = NULL; break; } while(!g_queue_is_empty(children_root_stack)) diff --git a/atspi/atspi-misc.c b/atspi/atspi-misc.c index 02eab8c..5895935 100644 --- a/atspi/atspi-misc.c +++ b/atspi/atspi-misc.c @@ -1153,6 +1153,7 @@ out: dbus_error_free (&err); if (reply) dbus_message_unref(reply); + return NULL; } else if (reply && dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { -- 2.7.4 From c9981714ce932e989ab9e5bc0cb5011ff67eaeb3 Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Tue, 19 Sep 2017 19:59:53 +0900 Subject: [PATCH 12/16] Add atspi_accessible_get_default_label_info It is not possible to support default label feature using current at-spi2-core APIs without much of IPC. The following would be difficult case to cover. (top of accessible tree) - (bottom side) PageTab1 - Panel1 - PageTab2 - Panel2 - PageTab3 - Panel3(currently showing) Application could make as below: PageTab1 - Panel1 - PageTab3 - Panel3 - PageTab2 - Panel2(currently showing) or following tree would normally be possilbe: PageTab1 - Panel1 - PageTab2 - Panel2(currently showing) There are much of complicated case over there. So we are handling the default label object stack on toolkit(Elementary) side. The atspi_accessible_get_default_label_info returns default label information to be used screen-reader side. This is not stable. And this depends on toolkit side UI definition. The candidate of default label object could be changed by UI definition. AtspiAccessibleDefaultLabelInfo *dli; dli = atspi_accessible_get_default_label_info(root, &error); You have to free alocated resource as below, if it is not necessary any more. g_object_unref(dli->obj); free(dli); Change-Id: I444906e6d020ea6fe2536a73473e0e796a015d05 --- atspi/atspi-accessible.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ atspi/atspi-accessible.h | 8 ++++++++ atspi/atspi-types.h | 1 + 3 files changed, 57 insertions(+) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index 7e889e9..6612a21 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -565,6 +565,54 @@ atspi_accessible_get_reading_material (AtspiAccessible *obj, GError **error) return reading_material; } +/** + * atspi_accessible_get_default_label_info: + * @obj: a pointer to the #AtspiAccessible object would be window. + * + * Gets default label information + * + * Returns: default label information to be used screen-reader side. + * This is not stable. And this depends on toolkit side UI definition. + * The candidate of default label object could be changed by UI definition. + * You have to handle all alocated memory as below on screen-reader side. + * + * AtspiAccessibleDefaultLabelInfo *dli + * g_object_unref(dli->obj); + * free(dli); + **/ +AtspiAccessibleDefaultLabelInfo * +atspi_accessible_get_default_label_info (AtspiAccessible *obj, GError **error) +{ + AtspiAccessibleDefaultLabelInfo *default_label_info = NULL; + AtspiAccessible *default_label_object; + dbus_uint32_t role; + DBusMessage *reply; + DBusMessageIter iter; + + g_return_val_if_fail (obj != NULL, NULL); + + reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetDefaultLabelInfo", error, ""); + + _ATSPI_DBUS_CHECK_SIG (reply, "(so)u", NULL, NULL); + + default_label_info = calloc(1, sizeof(AtspiAccessibleDefaultLabelInfo)); + if (!default_label_info) + { + return default_label_info; + } + + dbus_message_iter_init (reply, &iter); + + default_label_object = _atspi_dbus_return_accessible_from_iter (&iter); + default_label_info->obj = default_label_object; + + dbus_message_iter_get_basic (&iter, &role); + default_label_info->role = role; + dbus_message_iter_next (&iter); + + return default_label_info; +} + static unsigned char are_objects_on_the_same_bus(AtspiAccessible *obj1, AtspiAccessible *obj2) { const char *bus_name_1 = obj1->parent.app->bus_name; diff --git a/atspi/atspi-accessible.h b/atspi/atspi-accessible.h index 0b5152b..9efc168 100644 --- a/atspi/atspi-accessible.h +++ b/atspi/atspi-accessible.h @@ -45,6 +45,12 @@ G_BEGIN_DECLS typedef struct _AtspiAccessiblePrivate AtspiAccessiblePrivate; +struct _AtspiAccessibleDefaultLabelInfo +{ + AtspiAccessible *obj; + AtspiRole role; +}; + struct _AtspiAccessibleReadingMaterial { AtspiAccessible *parent; @@ -115,6 +121,8 @@ AtspiAccessible *atspi_accessible_get_neighbor (AtspiAccessible *root, AtspiAcce AtspiAccessibleReadingMaterial *atspi_accessible_get_reading_material (AtspiAccessible *obj, GError **error); +AtspiAccessibleDefaultLabelInfo *atspi_accessible_get_default_label_info (AtspiAccessible *obj, GError **error); + AtspiAccessible * atspi_accessible_get_parent (AtspiAccessible *obj, GError **error); gint atspi_accessible_get_child_count (AtspiAccessible *obj, GError **error); diff --git a/atspi/atspi-types.h b/atspi/atspi-types.h index 45b815c..d09a972 100644 --- a/atspi/atspi-types.h +++ b/atspi/atspi-types.h @@ -44,6 +44,7 @@ typedef struct _AtspiTableCell AtspiTableCell; typedef struct _AtspiText AtspiText; typedef struct _AtspiValue AtspiValue; typedef struct _AtspiAccessibleReadingMaterial AtspiAccessibleReadingMaterial; +typedef struct _AtspiAccessibleDefaultLabelInfo AtspiAccessibleDefaultLabelInfo; typedef guint AtspiControllerEventMask; -- 2.7.4 From 819f38462c452c3cd7d7fb08adae1f4e018a9c01 Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Tue, 17 Oct 2017 21:02:32 +0900 Subject: [PATCH 13/16] test: enhance accessible tree information Enhance node information using eo address expression Change-Id: I02e057013812073e7b7484cf3d87516f6a9d9053 --- test/at_spi2_tool.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/at_spi2_tool.c b/test/at_spi2_tool.c index 08c2af5..c391003 100644 --- a/test/at_spi2_tool.c +++ b/test/at_spi2_tool.c @@ -6,6 +6,7 @@ #include #include #include +#include #define ERROR_STATE -1 #define SAFE_BUFFER_SIZE 2048 @@ -300,6 +301,9 @@ static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes char *node_name = atspi_accessible_get_name(node, NULL); char *node_role_name = atspi_accessible_get_role_name(node, NULL); char *unique_id = atspi_accessible_get_unique_id(node, NULL); + char *path = atspi_accessible_get_path(node, NULL); + unsigned long long eo_ptr = 0; + sscanf(path, "%llu", &eo_ptr); char *attributes = _get_attributes(node, length_limit, attributes_are_too_long); Box_Size *box_size = _get_box_size(node); @@ -309,8 +313,8 @@ static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes bool current_node_has_relations = (relations && relations->len); char result[SAFE_BUFFER_SIZE]; - int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s],[%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s]]", - unique_id, + int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s(%p)],[%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s]]", + unique_id, (uintptr_t)eo_ptr, node_role_name, attributes, box_size->x, @@ -330,6 +334,7 @@ static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes free(node_name); free(node_role_name); free(unique_id); + free(path); free(attributes); if (box_size) { free(box_size->width); -- 2.7.4 From a97cc93731bec6122888c929726a0bdb864be0bb Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Wed, 18 Oct 2017 17:23:17 +0900 Subject: [PATCH 14/16] test : enhance accessible state information Change-Id: I112a34525f66e204aa5dbab51ad0faadbf25d3da --- test/at_spi2_tool.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/at_spi2_tool.c b/test/at_spi2_tool.c index 08c2af5..cdbc141 100644 --- a/test/at_spi2_tool.c +++ b/test/at_spi2_tool.c @@ -249,8 +249,11 @@ static char *_get_states(AtspiAccessible *node, int length_limit) for (int i = 0; i < states->len; i++) { state_type = g_array_index(states, AtspiStateType, i); - char node_state_str[8]; - snprintf(node_state_str, 8, "(%d)", state_type); + char node_state_str[27] = ""; + strncat(node_state_str, "(", sizeof(node_state_str) - strlen(node_state_str) - 1); + strncat(node_state_str, atspi_state_names[state_type], sizeof(node_state_str) - strlen(node_state_str) - 1); + strncat(node_state_str, ")", sizeof(node_state_str) - strlen(node_state_str) - 1); + _combine_strings(&state_string, node_state_str); } -- 2.7.4 From b5105db0d1f719f5b0fd45d0fd02e9ca1b8b9b51 Mon Sep 17 00:00:00 2001 From: Radoslaw Cybulski Date: Tue, 6 Mar 2018 18:24:49 +0100 Subject: [PATCH 15/16] Fixes glib runtime warnings on freeing empty arrays Change-Id: Iba679a3cfbcdcdfd252c291d5f8bcf433c4d7198 --- test/at_spi2_tool.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/at_spi2_tool.c b/test/at_spi2_tool.c index 81bf021..c0daae4 100644 --- a/test/at_spi2_tool.c +++ b/test/at_spi2_tool.c @@ -258,7 +258,8 @@ static char *_get_states(AtspiAccessible *node, int length_limit) _combine_strings(&state_string, node_state_str); } - g_array_free(states, 0); + if (states) + g_array_free(states, 0); g_object_unref(node_state_set); _truncate_string(state_string, length_limit); @@ -345,7 +346,8 @@ static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes free(box_size); } free(states); - g_array_free(relations, TRUE); + if (relations) + g_array_free(relations, TRUE); return g_strdup(result); } @@ -499,7 +501,8 @@ static void _print_relations_for_object(AtspiAccessible *node) { printf("\n"); _print_horizontal_line_in_relations_table(); - g_array_free(relations, TRUE); + if (relations) + g_array_free(relations, TRUE); } typedef void (*print_information_about_object_function)(AtspiAccessible *); -- 2.7.4 From 3627a21a62fe61592d92968e3dc67a78fde63bf0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Pawe=C5=82=20Stawicki?= Date: Thu, 15 Mar 2018 18:21:04 +0100 Subject: [PATCH 16/16] [prevent][109656] Fix for resource leakage Change-Id: Ic2abd2a7c7eafd530d147711d8881fb87788e140 --- test/memory.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/memory.c b/test/memory.c index df5e0a1..2db9871 100644 --- a/test/memory.c +++ b/test/memory.c @@ -13,18 +13,17 @@ basic (AtspiAccessible *obj) gint count; gint i; AtspiAccessible *accessible; - GError *error = NULL; - str = atspi_accessible_get_name (obj, &error); + str = atspi_accessible_get_name (obj, NULL); if (str) g_free (str); accessible = atspi_accessible_get_parent (obj, NULL); if (accessible) g_object_unref (accessible); - count = atspi_accessible_get_child_count (obj, &error); + count = atspi_accessible_get_child_count (obj, NULL); for (i = 0; i < count; i++) { - accessible = atspi_accessible_get_child_at_index (obj, i, &error); + accessible = atspi_accessible_get_child_at_index (obj, i, NULL); if (accessible) g_object_unref (accessible); } -- 2.7.4