Commit 122d3dc5 authored by Alexander Surkov's avatar Alexander Surkov Committed by Commit Bot

Port ax_dump_tree tool on mac: support accessibility tree filters

Bug: 1124366
Change-Id: Ib4e8309bf5e248623af7577fb74d3451d6c39bf8
AX-Relnotes: n/a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2396620
Commit-Queue: Alexander Surkov <asurkov@igalia.com>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805389}
parent a30bde70
...@@ -25,6 +25,8 @@ using base::SysNSStringToUTF8; ...@@ -25,6 +25,8 @@ using base::SysNSStringToUTF8;
using base::SysNSStringToUTF16; using base::SysNSStringToUTF16;
using base::SysUTF16ToNSString; using base::SysUTF16ToNSString;
using content::a11y::AttributeInvoker; using content::a11y::AttributeInvoker;
using content::a11y::AttributeNamesOf;
using content::a11y::AttributeValueOf;
using content::a11y::ChildrenOf; using content::a11y::ChildrenOf;
using content::a11y::IsAXUIElement; using content::a11y::IsAXUIElement;
using content::a11y::IsBrowserAccessibilityCocoa; using content::a11y::IsBrowserAccessibilityCocoa;
...@@ -79,10 +81,7 @@ class AccessibilityTreeFormatterMac : public AccessibilityTreeFormatterBase { ...@@ -79,10 +81,7 @@ class AccessibilityTreeFormatterMac : public AccessibilityTreeFormatterBase {
const std::string GetDenyNodeString() override; const std::string GetDenyNodeString() override;
const std::string GetRunUntilEventString() override; const std::string GetRunUntilEventString() override;
void AddProperties(const BrowserAccessibilityCocoa* node, void AddProperties(const id node,
const LineIndexer* line_indexer,
base::Value* dict) const;
void AddProperties(const AXUIElementRef node,
const LineIndexer* line_indexer, const LineIndexer* line_indexer,
base::Value* dict) const; base::Value* dict) const;
...@@ -179,12 +178,7 @@ void AccessibilityTreeFormatterMac::RecursiveBuildAccessibilityTree( ...@@ -179,12 +178,7 @@ void AccessibilityTreeFormatterMac::RecursiveBuildAccessibilityTree(
const id node, const id node,
const LineIndexer* line_indexer, const LineIndexer* line_indexer,
base::DictionaryValue* dict) const { base::DictionaryValue* dict) const {
if (IsAXUIElement(node)) { AddProperties(node, line_indexer, dict);
AddProperties(static_cast<AXUIElementRef>(node), line_indexer, dict);
} else if (IsBrowserAccessibilityCocoa(node)) {
AddProperties(static_cast<BrowserAccessibilityCocoa*>(node), line_indexer,
dict);
}
NSArray* children = ChildrenOf(node); NSArray* children = ChildrenOf(node);
auto child_dict_list = std::make_unique<base::ListValue>(); auto child_dict_list = std::make_unique<base::ListValue>();
...@@ -198,28 +192,39 @@ void AccessibilityTreeFormatterMac::RecursiveBuildAccessibilityTree( ...@@ -198,28 +192,39 @@ void AccessibilityTreeFormatterMac::RecursiveBuildAccessibilityTree(
} }
void AccessibilityTreeFormatterMac::AddProperties( void AccessibilityTreeFormatterMac::AddProperties(
const BrowserAccessibilityCocoa* cocoa_node, const id node,
const LineIndexer* line_indexer, const LineIndexer* line_indexer,
base::Value* dict) const { base::Value* dict) const {
// Chromium tree special processing
if (IsBrowserAccessibilityCocoa(node)) {
BrowserAccessibilityCocoa* cocoa_node =
static_cast<BrowserAccessibilityCocoa*>(node);
// DOM element id // DOM element id
BrowserAccessibility* node = [cocoa_node owner]; BrowserAccessibility* owner_node = [cocoa_node owner];
dict->SetKey("id", base::Value(base::NumberToString16(node->GetId()))); dict->SetKey("id",
base::Value(base::NumberToString16(owner_node->GetId())));
// Position and size
dict->SetPath(kPositionDictAttr, PopulatePosition(cocoa_node));
dict->SetPath(kSizeDictAttr, PopulateSize(cocoa_node));
}
// Dump all attributes if match-all filter is specified. // Dump all attributes if match-all filter is specified.
if (HasMatchAllPropertyFilter()) { if (HasMatchAllPropertyFilter()) {
for (NSString* attribute : [cocoa_node accessibilityAttributeNames]) { NSArray* attributes = AttributeNamesOf(node);
for (NSString* attribute : attributes) {
dict->SetPath( dict->SetPath(
SysNSStringToUTF8(attribute), SysNSStringToUTF8(attribute),
PopulateObject([cocoa_node accessibilityAttributeValue:attribute], PopulateObject(AttributeValueOf(node, attribute), line_indexer));
line_indexer));
} }
return; return;
} }
// Otherwise dump attributes matching allow filters only. // Otherwise dump attributes matching allow filters only.
std::string line_index = line_indexer->IndexBy(cocoa_node); std::string line_index = line_indexer->IndexBy(node);
for (const PropertyNode& property_node : PropertyFilterNodesFor(line_index)) { for (const PropertyNode& property_node : PropertyFilterNodesFor(line_index)) {
AttributeInvoker invoker(cocoa_node, line_indexer); AttributeInvoker invoker(node, line_indexer);
OptionalNSObject value = invoker.Invoke(property_node); OptionalNSObject value = invoker.Invoke(property_node);
if (value.IsNotApplicable()) { if (value.IsNotApplicable()) {
continue; continue;
...@@ -232,31 +237,6 @@ void AccessibilityTreeFormatterMac::AddProperties( ...@@ -232,31 +237,6 @@ void AccessibilityTreeFormatterMac::AddProperties(
dict->SetPath(property_node.original_property, dict->SetPath(property_node.original_property,
PopulateObject(*value, line_indexer)); PopulateObject(*value, line_indexer));
} }
// Position and size
dict->SetPath(kPositionDictAttr, PopulatePosition(cocoa_node));
dict->SetPath(kSizeDictAttr, PopulateSize(cocoa_node));
}
void AccessibilityTreeFormatterMac::AddProperties(
const AXUIElementRef node,
const LineIndexer* line_indexer,
base::Value* dict) const {
NSArray* attributes = nil;
if (AXUIElementCopyAttributeNames(node, (CFArrayRef*)&attributes) ==
kAXErrorSuccess) {
for (uint32_t i = 0; i < [attributes count]; i++) {
NSString* attribute =
static_cast<NSString*>([attributes objectAtIndex:i]);
CFTypeRef value_ref;
if ((AXUIElementCopyAttributeValue(
node, static_cast<CFStringRef>(attribute), &value_ref)) ==
kAXErrorSuccess) {
dict->SetPath(SysNSStringToUTF8(attribute),
PopulateObject(static_cast<id>(value_ref), line_indexer));
}
}
}
} }
base::Value AccessibilityTreeFormatterMac::PopulateSize( base::Value AccessibilityTreeFormatterMac::PopulateSize(
......
...@@ -27,6 +27,22 @@ bool IsAXUIElement(const id node); ...@@ -27,6 +27,22 @@ bool IsAXUIElement(const id node);
*/ */
NSArray* ChildrenOf(const id node); NSArray* ChildrenOf(const id node);
/**
* Returns (parameterized) attributes of an accessible object, (either
* AXUIElement or BrowserAccessibilityCocoa).
*/
NSArray* AttributeNamesOf(const id node);
NSArray* ParameterizedAttributeNamesOf(const id node);
/**
* Returns (parameterized) attribute value on a given node (either AXUIElement
* or BrowserAccessibilityCocoa)
*/
id AttributeValueOf(const id node, NSString* attribute);
id ParameterizedAttributeValueOf(const id node,
NSString* attribute,
id parameter);
/** /**
* Converts accessible node object to a line index in the formatted * Converts accessible node object to a line index in the formatted
* accessibility tree, the node is placed at, and vice versa. * accessibility tree, the node is placed at, and vice versa.
...@@ -79,8 +95,7 @@ class OptionalNSObject final { ...@@ -79,8 +95,7 @@ class OptionalNSObject final {
// Invokes attributes matching the given property filter. // Invokes attributes matching the given property filter.
class AttributeInvoker final { class AttributeInvoker final {
public: public:
AttributeInvoker(const BrowserAccessibilityCocoa* cocoa_node, AttributeInvoker(const id node, const LineIndexer* line_indexer);
const LineIndexer* line_indexer);
// Invokes an attribute matching to a property filter. // Invokes an attribute matching to a property filter.
OptionalNSObject Invoke(const PropertyNode& property_node) const; OptionalNSObject Invoke(const PropertyNode& property_node) const;
...@@ -101,7 +116,7 @@ class AttributeInvoker final { ...@@ -101,7 +116,7 @@ class AttributeInvoker final {
gfx::NativeViewAccessible LineIndexToNode( gfx::NativeViewAccessible LineIndexToNode(
const base::string16 line_index) const; const base::string16 line_index) const;
const BrowserAccessibilityCocoa* cocoa_node; const id node;
const LineIndexer* line_indexer; const LineIndexer* line_indexer;
const NSArray* attributes; const NSArray* attributes;
const NSArray* parameterized_attributes; const NSArray* parameterized_attributes;
......
...@@ -76,6 +76,85 @@ NSArray* ChildrenOf(const id node) { ...@@ -76,6 +76,85 @@ NSArray* ChildrenOf(const id node) {
return nil; return nil;
} }
NSArray* AttributeNamesOf(const id node) {
if (IsBrowserAccessibilityCocoa(node)) {
return [node accessibilityAttributeNames];
}
if (IsAXUIElement(node)) {
CFArrayRef attributes_ref;
if (AXUIElementCopyAttributeNames(static_cast<AXUIElementRef>(node),
&attributes_ref) == kAXErrorSuccess) {
return static_cast<NSArray*>(attributes_ref);
}
return nil;
}
NOTREACHED();
return nil;
}
NSArray* ParameterizedAttributeNamesOf(const id node) {
if (IsBrowserAccessibilityCocoa(node)) {
return [node accessibilityParameterizedAttributeNames];
}
if (IsAXUIElement(node)) {
CFArrayRef attributes_ref;
if (AXUIElementCopyParameterizedAttributeNames(
static_cast<AXUIElementRef>(node), &attributes_ref) ==
kAXErrorSuccess) {
return static_cast<NSArray*>(attributes_ref);
}
return nil;
}
NOTREACHED();
return nil;
}
id AttributeValueOf(const id node, NSString* attribute) {
if (IsBrowserAccessibilityCocoa(node)) {
return [node accessibilityAttributeValue:attribute];
}
if (IsAXUIElement(node)) {
CFTypeRef value_ref;
if ((AXUIElementCopyAttributeValue(static_cast<AXUIElementRef>(node),
static_cast<CFStringRef>(attribute),
&value_ref)) == kAXErrorSuccess) {
return static_cast<id>(value_ref);
}
return nil;
}
NOTREACHED();
return nil;
}
id ParameterizedAttributeValueOf(const id node,
NSString* attribute,
id parameter) {
if (IsBrowserAccessibilityCocoa(node)) {
return [node accessibilityAttributeValue:attribute forParameter:parameter];
}
if (IsAXUIElement(node)) {
CFTypeRef value_ref;
if ((AXUIElementCopyParameterizedAttributeValue(
static_cast<AXUIElementRef>(node),
static_cast<CFStringRef>(attribute),
static_cast<CFTypeRef>(parameter), &value_ref)) ==
kAXErrorSuccess) {
return static_cast<id>(value_ref);
}
return nil;
}
NOTREACHED();
return nil;
}
// Line indexers // Line indexers
LineIndexer::LineIndexer(const gfx::NativeViewAccessible node) { LineIndexer::LineIndexer(const gfx::NativeViewAccessible node) {
...@@ -115,12 +194,11 @@ void LineIndexer::Build(const gfx::NativeViewAccessible node, int* counter) { ...@@ -115,12 +194,11 @@ void LineIndexer::Build(const gfx::NativeViewAccessible node, int* counter) {
// AttributeInvoker // AttributeInvoker
AttributeInvoker::AttributeInvoker(const BrowserAccessibilityCocoa* cocoa_node, AttributeInvoker::AttributeInvoker(const id node,
const LineIndexer* line_indexer) const LineIndexer* line_indexer)
: cocoa_node(cocoa_node), line_indexer(line_indexer) { : node(node), line_indexer(line_indexer) {
attributes = [cocoa_node accessibilityAttributeNames]; attributes = AttributeNamesOf(node);
parameterized_attributes = parameterized_attributes = ParameterizedAttributeNamesOf(node);
[cocoa_node accessibilityParameterizedAttributeNames];
} }
OptionalNSObject AttributeInvoker::Invoke( OptionalNSObject AttributeInvoker::Invoke(
...@@ -129,7 +207,7 @@ OptionalNSObject AttributeInvoker::Invoke( ...@@ -129,7 +207,7 @@ OptionalNSObject AttributeInvoker::Invoke(
for (NSString* attribute : attributes) { for (NSString* attribute : attributes) {
if (property_node.IsMatching(SysNSStringToUTF8(attribute))) { if (property_node.IsMatching(SysNSStringToUTF8(attribute))) {
return OptionalNSObject::NotNullOrNotApplicable( return OptionalNSObject::NotNullOrNotApplicable(
[cocoa_node accessibilityAttributeValue:attribute]); AttributeValueOf(node, attribute));
} }
} }
...@@ -138,9 +216,8 @@ OptionalNSObject AttributeInvoker::Invoke( ...@@ -138,9 +216,8 @@ OptionalNSObject AttributeInvoker::Invoke(
if (property_node.IsMatching(SysNSStringToUTF8(attribute))) { if (property_node.IsMatching(SysNSStringToUTF8(attribute))) {
OptionalNSObject param = ParamByPropertyNode(property_node); OptionalNSObject param = ParamByPropertyNode(property_node);
if (param.IsNotNil()) { if (param.IsNotNil()) {
return OptionalNSObject([cocoa_node return OptionalNSObject(
accessibilityAttributeValue:attribute ParameterizedAttributeValueOf(node, attribute, *param));
forParameter:*param]);
} }
return param; return param;
} }
......
...@@ -26,45 +26,55 @@ constexpr char kAllowOptEmptyStr[] = "@ALLOW-EMPTY:"; ...@@ -26,45 +26,55 @@ constexpr char kAllowOptEmptyStr[] = "@ALLOW-EMPTY:";
constexpr char kAllowOptStr[] = "@ALLOW:"; constexpr char kAllowOptStr[] = "@ALLOW:";
constexpr char kDenyOptStr[] = "@DENY:"; constexpr char kDenyOptStr[] = "@DENY:";
std::unique_ptr<base::DictionaryValue> BuildTreeForPattern(
const base::StringPiece& pattern,
AccessibilityTreeFormatter* formatter) {
return formatter->BuildAccessibilityTreeForPattern(pattern);
}
std::unique_ptr<base::DictionaryValue> BuildTreeForWindow(
gfx::AcceleratedWidget widget,
AccessibilityTreeFormatter* formatter) {
return formatter->BuildAccessibilityTreeForWindow(widget);
}
AXTreeServer::AXTreeServer(const base::StringPiece& pattern, AXTreeServer::AXTreeServer(const base::StringPiece& pattern,
const base::FilePath& filters_path, const base::FilePath& filters_path,
bool use_json) { bool use_json) {
std::unique_ptr<AccessibilityTreeFormatter> formatter( Run(base::BindOnce(&BuildTreeForPattern, pattern), filters_path, use_json);
AccessibilityTreeFormatter::Create());
// Get accessibility tree as nested dictionary.
base::string16 accessibility_contents_utf16;
std::unique_ptr<base::DictionaryValue> dict =
formatter->BuildAccessibilityTreeForPattern(pattern);
if (!dict) {
LOG(ERROR) << "Error: Failed to get accessibility tree";
return;
}
Format(*formatter, *dict, filters_path, use_json);
} }
AXTreeServer::AXTreeServer(gfx::AcceleratedWidget widget, AXTreeServer::AXTreeServer(gfx::AcceleratedWidget widget,
const base::FilePath& filters_path, const base::FilePath& filters_path,
bool use_json) { bool use_json) {
Run(base::BindOnce(&BuildTreeForWindow, widget), filters_path, use_json);
}
void AXTreeServer::Run(BuildTree build_tree,
const base::FilePath& filters_path,
bool use_json) {
std::unique_ptr<AccessibilityTreeFormatter> formatter( std::unique_ptr<AccessibilityTreeFormatter> formatter(
AccessibilityTreeFormatter::Create()); AccessibilityTreeFormatter::Create());
// Get accessibility tree as nested dictionary. // Set filters.
std::unique_ptr<base::DictionaryValue> dict = std::vector<AccessibilityTreeFormatter::PropertyFilter> filters =
formatter->BuildAccessibilityTreeForWindow(widget); GetPropertyFilters(filters_path);
formatter->SetPropertyFilters(filters);
// Get accessibility tree as a nested dictionary.
std::unique_ptr<base::DictionaryValue> dict =
std::move(build_tree).Run(formatter.get());
if (!dict) { if (!dict) {
LOG(ERROR) << "Failed to get accessibility tree"; LOG(ERROR) << "Failed to get accessibility tree";
return; return;
} }
Format(*formatter, *dict, filters_path, use_json); // Format the tree.
Format(*formatter, *dict, use_json);
} }
std::vector<AccessibilityTreeFormatter::PropertyFilter> GetPropertyFilters( std::vector<AccessibilityTreeFormatter::PropertyFilter>
const base::FilePath& filters_path) { AXTreeServer::GetPropertyFilters(const base::FilePath& filters_path) {
std::vector<AccessibilityTreeFormatter::PropertyFilter> filters; std::vector<AccessibilityTreeFormatter::PropertyFilter> filters;
if (!filters_path.empty()) { if (!filters_path.empty()) {
std::string raw_filters_text; std::string raw_filters_text;
...@@ -102,14 +112,7 @@ std::vector<AccessibilityTreeFormatter::PropertyFilter> GetPropertyFilters( ...@@ -102,14 +112,7 @@ std::vector<AccessibilityTreeFormatter::PropertyFilter> GetPropertyFilters(
void AXTreeServer::Format(AccessibilityTreeFormatter& formatter, void AXTreeServer::Format(AccessibilityTreeFormatter& formatter,
const base::DictionaryValue& dict, const base::DictionaryValue& dict,
const base::FilePath& filters_path,
bool use_json) { bool use_json) {
std::vector<AccessibilityTreeFormatter::PropertyFilter> filters =
GetPropertyFilters(filters_path);
// Set filters.
formatter.SetPropertyFilters(filters);
std::string accessibility_contents; std::string accessibility_contents;
// Format accessibility tree as JSON or text. // Format accessibility tree as JSON or text.
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <string> #include <string>
#include "base/callback.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "content/public/browser/accessibility_tree_formatter.h" #include "content/public/browser/accessibility_tree_formatter.h"
...@@ -26,9 +27,21 @@ class AXTreeServer final { ...@@ -26,9 +27,21 @@ class AXTreeServer final {
bool use_json); bool use_json);
private: private:
using BuildTree = base::OnceCallback<std::unique_ptr<base::DictionaryValue>(
AccessibilityTreeFormatter*)>;
// Builds and formats the accessible tree.
void Run(BuildTree build_tree,
const base::FilePath& filters_path,
bool use_json);
// Generates property filters.
std::vector<AccessibilityTreeFormatter::PropertyFilter> GetPropertyFilters(
const base::FilePath& filters_path);
// Formats and dumps into console the tree.
void Format(AccessibilityTreeFormatter& formatter, void Format(AccessibilityTreeFormatter& formatter,
const base::DictionaryValue& dict, const base::DictionaryValue& dict,
const base::FilePath& filters_path,
bool use_json); bool use_json);
#if defined(OS_WIN) #if defined(OS_WIN)
......
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