Commit 8a54c74b authored by Aaron Leventhal's avatar Aaron Leventhal Committed by Commit Bot

ax_dump_tree

Standalone executable tool that helps developers view IAccessible2 object trees from any application.
This tool is an alternative to AccProbe, which is buggy, and AViewer, which is not feature rich.
The first version of ax_dump_tree is barebones and simply displays events on console, but has
a lot of room for future expansion (e.g., serve info to a GUI, dump the tree structure etc.).
The tool is similar is use to ax_dump_events.

As an example of how this makes easier, when there is a bug with NVDA/JAWS in Chrome that doesn't
occur with Firefox, we can look at the object tree emitted by Firefox for the same content.

The ax_dump_tree tool shares code and formatting with the DumpAccessibilityTree browsertests.

Use as follows:
ax_dump_tree --pid=[processid]
ax_dump_tree --window=[hwnd]   (start with 0x to use hex value)

TBR=nektar,dpranke

Bug: 771747
Change-Id: Idfc17ca1a1d44006ad32dcca07303ce1d93f7669
Reviewed-on: https://chromium-review.googlesource.com/724239Reviewed-by: default avatarAaron Leventhal <aleventhal@chromium.org>
Reviewed-by: default avatarDirk Pranke <dpranke@chromium.org>
Commit-Queue: Aaron Leventhal <aleventhal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#509597}
parent 8a79c9bb
...@@ -392,8 +392,10 @@ group("gn_all") { ...@@ -392,8 +392,10 @@ group("gn_all") {
] ]
} }
if (is_win || is_mac) { # Accessibility API debugging tools.
if (is_win) {
deps += [ "//tools/accessibility/inspect:ax_dump_events" ] deps += [ "//tools/accessibility/inspect:ax_dump_events" ]
deps += [ "//tools/accessibility/inspect:ax_dump_tree" ]
} }
if (is_linux) { if (is_linux) {
......
...@@ -109,6 +109,8 @@ class AccessibilityEventRecorderWin : public AccessibilityEventRecorder { ...@@ -109,6 +109,8 @@ class AccessibilityEventRecorderWin : public AccessibilityEventRecorder {
HWINEVENTHOOK win_event_hook_handle_; HWINEVENTHOOK win_event_hook_handle_;
static AccessibilityEventRecorderWin* instance_; static AccessibilityEventRecorderWin* instance_;
// Initializes COM services when standalone dump events tool is used.
base::win::ScopedCOMInitializer com_initializer; base::win::ScopedCOMInitializer com_initializer;
}; };
......
...@@ -45,6 +45,12 @@ void AccessibilityTreeFormatter::FormatAccessibilityTree( ...@@ -45,6 +45,12 @@ void AccessibilityTreeFormatter::FormatAccessibilityTree(
RecursiveFormatAccessibilityTree(*(dict.get()), contents); RecursiveFormatAccessibilityTree(*(dict.get()), contents);
} }
void AccessibilityTreeFormatter::FormatAccessibilityTree(
const base::DictionaryValue& dict,
base::string16* contents) {
RecursiveFormatAccessibilityTree(dict, contents);
}
void AccessibilityTreeFormatter::RecursiveFormatAccessibilityTree( void AccessibilityTreeFormatter::RecursiveFormatAccessibilityTree(
const base::DictionaryValue& dict, base::string16* contents, int depth) { const base::DictionaryValue& dict, base::string16* contents, int depth) {
base::string16 indent = base::string16(depth * kIndentSymbolCount, base::string16 indent = base::string16(depth * kIndentSymbolCount,
...@@ -64,7 +70,8 @@ void AccessibilityTreeFormatter::RecursiveFormatAccessibilityTree( ...@@ -64,7 +70,8 @@ void AccessibilityTreeFormatter::RecursiveFormatAccessibilityTree(
return; return;
const base::ListValue* children; const base::ListValue* children;
dict.GetList(kChildrenDictAttr, &children); if (!dict.GetList(kChildrenDictAttr, &children))
return;
const base::DictionaryValue* child_dict; const base::DictionaryValue* child_dict;
for (size_t i = 0; i < children->GetSize(); i++) { for (size_t i = 0; i < children->GetSize(); i++) {
children->GetDictionary(i, &child_dict); children->GetDictionary(i, &child_dict);
......
...@@ -11,11 +11,13 @@ ...@@ -11,11 +11,13 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/process/process_handle.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/values.h" #include "base/values.h"
#include "content/browser/accessibility/browser_accessibility.h" #include "content/browser/accessibility/browser_accessibility.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "ui/gfx/native_widget_types.h"
namespace { namespace {
const char kChildrenDictAttr[] = "children"; const char kChildrenDictAttr[] = "children";
...@@ -75,12 +77,23 @@ class CONTENT_EXPORT AccessibilityTreeFormatter { ...@@ -75,12 +77,23 @@ class CONTENT_EXPORT AccessibilityTreeFormatter {
// "children": [ ] // "children": [ ]
// } ] // } ]
// } // }
// Build an accessibility tree for the current Chrome app.
virtual std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree( virtual std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree(
BrowserAccessibility* root) = 0; BrowserAccessibility* root) = 0;
// Build an accessibility tree for any process with a window.
virtual std::unique_ptr<base::DictionaryValue>
BuildAccessibilityTreeForProcess(base::ProcessId pid) = 0;
// Build an accessibility tree for any window.
virtual std::unique_ptr<base::DictionaryValue>
BuildAccessibilityTreeForWindow(gfx::AcceleratedWidget widget) = 0;
// Dumps a BrowserAccessibility tree into a string. // Dumps a BrowserAccessibility tree into a string.
void FormatAccessibilityTree( void FormatAccessibilityTree(
BrowserAccessibility* root, base::string16* contents); BrowserAccessibility* root, base::string16* contents);
void FormatAccessibilityTree(const base::DictionaryValue& tree_node,
base::string16* contents);
// Set regular expression filters that apply to each component of every // Set regular expression filters that apply to each component of every
// line before it's output. // line before it's output.
......
...@@ -17,6 +17,20 @@ AccessibilityTreeFormatterBrowser::BuildAccessibilityTree( ...@@ -17,6 +17,20 @@ AccessibilityTreeFormatterBrowser::BuildAccessibilityTree(
return dict; return dict;
} }
std::unique_ptr<base::DictionaryValue>
AccessibilityTreeFormatterBrowser::BuildAccessibilityTreeForProcess(
base::ProcessId pid) {
NOTREACHED();
return nullptr;
}
std::unique_ptr<base::DictionaryValue>
AccessibilityTreeFormatterBrowser::BuildAccessibilityTreeForWindow(
gfx::AcceleratedWidget widget) {
NOTREACHED();
return nullptr;
}
void AccessibilityTreeFormatterBrowser::RecursiveBuildAccessibilityTree( void AccessibilityTreeFormatterBrowser::RecursiveBuildAccessibilityTree(
const BrowserAccessibility& node, const BrowserAccessibility& node,
base::DictionaryValue* dict) { base::DictionaryValue* dict) {
......
...@@ -25,6 +25,12 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBrowser ...@@ -25,6 +25,12 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBrowser
std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree( std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree(
BrowserAccessibility* root) override; BrowserAccessibility* root) override;
std::unique_ptr<base::DictionaryValue> BuildAccessibilityTreeForProcess(
base::ProcessId pid) override;
std::unique_ptr<base::DictionaryValue> BuildAccessibilityTreeForWindow(
gfx::AcceleratedWidget widget) override;
protected: protected:
void RecursiveBuildAccessibilityTree(const BrowserAccessibility& node, void RecursiveBuildAccessibilityTree(const BrowserAccessibility& node,
base::DictionaryValue* dict); base::DictionaryValue* dict);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/values.h" #include "base/values.h"
#include "base/win/scoped_bstr.h" #include "base/win/scoped_bstr.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/scoped_comptr.h" #include "base/win/scoped_comptr.h"
#include "base/win/scoped_variant.h" #include "base/win/scoped_variant.h"
#include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h" #include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
...@@ -28,6 +29,32 @@ ...@@ -28,6 +29,32 @@
#include "third_party/iaccessible2/ia2_api_all.h" #include "third_party/iaccessible2/ia2_api_all.h"
#include "ui/base/win/atl_module.h" #include "ui/base/win/atl_module.h"
namespace {
struct HwndWithProcId {
HwndWithProcId(const base::ProcessId id) : pid(id), hwnd(nullptr) {}
const base::ProcessId pid;
HWND hwnd;
};
BOOL CALLBACK EnumWindowsProcPid(HWND hwnd, LPARAM lParam) {
DWORD process_id;
GetWindowThreadProcessId(hwnd, &process_id);
HwndWithProcId* hwnd_with_proc_id = (HwndWithProcId*)lParam;
if (process_id == static_cast<DWORD>(hwnd_with_proc_id->pid)) {
hwnd_with_proc_id->hwnd = hwnd;
;
return FALSE;
}
return TRUE;
}
HWND GetHwndForProcess(base::ProcessId pid) {
HwndWithProcId hwnd_with_proc_id(pid);
EnumWindows(&EnumWindowsProcPid, (LPARAM)&hwnd_with_proc_id);
return hwnd_with_proc_id.hwnd;
}
} // namespace
namespace content { namespace content {
...@@ -38,6 +65,10 @@ class AccessibilityTreeFormatterWin : public AccessibilityTreeFormatter { ...@@ -38,6 +65,10 @@ class AccessibilityTreeFormatterWin : public AccessibilityTreeFormatter {
std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree( std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree(
BrowserAccessibility* start) override; BrowserAccessibility* start) override;
std::unique_ptr<base::DictionaryValue> BuildAccessibilityTreeForWindow(
gfx::AcceleratedWidget hwnd) override;
std::unique_ptr<base::DictionaryValue> BuildAccessibilityTreeForProcess(
base::ProcessId pid) override;
std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree( std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree(
base::win::ScopedComPtr<IAccessible> start, base::win::ScopedComPtr<IAccessible> start,
LONG window_x = 0, LONG window_x = 0,
...@@ -77,6 +108,9 @@ class AccessibilityTreeFormatterWin : public AccessibilityTreeFormatter { ...@@ -77,6 +108,9 @@ class AccessibilityTreeFormatterWin : public AccessibilityTreeFormatter {
void AddIA2ValueProperties(const base::win::ScopedComPtr<IAccessible>, void AddIA2ValueProperties(const base::win::ScopedComPtr<IAccessible>,
base::DictionaryValue* dict); base::DictionaryValue* dict);
base::string16 ToString(const base::DictionaryValue& node) override; base::string16 ToString(const base::DictionaryValue& node) override;
// Initializes COM services when standalone dump events tool is used.
base::win::ScopedCOMInitializer com_initializer;
}; };
// static // static
...@@ -177,9 +211,7 @@ AccessibilityTreeFormatterWin::BuildAccessibilityTree( ...@@ -177,9 +211,7 @@ AccessibilityTreeFormatterWin::BuildAccessibilityTree(
BrowserAccessibility* start_node) { BrowserAccessibility* start_node) {
DCHECK(start_node); DCHECK(start_node);
VARIANT variant_self; base::win::ScopedVariant variant_self(CHILDID_SELF);
variant_self.vt = VT_I4;
variant_self.lVal = CHILDID_SELF;
LONG root_x, root_y, root_width, root_height; LONG root_x, root_y, root_width, root_height;
BrowserAccessibility* root = BrowserAccessibility* root =
start_node->manager()->GetRootManager()->GetRoot(); start_node->manager()->GetRootManager()->GetRoot();
...@@ -206,6 +238,33 @@ AccessibilityTreeFormatterWin::BuildAccessibilityTree( ...@@ -206,6 +238,33 @@ AccessibilityTreeFormatterWin::BuildAccessibilityTree(
return dict; return dict;
} }
std::unique_ptr<base::DictionaryValue>
AccessibilityTreeFormatterWin::BuildAccessibilityTreeForWindow(
gfx::AcceleratedWidget hwnd) {
if (!hwnd)
return nullptr;
// Get IAccessible* for window
base::win::ScopedComPtr<IAccessible> start;
HRESULT hr = ::AccessibleObjectFromWindow(
hwnd, static_cast<DWORD>(OBJID_CLIENT), IID_PPV_ARGS(&start));
if (FAILED(hr))
return nullptr;
auto dict(std::make_unique<base::DictionaryValue>());
RecursiveBuildAccessibilityTree(start, dict.get(), 0, 0);
return dict;
}
std::unique_ptr<base::DictionaryValue>
AccessibilityTreeFormatterWin::BuildAccessibilityTreeForProcess(
base::ProcessId pid) {
// Get HWND for process id.
HWND hwnd = GetHwndForProcess(pid);
return BuildAccessibilityTreeForWindow(hwnd);
}
void AccessibilityTreeFormatterWin::RecursiveBuildAccessibilityTree( void AccessibilityTreeFormatterWin::RecursiveBuildAccessibilityTree(
const base::win::ScopedComPtr<IAccessible> node, const base::win::ScopedComPtr<IAccessible> node,
base::DictionaryValue* dict, base::DictionaryValue* dict,
...@@ -219,20 +278,56 @@ void AccessibilityTreeFormatterWin::RecursiveBuildAccessibilityTree( ...@@ -219,20 +278,56 @@ void AccessibilityTreeFormatterWin::RecursiveBuildAccessibilityTree(
if (S_OK != node->get_accChildCount(&child_count)) if (S_OK != node->get_accChildCount(&child_count))
return; return;
for (int index = 1; index <= child_count; ++index) { std::unique_ptr<VARIANT[]> children_array(new VARIANT[child_count]);
base::win::ScopedVariant childid_index(index); LONG obtained_count = 0;
base::win::ScopedComPtr<IDispatch> child_dispatch; HRESULT hr = AccessibleChildren(node.Get(), 0, child_count,
base::win::ScopedComPtr<IAccessible> child_accessible; children_array.get(), &obtained_count);
if (S_OK == if (hr != S_OK)
node->get_accChild(childid_index, child_dispatch.GetAddressOf()) && return;
S_OK == child_dispatch.CopyTo(child_accessible.GetAddressOf())) {
for (LONG index = 0; index < obtained_count; index++) {
base::win::ScopedVariant child_variant;
child_variant.Reset(
children_array[index]); // Sets without adding another reference.
std::unique_ptr<base::DictionaryValue> child_dict( std::unique_ptr<base::DictionaryValue> child_dict(
new base::DictionaryValue); new base::DictionaryValue);
RecursiveBuildAccessibilityTree(child_accessible, child_dict.get(), base::win::ScopedComPtr<IDispatch> dispatch;
root_x, root_y); if (child_variant.type() == VT_DISPATCH) {
children->Append(std::move(child_dict)); dispatch = V_DISPATCH(child_variant.ptr());
} else if (child_variant.type() == VT_I4) {
HRESULT hr = node->get_accChild(child_variant, dispatch.GetAddressOf());
if (FAILED(hr)) {
child_dict->SetString("error",
base::ASCIIToUTF16("<Error retrieving child>"));
} else if (!dispatch) {
// Partial child does not have its own object.
// Add minimal info -- role and name.
base::win::ScopedVariant role_variant;
if (SUCCEEDED(
node->get_accRole(child_variant, role_variant.Receive()))) {
if (role_variant.type() == VT_I4) {
child_dict->SetString("role",
base::ASCIIToUTF16(" <partial child>"));
} }
} }
base::win::ScopedBstr temp_bstr;
if (S_OK == node->get_accName(child_variant, temp_bstr.Receive())) {
base::string16 name = base::string16(temp_bstr, temp_bstr.Length());
child_dict->SetString("name", name);
}
}
} else {
child_dict->SetString("error",
base::ASCIIToUTF16("<Unknown child type>"));
}
if (dispatch) {
base::win::ScopedComPtr<IAccessible> accessible;
if (SUCCEEDED(dispatch.As(&accessible)))
RecursiveBuildAccessibilityTree(accessible, child_dict.get(), root_x,
root_y);
}
children->Append(std::move(child_dict));
}
dict->Set(kChildrenDictAttr, std::move(children)); dict->Set(kChildrenDictAttr, std::move(children));
} }
...@@ -286,26 +381,27 @@ void AccessibilityTreeFormatterWin::AddProperties( ...@@ -286,26 +381,27 @@ void AccessibilityTreeFormatterWin::AddProperties(
} }
} }
base::string16 RoleVariantToString(const base::win::ScopedVariant& role) {
if (role.type() == VT_I4) {
return IAccessible2RoleToString(V_I4(role.ptr()));
} else if (role.type() == VT_BSTR) {
BSTR bstr_role = V_BSTR(role.ptr());
return base::string16(bstr_role, SysStringLen(bstr_role));
}
return base::string16();
}
void AccessibilityTreeFormatterWin::AddMSAAProperties( void AccessibilityTreeFormatterWin::AddMSAAProperties(
const base::win::ScopedComPtr<IAccessible> node, const base::win::ScopedComPtr<IAccessible> node,
base::DictionaryValue* dict, base::DictionaryValue* dict,
LONG root_x, LONG root_x,
LONG root_y) { LONG root_y) {
VARIANT variant_self; base::win::ScopedVariant variant_self(CHILDID_SELF);
variant_self.vt = VT_I4;
variant_self.lVal = CHILDID_SELF;
base::win::ScopedBstr temp_bstr; base::win::ScopedBstr temp_bstr;
base::win::ScopedVariant ia_role_variant;
LONG ia_role = 0; LONG ia_role = 0;
VARIANT ia_role_variant; if (SUCCEEDED(node->get_accRole(variant_self, ia_role_variant.Receive()))) {
if (SUCCEEDED(node->get_accRole(variant_self, &ia_role_variant))) { dict->SetString("role", RoleVariantToString(ia_role_variant));
if (ia_role_variant.vt == VT_I4) {
ia_role = ia_role_variant.lVal;
dict->SetString("role", IAccessible2RoleToString(ia_role));
} else if (ia_role_variant.vt == VT_BSTR) {
dict->SetString("role", base::string16(ia_role_variant.bstrVal));
}
} }
// If S_FALSE it means there is no name // If S_FALSE it means there is no name
...@@ -323,22 +419,10 @@ void AccessibilityTreeFormatterWin::AddMSAAProperties( ...@@ -323,22 +419,10 @@ void AccessibilityTreeFormatterWin::AddMSAAProperties(
temp_bstr.Reset(); temp_bstr.Reset();
int32_t ia_state = 0; int32_t ia_state = 0;
VARIANT ia_state_variant; base::win::ScopedVariant ia_state_variant;
if (node->get_accState(variant_self, &ia_state_variant) == S_OK && if (node->get_accState(variant_self, ia_state_variant.Receive()) == S_OK &&
ia_state_variant.vt == VT_I4) { ia_state_variant.type() == VT_I4) {
ia_state = ia_state_variant.intVal; ia_state = ia_state_variant.ptr()->intVal;
if (true /* reduced_flakiness_mode_ */) { // TODO
// Avoid flakiness: these states depend on whether the window is focused
// and the position of the mouse cursor.
ia_state &= ~STATE_SYSTEM_HOTTRACKED;
ia_state &= ~STATE_SYSTEM_OFFSCREEN;
// For testing, having the focused state may also cause flakiness if the
// window isn't in the foreground.
ia_state &= ~STATE_SYSTEM_FOCUSED;
}
std::vector<base::string16> state_strings; std::vector<base::string16> state_strings;
IAccessibleStateToStringVector(ia_state, &state_strings); IAccessibleStateToStringVector(ia_state, &state_strings);
std::unique_ptr<base::ListValue> states(new base::ListValue()); std::unique_ptr<base::ListValue> states(new base::ListValue());
...@@ -644,23 +728,28 @@ void AccessibilityTreeFormatterWin::AddIA2ValueProperties( ...@@ -644,23 +728,28 @@ void AccessibilityTreeFormatterWin::AddIA2ValueProperties(
if (S_OK != QueryIAccessibleValue(node.Get(), ia2value.GetAddressOf())) if (S_OK != QueryIAccessibleValue(node.Get(), ia2value.GetAddressOf()))
return; // No IA2Value, we are finished with this node. return; // No IA2Value, we are finished with this node.
VARIANT currentValue; base::win::ScopedVariant currentValue;
if (ia2value->get_currentValue(&currentValue) == S_OK) if (ia2value->get_currentValue(currentValue.Receive()) == S_OK)
dict->SetDouble("currentValue", V_R8(&currentValue)); dict->SetDouble("currentValue", V_R8(currentValue.ptr()));
VARIANT minimumValue; base::win::ScopedVariant minimumValue;
if (ia2value->get_minimumValue(&minimumValue) == S_OK) if (ia2value->get_minimumValue(minimumValue.Receive()) == S_OK)
dict->SetDouble("minimumValue", V_R8(&minimumValue)); dict->SetDouble("minimumValue", V_R8(minimumValue.ptr()));
VARIANT maximumValue; base::win::ScopedVariant maximumValue;
if (ia2value->get_maximumValue(&maximumValue) == S_OK) if (ia2value->get_maximumValue(maximumValue.Receive()) == S_OK)
dict->SetDouble("maximumValue", V_R8(&maximumValue)); dict->SetDouble("maximumValue", V_R8(maximumValue.ptr()));
} }
base::string16 AccessibilityTreeFormatterWin::ToString( base::string16 AccessibilityTreeFormatterWin::ToString(
const base::DictionaryValue& dict) { const base::DictionaryValue& dict) {
base::string16 line; base::string16 line;
base::string16 error_value;
if (dict.GetString("error", &error_value)) {
return error_value;
}
base::string16 role_value; base::string16 role_value;
dict.GetString("role", &role_value); dict.GetString("role", &role_value);
WriteAttribute(true, base::UTF16ToUTF8(role_value), &line); WriteAttribute(true, base::UTF16ToUTF8(role_value), &line);
......
...@@ -154,7 +154,7 @@ void DumpAccessibilityTreeTest::AddDefaultFilters( ...@@ -154,7 +154,7 @@ void DumpAccessibilityTreeTest::AddDefaultFilters(
AddFilter(filters, "DEFAULT"); AddFilter(filters, "DEFAULT");
AddFilter(filters, "EXPANDED"); AddFilter(filters, "EXPANDED");
AddFilter(filters, "FLOATING"); AddFilter(filters, "FLOATING");
AddFilter(filters, "FOCUS*"); AddFilter(filters, "FOCUSABLE");
AddFilter(filters, "HASPOPUP"); AddFilter(filters, "HASPOPUP");
AddFilter(filters, "INVISIBLE"); AddFilter(filters, "INVISIBLE");
AddFilter(filters, "MARQUEED"); AddFilter(filters, "MARQUEED");
...@@ -181,6 +181,10 @@ void DumpAccessibilityTreeTest::AddDefaultFilters( ...@@ -181,6 +181,10 @@ void DumpAccessibilityTreeTest::AddDefaultFilters(
AddFilter(filters, "IA2_STATE_REQUIRED"); AddFilter(filters, "IA2_STATE_REQUIRED");
AddFilter(filters, "IA2_STATE_STALE"); AddFilter(filters, "IA2_STATE_STALE");
AddFilter(filters, "IA2_STATE_TRANSIENT"); AddFilter(filters, "IA2_STATE_TRANSIENT");
// Reduce flakiness.
AddFilter(filters, "FOCUSED", Filter::DENY);
AddFilter(filters, "HOTTRACKED", Filter::DENY);
AddFilter(filters, "OFFSCREEN", Filter::DENY);
// //
// Blink // Blink
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
# Copyright 2017 The Chromium Authors. All rights reserved. # Copyright 2017 The Chromium Authors. All rights reserved.
# found in the LICENSE file. # found in the LICENSE file.
executable("ax_dump_events") { if (is_win) {
executable("ax_dump_events") {
testonly = true testonly = true
sources = [ sources = [
...@@ -19,4 +20,24 @@ executable("ax_dump_events") { ...@@ -19,4 +20,24 @@ executable("ax_dump_events") {
if (is_win) { if (is_win) {
libs = [ "oleacc.lib" ] libs = [ "oleacc.lib" ]
} }
}
executable("ax_dump_tree") {
testonly = true
sources = [
"ax_dump_tree.cc",
"ax_tree_server.cc",
]
deps = [
"//base",
"//base/test:test_support",
"//content/test:test_support",
]
if (is_win) {
libs = [ "oleacc.lib" ]
}
}
} }
...@@ -11,6 +11,16 @@ ...@@ -11,6 +11,16 @@
char kPidSwitch[] = "pid"; char kPidSwitch[] = "pid";
// Convert from string to int, whether in 0x hex format or decimal format.
bool StringToInt(std::string str, int* result) {
if (str.empty())
return false;
bool is_hex =
str.size() > 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X');
return is_hex ? base::HexStringToInt(str, result)
: base::StringToInt(str, result);
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
base::AtExitManager at_exit_manager; base::AtExitManager at_exit_manager;
// TODO(aleventhal) Want callback after Ctrl+C or some global keystroke: // TODO(aleventhal) Want callback after Ctrl+C or some global keystroke:
...@@ -19,11 +29,13 @@ int main(int argc, char** argv) { ...@@ -19,11 +29,13 @@ int main(int argc, char** argv) {
base::CommandLine::Init(argc, argv); base::CommandLine::Init(argc, argv);
std::string pid_str = std::string pid_str =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kPidSwitch); base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kPidSwitch);
int pid = 0; int pid;
if (!pid_str.empty()) if (!pid_str.empty()) {
base::StringToInt(pid_str, &pid); if (StringToInt(pid_str, &pid)) {
std::unique_ptr<content::AXEventServer> server( std::unique_ptr<content::AXEventServer> server(
new content::AXEventServer(pid)); new content::AXEventServer(pid));
}
}
return 0; return 0;
} }
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "tools/accessibility/inspect/ax_tree_server.h"
char kPidSwitch[] = "pid";
char kWindowSwitch[] = "window";
// Convert from string to int, whether in 0x hex format or decimal format.
bool StringToInt(std::string str, int* result) {
if (str.empty())
return false;
bool is_hex =
str.size() > 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X');
return is_hex ? base::HexStringToInt(str, result)
: base::StringToInt(str, result);
}
int main(int argc, char** argv) {
base::AtExitManager at_exit_manager;
base::CommandLine::Init(argc, argv);
std::string window_str =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
kWindowSwitch);
if (!window_str.empty()) {
int window;
if (StringToInt(window_str, &window)) {
gfx::AcceleratedWidget widget(
reinterpret_cast<gfx::AcceleratedWidget>(window));
std::unique_ptr<content::AXTreeServer> server(
new content::AXTreeServer(widget));
return 0;
}
}
std::string pid_str =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kPidSwitch);
if (!pid_str.empty()) {
int pid;
if (StringToInt(pid_str, &pid)) {
base::ProcessId process_id = static_cast<base::ProcessId>(pid);
std::unique_ptr<content::AXTreeServer> server(
new content::AXTreeServer(process_id));
}
}
return 0;
}
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "tools/accessibility/inspect/ax_tree_server.h"
#include <iostream>
#include <string>
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
namespace content {
AXTreeServer::AXTreeServer(base::ProcessId pid) {
std::unique_ptr<AccessibilityTreeFormatter> formatter(
AccessibilityTreeFormatter::Create());
// Get accessibility tree as nested dictionary.
base::string16 accessibility_contents_utf16;
std::unique_ptr<base::DictionaryValue> dict =
formatter->BuildAccessibilityTreeForProcess(pid);
if (!dict) {
std::cout << "Failed to get accessibility tree";
return;
}
Format(*formatter, *dict);
}
AXTreeServer::AXTreeServer(gfx::AcceleratedWidget widget) {
std::unique_ptr<AccessibilityTreeFormatter> formatter(
AccessibilityTreeFormatter::Create());
// Get accessibility tree as nested dictionary.
std::unique_ptr<base::DictionaryValue> dict =
formatter->BuildAccessibilityTreeForWindow(widget);
if (!dict) {
std::cout << "Failed to get accessibility tree";
return;
}
Format(*formatter, *dict);
}
void AXTreeServer::Format(AccessibilityTreeFormatter& formatter,
base::DictionaryValue& dict) {
// Set filters.
std::vector<AccessibilityTreeFormatter::Filter> filters;
filters.push_back(AccessibilityTreeFormatter::Filter(
base::ASCIIToUTF16("*"), AccessibilityTreeFormatter::Filter::ALLOW));
formatter.SetFilters(filters);
// Format accessibility tree as text.
base::string16 accessibility_contents_utf16;
formatter.FormatAccessibilityTree(dict, &accessibility_contents_utf16);
// Write to console.
std::cout << accessibility_contents_utf16;
}
} // namespace content
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef AX_TREE_SERVER_H_
#define AX_TREE_SERVER_H_
#include <string>
#include "content/browser/accessibility/accessibility_tree_formatter.h"
namespace content {
class AXTreeServer {
public:
explicit AXTreeServer(base::ProcessId pid);
explicit AXTreeServer(gfx::AcceleratedWidget widget);
private:
DISALLOW_COPY_AND_ASSIGN(AXTreeServer);
void Format(AccessibilityTreeFormatter& formatter,
base::DictionaryValue& dict);
};
} // namespace content
#endif // AX_TREE_SERVER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment