0

Add Aura/Linux support to ax_dump_events

Bug: 879147
Change-Id: I167a41fc75206e631d09608acf748a11ef143c22
Reviewed-on: https://chromium-review.googlesource.com/1210182
Commit-Queue: Martin Robinson <mrobinson@igalia.com>
Reviewed-by: Jochen Eisinger <jochen@chromium.org>
Reviewed-by: Dominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#591989}
This commit is contained in:
Martin Robinson
2018-09-18 10:30:21 +00:00
committed by Commit Bot
parent 38300420bf
commit e4fddeded4
13 changed files with 464 additions and 77 deletions

@ -744,7 +744,10 @@ group("gn_all") {
}
if (use_atk) {
deps += [ "//tools/accessibility/inspect:ax_dump_tree" ]
deps += [
"//tools/accessibility/inspect:ax_dump_events",
"//tools/accessibility/inspect:ax_dump_tree",
]
}
}

@ -11,8 +11,7 @@
namespace content {
AccessibilityEventRecorder::AccessibilityEventRecorder(
BrowserAccessibilityManager* manager,
base::ProcessId pid)
BrowserAccessibilityManager* manager)
: manager_(manager) {}
AccessibilityEventRecorder::~AccessibilityEventRecorder() = default;
@ -21,8 +20,9 @@ AccessibilityEventRecorder::~AccessibilityEventRecorder() = default;
// static
AccessibilityEventRecorder& AccessibilityEventRecorder::GetInstance(
BrowserAccessibilityManager* manager,
base::ProcessId pid) {
static base::NoDestructor<AccessibilityEventRecorder> instance(manager, pid);
base::ProcessId pid,
const base::StringPiece& application_name_match_pattern) {
static base::NoDestructor<AccessibilityEventRecorder> instance(manager);
return *instance;
}
#endif

