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:

committed by
Commit Bot

parent
38300420bf
commit
e4fddeded4
5
BUILD.gn
5
BUILD.gn
@ -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:
|
||||
|
Reference in New Issue
Block a user