Skip to content

Commit

Permalink
ax_tools: no warning noise when dumping an ax tree by ax_dump tools
Browse files Browse the repository at this point in the history
Reorganize AXElementWrapper to move warning logic out and let decide
the caller whether a warning should be reported or ignored.

Because of imperfect NSAccessbility implementation in browsers, certain
methods fail. When ax_dump_tree dumps an ax tree, it calls all possible
NSAccessibility attributes, and thus the ax_dump_tree output may get
noisy. Scope the warnings reporting to ax scripting which makes
targeted NSAccessibility attribute calls only, and thus it doesn't have
the same issue as ax_dump_tree has. In other words give a proper
implementation of the feature implemented in CL 3585030.

Followup of: https://chromium-review.googlesource.com/c/chromium/src/+/3585030

Change-Id: I1556d529a810beba49379352a37664f05e89398a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3810553
Reviewed-by: Nektarios Paisios <nektar@chromium.org>
Commit-Queue: Alexander Surkkov <asurkov@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1033547}
  • Loading branch information
asurkov authored and Chromium LUCI CQ committed Aug 10, 2022
1 parent f7e23c1 commit 0feb1a2
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 95 deletions.
Expand Up @@ -7,17 +7,13 @@

#include "base/memory/raw_ptr.h"
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/platform/inspect/ax_optional.h"
#include "ui/accessibility/platform/inspect/ax_tree_indexer_mac.h"

namespace ui {

class AXElementWrapper;
class AXPropertyNode;

// Optional tri-state id object.
using AXOptionalNSObject = AXOptional<id>;

// Invokes a script instruction describing a call unit which represents
// a sequence of calls.
class AX_EXPORT AXCallStatementInvoker final {
Expand Down
Expand Up @@ -200,9 +200,11 @@
return rvalue;
}
// Getter. Make sure to expose null values in ax scripts.
id value = ax_element.GetAttributeValue(attribute);
return IsDumpingTree() ? AXOptionalNSObject::NotNullOrNotApplicable(value)
: AXOptionalNSObject(value);
AXOptionalNSObject optional_value =
ax_element.GetAttributeValue(attribute);
return IsDumpingTree()
? AXOptionalNSObject::NotNullOrNotApplicable(*optional_value)
: optional_value;
}
}

Expand All @@ -211,8 +213,7 @@
if (property_node.IsMatching(base::SysNSStringToUTF8(attribute))) {
AXOptionalNSObject param = ParamFrom(property_node);
if (param.IsNotNull()) {
return AXOptionalNSObject(
ax_element.GetParameterizedAttributeValue(attribute, *param));
return ax_element.GetParameterizedAttributeValue(attribute, *param);
}
return param;
}
Expand Down
17 changes: 14 additions & 3 deletions ui/accessibility/platform/inspect/ax_element_wrapper_mac.h
Expand Up @@ -11,9 +11,13 @@
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/platform/inspect/ax_inspect.h"
#include "ui/accessibility/platform/inspect/ax_optional.h"

namespace ui {

// Optional tri-state id object.
using AXOptionalNSObject = AXOptional<id>;

// A wrapper around AXUIElement or NSAccessibilityElement object.
class AX_EXPORT AXElementWrapper final {
public:
Expand Down Expand Up @@ -65,8 +69,9 @@ class AX_EXPORT AXElementWrapper final {
NSArray* ParameterizedAttributeNames() const;

// Returns (parameterized) attribute value on the object.
id GetAttributeValue(NSString* attribute) const;
id GetParameterizedAttributeValue(NSString* attribute, id parameter) const;
AXOptionalNSObject GetAttributeValue(NSString* attribute) const;
AXOptionalNSObject GetParameterizedAttributeValue(NSString* attribute,
id parameter) const;

// Performs the given selector on the object and returns the result. If
// the object does not conform to the NSAccessibility protocol or the selector
Expand Down Expand Up @@ -136,8 +141,14 @@ class AX_EXPORT AXElementWrapper final {
template <typename... Args>
void SetInvocationArguments(NSInvocation*, int) const {}

// Generates an error message from the given error.
std::string AXErrorMessage(AXError, const std::string& message) const;

// Returns true on success, otherwise returns false and logs error.
bool AXSuccess(AXError, const std::string& message) const;
bool AXSuccess(AXError result, const std::string& message) const;

// Converts the given value and the error object into AXOptional object.
AXOptionalNSObject ToOptional(id, AXError, const std::string& message) const;

const id node_;
};
Expand Down
71 changes: 45 additions & 26 deletions ui/accessibility/platform/inspect/ax_element_wrapper_mac.mm
Expand Up @@ -24,6 +24,9 @@

using base::SysNSStringToUTF8;

constexpr char kUnsupportedObject[] =
"Only AXUIElementRef and BrowserAccessibilityCocoa are supported.";

// static
bool AXElementWrapper::IsValidElement(const id node) {
return AXElementWrapper(node).IsValidElement();
Expand Down Expand Up @@ -65,7 +68,7 @@

std::string AXElementWrapper::DOMId() const {
const id domid_value =
GetAttributeValue(base::SysUTF8ToNSString("AXDOMIdentifier"));
*GetAttributeValue(base::SysUTF8ToNSString("AXDOMIdentifier"));
return base::SysNSStringToUTF8(static_cast<NSString*>(domid_value));
}

Expand Down Expand Up @@ -98,7 +101,7 @@
return NSMakeSize(0, 0);
}

id value = GetAttributeValue(NSAccessibilitySizeAttribute);
id value = *GetAttributeValue(NSAccessibilitySizeAttribute);
if (value && CFGetTypeID(value) == AXValueGetTypeID()) {
AXValueType type = AXValueGetType(static_cast<AXValueRef>(value));
if (type == kAXValueCGSizeType) {
Expand All @@ -122,7 +125,7 @@
return NSMakePoint(0, 0);
}

id value = GetAttributeValue(NSAccessibilityPositionAttribute);
id value = *GetAttributeValue(NSAccessibilityPositionAttribute);
if (value && CFGetTypeID(value) == AXValueGetTypeID()) {
AXValueType type = AXValueGetType(static_cast<AXValueRef>(value));
if (type == kAXValueCGPointType) {
Expand Down Expand Up @@ -171,30 +174,30 @@
return nil;
}

id AXElementWrapper::GetAttributeValue(NSString* attribute) const {
AXOptionalNSObject AXElementWrapper::GetAttributeValue(
NSString* attribute) const {
if (IsNSAccessibilityElement())
return [node_ accessibilityAttributeValue:attribute];
return AXOptionalNSObject([node_ accessibilityAttributeValue:attribute]);

if (IsAXUIElement()) {
CFTypeRef value_ref;
AXError result = AXUIElementCopyAttributeValue(
static_cast<AXUIElementRef>(node_), static_cast<CFStringRef>(attribute),
&value_ref);
if (AXSuccess(result, "AXGetAttributeValue(" +
base::SysNSStringToUTF8(attribute) + ")"))
return static_cast<id>(value_ref);
return nil;
return ToOptional(
static_cast<id>(value_ref), result,
"AXGetAttributeValue(" + base::SysNSStringToUTF8(attribute) + ")");
}

NOTREACHED()
<< "Only AXUIElementRef and BrowserAccessibilityCocoa are supported.";
return nil;
return AXOptionalNSObject::Error(kUnsupportedObject);
}

id AXElementWrapper::GetParameterizedAttributeValue(NSString* attribute,
id parameter) const {
AXOptionalNSObject AXElementWrapper::GetParameterizedAttributeValue(
NSString* attribute,
id parameter) const {
if (IsNSAccessibilityElement())
return [node_ accessibilityAttributeValue:attribute forParameter:parameter];
return AXOptionalNSObject([node_ accessibilityAttributeValue:attribute
forParameter:parameter]);

if (IsAXUIElement()) {
// Convert NSValue parameter to CFTypeRef if needed.
Expand All @@ -211,16 +214,13 @@
AXError result = AXUIElementCopyParameterizedAttributeValue(
static_cast<AXUIElementRef>(node_), static_cast<CFStringRef>(attribute),
parameter_ref, &value_ref);
if (AXSuccess(result, "GetParameterizedAttributeValue(" +
base::SysNSStringToUTF8(attribute) + ")"))
return static_cast<id>(value_ref);

return nil;
return ToOptional(static_cast<id>(value_ref), result,
"GetParameterizedAttributeValue(" +
base::SysNSStringToUTF8(attribute) + ")");
}

NOTREACHED()
<< "Only AXUIElementRef and BrowserAccessibilityCocoa are supported.";
return nil;
return AXOptionalNSObject::Error(kUnsupportedObject);
}

absl::optional<id> AXElementWrapper::PerformSelector(
Expand Down Expand Up @@ -301,10 +301,10 @@
<< "Only AXUIElementRef and BrowserAccessibilityCocoa are supported.";
}

bool AXElementWrapper::AXSuccess(AXError result,
const std::string& message) const {
std::string AXElementWrapper::AXErrorMessage(AXError result,
const std::string& message) const {
if (result == kAXErrorSuccess) {
return true;
return {};
}

std::string error;
Expand Down Expand Up @@ -334,10 +334,29 @@
error = "unknown error";
break;
}
LOG(WARNING) << message << ": " << error;
return {message + ": " + error};
}

bool AXElementWrapper::AXSuccess(AXError result,
const std::string& message) const {
std::string message_text = AXErrorMessage(result, message);
if (message_text.empty())
return true;

LOG(WARNING) << message_text;
return false;
}

AXOptionalNSObject AXElementWrapper::ToOptional(
id value,
AXError result,
const std::string& message) const {
if (result == kAXErrorSuccess)
return AXOptionalNSObject(value);

return AXOptionalNSObject::Error(AXErrorMessage(result, message));
}

} // namespace ui

#pragma clang diagnostic pop
3 changes: 0 additions & 3 deletions ui/accessibility/platform/inspect/ax_inspect_utils_mac.h
Expand Up @@ -32,9 +32,6 @@ AX_EXPORT std::pair<AXUIElementRef, int> FindAXUIElement(const AXTreeSelector&);
AX_EXPORT AXUIElementRef FindAXWindowChild(AXUIElementRef parent,
const std::string& pattern);

// Returns true on success, otherwise returns false and logs error.
AX_EXPORT bool AXSuccess(AXError, const std::string& message);

} // namespace ui

#endif // UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_INSPECT_UTILS_MAC_H_
42 changes: 3 additions & 39 deletions ui/accessibility/platform/inspect/ax_inspect_utils_mac.mm
Expand Up @@ -173,7 +173,7 @@ AXUIElementRef FindAXUIElement(const AXUIElementRef node,
// AXWebArea role.
AXElementWrapper ax_node(static_cast<id>(node));
NSString* role =
ax_node.GetAttributeValue(NSAccessibilityRoleAttribute);
*ax_node.GetAttributeValue(NSAccessibilityRoleAttribute);
return SysNSStringToUTF8(role) == "AXWebArea";
}));
}
Expand All @@ -194,54 +194,18 @@ AXUIElementRef FindAXWindowChild(AXUIElementRef parent,
id window = [children objectAtIndex:0];

AXElementWrapper ax_window(window);
NSString* role = ax_window.GetAttributeValue(NSAccessibilityRoleAttribute);
NSString* role = *ax_window.GetAttributeValue(NSAccessibilityRoleAttribute);
if (SysNSStringToUTF8(role) != "AXWindow")
return nil;

NSString* window_title =
ax_window.GetAttributeValue(NSAccessibilityTitleAttribute);
*ax_window.GetAttributeValue(NSAccessibilityTitleAttribute);
if (base::MatchPattern(SysNSStringToUTF8(window_title), pattern))
return static_cast<AXUIElementRef>(window);

return nil;
}

AX_EXPORT bool AXSuccess(AXError result, const std::string& message) {
if (result == kAXErrorSuccess) {
return true;
}

std::string error;
switch (result) {
case kAXErrorAttributeUnsupported:
error = "attribute unsupported";
break;
case kAXErrorParameterizedAttributeUnsupported:
error = "parameterized attribute unsupported";
break;
case kAXErrorNoValue:
error = "no value";
break;
case kAXErrorIllegalArgument:
error = "illegal argument";
break;
case kAXErrorInvalidUIElement:
error = "invalid UIElement";
break;
case kAXErrorCannotComplete:
error = "cannot complete";
break;
case kAXErrorNotImplemented:
error = "not implemented";
break;
default:
error = "unknown error";
break;
}
LOG(WARNING) << message << ": " << error;
return false;
}

} // namespace ui

#pragma clang diagnostic pop
30 changes: 20 additions & 10 deletions ui/accessibility/platform/inspect/ax_optional.h
Expand Up @@ -18,7 +18,12 @@ template <typename ValueType>
class AX_EXPORT AXOptional final {
public:
static constexpr AXOptional Unsupported() { return AXOptional(kUnsupported); }
static constexpr AXOptional Error() { return AXOptional(kError); }
static constexpr AXOptional Error(const char* error_text = nullptr) {
return error_text ? AXOptional(kError, error_text) : AXOptional(kError);
}
static constexpr AXOptional Error(const std::string& error_text) {
return AXOptional(kError, error_text);
}
static constexpr AXOptional NotApplicable() {
return AXOptional(kNotApplicable);
}
Expand All @@ -31,11 +36,11 @@ class AX_EXPORT AXOptional final {
}

explicit constexpr AXOptional(ValueType value_)
: value_(value_), flag_(kValue) {}
: value_(value_), state_(kValue) {}

bool constexpr IsUnsupported() const { return flag_ == kUnsupported; }
bool constexpr IsNotApplicable() const { return flag_ == kNotApplicable; }
bool constexpr IsError() const { return flag_ == kError; }
bool constexpr IsUnsupported() const { return state_ == kUnsupported; }
bool constexpr IsNotApplicable() const { return state_ == kNotApplicable; }
bool constexpr IsError() const { return state_ == kError; }

template <typename T = ValueType>
bool constexpr IsNotNull(
Expand All @@ -49,9 +54,12 @@ class AX_EXPORT AXOptional final {
return true;
}

bool constexpr HasValue() { return flag_ == kValue; }
bool constexpr HasValue() { return state_ == kValue; }
constexpr const ValueType& operator*() const { return value_; }

bool HasStateText() const { return !state_text_.empty(); }
std::string StateText() const { return state_text_; }

std::string ToString() const {
if (IsNotNull())
return "<value>";
Expand Down Expand Up @@ -85,12 +93,14 @@ class AX_EXPORT AXOptional final {
kUnsupported,
};

explicit constexpr AXOptional(State flag_) : value_(nullptr), flag_(flag_) {}
explicit constexpr AXOptional(ValueType value_, State flag_)
: value_(value_), flag_(flag_) {}
explicit constexpr AXOptional(State state, const std::string& state_text = {})
: value_(nullptr), state_(state), state_text_(state_text) {}
explicit constexpr AXOptional(ValueType value, State state)
: value_(value), state_(state) {}

ValueType value_;
State flag_;
State state_;
std::string state_text_;
};

} // namespace ui
Expand Down

0 comments on commit 0feb1a2

Please sign in to comment.