Commit 8c18e114 authored by Alexander Surkov's avatar Alexander Surkov Committed by Commit Bot

ax_dump_tool: support --active-tab selector on mac

--active-tab is a handy way to dump accessible tree of an active tab in
a browser and allows to reduce bulky output from the browser UI.
The selector is intended to be used in conjunction with other selectors,
for example, ax_dump_tree --firefox --active-tab will dump
accessible tree of a selected tab in Firefox browser.

Bug: 1124366
Change-Id: I5ddfa333d44d0e854e5b38d98f52e226a9a53e06
AX-Relnotes: n/a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2416773Reviewed-by: default avatarAlex Moshchuk <alexmos@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Commit-Queue: Alexander Surkov <asurkov@igalia.com>
Cr-Commit-Position: refs/heads/master@{#809333}
parent 8f5d0925
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
using base::StringPrintf; using base::StringPrintf;
using base::SysNSStringToUTF8; using base::SysNSStringToUTF8;
using base::SysNSStringToUTF16; using base::SysNSStringToUTF16;
using base::SysUTF16ToNSString;
using content::a11y::AttributeInvoker; using content::a11y::AttributeInvoker;
using content::a11y::AttributeNamesOf; using content::a11y::AttributeNamesOf;
using content::a11y::AttributeValueOf; using content::a11y::AttributeValueOf;
...@@ -75,6 +74,13 @@ class AccessibilityTreeFormatterMac : public AccessibilityTreeFormatterBase { ...@@ -75,6 +74,13 @@ class AccessibilityTreeFormatterMac : public AccessibilityTreeFormatterBase {
const TreeSelector& selector) override; const TreeSelector& selector) override;
private: private:
std::unique_ptr<base::DictionaryValue> BuildAccessibilityTreeForAXUIElement(
AXUIElementRef node) const;
// Return AXElement in a tree by a given criteria
using FindCriteria = base::RepeatingCallback<bool(id)>;
id FindAXUIElement(const id node, const FindCriteria& criteria) const;
void RecursiveBuildAccessibilityTree(const id node, void RecursiveBuildAccessibilityTree(const id node,
const LineIndexer* line_indexer, const LineIndexer* line_indexer,
base::DictionaryValue* dict) const; base::DictionaryValue* dict) const;
...@@ -164,12 +170,8 @@ AccessibilityTreeFormatterMac::BuildAccessibilityTree( ...@@ -164,12 +170,8 @@ AccessibilityTreeFormatterMac::BuildAccessibilityTree(
std::unique_ptr<base::DictionaryValue> std::unique_ptr<base::DictionaryValue>
AccessibilityTreeFormatterMac::BuildAccessibilityTreeForWindow( AccessibilityTreeFormatterMac::BuildAccessibilityTreeForWindow(
gfx::AcceleratedWidget widget) { gfx::AcceleratedWidget widget) {
AXUIElementRef application = AXUIElementCreateApplication(widget); return BuildAccessibilityTreeForAXUIElement(
LineIndexer line_indexer(static_cast<id>(application)); AXUIElementCreateApplication(widget));
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
RecursiveBuildAccessibilityTree(static_cast<id>(application), &line_indexer,
dict.get());
return dict;
} }
std::unique_ptr<base::DictionaryValue> std::unique_ptr<base::DictionaryValue>
...@@ -179,36 +181,76 @@ AccessibilityTreeFormatterMac::BuildAccessibilityTreeForSelector( ...@@ -179,36 +181,76 @@ AccessibilityTreeFormatterMac::BuildAccessibilityTreeForSelector(
kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
kCGNullWindowID); kCGNullWindowID);
std::string title = selector.pattern; std::string title;
switch (selector.type) { if (selector.types & TreeSelector::Chrome) {
case TreeSelector::Chrome: title = kChromeTitle;
title = kChromeTitle; } else if (selector.types & TreeSelector::Chromium) {
break; title = kChromiumTitle;
case TreeSelector::Chromium: } else if (selector.types & TreeSelector::Firefox) {
title = kChromiumTitle; title = kFirefoxTitle;
break; } else if (selector.types & TreeSelector::Safari) {
case TreeSelector::Firefox: title = kSafariTitle;
title = kFirefoxTitle;
break;
case TreeSelector::Safari:
title = kSafariTitle;
break;
default:
break;
} }
for (NSDictionary* window_info in windows) { for (NSDictionary* window_info in windows) {
NSString* window_name = NSNumber* pid =
(NSString*)[window_info objectForKey:@"kCGWindowOwnerName"]; static_cast<NSNumber*>([window_info objectForKey:@"kCGWindowOwnerPID"]);
if (SysNSStringToUTF8(window_name) == title) { std::string window_name = SysNSStringToUTF8(static_cast<NSString*>(
NSNumber* pid = [window_info objectForKey:@"kCGWindowOwnerName"]));
(NSNumber*)[window_info objectForKey:@"kCGWindowOwnerPID"];
if (window_name == selector.pattern) {
return BuildAccessibilityTreeForWindow([pid intValue]); return BuildAccessibilityTreeForWindow([pid intValue]);
} }
if (window_name == title) {
AXUIElementRef node = AXUIElementCreateApplication([pid intValue]);
if (selector.types & TreeSelector::ActiveTab) {
node = static_cast<AXUIElementRef>(FindAXUIElement(
static_cast<id>(node), base::BindRepeating([](const id node) {
// Only active tab in exposed in browsers, thus find first
// AXWebArea role.
NSString* role =
AttributeValueOf(node, NSAccessibilityRoleAttribute);
return SysNSStringToUTF8(role) == "AXWebArea";
})));
}
if (node) {
return BuildAccessibilityTreeForAXUIElement(node);
}
}
} }
return nullptr; return nullptr;
} }
std::unique_ptr<base::DictionaryValue>
AccessibilityTreeFormatterMac::BuildAccessibilityTreeForAXUIElement(
AXUIElementRef node) const {
LineIndexer line_indexer(static_cast<id>(node));
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
RecursiveBuildAccessibilityTree(static_cast<id>(node), &line_indexer,
dict.get());
return dict;
}
id AccessibilityTreeFormatterMac::FindAXUIElement(
const id node,
const FindCriteria& criteria) const {
if (criteria.Run(node)) {
return node;
}
NSArray* children = ChildrenOf(node);
for (id child in children) {
id found = FindAXUIElement(child, criteria);
if (found != nil) {
return found;
}
}
return nil;
}
void AccessibilityTreeFormatterMac::RecursiveBuildAccessibilityTree( void AccessibilityTreeFormatterMac::RecursiveBuildAccessibilityTree(
const id node, const id node,
const LineIndexer* line_indexer, const LineIndexer* line_indexer,
......
...@@ -103,17 +103,18 @@ class CONTENT_EXPORT AccessibilityTreeFormatter ...@@ -103,17 +103,18 @@ class CONTENT_EXPORT AccessibilityTreeFormatter
// an accessible name of a root of some accessible subtree. // an accessible name of a root of some accessible subtree.
struct TreeSelector { struct TreeSelector {
enum Type { enum Type {
None, None = 0,
Chrome, ActiveTab = 1 << 0,
Chromium, Chrome = 1 << 1,
Firefox, Chromium = 1 << 2,
Safari, Firefox = 1 << 3,
Safari = 1 << 4,
}; };
Type type; int types;
std::string pattern; std::string pattern;
TreeSelector(Type type, const std::string& pattern) TreeSelector(int types, const std::string& pattern)
: type(type), pattern(pattern) {} : types(types), pattern(pattern) {}
}; };
// Create the appropriate native subclass of AccessibilityTreeFormatter. // Create the appropriate native subclass of AccessibilityTreeFormatter.
......
...@@ -20,33 +20,51 @@ This tool helps to inspect accessibility trees of applications. Trees are dumped ...@@ -20,33 +20,51 @@ This tool helps to inspect accessibility trees of applications. Trees are dumped
`autoninja -C out/Default ax_dump_tree` `autoninja -C out/Default ax_dump_tree`
### Run This will generate `ax_dump_tree` executable in `out/Default` directory.
On Windows run ### Prerequesties
`ax_dump_tree --window=id`
where `id` is HWND of a window to dump accessible tree for.
On Mac and Linux, run #### Mac
`ax_dump_tree --pid=id`
where `pid` is process id of an application to dump accessible tree for.
Alternatively, you can indicate an application by its title: 1) Turn on Accessibility for Terminal in Security & Privacy System Preferences.
`ax_dump_tree --pattern=title`
2) Some applications keep accessibility inactive, which prevents them to generate accessible trees. Thus either:
* start VoiceOver (`CMD+F5`) or
* use application specific runtime flags
** Chromium: `Chromium.app/Contents/MacOS/Chromium --force-renderer-accessibility`
### Run
To dump an accessible tree, run:
`ax_dump_tree <options>`
Also these pre-defined application selectors are available: At your convenience the number of pre-defined application selectors are available:
`--chrome` for Chrome browser `--chrome` for Chrome browser
`--chromium` for Chrome browser `--chromium` for Chromium browser
`--firefox` for Firefox browser `--firefox` for Firefox browser
`--safari` for Safari browser `--safari` for Safari browser
`--active-tab` to dump a tree of active tab of selected browser.
You can also specify an application by its title:
`ax_dump_tree --pattern=title`
Alternatively you can dump a tree by HWDN on Windows:
`--window=HWDN`
Note, to use a hex window handle prefix it with `0x`.
Or by application PID on Mac and Linux:
`--pid=process_id`
Other options:
`--json` to output a tree in JSON format
`--filters=absolute_path_to_filters.txt` to filter properties, use where the filters text file has a series of `@ALLOW` and/or `@DENY` lines. See example-tree-filters.txt in tools/accessibility/inspect.
`--help` for help
Notes:
* To use a hex window handle prefix it with `0x`.
* For json output, use the `--json` option
* To filter certain properties, use `--filters=[absolute-path-to-filters.txt]` where the filters text file has a series of `@ALLOW` and/or `@DENY` lines. See example-tree-filters.txt in tools/accessibility/inspect.
* [Mac] You have to turn on Accessibility for Terminal in Security & Privacy System Preferences.
## Convenience PowerShell scripts ## Convenience PowerShell scripts
Note: Windows only.
Run these scripts to avoid the difficulty of looking up the process id or window handle you want to inspect. Run these scripts to avoid the difficulty of looking up the process id or window handle you want to inspect.
Sometimes there may be several windows open for the given app, and disambuation. In this case, after you run the script, it will list top level windows/processes and ask you to re-run with an argument that includes a substring from the window title you want to inspect the tree/events for. For example, `chrome-tree live` will inspect a tab with the name "Live region tests" (the title matcher is case insensitive). Sometimes there may be several windows open for the given app, and disambuation. In this case, after you run the script, it will list top level windows/processes and ask you to re-run with an argument that includes a substring from the window title you want to inspect the tree/events for. For example, `chrome-tree live` will inspect a tab with the name "Live region tests" (the title matcher is case insensitive).
......
...@@ -25,6 +25,7 @@ char kFiltersSwitch[] = "filters"; ...@@ -25,6 +25,7 @@ char kFiltersSwitch[] = "filters";
char kJsonSwitch[] = "json"; char kJsonSwitch[] = "json";
char kHelpSwitch[] = "help"; char kHelpSwitch[] = "help";
char kActiveTabSwitch[] = "active-tab";
char kChromeSwitch[] = "chrome"; char kChromeSwitch[] = "chrome";
char kChromiumSwitch[] = "chromium"; char kChromiumSwitch[] = "chromium";
char kFirefoxSwitch[] = "firefox"; char kFirefoxSwitch[] = "firefox";
...@@ -75,6 +76,7 @@ void PrintHelp() { ...@@ -75,6 +76,7 @@ void PrintHelp() {
printf(" --chromium\tChromium browser\n"); printf(" --chromium\tChromium browser\n");
printf(" --firefox\tFirefox browser\n"); printf(" --firefox\tFirefox browser\n");
printf(" --safari\tSafari browser\n"); printf(" --safari\tSafari browser\n");
printf(" --active-tab\tActive tab of a choosen browser\n");
printf( printf(
" --filters\tfile containing property filters used to filter out\n" " --filters\tfile containing property filters used to filter out\n"
" \t\taccessible tree, see example-tree-filters.txt as an example\n"); " \t\taccessible tree, see example-tree-filters.txt as an example\n");
...@@ -114,21 +116,24 @@ int main(int argc, char** argv) { ...@@ -114,21 +116,24 @@ int main(int argc, char** argv) {
return 0; return 0;
} }
TreeSelector::Type selector_type = TreeSelector::None; int selectors = TreeSelector::None;
if (command_line->HasSwitch(kChromeSwitch)) { if (command_line->HasSwitch(kChromeSwitch)) {
selector_type = TreeSelector::Chrome; selectors = TreeSelector::Chrome;
} else if (command_line->HasSwitch(kChromiumSwitch)) { } else if (command_line->HasSwitch(kChromiumSwitch)) {
selector_type = TreeSelector::Chromium; selectors = TreeSelector::Chromium;
} else if (command_line->HasSwitch(kFirefoxSwitch)) { } else if (command_line->HasSwitch(kFirefoxSwitch)) {
selector_type = TreeSelector::Firefox; selectors = TreeSelector::Firefox;
} else if (command_line->HasSwitch(kSafariSwitch)) { } else if (command_line->HasSwitch(kSafariSwitch)) {
selector_type = TreeSelector::Safari; selectors = TreeSelector::Safari;
}
if (command_line->HasSwitch(kActiveTabSwitch)) {
selectors |= TreeSelector::ActiveTab;
} }
std::string pattern_str = command_line->GetSwitchValueASCII(kPatternSwitch); std::string pattern_str = command_line->GetSwitchValueASCII(kPatternSwitch);
if (selector_type != TreeSelector::None || !pattern_str.empty()) { if (selectors != TreeSelector::None || !pattern_str.empty()) {
std::unique_ptr<content::AXTreeServer> server(new content::AXTreeServer( std::unique_ptr<content::AXTreeServer> server(new content::AXTreeServer(
TreeSelector(selector_type, pattern_str), filters_path, use_json)); TreeSelector(selectors, pattern_str), filters_path, use_json));
return 0; return 0;
} }
......
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