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;
using base::SysNSStringToUTF16;
using base::SysUTF16ToNSString;
using content::a11y::AttributeInvoker;
using content::a11y::AttributeNamesOf;
using content::a11y::AttributeValueOf;
using content::a11y::ChildrenOf;
using content::a11y::IsAXUIElement;
using content::a11y::IsBrowserAccessibilityCocoa;
......@@ -79,10 +81,7 @@ class AccessibilityTreeFormatterMac : public AccessibilityTreeFormatterBase {
const std::string GetDenyNodeString() override;
const std::string GetRunUntilEventString() override;
void AddProperties(const BrowserAccessibilityCocoa* node,
const LineIndexer* line_indexer,
base::Value* dict) const;
void AddProperties(const AXUIElementRef node,
void AddProperties(const id node,
const LineIndexer* line_indexer,
base::Value* dict) const;
......@@ -179,12 +178,7 @@ void AccessibilityTreeFormatterMac::RecursiveBuildAccessibilityTree(
const id node,
const LineIndexer* line_indexer,
base::DictionaryValue* dict) const {
if (IsAXUIElement(node)) {
AddProperties(static_cast<AXUIElementRef>(node), line_indexer, dict);
} else if (IsBrowserAccessibilityCocoa(node)) {
AddProperties(static_cast<BrowserAccessibilityCocoa*>(node), line_indexer,
dict);
}
AddProperties(node, line_indexer, dict);
NSArray* children = ChildrenOf(node);
auto child_dict_list = std::make_unique<base::ListValue>();
......@@ -198,28 +192,39 @@ void AccessibilityTreeFormatterMac::RecursiveBuildAccessibilityTree(
}
void AccessibilityTreeFormatterMac::AddProperties(
const BrowserAccessibilityCocoa* cocoa_node,
const id node,
const LineIndexer* line_indexer,
base::Value* dict) const {
// Chromium tree special processing
if (IsBrowserAccessibilityCocoa(node)) {
BrowserAccessibilityCocoa* cocoa_node =
static_cast<BrowserAccessibilityCocoa*>(node);
// DOM element id
BrowserAccessibility* node = [cocoa_node owner];
dict->SetKey("id", base::Value(base::NumberToString16(node->GetId())));
BrowserAccessibility* owner_node = [cocoa_node owner];
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.
if (HasMatchAllPropertyFilter()) {
for (NSString* attribute : [cocoa_node accessibilityAttributeNames]) {
NSArray* attributes = AttributeNamesOf(node);
for (NSString* attribute : attributes) {
dict->SetPath(
SysNSStringToUTF8(attribute),
PopulateObject([cocoa_node accessibilityAttributeValue:attribute],
line_indexer));
PopulateObject(AttributeValueOf(node, attribute), line_indexer));
}
return;
}
// 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)) {
AttributeInvoker invoker(cocoa_node, line_indexer);
AttributeInvoker invoker(node, line_indexer);
OptionalNSObject value = invoker.Invoke(property_node);
if (value.IsNotApplicable()) {
continue;
......@@ -232,31 +237,6 @@ void AccessibilityTreeFormatterMac::AddProperties(
dict->SetPath(property_node.original_property,
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(
......
......@@ -27,6 +27,22 @@ bool IsAXUIElement(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
* accessibility tree, the node is placed at, and vice versa.
......@@ -79,8 +95,7 @@ class OptionalNSObject final {
// Invokes attributes matching the given property filter.
class AttributeInvoker final {
public:
AttributeInvoker(const BrowserAccessibilityCocoa* cocoa_node,
const LineIndexer* line_indexer);
AttributeInvoker(const id node, const LineIndexer* line_indexer);
// Invokes an attribute matching to a property filter.
OptionalNSObject Invoke(const PropertyNode& property_node) const;
......@@ -101,7 +116,7 @@ class AttributeInvoker final {
gfx::NativeViewAccessible LineIndexToNode(
const base::string16 line_index) const;
const BrowserAccessibilityCocoa* cocoa_node;
const id node;
const LineIndexer* line_indexer;
const NSArray* attributes;
const NSArray* parameterized_attributes;
......
......@@ -76,6 +76,85 @@ NSArray* ChildrenOf(const id node) {
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
LineIndexer::LineIndexer(const gfx::NativeViewAccessible node) {
......@@ -115,12 +194,11 @@ void LineIndexer::Build(const gfx::NativeViewAccessible node, int* counter) {
// AttributeInvoker
AttributeInvoker::AttributeInvoker(const BrowserAccessibilityCocoa* cocoa_node,
AttributeInvoker::AttributeInvoker(const id node,
const LineIndexer* line_indexer)
: cocoa_node(cocoa_node), line_indexer(line_indexer) {
attributes = [cocoa_node accessibilityAttributeNames];
parameterized_attributes =
[cocoa_node accessibilityParameterizedAttributeNames];
: node(node), line_indexer(line_indexer) {
attributes = AttributeNamesOf(node);
parameterized_attributes = ParameterizedAttributeNamesOf(node);
}
OptionalNSObject AttributeInvoker::Invoke(
......@@ -129,7 +207,7 @@ OptionalNSObject AttributeInvoker::Invoke(
for (NSString* attribute : attributes) {
if (property_node.IsMatching(SysNSStringToUTF8(attribute))) {
return OptionalNSObject::NotNullOrNotApplicable(
[cocoa_node accessibilityAttributeValue:attribute]);
AttributeValueOf(node, attribute));
}
}
......@@ -138,9 +216,8 @@ OptionalNSObject AttributeInvoker::Invoke(
if (property_node.IsMatching(SysNSStringToUTF8(attribute))) {
OptionalNSObject param = ParamByPropertyNode(property_node);
if (param.IsNotNil()) {
return OptionalNSObject([cocoa_node
accessibilityAttributeValue:attribute
forParameter:*param]);
return OptionalNSObject(
ParameterizedAttributeValueOf(node, attribute, *param));
}
return param;
}
......
......@@ -26,45 +26,55 @@ constexpr char kAllowOptEmptyStr[] = "@ALLOW-EMPTY:";
constexpr char kAllowOptStr[] = "@ALLOW:";
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,
const base::FilePath& filters_path,
bool use_json) {
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->BuildAccessibilityTreeForPattern(pattern);
if (!dict) {
LOG(ERROR) << "Error: Failed to get accessibility tree";
return;
}
Format(*formatter, *dict, filters_path, use_json);
Run(base::BindOnce(&BuildTreeForPattern, pattern), filters_path, use_json);
}
AXTreeServer::AXTreeServer(gfx::AcceleratedWidget widget,
const base::FilePath& filters_path,
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(
AccessibilityTreeFormatter::Create());
// Get accessibility tree as nested dictionary.
std::unique_ptr<base::DictionaryValue> dict =
formatter->BuildAccessibilityTreeForWindow(widget);
// Set filters.
std::vector<AccessibilityTreeFormatter::PropertyFilter> filters =
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) {
LOG(ERROR) << "Failed to get accessibility tree";
return;
}
Format(*formatter, *dict, filters_path, use_json);
// Format the tree.
Format(*formatter, *dict, use_json);
}
std::vector<AccessibilityTreeFormatter::PropertyFilter> GetPropertyFilters(
const base::FilePath& filters_path) {
std::vector<AccessibilityTreeFormatter::PropertyFilter>
AXTreeServer::GetPropertyFilters(const base::FilePath& filters_path) {
std::vector<AccessibilityTreeFormatter::PropertyFilter> filters;
if (!filters_path.empty()) {
std::string raw_filters_text;
......@@ -102,14 +112,7 @@ std::vector<AccessibilityTreeFormatter::PropertyFilter> GetPropertyFilters(
void AXTreeServer::Format(AccessibilityTreeFormatter& formatter,
const base::DictionaryValue& dict,
const base::FilePath& filters_path,
bool use_json) {
std::vector<AccessibilityTreeFormatter::PropertyFilter> filters =
GetPropertyFilters(filters_path);
// Set filters.
formatter.SetPropertyFilters(filters);
std::string accessibility_contents;
// Format accessibility tree as JSON or text.
......
......@@ -7,6 +7,7 @@
#include <string>
#include "base/callback.h"
#include "build/build_config.h"
#include "content/public/browser/accessibility_tree_formatter.h"
......@@ -26,9 +27,21 @@ class AXTreeServer final {
bool use_json);
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,
const base::DictionaryValue& dict,
const base::FilePath& filters_path,
bool use_json);
#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