@ -40,7 +40,9 @@ class AccessibilityEventRecorder {
// Get the right platform-specific subclass.
static AccessibilityEventRecorder& GetInstance(
BrowserAccessibilityManager* manager = nullptr,
base::ProcessId pid = 0);
base::ProcessId pid = 0,
const base::StringPiece& application_name_match_pattern =
base::StringPiece());
virtual ~AccessibilityEventRecorder();
void set_only_web_events(bool only_web_events) {
@ -55,8 +57,7 @@ class AccessibilityEventRecorder {
const std::vector<std::string>& event_logs() { return event_logs_; }
protected:
AccessibilityEventRecorder(BrowserAccessibilityManager* manager,
base::ProcessId pid);
AccessibilityEventRecorder(BrowserAccessibilityManager* manager);
void OnEvent(const std::string& event);

@ -6,9 +6,13 @@
#include <atk/atk.h>
#include <atk/atkutil.h>
#include <atspi/atspi.h>
#include "base/process/process_handle.h"
#include "base/stl_util.h"
#include "base/strings/pattern.h"
#include "base/strings/stringprintf.h"
#include "content/browser/accessibility/accessibility_tree_formatter_utils_auralinux.h"
#include "content/browser/accessibility/browser_accessibility_auralinux.h"
#include "content/browser/accessibility/browser_accessibility_manager.h"
@ -18,82 +22,114 @@
namespace content {
// This class has two distinct event recording code paths. When we are
// recording events in-process (typically this is used for
// DumpAccessibilityEvents tests), we use ATK's global event handlers. Since
// ATK doesn't support intercepting events from other processes, if we have a
// non-zero PID or an accessibility application name pattern, we use AT-SPI2
// directly to intercept events. Since AT-SPI2 should be capable of
// intercepting events in-process as well, eventually it would be nice to
// remove the ATK code path entirely.
class AccessibilityEventRecorderAuraLinux : public AccessibilityEventRecorder {
public:
explicit AccessibilityEventRecorderAuraLinux(
BrowserAccessibilityManager* manager,
base::ProcessId pid);
base::ProcessId pid,
const base::StringPiece& application_name_match_pattern);
~AccessibilityEventRecorderAuraLinux() override;
void ProcessEvent(const char* event,
unsigned int n_params,
const GValue* params);
void ProcessATKEvent(const char* event,
unsigned int n_params,
const GValue* params);
void ProcessATSPIEvent(const AtspiEvent* event);
private:
void AddGlobalListener(const char* event_name);
void AddGlobalListeners();
void RemoveGlobalListeners();
bool ShouldUseATSPI();
void AddATKEventListener(const char* event_name);
void AddATKEventListeners();
void RemoveATKEventListeners();
bool IncludeState(AtkStateType state_type);
std::vector<unsigned int> listener_ids_;
void AddATSPIEventListeners();
void RemoveATSPIEventListeners();
AtspiEventListener* atspi_event_listener_ = nullptr;
base::ProcessId pid_;
base::StringPiece application_name_match_pattern_;
std::vector<unsigned int> atk_listener_ids_;
};
// static
gboolean OnEventReceived(GSignalInvocationHint* hint,
unsigned int n_params,
const GValue* params,
gpointer data) {
gboolean OnATKEventReceived(GSignalInvocationHint* hint,
unsigned int n_params,
const GValue* params,
gpointer data) {
GSignalQuery query;
g_signal_query(hint->signal_id, &query);
static_cast<AccessibilityEventRecorderAuraLinux&>(
AccessibilityEventRecorder::GetInstance())
.ProcessEvent(query.signal_name, n_params, params);
.ProcessATKEvent(query.signal_name, n_params, params);
return true;
}
// static
AccessibilityEventRecorder& AccessibilityEventRecorder::GetInstance(
BrowserAccessibilityManager* manager,
base::ProcessId pid) {
base::ProcessId pid,
const base::StringPiece& application_name_match_pattern) {
static base::NoDestructor<AccessibilityEventRecorderAuraLinux> instance(
manager, pid);
manager, pid, application_name_match_pattern);
return *instance;
}
bool AccessibilityEventRecorderAuraLinux::ShouldUseATSPI() {
return pid_ != base::GetCurrentProcId() ||
!application_name_match_pattern_.empty();
}
AccessibilityEventRecorderAuraLinux::AccessibilityEventRecorderAuraLinux(
BrowserAccessibilityManager* manager,
base::ProcessId pid)
: AccessibilityEventRecorder(manager, pid) {
AddGlobalListeners();
base::ProcessId pid,
const base::StringPiece& application_name_match_pattern)
: AccessibilityEventRecorder(manager),
pid_(pid),
application_name_match_pattern_(application_name_match_pattern) {
if (ShouldUseATSPI())
AddATSPIEventListeners();
else
AddATKEventListeners();
}
AccessibilityEventRecorderAuraLinux::~AccessibilityEventRecorderAuraLinux() {}
AccessibilityEventRecorderAuraLinux::~AccessibilityEventRecorderAuraLinux() {
RemoveATSPIEventListeners();
}
void AccessibilityEventRecorderAuraLinux::AddGlobalListener(
void AccessibilityEventRecorderAuraLinux::AddATKEventListener(
const char* event_name) {
unsigned id = atk_add_global_event_listener(OnEventReceived, event_name);
unsigned id = atk_add_global_event_listener(OnATKEventReceived, event_name);
if (!id)
LOG(FATAL) << "atk_add_global_event_listener failed for " << event_name;
listener_ids_.push_back(id);
atk_listener_ids_.push_back(id);
}
void AccessibilityEventRecorderAuraLinux::AddGlobalListeners() {
void AccessibilityEventRecorderAuraLinux::AddATKEventListeners() {
GObject* gobject = G_OBJECT(g_object_new(G_TYPE_OBJECT, nullptr, nullptr));
g_object_unref(atk_no_op_object_new(gobject));
g_object_unref(gobject);
AddGlobalListener("ATK:AtkObject:state-change");
AddGlobalListener("ATK:AtkObject:focus-event");
AddGlobalListener("ATK:AtkObject:property-change");
AddATKEventListener("ATK:AtkObject:state-change");
AddATKEventListener("ATK:AtkObject:focus-event");
AddATKEventListener("ATK:AtkObject:property-change");
}
void AccessibilityEventRecorderAuraLinux::RemoveGlobalListeners() {
for (const auto& id : listener_ids_)
void AccessibilityEventRecorderAuraLinux::RemoveATKEventListeners() {
for (const auto& id : atk_listener_ids_)
atk_remove_global_event_listener(id);
listener_ids_.clear();
atk_listener_ids_.clear();
}
// Pruning states which are not supported on older bots makes it possible to
@ -114,12 +150,13 @@ bool AccessibilityEventRecorderAuraLinux::IncludeState(
}
}
void AccessibilityEventRecorderAuraLinux::ProcessEvent(const char* event,
unsigned int n_params,
const GValue* params) {
void AccessibilityEventRecorderAuraLinux::ProcessATKEvent(
const char* event,
unsigned int n_params,
const GValue* params) {
// If we don't have a root object, it means the tree is being destroyed.
if (!manager_->GetRoot()) {
RemoveGlobalListeners();
RemoveATKEventListeners();
return;
}
@ -165,4 +202,180 @@ void AccessibilityEventRecorderAuraLinux::ProcessEvent(const char* event,
OnEvent(log);
}
// This list is composed of the sorted event names taken from the list provided
// in the libatspi documentation at:
// https://developer.gnome.org/libatspi/stable/AtspiEventListener.html#atspi-event-listener-register
const char* const kEventNames[] = {
"object:active-descendant-changed",
"object:children-changed",
"object:column-deleted",
"object:column-inserted",
"object:column-reordered",
"object:model-changed",
"object:property-change",
"object:property-change:accessible-description",
"object:property-change:accessible-name",
"object:property-change:accessible-parent",
"object:property-change:accessible-role",
"object:property-change:accessible-table-caption",
"object:property-change:accessible-table-column-description",
"object:property-change:accessible-table-column-header",
"object:property-change:accessible-table-row-description",
"object:property-change:accessible-table-row-header",
"object:property-change:accessible-table-summary",
"object:property-change:accessible-value",
"object:row-deleted",
"object:row-inserted",
"object:row-reordered",
"object:selection-changed",
"object:state-changed",
"object:text-caret-moved",
"object:text-changed",
"object:text-selection-changed",
"object:visible-data-changed",
"window:activate",
"window:close",
"window:create",
"window:deactivate",
"window:desktop-create",
"window:desktop-destroy",
"window:lower",
"window:maximize",
"window:minimize",
"window:move",
"window:raise",
"window:reparent",
"window:resize",
"window:restore",
"window:restyle",
"window:shade",
"window:unshade",
};
static void OnATSPIEventReceived(AtspiEvent* event, void* data) {
static_cast<AccessibilityEventRecorderAuraLinux*>(data)->ProcessATSPIEvent(
event);
g_boxed_free(ATSPI_TYPE_EVENT, static_cast<void*>(event));
}
void AccessibilityEventRecorderAuraLinux::AddATSPIEventListeners() {
atspi_init();
atspi_event_listener_ =
atspi_event_listener_new(OnATSPIEventReceived, this, nullptr);
GError* error = nullptr;
for (size_t i = 0; i < base::size(kEventNames); i++) {
atspi_event_listener_register(atspi_event_listener_, kEventNames[i],
&error);
if (error) {
LOG(ERROR) << "Could not register event listener for " << kEventNames[i];
g_clear_error(&error);
}
}
}
void AccessibilityEventRecorderAuraLinux::RemoveATSPIEventListeners() {
if (!atspi_event_listener_)
return;
GError* error = nullptr;
for (size_t i = 0; i < base::size(kEventNames); i++) {
atspi_event_listener_deregister(atspi_event_listener_, kEventNames[i],
nullptr);
if (error) {
LOG(ERROR) << "Could not deregister event listener for "
<< kEventNames[i];
g_clear_error(&error);
}
}
g_object_unref(atspi_event_listener_);
atspi_event_listener_ = nullptr;
}
void AccessibilityEventRecorderAuraLinux::ProcessATSPIEvent(
const AtspiEvent* event) {
GError* error = nullptr;
if (!application_name_match_pattern_.empty()) {
AtspiAccessible* application =
atspi_accessible_get_application(event->source, &error);
if (error || !application)
return;
char* application_name = atspi_accessible_get_name(application, &error);
g_object_unref(application);
if (error || !application_name) {
g_clear_error(&error);
return;
}
if (!base::MatchPattern(application_name,
application_name_match_pattern_)) {
return;
}
free(application_name);
}
if (pid_) {
int pid = atspi_accessible_get_process_id(event->source, &error);
if (!error && pid != pid_)
return;
g_clear_error(&error);
}
std::stringstream output;
output << event->type << " ";
GHashTable* attributes =
atspi_accessible_get_attributes(event->source, &error);
std::string html_tag, html_class, html_id;
if (!error && attributes) {
if (char* tag = static_cast<char*>(g_hash_table_lookup(attributes, "tag")))
html_tag = tag;
if (char* id = static_cast<char*>(g_hash_table_lookup(attributes, "id")))
html_id = id;
if (char* class_chars =
static_cast<char*>(g_hash_table_lookup(attributes, "class")))
html_class = std::string(".") + class_chars;
g_hash_table_unref(attributes);
}
g_clear_error(&error);
if (!html_tag.empty())
output << "<" << html_tag << html_id << html_class << ">";
AtspiRole role = atspi_accessible_get_role(event->source, &error);
output << "role=";
if (!error)
output << ATSPIRoleToString(role);
else
output << "#error";
g_clear_error(&error);
char* name = atspi_accessible_get_name(event->source, &error);
output << " name=";
if (!error && name)
output << name;
else
output << "#error";
g_clear_error(&error);
free(name);
AtspiStateSet* atspi_states = atspi_accessible_get_state_set(event->source);
GArray* state_array = atspi_state_set_get_states(atspi_states);
std::vector<std::string> states;
for (unsigned i = 0; i < state_array->len; i++) {
AtspiStateType state_type = g_array_index(state_array, AtspiStateType, i);
states.push_back(ATSPIStateToString(state_type));
}
g_array_free(state_array, TRUE);
g_object_unref(atspi_states);
output << " ";
std::copy(states.begin(), states.end(),
std::ostream_iterator<std::string>(output, ", "));
OnEvent(output.str());
}
} // namespace content

@ -8,6 +8,7 @@
#include <string>
#include "base/logging.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/strings/stringprintf.h"
@ -63,7 +64,14 @@ static void EventReceivedThunk(
// static
AccessibilityEventRecorder& AccessibilityEventRecorder::GetInstance(
BrowserAccessibilityManager* manager,
base::ProcessId pid) {
base::ProcessId pid,
const base::StringPiece& application_name_match_pattern) {
if (!application_name_match_pattern.empty()) {
LOG(ERROR) << "Recording accessibility events from an application name "
"match pattern not supported on this platform yet.";
NOTREACHED();
}
static base::NoDestructor<AccessibilityEventRecorderMac> instance(manager,
pid);
return *instance;
@ -72,8 +80,7 @@ AccessibilityEventRecorder& AccessibilityEventRecorder::GetInstance(
AccessibilityEventRecorderMac::AccessibilityEventRecorderMac(
BrowserAccessibilityManager* manager,
base::ProcessId pid)
: AccessibilityEventRecorder(manager, pid),
observer_run_loop_source_(NULL) {
: AccessibilityEventRecorder(manager), observer_run_loop_source_(NULL) {
if (kAXErrorSuccess != AXObserverCreate(pid, EventReceivedThunk,
observer_ref_.InitializeInto())) {
LOG(FATAL) << "Failed to create AXObserverRef";

@ -87,8 +87,10 @@ class AccessibilityEventRecorderWin : public AccessibilityEventRecorder {
DWORD event_time);
private:
AccessibilityEventRecorderWin(BrowserAccessibilityManager* manager,
base::ProcessId pid);
AccessibilityEventRecorderWin(
BrowserAccessibilityManager* manager,
base::ProcessId pid,
const base::StringPiece& application_name_match_pattern);
// Called by the thunk registered by SetWinEventHook. Retrieves accessibility
// info about the node the event was fired on and appends a string to
@ -115,9 +117,16 @@ class AccessibilityEventRecorderWin : public AccessibilityEventRecorder {
// static
AccessibilityEventRecorder& AccessibilityEventRecorder::GetInstance(
BrowserAccessibilityManager* manager,
base::ProcessId pid) {
static base::NoDestructor<AccessibilityEventRecorderWin> instance(manager,
pid);
base::ProcessId pid,
const base::StringPiece& application_name_match_pattern) {
if (!application_name_match_pattern.empty()) {
LOG(ERROR) << "Recording accessibility events from an application name "
"match pattern not supported on this platform yet.";
NOTREACHED();
}
static base::NoDestructor<AccessibilityEventRecorderWin> instance(
manager, pid, application_name_match_pattern);
return *instance;
}
@ -137,8 +146,9 @@ CALLBACK void AccessibilityEventRecorderWin::WinEventHookThunk(
AccessibilityEventRecorderWin::AccessibilityEventRecorderWin(
BrowserAccessibilityManager* manager,
base::ProcessId pid)
: AccessibilityEventRecorder(manager, pid) {
base::ProcessId pid,
const base::StringPiece& application_name_match_pattern)
: AccessibilityEventRecorder(manager) {
// For now, just use out of context events when running as a utility to watch
// events (no BrowserAccessibilityManager), because otherwise Chrome events
// are not getting reported. Being in context is better so that for

@ -82,4 +82,136 @@ CONTENT_EXPORT const char* ATSPIStateToString(AtspiStateType state) {
state);
}
CONTENT_EXPORT const char* ATSPIRoleToString(AtspiRole role) {
static const PlatformConstantToNameEntry role_table[] = {
QUOTE(ATSPI_ROLE_ACCELERATOR_LABEL),
QUOTE(ATSPI_ROLE_ALERT),
QUOTE(ATSPI_ROLE_ANIMATION),
QUOTE(ATSPI_ROLE_APPLICATION),
QUOTE(ATSPI_ROLE_ARROW),
QUOTE(ATSPI_ROLE_ARTICLE),
QUOTE(ATSPI_ROLE_AUDIO),
QUOTE(ATSPI_ROLE_AUTOCOMPLETE),
QUOTE(ATSPI_ROLE_BLOCK_QUOTE),
QUOTE(ATSPI_ROLE_CALENDAR),
QUOTE(ATSPI_ROLE_CANVAS),
QUOTE(ATSPI_ROLE_CAPTION),
QUOTE(ATSPI_ROLE_CHART),
QUOTE(ATSPI_ROLE_CHECK_BOX),
QUOTE(ATSPI_ROLE_CHECK_MENU_ITEM),
QUOTE(ATSPI_ROLE_COLOR_CHOOSER),
QUOTE(ATSPI_ROLE_COLUMN_HEADER),
QUOTE(ATSPI_ROLE_COMBO_BOX),
QUOTE(ATSPI_ROLE_COMMENT),
QUOTE(ATSPI_ROLE_DATE_EDITOR),
QUOTE(ATSPI_ROLE_DEFINITION),
QUOTE(ATSPI_ROLE_DESCRIPTION_LIST),
QUOTE(ATSPI_ROLE_DESCRIPTION_TERM),
QUOTE(ATSPI_ROLE_DESCRIPTION_VALUE),
QUOTE(ATSPI_ROLE_DESKTOP_FRAME),
QUOTE(ATSPI_ROLE_DESKTOP_ICON),
QUOTE(ATSPI_ROLE_DIAL),
QUOTE(ATSPI_ROLE_DIALOG),
QUOTE(ATSPI_ROLE_DIRECTORY_PANE),
QUOTE(ATSPI_ROLE_DOCUMENT_EMAIL),
QUOTE(ATSPI_ROLE_DOCUMENT_FRAME),
QUOTE(ATSPI_ROLE_DOCUMENT_PRESENTATION),
QUOTE(ATSPI_ROLE_DOCUMENT_SPREADSHEET),
QUOTE(ATSPI_ROLE_DOCUMENT_TEXT),
QUOTE(ATSPI_ROLE_DOCUMENT_WEB),
QUOTE(ATSPI_ROLE_DRAWING_AREA),
QUOTE(ATSPI_ROLE_EDITBAR),
QUOTE(ATSPI_ROLE_EMBEDDED),
QUOTE(ATSPI_ROLE_ENTRY),
QUOTE(ATSPI_ROLE_EXTENDED),
QUOTE(ATSPI_ROLE_FILE_CHOOSER),
QUOTE(ATSPI_ROLE_FILLER),
QUOTE(ATSPI_ROLE_FOCUS_TRAVERSABLE),
QUOTE(ATSPI_ROLE_FONT_CHOOSER),
QUOTE(ATSPI_ROLE_FOOTER),
QUOTE(ATSPI_ROLE_FOOTNOTE),
QUOTE(ATSPI_ROLE_FORM),
QUOTE(ATSPI_ROLE_FRAME),
QUOTE(ATSPI_ROLE_GLASS_PANE),
QUOTE(ATSPI_ROLE_GROUPING),
QUOTE(ATSPI_ROLE_HEADER),
QUOTE(ATSPI_ROLE_HEADING),
QUOTE(ATSPI_ROLE_HTML_CONTAINER),
QUOTE(ATSPI_ROLE_ICON),
QUOTE(ATSPI_ROLE_IMAGE),
QUOTE(ATSPI_ROLE_IMAGE_MAP),
QUOTE(ATSPI_ROLE_INFO_BAR),
QUOTE(ATSPI_ROLE_INPUT_METHOD_WINDOW),
QUOTE(ATSPI_ROLE_INTERNAL_FRAME),
QUOTE(ATSPI_ROLE_INVALID),
QUOTE(ATSPI_ROLE_LABEL),
QUOTE(ATSPI_ROLE_LANDMARK),
QUOTE(ATSPI_ROLE_LAYERED_PANE),
QUOTE(ATSPI_ROLE_LEVEL_BAR),
QUOTE(ATSPI_ROLE_LINK),
QUOTE(ATSPI_ROLE_LIST),
QUOTE(ATSPI_ROLE_LIST_BOX),
QUOTE(ATSPI_ROLE_LIST_ITEM),
QUOTE(ATSPI_ROLE_LOG),
QUOTE(ATSPI_ROLE_MARQUEE),
QUOTE(ATSPI_ROLE_MATH),
QUOTE(ATSPI_ROLE_MATH_FRACTION),
QUOTE(ATSPI_ROLE_MATH_ROOT),
QUOTE(ATSPI_ROLE_MENU),
QUOTE(ATSPI_ROLE_MENU_BAR),
QUOTE(ATSPI_ROLE_MENU_ITEM),
QUOTE(ATSPI_ROLE_NOTIFICATION),
QUOTE(ATSPI_ROLE_OPTION_PANE),
QUOTE(ATSPI_ROLE_PAGE),
QUOTE(ATSPI_ROLE_PAGE_TAB),
QUOTE(ATSPI_ROLE_PAGE_TAB_LIST),
QUOTE(ATSPI_ROLE_PANEL),
QUOTE(ATSPI_ROLE_PARAGRAPH),
QUOTE(ATSPI_ROLE_PASSWORD_TEXT),
QUOTE(ATSPI_ROLE_POPUP_MENU),
QUOTE(ATSPI_ROLE_PROGRESS_BAR),
QUOTE(ATSPI_ROLE_PUSH_BUTTON),
QUOTE(ATSPI_ROLE_RADIO_BUTTON),
QUOTE(ATSPI_ROLE_RADIO_MENU_ITEM),
QUOTE(ATSPI_ROLE_RATING),
QUOTE(ATSPI_ROLE_REDUNDANT_OBJECT),
QUOTE(ATSPI_ROLE_ROOT_PANE),
QUOTE(ATSPI_ROLE_ROW_HEADER),
QUOTE(ATSPI_ROLE_RULER),
QUOTE(ATSPI_ROLE_SCROLL_BAR),
QUOTE(ATSPI_ROLE_SCROLL_PANE),
QUOTE(ATSPI_ROLE_SECTION),
QUOTE(ATSPI_ROLE_SEPARATOR),
QUOTE(ATSPI_ROLE_SLIDER),
QUOTE(ATSPI_ROLE_SPIN_BUTTON),
QUOTE(ATSPI_ROLE_SPLIT_PANE),
QUOTE(ATSPI_ROLE_STATIC),
QUOTE(ATSPI_ROLE_STATUS_BAR),
QUOTE(ATSPI_ROLE_SUBSCRIPT),
QUOTE(ATSPI_ROLE_SUPERSCRIPT),
QUOTE(ATSPI_ROLE_TABLE),
QUOTE(ATSPI_ROLE_TABLE_CELL),
QUOTE(ATSPI_ROLE_TABLE_COLUMN_HEADER),
QUOTE(ATSPI_ROLE_TABLE_ROW),
QUOTE(ATSPI_ROLE_TABLE_ROW_HEADER),
QUOTE(ATSPI_ROLE_TEAROFF_MENU_ITEM),
QUOTE(ATSPI_ROLE_TERMINAL),
QUOTE(ATSPI_ROLE_TEXT),
QUOTE(ATSPI_ROLE_TIMER),
QUOTE(ATSPI_ROLE_TITLE_BAR),
QUOTE(ATSPI_ROLE_TOGGLE_BUTTON),
QUOTE(ATSPI_ROLE_TOOL_BAR),
QUOTE(ATSPI_ROLE_TOOL_TIP),
QUOTE(ATSPI_ROLE_TREE),
QUOTE(ATSPI_ROLE_TREE_ITEM),
QUOTE(ATSPI_ROLE_TREE_TABLE),
QUOTE(ATSPI_ROLE_UNKNOWN),
QUOTE(ATSPI_ROLE_VIDEO),
QUOTE(ATSPI_ROLE_VIEWPORT),
QUOTE(ATSPI_ROLE_WINDOW),
};
return GetNameForPlatformConstant(role_table, base::size(role_table), role);
}
} // namespace content

@ -12,6 +12,7 @@
namespace content {
CONTENT_EXPORT const char* ATSPIStateToString(AtspiStateType state);
CONTENT_EXPORT const char* ATSPIRoleToString(AtspiRole role);
} // namespace content

@ -423,7 +423,10 @@ jumbo_static_library("test_support") {
if (use_atk) {
sources +=
[ "../browser/accessibility/accessibility_event_recorder_auralinux.cc" ]
configs += [ "//build/config/linux/atk" ]
configs += [
"//build/config/linux/atk",
"//build/config/linux:atspi2",
]
}
if (use_glib) {

@ -2,24 +2,22 @@
# Copyright 2017 The Chromium Authors. All rights reserved.
# found in the LICENSE file.
if (is_win) {
executable("ax_dump_events") {
testonly = true
executable("ax_dump_events") {
testonly = true
sources = [
"ax_dump_events.cc",
"ax_event_server.cc",
]
sources = [
"ax_dump_events.cc",
"ax_event_server.cc",
]
deps = [
"//base",
"//base/test:test_support",
"//content/test:test_support",
]
deps = [
"//base",
"//base/test:test_support",
"//content/test:test_support",
]
if (is_win) {
libs = [ "oleacc.lib" ]
}
if (is_win) {
libs = [ "oleacc.lib" ]
}
}

@ -16,6 +16,7 @@
namespace {
constexpr char kPidSwitch[] = "pid";
constexpr char kPatternSwitch[] = "pattern";
// Convert from string to int, whether in 0x hex format or decimal format.
bool StringToInt(std::string str, int* result) {
@ -35,25 +36,35 @@ bool AXDumpEventsLogMessageHandler(int severity,
printf("%s", str.substr(message_start).c_str());
return true;
}
} // namespace
int main(int argc, char** argv) {
logging::SetLogMessageHandler(AXDumpEventsLogMessageHandler);
base::CommandLine::Init(argc, argv);
const std::string pid_str =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kPidSwitch);
int pid;
if (pid_str.empty() || !StringToInt(pid_str, &pid)) {
LOG(ERROR) << "* Error: No process id provided via --pid=[process-id].";
const std::string pattern_str =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
kPatternSwitch);
if (pid_str.empty() && pattern_str.empty()) {
LOG(ERROR) << "* Error: No process id provided via --pid=[process-id] or"
" application name pattern via --pattern=[pattern].";
return 1;
}
int pid = 0;
if (!pid_str.empty()) {
if (!StringToInt(pid_str, &pid)) {
LOG(ERROR) << "* Error: Could not convert process id to integer.";
return 1;
}
}
base::AtExitManager exit_manager;
base::MessageLoopForUI message_loop;
const auto server = std::make_unique<tools::AXEventServer>(pid);
const auto server = std::make_unique<tools::AXEventServer>(pid, pattern_str);
base::RunLoop().Run();
return 0;
}

@ -11,9 +11,11 @@
namespace tools {
AXEventServer::AXEventServer(base::ProcessId pid)
: recorder_(
content::AccessibilityEventRecorder::GetInstance(nullptr, pid)) {
AXEventServer::AXEventServer(base::ProcessId pid,
const base::StringPiece& pattern)
: recorder_(content::AccessibilityEventRecorder::GetInstance(nullptr,
pid,
pattern)) {
recorder_.ListenToEvents(
base::BindRepeating(&AXEventServer::OnEvent, base::Unretained(this)));

@ -17,7 +17,13 @@ namespace tools {
class AXEventServer final {
public:
explicit AXEventServer(base::ProcessId pid);
// `application_name_match_pattern` is a matching pattern, which may contain
// wildcard characters, to be matched against the accessibility application
// name. Only events that match the pattern will be shown. If the pattern is
// empty, it is unused.
explicit AXEventServer(
base::ProcessId pid,
const base::StringPiece& application_name_match_pattern);
~AXEventServer();
private: