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 @@
using base::StringPrintf;
using base::SysNSStringToUTF8;
using base::SysNSStringToUTF16;
using base::SysUTF16ToNSString;
using content::a11y::AttributeInvoker;
using content::a11y::AttributeNamesOf;
using content::a11y::AttributeValueOf;
......@@ -75,6 +74,13 @@ class AccessibilityTreeFormatterMac : public AccessibilityTreeFormatterBase {
const TreeSelector& selector) override;
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,
const LineIndexer* line_indexer,
base::DictionaryValue* dict) const;
......@@ -164,12 +170,8 @@ AccessibilityTreeFormatterMac::BuildAccessibilityTree(
std::unique_ptr<base::DictionaryValue>
AccessibilityTreeFormatterMac::BuildAccessibilityTreeForWindow(
gfx::AcceleratedWidget widget) {
AXUIElementRef application = AXUIElementCreateApplication(widget);
LineIndexer line_indexer(static_cast<id>(application));
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
RecursiveBuildAccessibilityTree(static_cast<id>(application), &line_indexer,
dict.get());
return dict;
return BuildAccessibilityTreeForAXUIElement(
AXUIElementCreateApplication(widget));
}
std::unique_ptr<base::DictionaryValue>
......@@ -179,36 +181,76 @@ AccessibilityTreeFormatterMac::BuildAccessibilityTreeForSelector(
kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
kCGNullWindowID);
std::string title = selector.pattern;
switch (selector.type) {
case TreeSelector::Chrome:
title = kChromeTitle;
break;
case TreeSelector::Chromium:
title = kChromiumTitle;
break;
case TreeSelector::Firefox:
title = kFirefoxTitle;
break;
case TreeSelector::Safari:
title = kSafariTitle;
break;
default:
break;
std::string title;
if (selector.types & TreeSelector::Chrome) {
title = kChromeTitle;
} else if (selector.types & TreeSelector::Chromium) {
title = kChromiumTitle;
} else if (selector.types & TreeSelector::Firefox) {
title = kFirefoxTitle;
} else if (selector.types & TreeSelector::Safari) {
title = kSafariTitle;
}
for (NSDictionary* window_info in windows) {
NSString* window_name =
(NSString*)[window_info objectForKey:@"kCGWindowOwnerName"];
if (SysNSStringToUTF8(window_name) == title) {
NSNumber* pid =
(NSNumber*)[window_info objectForKey:@"kCGWindowOwnerPID"];
NSNumber* pid =
static_cast<NSNumber*>([window_info objectForKey:@"kCGWindowOwnerPID"]);
std::string window_name = SysNSStringToUTF8(static_cast<NSString*>(
[window_info objectForKey:@"kCGWindowOwnerName"]));
if (window_name == selector.pattern) {
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;
}
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(
const id node,
const LineIndexer* line_indexer,
......
......@@ -103,17 +103,18 @@ class CONTENT_EXPORT AccessibilityTreeFormatter
// an accessible name of a root of some accessible subtree.
struct TreeSelector {
enum Type {
None,
Chrome,
Chromium,
Firefox,
Safari,
None = 0,
ActiveTab = 1 << 0,
Chrome = 1 << 1,
Chromium = 1 << 2,
Firefox = 1 << 3,
Safari = 1 << 4,
};
Type type;
int types;
std::string pattern;
TreeSelector(Type type, const std::string& pattern)
: type(type), pattern(pattern) {}
TreeSelector(int types, const std::string& pattern)
: types(types), pattern(pattern) {}
};
// Create the appropriate native subclass of AccessibilityTreeFormatter.
......
......@@ -20,33 +20,51 @@ This tool helps to inspect accessibility trees of applications. Trees are dumped
`autoninja -C out/Default ax_dump_tree`
### Run
This will generate `ax_dump_tree` executable in `out/Default` directory.
On Windows run
`ax_dump_tree --window=id`
where `id` is HWND of a window to dump accessible tree for.
### Prerequesties
On Mac and Linux, run
`ax_dump_tree --pid=id`
where `pid` is process id of an application to dump accessible tree for.
#### Mac
Alternatively, you can indicate an application by its title:
`ax_dump_tree --pattern=title`
1) Turn on Accessibility for Terminal in Security & Privacy System Preferences.
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
`--chromium` for Chrome browser
`--chromium` for Chromium browser
`--firefox` for Firefox 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
Note: Windows only.
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).
......
......@@ -25,6 +25,7 @@ char kFiltersSwitch[] = "filters";
char kJsonSwitch[] = "json";
char kHelpSwitch[] = "help";
char kActiveTabSwitch[] = "active-tab";
char kChromeSwitch[] = "chrome";
char kChromiumSwitch[] = "chromium";
char kFirefoxSwitch[] = "firefox";
......@@ -75,6 +76,7 @@ void PrintHelp() {
printf(" --chromium\tChromium browser\n");
printf(" --firefox\tFirefox browser\n");
printf(" --safari\tSafari browser\n");
printf(" --active-tab\tActive tab of a choosen browser\n");
printf(
" --filters\tfile containing property filters used to filter out\n"
" \t\taccessible tree, see example-tree-filters.txt as an example\n");
......@@ -114,21 +116,24 @@ int main(int argc, char** argv) {
return 0;
}
TreeSelector::Type selector_type = TreeSelector::None;
int selectors = TreeSelector::None;
if (command_line->HasSwitch(kChromeSwitch)) {
selector_type = TreeSelector::Chrome;
selectors = TreeSelector::Chrome;
} else if (command_line->HasSwitch(kChromiumSwitch)) {
selector_type = TreeSelector::Chromium;
selectors = TreeSelector::Chromium;
} else if (command_line->HasSwitch(kFirefoxSwitch)) {
selector_type = TreeSelector::Firefox;
selectors = TreeSelector::Firefox;
} 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);
if (selector_type != TreeSelector::None || !pattern_str.empty()) {
if (selectors != TreeSelector::None || !pattern_str.empty()) {
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;
}
......
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