0

Convert /ui/accessibility to use ARC

See https://chromium.googlesource.com/chromium/src/+/main/docs/mac/arc.md
for information about this conversion.

Bug: 1280317
Change-Id: Ib21037a39e5309515205171372ce17df831dd545
Include-Ci-Only-Tests: true
Cq-Include-Trybots: luci.chrome.try:mac-chrome
Validate-Test-Flakiness: skip
AX-Relnotes: n/a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4577142
Reviewed-by: Leonard Grey <lgrey@chromium.org>
Code-Coverage: Findit <findit-for-me@appspot.gserviceaccount.com>
Reviewed-by: David Tseng <dtseng@chromium.org>
Auto-Submit: Avi Drissman <avi@chromium.org>
Commit-Queue: Avi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1152257}
This commit is contained in:
Avi Drissman
2023-06-02 00:17:12 +00:00
committed by Chromium LUCI CQ
parent 4756d043c4
commit c41a68c1d0
28 changed files with 647 additions and 514 deletions

@@ -42,14 +42,6 @@ struct CONTENT_EXPORT AXTextEdit {
// Returns true if the given object is an NSRange instance. // Returns true if the given object is an NSRange instance.
bool IsNSRange(id value); bool IsNSRange(id value);
// Returns an AXTextMarker representing the given position in the tree.
id AXTextMarkerFrom(const BrowserAccessibilityCocoa* anchor,
int offset,
ax::mojom::TextAffinity affinity);
// Returns an AXTextMarkerRange that spans the given AXTextMarkers.
id AXTextMarkerRangeFrom(id anchor_text_marker, id focus_text_marker);
} // namespace content } // namespace content
// BrowserAccessibilityCocoa is a cocoa wrapper around the BrowserAccessibility // BrowserAccessibilityCocoa is a cocoa wrapper around the BrowserAccessibility

@@ -7,6 +7,7 @@
#import "content/browser/accessibility/browser_accessibility_mac.h" #import "content/browser/accessibility/browser_accessibility_mac.h"
#include "base/debug/stack_trace.h" #include "base/debug/stack_trace.h"
#include "base/memory/scoped_policy.h"
#import "base/task/single_thread_task_runner.h" #import "base/task/single_thread_task_runner.h"
#include "base/task/single_thread_task_runner.h" #include "base/task/single_thread_task_runner.h"
#include "base/time/time.h" #include "base/time/time.h"
@@ -69,7 +70,7 @@ void BrowserAccessibilityMac::ReplaceNativeObject() {
// because we need to retrieve some information from the old wrapper in order // because we need to retrieve some information from the old wrapper in order
// to add it to the new one, e.g. its list of children. // to add it to the new one, e.g. its list of children.
base::scoped_nsobject<AXPlatformNodeCocoa> old_native_obj( base::scoped_nsobject<AXPlatformNodeCocoa> old_native_obj(
platform_node_->ReleaseNativeWrapper()); platform_node_->ReleaseNativeWrapper(), base::scoped_policy::RETAIN);
// We should have never called this method if a native wrapper has not been // We should have never called this method if a native wrapper has not been
// created, but keep a null check just in case. // created, but keep a null check just in case.

@@ -611,6 +611,10 @@ source_set("browser_sources") {
sources += [ "context_factory.h" ] sources += [ "context_factory.h" ]
} }
if (is_apple) {
configs += [ "//build/config/compiler:enable_arc" ]
}
if (is_mac) { if (is_mac) {
sources += [ sources += [
"ax_inspect_factory_mac.mm", "ax_inspect_factory_mac.mm",

@@ -9,6 +9,10 @@
#include "ui/accessibility/platform/inspect/ax_event_recorder_mac.h" #include "ui/accessibility/platform/inspect/ax_event_recorder_mac.h"
#include "ui/accessibility/platform/inspect/ax_tree_formatter_mac.h" #include "ui/accessibility/platform/inspect/ax_tree_formatter_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace content { namespace content {
// static // static
@@ -22,8 +26,8 @@ std::unique_ptr<ui::AXEventRecorder> AXInspectFactory::CreatePlatformRecorder(
BrowserAccessibilityManager*, BrowserAccessibilityManager*,
base::ProcessId pid, base::ProcessId pid,
const ui::AXTreeSelector& selector) { const ui::AXTreeSelector& selector) {
return AXInspectFactory::CreateRecorder(ui::AXApiType::kMac, nullptr, pid, return AXInspectFactory::CreateRecorder(ui::AXApiType::kMac,
selector); /*manager=*/nullptr, pid, selector);
} }
// static // static

@@ -6,6 +6,10 @@
#include "base/observer_list.h" #include "base/observer_list.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace content { namespace content {
ScopedNotifyNativeEventProcessorObserver:: ScopedNotifyNativeEventProcessorObserver::
@@ -15,13 +19,13 @@ ScopedNotifyNativeEventProcessorObserver::
NSEvent* event) NSEvent* event)
: observer_list_(observer_list), event_(event) { : observer_list_(observer_list), event_(event) {
for (auto& observer : *observer_list_) for (auto& observer : *observer_list_)
observer.WillRunNativeEvent(event_); observer.WillRunNativeEvent((__bridge const void*)event_);
} }
ScopedNotifyNativeEventProcessorObserver:: ScopedNotifyNativeEventProcessorObserver::
~ScopedNotifyNativeEventProcessorObserver() { ~ScopedNotifyNativeEventProcessorObserver() {
for (auto& obs : *observer_list_) { for (auto& obs : *observer_list_) {
obs.DidRunNativeEvent(event_); obs.DidRunNativeEvent((__bridge const void*)event_);
} }
} }

@@ -243,6 +243,8 @@ component("platform") {
"inspect/ax_tree_indexer_mac.h", "inspect/ax_tree_indexer_mac.h",
] ]
configs += [ "//build/config/compiler:enable_arc" ]
frameworks = [ frameworks = [
"AppKit.framework", "AppKit.framework",
"Foundation.framework", "Foundation.framework",

@@ -135,7 +135,7 @@ struct COMPONENT_EXPORT(AX_PLATFORM) AXTextStateChangeIntent final {
AXTextSelection selection); AXTextSelection selection);
// Constructs an editing intent. // Constructs an editing intent.
AXTextStateChangeIntent(AXTextEditType edit); explicit AXTextStateChangeIntent(AXTextEditType edit);
AXTextStateChangeIntent(const AXTextStateChangeIntent& intent); AXTextStateChangeIntent(const AXTextStateChangeIntent& intent);

@@ -7,6 +7,10 @@
#include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_event_intent.h" #include "ui/accessibility/ax_event_intent.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace ui { namespace ui {
// static // static
@@ -107,7 +111,7 @@ AXTextSelection AXTextSelection::FromDirectionAndGranularity(
break; break;
} }
return AXTextSelection(direction, granularity, /* focus_change */ false); return AXTextSelection(direction, granularity, /*focus_change=*/false);
} }
AXTextSelection::AXTextSelection() = default; AXTextSelection::AXTextSelection() = default;
@@ -133,7 +137,7 @@ AXTextStateChangeIntent::DefaultFocusTextStateChangeIntent() {
AXTextStateChangeType::kSelectionMove, AXTextStateChangeType::kSelectionMove,
AXTextSelection(AXTextSelectionDirection::kDiscontiguous, AXTextSelection(AXTextSelectionDirection::kDiscontiguous,
AXTextSelectionGranularity::kUnknown, AXTextSelectionGranularity::kUnknown,
/* focus_change */ true)); /*focus_change=*/true));
} }
// static // static
@@ -143,7 +147,7 @@ AXTextStateChangeIntent::DefaultSelectionChangeIntent() {
AXTextStateChangeType::kSelectionMove, AXTextStateChangeType::kSelectionMove,
AXTextSelection(AXTextSelectionDirection::kDiscontiguous, AXTextSelection(AXTextSelectionDirection::kDiscontiguous,
AXTextSelectionGranularity::kUnknown, AXTextSelectionGranularity::kUnknown,
/* focus_change */ false)); /*focus_change=*/false));
} }
AXTextStateChangeIntent::AXTextStateChangeIntent() = default; AXTextStateChangeIntent::AXTextStateChangeIntent() = default;

@@ -5,31 +5,24 @@
#ifndef UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_COCOA_H_ #ifndef UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_COCOA_H_
#define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_COCOA_H_ #define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_COCOA_H_
#include <memory>
#import <Accessibility/Accessibility.h> #import <Accessibility/Accessibility.h>
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#include "base/component_export.h" #include "base/component_export.h"
#include "base/mac/scoped_nsobject.h"
#include "ui/accessibility/ax_enums.mojom-forward.h" #include "ui/accessibility/ax_enums.mojom-forward.h"
namespace ui { namespace ui {
class AXPlatformNodeBase; class AXPlatformNodeBase;
class AXPlatformNodeDelegate; class AXPlatformNodeDelegate;
struct AXAnnouncementSpec {
AXAnnouncementSpec();
~AXAnnouncementSpec();
base::scoped_nsobject<NSString> announcement;
base::scoped_nsobject<NSWindow> window;
bool is_polite;
};
} // namespace ui } // namespace ui
COMPONENT_EXPORT(AX_PLATFORM)
@interface AXAnnouncementSpec : NSObject
@property(nonatomic, readonly) NSString* announcement;
@property(nonatomic, readonly) NSWindow* window;
@property(nonatomic, readonly) BOOL polite;
@end
COMPONENT_EXPORT(AX_PLATFORM) COMPONENT_EXPORT(AX_PLATFORM)
@interface AXPlatformNodeCocoa @interface AXPlatformNodeCocoa
: NSAccessibilityElement <NSAccessibility, AXCustomContentProvider> : NSAccessibilityElement <NSAccessibility, AXCustomContentProvider>
@@ -75,17 +68,15 @@ COMPONENT_EXPORT(AX_PLATFORM)
@property(nonatomic, readonly) ui::AXPlatformNodeDelegate* nodeDelegate; @property(nonatomic, readonly) ui::AXPlatformNodeDelegate* nodeDelegate;
// Returns the data necessary to queue an NSAccessibility announcement if // Returns the data necessary to queue an NSAccessibility announcement if
// |eventType| should be announced, or nullptr otherwise. // |eventType| should be announced, or nil otherwise.
- (std::unique_ptr<ui::AXAnnouncementSpec>)announcementForEvent: - (AXAnnouncementSpec*)announcementForEvent:(ax::mojom::Event)eventType;
(ax::mojom::Event)eventType;
// Ask the system to announce |announcementText|. This is debounced to happen // Ask the system to announce |announcementText|. This is debounced to happen
// at most every |kLiveRegionDebounceMillis| per node, with only the most // at most every |kLiveRegionDebounceMillis| per node, with only the most
// recent announcement text read, to account for situations with multiple // recent announcement text read, to account for situations with multiple
// notifications happening one after another (for example, results for // notifications happening one after another (for example, results for
// find-in-page updating rapidly as they come in from subframes). // find-in-page updating rapidly as they come in from subframes).
- (void)scheduleLiveRegionAnnouncement: - (void)scheduleLiveRegionAnnouncement:(AXAnnouncementSpec*)announcement;
(std::unique_ptr<ui::AXAnnouncementSpec>)announcement;
- (id)AXWindow; - (id)AXWindow;

@@ -5,6 +5,7 @@
#import "ui/accessibility/platform/ax_platform_node_cocoa.h" #import "ui/accessibility/platform/ax_platform_node_cocoa.h"
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#include <Foundation/Foundation.h>
#include "base/logging.h" #include "base/logging.h"
#include "base/mac/foundation_util.h" #include "base/mac/foundation_util.h"
@@ -27,19 +28,32 @@
#import "ui/gfx/mac/coordinate_conversion.h" #import "ui/gfx/mac/coordinate_conversion.h"
#include "ui/strings/grit/ax_strings.h" #include "ui/strings/grit/ax_strings.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using AXRange = ui::AXPlatformNodeDelegate::AXRange; using AXRange = ui::AXPlatformNodeDelegate::AXRange;
namespace ui { @interface AXAnnouncementSpec ()
AXAnnouncementSpec::AXAnnouncementSpec() = default; @property(nonatomic, strong) NSString* announcement;
AXAnnouncementSpec::~AXAnnouncementSpec() = default; @property(nonatomic, strong) NSWindow* window;
@property(nonatomic, assign) BOOL polite;
} // namespace ui @end
@implementation AXAnnouncementSpec
@synthesize announcement = _announcement;
@synthesize window = _window;
@synthesize polite = _polite;
@end
namespace { namespace {
// Same length as web content/WebKit. // Same length as web content/WebKit.
static int kLiveRegionDebounceMillis = 20; int kLiveRegionDebounceMillis = 20;
using RoleMap = std::map<ax::mojom::Role, NSString*>; using RoleMap = std::map<ax::mojom::Role, NSString*>;
using EventMap = std::map<ax::mojom::Event, NSString*>; using EventMap = std::map<ax::mojom::Event, NSString*>;
@@ -236,7 +250,7 @@ void CollectAncestorRoles(
@implementation AXPlatformNodeCocoa { @implementation AXPlatformNodeCocoa {
// This field is not a raw_ptr<> because it requires @property rewrite. // This field is not a raw_ptr<> because it requires @property rewrite.
RAW_PTR_EXCLUSION ui::AXPlatformNodeBase* _node; // Weak. Retains us. RAW_PTR_EXCLUSION ui::AXPlatformNodeBase* _node; // Weak. Retains us.
std::unique_ptr<ui::AXAnnouncementSpec> _pendingAnnouncement; AXAnnouncementSpec* __strong _pendingAnnouncement;
} }
@synthesize node = _node; @synthesize node = _node;
@@ -329,9 +343,9 @@ void CollectAncestorRoles(
break; break;
} }
// On Mac OS X, the accessible name of an object is exposed as its // On macOS, the accessible name of an object is exposed as its title if it
// title if it comes from visible text, and as its description // comes from visible text, and as its description otherwise, but never both.
// otherwise, but never both. //
// Note: a placeholder is often visible text, but since it aids in data entry // Note: a placeholder is often visible text, but since it aids in data entry
// it is similar to accessibilityValue, and thus cannot be exposed either in // it is similar to accessibilityValue, and thus cannot be exposed either in
// accessibilityTitle or in accessibilityLabel. // accessibilityTitle or in accessibilityLabel.
@@ -785,7 +799,7 @@ void CollectAncestorRoles(
// Add annotation information // Add annotation information
int leafTextLength = leafTextRange.GetText().length(); int leafTextLength = leafTextRange.GetText().length();
DCHECK_LE(static_cast<unsigned long>(anchorStartOffset + leafTextLength), DCHECK_LE(static_cast<unsigned long>(anchorStartOffset + leafTextLength),
[attributedString length]); attributedString.length);
NSRange leafRange = NSMakeRange(anchorStartOffset, leafTextLength); NSRange leafRange = NSMakeRange(anchorStartOffset, leafTextLength);
CollectAncestorRoles(*anchor, ancestor_roles); CollectAncestorRoles(*anchor, ancestor_roles);
@@ -821,16 +835,15 @@ void CollectAncestorRoles(
// TODO(crbug.com/958811): Implement NSAccessibilityVisibleNameKey. // TODO(crbug.com/958811): Implement NSAccessibilityVisibleNameKey.
if (text_attrs.font_size != ui::AXTextAttributes::kUnsetValue) { if (text_attrs.font_size != ui::AXTextAttributes::kUnsetValue) {
[fontAttributes setValue:@(text_attrs.font_size) fontAttributes[NSAccessibilityFontSizeKey] = @(text_attrs.font_size);
forKey:NSAccessibilityFontSizeKey];
} }
if (text_attrs.HasTextStyle(ax::mojom::TextStyle::kBold)) { if (text_attrs.HasTextStyle(ax::mojom::TextStyle::kBold)) {
[fontAttributes setValue:@YES forKey:@"AXFontBold"]; fontAttributes[@"AXFontBold"] = @YES;
} }
if (text_attrs.HasTextStyle(ax::mojom::TextStyle::kItalic)) { if (text_attrs.HasTextStyle(ax::mojom::TextStyle::kItalic)) {
[fontAttributes setValue:@YES forKey:@"AXFontItalic"]; fontAttributes[@"AXFontItalic"] = @YES;
} }
[attributedString addAttribute:NSAccessibilityFontTextAttribute [attributedString addAttribute:NSAccessibilityFontTextAttribute
@@ -913,24 +926,24 @@ void CollectAncestorRoles(
return base::SysUTF8ToNSString(_node->GetName()); return base::SysUTF8ToNSString(_node->GetName());
} }
- (std::unique_ptr<ui::AXAnnouncementSpec>)announcementForEvent: - (AXAnnouncementSpec*)announcementForEvent:(ax::mojom::Event)eventType {
(ax::mojom::Event)eventType {
// Only alerts and live region changes should be announced. // Only alerts and live region changes should be announced.
DCHECK(eventType == ax::mojom::Event::kAlert || DCHECK(eventType == ax::mojom::Event::kAlert ||
eventType == ax::mojom::Event::kLiveRegionChanged); eventType == ax::mojom::Event::kLiveRegionChanged);
std::string liveStatus = std::string liveStatus =
_node->GetStringAttribute(ax::mojom::StringAttribute::kLiveStatus); _node->GetStringAttribute(ax::mojom::StringAttribute::kLiveStatus);
// If live status is explicitly set to off, don't announce. // If live status is explicitly set to off, don't announce.
if (liveStatus == "off") if (liveStatus == "off") {
return nullptr; return nil;
}
NSString* name = [self getName]; NSString* name = [self getName];
NSString* announcementText = NSString* announcementText =
[name length] > 0 name.length > 0 ? name
? name : base::SysUTF16ToNSString(_node->GetTextContentUTF16());
: base::SysUTF16ToNSString(_node->GetTextContentUTF16()); if (announcementText.length == 0) {
if ([announcementText length] == 0) return nil;
return nullptr; }
const std::string& description = const std::string& description =
_node->GetStringAttribute(ax::mojom::StringAttribute::kDescription); _node->GetStringAttribute(ax::mojom::StringAttribute::kDescription);
@@ -942,34 +955,31 @@ void CollectAncestorRoles(
base::SysUTF8ToNSString(description)]; base::SysUTF8ToNSString(description)];
} }
auto announcement = std::make_unique<ui::AXAnnouncementSpec>(); AXAnnouncementSpec* spec = [[AXAnnouncementSpec alloc] init];
announcement->announcement = spec.announcement = announcementText;
base::scoped_nsobject<NSString>([announcementText retain]); spec.window = [self AXWindow];
announcement->window = spec.polite = liveStatus != "assertive";
base::scoped_nsobject<NSWindow>([[self AXWindow] retain]); return spec;
announcement->is_polite = liveStatus != "assertive";
return announcement;
} }
- (void)scheduleLiveRegionAnnouncement: - (void)scheduleLiveRegionAnnouncement:(AXAnnouncementSpec*)announcement {
(std::unique_ptr<ui::AXAnnouncementSpec>)announcement {
if (_pendingAnnouncement) { if (_pendingAnnouncement) {
// An announcement is already in flight, so just reset the contents. This is // An announcement is already in flight, so just reset the contents. This is
// threadsafe because the dispatch is on the main queue. // threadsafe because the dispatch is on the main queue.
_pendingAnnouncement = std::move(announcement); _pendingAnnouncement = announcement;
return; return;
} }
_pendingAnnouncement = std::move(announcement); _pendingAnnouncement = announcement;
dispatch_after( dispatch_after(
kLiveRegionDebounceMillis * NSEC_PER_MSEC, dispatch_get_main_queue(), ^{ kLiveRegionDebounceMillis * NSEC_PER_MSEC, dispatch_get_main_queue(), ^{
if (!_pendingAnnouncement) { if (!self->_pendingAnnouncement) {
return; return;
} }
PostAnnouncementNotification(_pendingAnnouncement->announcement, PostAnnouncementNotification(self->_pendingAnnouncement.announcement,
_pendingAnnouncement->window, self->_pendingAnnouncement.window,
_pendingAnnouncement->is_polite); self->_pendingAnnouncement.polite);
_pendingAnnouncement.reset(); self->_pendingAnnouncement = nil;
}); });
} }
@@ -983,8 +993,9 @@ void CollectAncestorRoles(
} }
- (id)accessibilityHitTest:(NSPoint)point { - (id)accessibilityHitTest:(NSPoint)point {
if (!NSPointInRect(point, [self boundsInScreen])) if (!NSPointInRect(point, self.boundsInScreen)) {
return nil; return nil;
}
for (id child in [[self AXChildren] reverseObjectEnumerator]) { for (id child in [[self AXChildren] reverseObjectEnumerator]) {
if (!NSPointInRect(point, [child accessibilityFrame])) if (!NSPointInRect(point, [child accessibilityFrame]))
@@ -1012,8 +1023,7 @@ void CollectAncestorRoles(
if (!_node) if (!_node)
return @[]; return @[];
base::scoped_nsobject<NSMutableArray> axActions( NSMutableArray* axActions = [NSMutableArray array];
[[NSMutableArray alloc] init]);
const ActionList& action_list = GetActionList(); const ActionList& action_list = GetActionList();
// VoiceOver expects the "press" action to be first. Note that some roles // VoiceOver expects the "press" action to be first. Note that some roles
@@ -1027,7 +1037,7 @@ void CollectAncestorRoles(
if (AlsoUseShowMenuActionForDefaultAction(*_node)) if (AlsoUseShowMenuActionForDefaultAction(*_node))
[axActions addObject:NSAccessibilityShowMenuAction]; [axActions addObject:NSAccessibilityShowMenuAction];
return axActions.autorelease(); return axActions;
} }
- (void)accessibilityPerformAction:(NSString*)action { - (void)accessibilityPerformAction:(NSString*)action {
@@ -1092,10 +1102,9 @@ void CollectAncestorRoles(
]; ];
// Required for all text, including protected textfields. // Required for all text, including protected textfields.
NSString* const kTextAttributes = NSAccessibilityPlaceholderValueAttribute; NSString* const kTextAttributes = NSAccessibilityPlaceholderValueAttribute;
base::scoped_nsobject<NSMutableArray> axAttributes(
[[NSMutableArray alloc] init]);
[axAttributes addObjectsFromArray:kAllRoleAttributes];
NSMutableArray* axAttributes =
[NSMutableArray arrayWithArray:kAllRoleAttributes];
ax::mojom::Role role = _node->GetRole(); ax::mojom::Role role = _node->GetRole();
switch (role) { switch (role) {
case ax::mojom::Role::kTextField: case ax::mojom::Role::kTextField:
@@ -1292,7 +1301,7 @@ void CollectAncestorRoles(
if ([self titleUIElement]) if ([self titleUIElement])
[axAttributes addObject:NSAccessibilityTitleUIElementAttribute]; [axAttributes addObject:NSAccessibilityTitleUIElementAttribute];
return axAttributes.autorelease(); return axAttributes;
} }
- (NSArray*)accessibilityParameterizedAttributeNames { - (NSArray*)accessibilityParameterizedAttributeNames {
@@ -1342,8 +1351,11 @@ void CollectAncestorRoles(
return nil; // Return nil when detached. Even for ax::mojom::Role. return nil; // Return nil when detached. Even for ax::mojom::Role.
SEL selector = NSSelectorFromString(attribute); SEL selector = NSSelectorFromString(attribute);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
if ([self respondsToSelector:selector]) if ([self respondsToSelector:selector])
return [self performSelector:selector]; return [self performSelector:selector];
#pragma clang diagnostic pop
return nil; return nil;
} }
@@ -1353,8 +1365,11 @@ void CollectAncestorRoles(
return nil; return nil;
SEL selector = NSSelectorFromString([attribute stringByAppendingString:@":"]); SEL selector = NSSelectorFromString([attribute stringByAppendingString:@":"]);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
if ([self respondsToSelector:selector]) if ([self respondsToSelector:selector])
return [self performSelector:selector withObject:parameter]; return [self performSelector:selector withObject:parameter];
#pragma clang diagnostic pop
return nil; return nil;
} }
@@ -1524,7 +1539,7 @@ void CollectAncestorRoles(
if (![self instanceActive]) if (![self instanceActive])
return nil; return nil;
NSMutableArray* elements = [[[NSMutableArray alloc] init] autorelease]; NSMutableArray* elements = [NSMutableArray array];
for (ui::AXNodeID id : for (ui::AXNodeID id :
_node->GetIntListAttribute(ax::mojom::IntListAttribute::kDetailsIds)) { _node->GetIntListAttribute(ax::mojom::IntListAttribute::kDetailsIds)) {
AXPlatformNodeCocoa* node = [self fromNodeID:id]; AXPlatformNodeCocoa* node = [self fromNodeID:id];
@@ -1532,14 +1547,14 @@ void CollectAncestorRoles(
[elements addObject:node]; [elements addObject:node];
} }
return [elements count] ? elements : nil; return elements.count ? elements : nil;
} }
- (NSArray*)AXDOMClassList { - (NSArray*)AXDOMClassList {
if (![self instanceActive]) if (![self instanceActive])
return nil; return nil;
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; NSMutableArray* ret = [NSMutableArray array];
std::string classes; std::string classes;
if (_node->GetStringAttribute(ax::mojom::StringAttribute::kClassName, if (_node->GetStringAttribute(ax::mojom::StringAttribute::kClassName,
@@ -1673,9 +1688,7 @@ void CollectAncestorRoles(
if (!container) if (!container)
return nil; return nil;
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; return @[ container->GetNativeViewAccessible() ];
[ret addObject:container->GetNativeViewAccessible()];
return ret;
} }
- (NSString*)AXPopupValue { - (NSString*)AXPopupValue {
@@ -1877,7 +1890,7 @@ void CollectAncestorRoles(
const auto checkedState = static_cast<ax::mojom::CheckedState>( const auto checkedState = static_cast<ax::mojom::CheckedState>(
_node->GetIntAttribute(ax::mojom::IntAttribute::kCheckedState)); _node->GetIntAttribute(ax::mojom::IntAttribute::kCheckedState));
if (checkedState == ax::mojom::CheckedState::kTrue) { if (checkedState == ax::mojom::CheckedState::kTrue) {
return @"\xE2\x9C\x93"; // UTF-8 for unicode 0x2713, "check mark" return @"\u2713"; // "check mark"
} }
return @""; return @"";
@@ -2009,8 +2022,8 @@ void CollectAncestorRoles(
// We potentially need to add text attributes to the whole text content // We potentially need to add text attributes to the whole text content
// because a spelling mistake might start or end outside the given range. // because a spelling mistake might start or end outside the given range.
NSMutableAttributedString* attributedTextContent = NSMutableAttributedString* attributedTextContent =
[[[NSMutableAttributedString alloc] [[NSMutableAttributedString alloc]
initWithString:base::SysUTF16ToNSString(textContent)] autorelease]; initWithString:base::SysUTF16ToNSString(textContent)];
if (!_node->IsText()) { if (!_node->IsText()) {
AXRange axRange(_node->GetDelegate()->CreateTextPositionAt(0), AXRange axRange(_node->GetDelegate()->CreateTextPositionAt(0),
_node->GetDelegate()->CreateTextPositionAt( _node->GetDelegate()->CreateTextPositionAt(
@@ -2027,11 +2040,12 @@ void CollectAncestorRoles(
return nil; return nil;
NSString* text = base::SysUTF16ToNSString(axRange.GetText()); NSString* text = base::SysUTF16ToNSString(axRange.GetText());
if ([text length] == 0) if (text.length == 0) {
return nil; return nil;
}
NSMutableAttributedString* attributedText = NSMutableAttributedString* attributedText =
[[[NSMutableAttributedString alloc] initWithString:text] autorelease]; [[NSMutableAttributedString alloc] initWithString:text];
// Currently, we only decorate the attributed string with misspelling // Currently, we only decorate the attributed string with misspelling
// and annotation information. // and annotation information.
[self addTextAnnotationsIn:&axRange to:attributedText]; [self addTextAnnotationsIn:&axRange to:attributedText];
@@ -2094,7 +2108,7 @@ void CollectAncestorRoles(
if (![self instanceActive]) if (![self instanceActive])
return nil; return nil;
// Mac OS X wants static text exposed in AXValue. // macOS wants static text exposed in AXValue.
if (ui::IsNameExposedInAXValueForRole([self internalRole])) if (ui::IsNameExposedInAXValueForRole([self internalRole]))
return @""; return @"";
@@ -2345,7 +2359,7 @@ void CollectAncestorRoles(
NSArray* rows = [self accessibilityRows]; NSArray* rows = [self accessibilityRows];
// accessibilityRows returns an empty array unless instanceActive does, // accessibilityRows returns an empty array unless instanceActive does,
// not exist, so we do not need to check if rows is nil at this time. // not exist, so we do not need to check if rows is nil at this time.
NSMutableArray* selectedRows = [[[NSMutableArray alloc] init] autorelease]; NSMutableArray* selectedRows = [NSMutableArray array];
for (id row in rows) { for (id row in rows) {
if ([[row accessibilitySelected] boolValue]) { if ([[row accessibilitySelected] boolValue]) {
[selectedRows addObject:row]; [selectedRows addObject:row];
@@ -2362,7 +2376,7 @@ void CollectAncestorRoles(
ui::AXPlatformNodeDelegate* delegate = _node->GetDelegate(); ui::AXPlatformNodeDelegate* delegate = _node->GetDelegate();
DCHECK(delegate); DCHECK(delegate);
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; NSMutableArray* ret = [NSMutableArray array];
// If this is a table, return all column headers. // If this is a table, return all column headers.
ax::mojom::Role role = _node->GetRole(); ax::mojom::Role role = _node->GetRole();
@@ -2522,30 +2536,28 @@ void CollectAncestorRoles(
if (!_node) if (!_node)
return 0; return 0;
return return [[self AXLineForIndex:@(index)] integerValue];
[[self AXLineForIndex:[NSNumber numberWithInteger:index]] integerValue];
} }
- (NSRange)accessibilityRangeForIndex:(NSInteger)index { - (NSRange)accessibilityRangeForIndex:(NSInteger)index {
if (!_node) if (!_node)
return NSMakeRange(0, 0); return NSMakeRange(0, 0);
return [[self AXRangeForIndex:[NSNumber numberWithInteger:index]] rangeValue]; return [[self AXRangeForIndex:@(index)] rangeValue];
} }
- (NSRange)accessibilityStyleRangeForIndex:(NSInteger)index { - (NSRange)accessibilityStyleRangeForIndex:(NSInteger)index {
if (!_node) if (!_node)
return NSMakeRange(0, 0); return NSMakeRange(0, 0);
return [[self AXStyleRangeForIndex:[NSNumber numberWithInteger:index]] return [[self AXStyleRangeForIndex:@(index)] rangeValue];
rangeValue];
} }
- (NSRange)accessibilityRangeForLine:(NSInteger)line { - (NSRange)accessibilityRangeForLine:(NSInteger)line {
if (!_node) if (!_node)
return NSMakeRange(0, 0); return NSMakeRange(0, 0);
return [[self AXRangeForLine:[NSNumber numberWithInteger:line]] rangeValue]; return [[self AXRangeForLine:@(line)] rangeValue];
} }
- (NSRange)accessibilityRangeForPosition:(NSPoint)point { - (NSRange)accessibilityRangeForPosition:(NSPoint)point {
@@ -2746,30 +2758,27 @@ void CollectAncestorRoles(
return nil; return nil;
} }
static NSDictionary* createMathSubSupScriptsPair( namespace {
AXPlatformNodeCocoa* subscript,
AXPlatformNodeCocoa* superscript) { NSDictionary* CreateMathSubSupScriptsPair(AXPlatformNodeCocoa* subscript,
AXPlatformNodeCocoa* nodes[2]; AXPlatformNodeCocoa* superscript) {
NSString* keys[2]; NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
NSUInteger count = 0;
if (subscript) { if (subscript) {
nodes[count] = subscript; dictionary[NSAccessibilityMathSubscriptAttribute] = subscript;
keys[count] = NSAccessibilityMathSubscriptAttribute;
count++;
} }
if (superscript) { if (superscript) {
nodes[count] = superscript; dictionary[NSAccessibilityMathSuperscriptAttribute] = superscript;
keys[count] = NSAccessibilityMathSuperscriptAttribute;
count++;
} }
return [[NSDictionary alloc] initWithObjects:nodes forKeys:keys count:count]; return dictionary;
} }
} // namespace
- (NSArray*)AXMathPostscripts { - (NSArray*)AXMathPostscripts {
if (![self instanceActive] || if (![self instanceActive] ||
_node->GetRole() != ax::mojom::Role::kMathMLMultiscripts) _node->GetRole() != ax::mojom::Role::kMathMLMultiscripts)
return nil; return nil;
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; NSMutableArray* ret = [NSMutableArray array];
bool foundBaseElement = false; bool foundBaseElement = false;
AXPlatformNodeCocoa* subscript = nullptr; AXPlatformNodeCocoa* subscript = nullptr;
for (AXPlatformNodeCocoa* child in [self AXChildren]) { for (AXPlatformNodeCocoa* child in [self AXChildren]) {
@@ -2784,7 +2793,7 @@ static NSDictionary* createMathSubSupScriptsPair(
continue; continue;
} }
AXPlatformNodeCocoa* superscript = child; AXPlatformNodeCocoa* superscript = child;
[ret addObject:createMathSubSupScriptsPair(subscript, superscript)]; [ret addObject:CreateMathSubSupScriptsPair(subscript, superscript)];
subscript = nullptr; subscript = nullptr;
} }
return [ret count] ? ret : nil; return [ret count] ? ret : nil;
@@ -2794,7 +2803,7 @@ static NSDictionary* createMathSubSupScriptsPair(
if (![self instanceActive] || if (![self instanceActive] ||
_node->GetRole() != ax::mojom::Role::kMathMLMultiscripts) _node->GetRole() != ax::mojom::Role::kMathMLMultiscripts)
return nil; return nil;
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; NSMutableArray* ret = [NSMutableArray array];
bool foundPrescriptDelimiter = false; bool foundPrescriptDelimiter = false;
AXPlatformNodeCocoa* subscript = nullptr; AXPlatformNodeCocoa* subscript = nullptr;
for (AXPlatformNodeCocoa* child in [self AXChildren]) { for (AXPlatformNodeCocoa* child in [self AXChildren]) {
@@ -2808,7 +2817,7 @@ static NSDictionary* createMathSubSupScriptsPair(
continue; continue;
} }
AXPlatformNodeCocoa* superscript = child; AXPlatformNodeCocoa* superscript = child;
[ret addObject:createMathSubSupScriptsPair(subscript, superscript)]; [ret addObject:CreateMathSubSupScriptsPair(subscript, superscript)];
subscript = nullptr; subscript = nullptr;
} }
return [ret count] ? ret : nil; return [ret count] ? ret : nil;

@@ -7,15 +7,17 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#include <memory>
#include "base/component_export.h" #include "base/component_export.h"
#include "base/mac/scoped_nsobject.h"
#include "ui/accessibility/platform/ax_platform_node_base.h" #include "ui/accessibility/platform/ax_platform_node_base.h"
@class AXPlatformNodeCocoa; @class AXPlatformNodeCocoa;
namespace ui { namespace ui {
class AXPlatformNodeMac : public AXPlatformNodeBase { class COMPONENT_EXPORT(AX_PLATFORM) AXPlatformNodeMac
: public AXPlatformNodeBase {
public: public:
~AXPlatformNodeMac() override; ~AXPlatformNodeMac() override;
AXPlatformNodeMac(const AXPlatformNodeMac&) = delete; AXPlatformNodeMac(const AXPlatformNodeMac&) = delete;
@@ -30,15 +32,9 @@ class AXPlatformNodeMac : public AXPlatformNodeBase {
void Destroy() override; void Destroy() override;
bool IsPlatformCheckable() const override; bool IsPlatformCheckable() const override;
AXPlatformNodeCocoa* GetNativeWrapper() const { return native_node_.get(); } AXPlatformNodeCocoa* GetNativeWrapper() const;
AXPlatformNodeCocoa* ReleaseNativeWrapper();
base::scoped_nsobject<AXPlatformNodeCocoa> ReleaseNativeWrapper() { void SetNativeWrapper(AXPlatformNodeCocoa* native_node);
return std::move(native_node_);
}
void SetNativeWrapper(AXPlatformNodeCocoa* native_node) {
return native_node_.reset(native_node);
}
protected: protected:
AXPlatformNodeMac(); AXPlatformNodeMac();
@@ -48,10 +44,11 @@ class AXPlatformNodeMac : public AXPlatformNodeBase {
PlatformAttributeList* attributes) override; PlatformAttributeList* attributes) override;
private: private:
base::scoped_nsobject<AXPlatformNodeCocoa> native_node_;
friend AXPlatformNode* AXPlatformNode::Create( friend AXPlatformNode* AXPlatformNode::Create(
AXPlatformNodeDelegate* delegate); AXPlatformNodeDelegate* delegate);
struct ObjCStorage;
std::unique_ptr<ObjCStorage> objc_storage_;
}; };
// Convenience function to determine whether an internal object role should // Convenience function to determine whether an internal object role should

@@ -7,6 +7,10 @@
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "ui/accessibility/platform/ax_platform_node_cocoa.h" #include "ui/accessibility/platform/ax_platform_node_cocoa.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace { namespace {
using RoleMap = std::map<ax::mojom::Role, NSString*>; using RoleMap = std::map<ax::mojom::Role, NSString*>;
@@ -59,15 +63,19 @@ AXPlatformNode* AXPlatformNode::FromNativeViewAccessible(
return nullptr; return nullptr;
} }
AXPlatformNodeMac::AXPlatformNodeMac() = default; struct AXPlatformNodeMac::ObjCStorage {
AXPlatformNodeCocoa* __strong native_node;
};
AXPlatformNodeMac::AXPlatformNodeMac()
: objc_storage_(std::make_unique<ObjCStorage>()) {}
AXPlatformNodeMac::~AXPlatformNodeMac() = default; AXPlatformNodeMac::~AXPlatformNodeMac() = default;
void AXPlatformNodeMac::Destroy() { void AXPlatformNodeMac::Destroy() {
if (native_node_) { if (objc_storage_->native_node) {
[native_node_ detach]; [objc_storage_->native_node detach];
// Also, nullify smart pointer to make accidental use-after-free impossible. // Also, clear the pointer to make accidental use-after-free impossible.
native_node_.reset(); objc_storage_->native_node = nil;
} }
AXPlatformNodeBase::Destroy(); AXPlatformNodeBase::Destroy();
} }
@@ -83,10 +91,26 @@ bool AXPlatformNodeMac::IsPlatformCheckable() const {
return AXPlatformNodeBase::IsPlatformCheckable(); return AXPlatformNodeBase::IsPlatformCheckable();
} }
AXPlatformNodeCocoa* AXPlatformNodeMac::GetNativeWrapper() const {
return objc_storage_->native_node;
}
AXPlatformNodeCocoa* AXPlatformNodeMac::ReleaseNativeWrapper() {
AXPlatformNodeCocoa* native_node = objc_storage_->native_node;
objc_storage_->native_node = nil;
return native_node;
}
void AXPlatformNodeMac::SetNativeWrapper(AXPlatformNodeCocoa* native_node) {
objc_storage_->native_node = native_node;
}
gfx::NativeViewAccessible AXPlatformNodeMac::GetNativeViewAccessible() { gfx::NativeViewAccessible AXPlatformNodeMac::GetNativeViewAccessible() {
if (!native_node_) if (!objc_storage_->native_node) {
native_node_.reset([[AXPlatformNodeCocoa alloc] initWithNode:this]); objc_storage_->native_node =
return native_node_.get(); [[AXPlatformNodeCocoa alloc] initWithNode:this];
}
return objc_storage_->native_node;
} }
void AXPlatformNodeMac::NotifyAccessibilityEvent(ax::mojom::Event event_type) { void AXPlatformNodeMac::NotifyAccessibilityEvent(ax::mojom::Event event_type) {
@@ -98,8 +122,9 @@ void AXPlatformNodeMac::NotifyAccessibilityEvent(ax::mojom::Event event_type) {
// regular NSAccessibility notification system. // regular NSAccessibility notification system.
if (event_type == ax::mojom::Event::kAlert || if (event_type == ax::mojom::Event::kAlert ||
event_type == ax::mojom::Event::kLiveRegionChanged) { event_type == ax::mojom::Event::kLiveRegionChanged) {
if (auto announcement = [native_node_ announcementForEvent:event_type]) { if (AXAnnouncementSpec* announcement =
[native_node_ scheduleLiveRegionAnnouncement:std::move(announcement)]; [objc_storage_->native_node announcementForEvent:event_type]) {
[objc_storage_->native_node scheduleLiveRegionAnnouncement:announcement];
} }
return; return;
} }
@@ -107,14 +132,14 @@ void AXPlatformNodeMac::NotifyAccessibilityEvent(ax::mojom::Event event_type) {
ax::mojom::Role role = GetRole(); ax::mojom::Role role = GetRole();
if (ui::IsMenuItem(role)) { if (ui::IsMenuItem(role)) {
// On Mac, map menu item selection to a focus event. // On Mac, map menu item selection to a focus event.
NotifyMacEvent(native_node_, ax::mojom::Event::kFocus); NotifyMacEvent(objc_storage_->native_node, ax::mojom::Event::kFocus);
return; return;
} else if (ui::IsListItem(role)) { } else if (ui::IsListItem(role)) {
if (const AXPlatformNodeBase* container = GetSelectionContainer()) { if (const AXPlatformNodeBase* container = GetSelectionContainer()) {
if (container->GetRole() == ax::mojom::Role::kListBox && if (container->GetRole() == ax::mojom::Role::kListBox &&
!container->HasState(ax::mojom::State::kMultiselectable) && !container->HasState(ax::mojom::State::kMultiselectable) &&
GetDelegate()->GetFocus() == GetNativeViewAccessible()) { GetDelegate()->GetFocus() == GetNativeViewAccessible()) {
NotifyMacEvent(native_node_, ax::mojom::Event::kFocus); NotifyMacEvent(objc_storage_->native_node, ax::mojom::Event::kFocus);
return; return;
} }
} }
@@ -123,12 +148,12 @@ void AXPlatformNodeMac::NotifyAccessibilityEvent(ax::mojom::Event event_type) {
// Otherwise, use mappings between ax::mojom::Event and NSAccessibility // Otherwise, use mappings between ax::mojom::Event and NSAccessibility
// notifications from the EventMap above. // notifications from the EventMap above.
NotifyMacEvent(native_node_, event_type); NotifyMacEvent(objc_storage_->native_node, event_type);
} }
void AXPlatformNodeMac::AnnounceText(const std::u16string& text) { void AXPlatformNodeMac::AnnounceText(const std::u16string& text) {
PostAnnouncementNotification(base::SysUTF16ToNSString(text), PostAnnouncementNotification(base::SysUTF16ToNSString(text),
[native_node_ AXWindow], false); [objc_storage_->native_node AXWindow], false);
} }
bool IsNameExposedInAXValueForRole(ax::mojom::Role role) { bool IsNameExposedInAXValueForRole(ax::mojom::Role role) {

@@ -6,6 +6,7 @@
#define UI_ACCESSIBILITY_PLATFORM_AX_PRIVATE_WEBKIT_CONSTANTS_MAC_H_ #define UI_ACCESSIBILITY_PLATFORM_AX_PRIVATE_WEBKIT_CONSTANTS_MAC_H_
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#include "base/component_export.h" #include "base/component_export.h"
namespace ui { namespace ui {

@@ -4,6 +4,10 @@
#include "ui/accessibility/platform/ax_private_webkit_constants_mac.h" #include "ui/accessibility/platform/ax_private_webkit_constants_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace ui { namespace ui {
const char* ToString(AXTextStateChangeType type) { const char* ToString(AXTextStateChangeType type) {

@@ -10,9 +10,9 @@
#include "base/component_export.h" #include "base/component_export.h"
#include "ui/accessibility/platform/ax_platform_node_delegate.h" #include "ui/accessibility/platform/ax_platform_node_delegate.h"
namespace ui { @class AXPlatformNodeCocoa;
class AXPlatformNodeCocoa; namespace ui {
// An AXTextMarker is used by applications like Chrome to store a position in // An AXTextMarker is used by applications like Chrome to store a position in
// the accessibility tree's text representation. It is a data structure whose // the accessibility tree's text representation. It is a data structure whose
@@ -48,7 +48,7 @@ id AXRangeToAXTextMarkerRange(AXPlatformNodeDelegate::AXRange);
// Returns the AXTextMarker representing the position within the given node. // Returns the AXTextMarker representing the position within the given node.
COMPONENT_EXPORT(AX_PLATFORM) COMPONENT_EXPORT(AX_PLATFORM)
id AXTextMarkerFrom(const AXPlatformNodeCocoa* anchor, id AXTextMarkerFrom(AXPlatformNodeCocoa* anchor,
int offset, int offset,
ax::mojom::TextAffinity affinity); ax::mojom::TextAffinity affinity);

@@ -4,45 +4,51 @@
#include "ui/accessibility/platform/ax_utils_mac.h" #include "ui/accessibility/platform/ax_utils_mac.h"
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
#include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_cftyperef.h"
#include "ui/accessibility/ax_range.h" #include "ui/accessibility/ax_range.h"
#include "ui/accessibility/platform/ax_platform_node_base.h" #include "ui/accessibility/platform/ax_platform_node_base.h"
#include "ui/accessibility/platform/ax_platform_node_cocoa.h" #include "ui/accessibility/platform/ax_platform_node_cocoa.h"
#include "ui/accessibility/platform/ax_platform_node_delegate.h" #include "ui/accessibility/platform/ax_platform_node_delegate.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace ui { namespace ui {
bool IsAXTextMarker(id object) { bool IsAXTextMarker(id object) {
if (object == nil) if (object == nil) {
return false; return false;
}
AXTextMarkerRef cf_text_marker = static_cast<AXTextMarkerRef>(object); return CFGetTypeID((__bridge CFTypeRef)object) == AXTextMarkerGetTypeID();
DCHECK(cf_text_marker);
return CFGetTypeID(cf_text_marker) == AXTextMarkerGetTypeID();
} }
bool IsAXTextMarkerRange(id object) { bool IsAXTextMarkerRange(id object) {
if (object == nil) if (object == nil) {
return false; return false;
}
AXTextMarkerRangeRef cf_marker_range = return CFGetTypeID((__bridge CFTypeRef)object) ==
static_cast<AXTextMarkerRangeRef>(object); AXTextMarkerRangeGetTypeID();
DCHECK(cf_marker_range);
return CFGetTypeID(cf_marker_range) == AXTextMarkerRangeGetTypeID();
} }
AXPlatformNodeDelegate::AXPosition AXTextMarkerToAXPosition(id text_marker) { AXPlatformNodeDelegate::AXPosition AXTextMarkerToAXPosition(id text_marker) {
if (!IsAXTextMarker(text_marker)) if (!IsAXTextMarker(text_marker)) {
return AXNodePosition::CreateNullPosition(); return AXNodePosition::CreateNullPosition();
}
AXTextMarkerRef cf_text_marker = static_cast<AXTextMarkerRef>(text_marker); AXTextMarkerRef cf_text_marker = (__bridge AXTextMarkerRef)text_marker;
if (AXTextMarkerGetLength(cf_text_marker) != if (AXTextMarkerGetLength(cf_text_marker) !=
sizeof(AXPlatformNodeDelegate::SerializedPosition)) sizeof(AXPlatformNodeDelegate::SerializedPosition)) {
return AXNodePosition::CreateNullPosition(); return AXNodePosition::CreateNullPosition();
}
const UInt8* source_buffer = AXTextMarkerGetBytePtr(cf_text_marker); const UInt8* source_buffer = AXTextMarkerGetBytePtr(cf_text_marker);
if (!source_buffer) if (!source_buffer) {
return AXNodePosition::CreateNullPosition(); return AXNodePosition::CreateNullPosition();
}
return AXNodePosition::Unserialize( return AXNodePosition::Unserialize(
*reinterpret_cast<const AXPlatformNodeDelegate::SerializedPosition*>( *reinterpret_cast<const AXPlatformNodeDelegate::SerializedPosition*>(
@@ -56,20 +62,21 @@ AXPlatformNodeDelegate::AXRange AXTextMarkerRangeToAXRange(
} }
AXTextMarkerRangeRef cf_marker_range = AXTextMarkerRangeRef cf_marker_range =
static_cast<AXTextMarkerRangeRef>(text_marker_range); (__bridge AXTextMarkerRangeRef)text_marker_range;
base::ScopedCFTypeRef<AXTextMarkerRef> start_marker( id start_marker =
AXTextMarkerRangeCopyStartMarker(cf_marker_range)); CFBridgingRelease(AXTextMarkerRangeCopyStartMarker(cf_marker_range));
base::ScopedCFTypeRef<AXTextMarkerRef> end_marker( id end_marker =
AXTextMarkerRangeCopyEndMarker(cf_marker_range)); CFBridgingRelease(AXTextMarkerRangeCopyEndMarker(cf_marker_range));
if (!start_marker.get() || !end_marker.get()) if (!start_marker || !end_marker) {
return AXPlatformNodeDelegate::AXRange(); return AXPlatformNodeDelegate::AXRange();
}
// |AXPlatformNodeDelegate::AXRange| takes ownership of its anchor and focus. // |AXPlatformNodeDelegate::AXRange| takes ownership of its anchor and focus.
AXPlatformNodeDelegate::AXPosition anchor = AXPlatformNodeDelegate::AXPosition anchor =
AXTextMarkerToAXPosition(static_cast<id>(start_marker.get())); AXTextMarkerToAXPosition(start_marker);
AXPlatformNodeDelegate::AXPosition focus = AXPlatformNodeDelegate::AXPosition focus =
AXTextMarkerToAXPosition(static_cast<id>(end_marker.get())); AXTextMarkerToAXPosition(end_marker);
return AXPlatformNodeDelegate::AXRange(std::move(anchor), std::move(focus)); return AXPlatformNodeDelegate::AXRange(std::move(anchor), std::move(focus));
} }
@@ -77,10 +84,9 @@ id AXPositionToAXTextMarker(AXPlatformNodeDelegate::AXPosition position) {
// AXTextMarkerCreate is a system function that makes a copy of the data // AXTextMarkerCreate is a system function that makes a copy of the data
// buffer given to it. // buffer given to it.
AXPlatformNodeDelegate::SerializedPosition serialized = position->Serialize(); AXPlatformNodeDelegate::SerializedPosition serialized = position->Serialize();
AXTextMarkerRef cf_text_marker = AXTextMarkerCreate( return CFBridgingRelease(AXTextMarkerCreate(
kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized), kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized),
sizeof(AXPlatformNodeDelegate::SerializedPosition)); sizeof(AXPlatformNodeDelegate::SerializedPosition)));
return [static_cast<id>(cf_text_marker) autorelease];
} }
id AXRangeToAXTextMarkerRange(AXPlatformNodeDelegate::AXRange range) { id AXRangeToAXTextMarkerRange(AXPlatformNodeDelegate::AXRange range) {
@@ -96,15 +102,14 @@ id AXRangeToAXTextMarkerRange(AXPlatformNodeDelegate::AXRange range) {
kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized_focus), kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized_focus),
sizeof(AXPlatformNodeDelegate::SerializedPosition))); sizeof(AXPlatformNodeDelegate::SerializedPosition)));
AXTextMarkerRangeRef cf_marker_range = return CFBridgingRelease(
AXTextMarkerRangeCreate(kCFAllocatorDefault, start_marker, end_marker); AXTextMarkerRangeCreate(kCFAllocatorDefault, start_marker, end_marker));
return [static_cast<id>(cf_marker_range) autorelease];
} }
id AXTextMarkerFrom(const AXPlatformNodeCocoa* anchor, id AXTextMarkerFrom(AXPlatformNodeCocoa* anchor,
int offset, int offset,
ax::mojom::TextAffinity affinity) { ax::mojom::TextAffinity affinity) {
AXPlatformNode* anchor_platform_node = [static_cast<id>(anchor) node]; AXPlatformNode* anchor_platform_node = anchor.node;
AXPlatformNodeDelegate* anchor_node = anchor_platform_node->GetDelegate(); AXPlatformNodeDelegate* anchor_node = anchor_platform_node->GetDelegate();
AXPlatformNodeDelegate::AXPosition position = AXPlatformNodeDelegate::AXPosition position =
anchor_node->CreateTextPositionAt(offset, affinity); anchor_node->CreateTextPositionAt(offset, affinity);
@@ -112,20 +117,19 @@ id AXTextMarkerFrom(const AXPlatformNodeCocoa* anchor,
} }
id AXTextMarkerRangeFrom(id start_textmarker, id end_textmarker) { id AXTextMarkerRangeFrom(id start_textmarker, id end_textmarker) {
AXTextMarkerRangeRef cf_marker_range = AXTextMarkerRangeCreate( return CFBridgingRelease(AXTextMarkerRangeCreate(
kCFAllocatorDefault, static_cast<AXTextMarkerRef>(start_textmarker), kCFAllocatorDefault, (__bridge AXTextMarkerRef)start_textmarker,
static_cast<AXTextMarkerRef>(end_textmarker)); (__bridge AXTextMarkerRef)end_textmarker));
return [static_cast<id>(cf_marker_range) autorelease];
} }
id AXTextMarkerRangeStart(id text_marker_range) { id AXTextMarkerRangeStart(id text_marker_range) {
return static_cast<id>(AXTextMarkerRangeCopyStartMarker( return CFBridgingRelease(AXTextMarkerRangeCopyStartMarker(
static_cast<AXTextMarkerRangeRef>(text_marker_range))); (__bridge AXTextMarkerRangeRef)text_marker_range));
} }
id AXTextMarkerRangeEnd(id text_marker_range) { id AXTextMarkerRangeEnd(id text_marker_range) {
return static_cast<id>(AXTextMarkerRangeCopyEndMarker( return CFBridgingRelease(AXTextMarkerRangeCopyEndMarker(
static_cast<AXTextMarkerRangeRef>(text_marker_range))); (__bridge AXTextMarkerRangeRef)text_marker_range));
} }
} // namespace ui } // namespace ui

@@ -9,6 +9,10 @@
#include "base/memory/raw_ptr.h" #include "base/memory/raw_ptr.h"
#include "ui/accessibility/platform/inspect/ax_tree_indexer_mac.h" #include "ui/accessibility/platform/inspect/ax_tree_indexer_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace ui { namespace ui {
class AXElementWrapper; class AXElementWrapper;
@@ -107,7 +111,7 @@ class COMPONENT_EXPORT(AX_PLATFORM) AXCallStatementInvoker final {
gfx::NativeViewAccessible LineIndexToNode( gfx::NativeViewAccessible LineIndexToNode(
const std::u16string line_index) const; const std::u16string line_index) const;
const id node; id __strong node;
// Map between AXUIElement objects and their DOMIds/accessible tree // Map between AXUIElement objects and their DOMIds/accessible tree
// line numbers. Owned by the caller and outlives this object. // line numbers. Owned by the caller and outlives this object.

@@ -12,6 +12,10 @@
#include "ui/accessibility/platform/inspect/ax_inspect_utils_mac.h" #include "ui/accessibility/platform/inspect/ax_inspect_utils_mac.h"
#include "ui/accessibility/platform/inspect/ax_property_node.h" #include "ui/accessibility/platform/inspect/ax_property_node.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace ui { namespace ui {
// Template specialization of AXOptional<id>::ToString(). // Template specialization of AXOptional<id>::ToString().
@@ -93,7 +97,7 @@ AXOptionalNSObject AXCallStatementInvoker::Invoke(
// Case 2: try to get target from the tree indexer. The target may refer to // Case 2: try to get target from the tree indexer. The target may refer to
// an accessible element by DOM id or by a line number (:LINE_NUM format) in // an accessible element by DOM id or by a line number (:LINE_NUM format) in
// a result accessible tree. The tree indexer keeps the mappings between // a result accessible tree. The tree indexer keeps the mappings between
// accesible elements and their DOM ids and line numbers. // accessible elements and their DOM ids and line numbers.
if (!target) if (!target)
target = indexer_->NodeBy(property_node.name_or_value); target = indexer_->NodeBy(property_node.name_or_value);
@@ -165,7 +169,7 @@ AXOptionalNSObject AXCallStatementInvoker::InvokeFor(
} }
if (AXElementWrapper::IsValidElement(target)) if (AXElementWrapper::IsValidElement(target))
return InvokeForAXElement({target}, property_node); return InvokeForAXElement(AXElementWrapper{target}, property_node);
if (IsAXTextMarkerRange(target)) { if (IsAXTextMarkerRange(target)) {
return InvokeForAXTextMarkerRange(target, property_node); return InvokeForAXTextMarkerRange(target, property_node);
@@ -283,7 +287,7 @@ AXOptionalNSObject AXCallStatementInvoker::InvokeForAXElement(
optional_arg_selector optional_arg_selector
? ax_element.Invoke<BOOL, SEL>(selector, *optional_arg_selector) ? ax_element.Invoke<BOOL, SEL>(selector, *optional_arg_selector)
: ax_element.Invoke<BOOL>(selector); : ax_element.Invoke<BOOL>(selector);
return AXOptionalNSObject([NSNumber numberWithBool:return_value]); return AXOptionalNSObject(@(return_value));
} }
if (property_node.name_or_value == "setAccessibilityFocused") if (property_node.name_or_value == "setAccessibilityFocused")

@@ -13,12 +13,16 @@
#include "ui/accessibility/platform/inspect/ax_inspect.h" #include "ui/accessibility/platform/inspect/ax_inspect.h"
#include "ui/accessibility/platform/inspect/ax_optional.h" #include "ui/accessibility/platform/inspect/ax_optional.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace ui { namespace ui {
// Optional tri-state id object. // Optional tri-state id object.
using AXOptionalNSObject = AXOptional<id>; using AXOptionalNSObject = AXOptional<id>;
// A wrapper around AXUIElement or NSAccessibilityElement object. // A wrapper around either AXUIElement or NSAccessibilityElement object.
class COMPONENT_EXPORT(AX_PLATFORM) AXElementWrapper final { class COMPONENT_EXPORT(AX_PLATFORM) AXElementWrapper final {
public: public:
// Returns true if the object is either NSAccessibilityElement or // Returns true if the object is either NSAccessibilityElement or
@@ -39,7 +43,7 @@ class COMPONENT_EXPORT(AX_PLATFORM) AXElementWrapper final {
// BrowserAccessibilityCocoa). // BrowserAccessibilityCocoa).
static std::string DOMIdOf(const id node); static std::string DOMIdOf(const id node);
AXElementWrapper(const id node) : node_(node) {} explicit AXElementWrapper(const id node) : node_(node) {}
// Returns true if the object is either an NSAccessibilityElement or // Returns true if the object is either an NSAccessibilityElement or
// AXUIElement. // AXUIElement.
@@ -150,7 +154,7 @@ class COMPONENT_EXPORT(AX_PLATFORM) AXElementWrapper final {
// Converts the given value and the error object into AXOptional object. // Converts the given value and the error object into AXOptional object.
AXOptionalNSObject ToOptional(id, AXError, const std::string& message) const; AXOptionalNSObject ToOptional(id, AXError, const std::string& message) const;
const id node_; id __strong node_;
}; };
} // namespace ui } // namespace ui

@@ -4,16 +4,25 @@
#include "ui/accessibility/platform/inspect/ax_element_wrapper_mac.h" #include "ui/accessibility/platform/inspect/ax_element_wrapper_mac.h"
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
#include <ostream> #include <ostream>
#include "base/apple/bridging.h"
#include "base/containers/fixed_flat_set.h" #include "base/containers/fixed_flat_set.h"
#include "base/debug/stack_trace.h" #include "base/debug/stack_trace.h"
#include "base/functional/callback.h" #include "base/functional/callback.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/strings/pattern.h" #include "base/strings/pattern.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "ui/accessibility/platform/ax_private_attributes_mac.h" #include "ui/accessibility/platform/ax_private_attributes_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// error: 'accessibilityAttributeNames' is deprecated: first deprecated in // error: 'accessibilityAttributeNames' is deprecated: first deprecated in
// macOS 10.10 - Use the NSAccessibility protocol methods instead (see // macOS 10.10 - Use the NSAccessibility protocol methods instead (see
// NSAccessibilityProtocols.h // NSAccessibilityProtocols.h
@@ -22,8 +31,6 @@
namespace ui { namespace ui {
using base::SysNSStringToUTF8;
constexpr char kUnsupportedObject[] = constexpr char kUnsupportedObject[] =
"Only AXUIElementRef and BrowserAccessibilityCocoa are supported."; "Only AXUIElementRef and BrowserAccessibilityCocoa are supported.";
@@ -32,20 +39,24 @@ bool AXElementWrapper::IsValidElement(const id node) {
return AXElementWrapper(node).IsValidElement(); return AXElementWrapper(node).IsValidElement();
} }
// static
bool AXElementWrapper::IsNSAccessibilityElement(const id node) { bool AXElementWrapper::IsNSAccessibilityElement(const id node) {
return AXElementWrapper(node).IsNSAccessibilityElement(); return AXElementWrapper(node).IsNSAccessibilityElement();
} }
// static
bool AXElementWrapper::IsAXUIElement(const id node) { bool AXElementWrapper::IsAXUIElement(const id node) {
return AXElementWrapper(node).IsAXUIElement(); return AXElementWrapper(node).IsAXUIElement();
} }
// static
NSArray* AXElementWrapper::ChildrenOf(const id node) { NSArray* AXElementWrapper::ChildrenOf(const id node) {
return AXElementWrapper(node).Children(); return AXElementWrapper(node).Children();
} }
// Returns DOM id of a given node (either AXUIElement or // Returns DOM id of a given node (either AXUIElement or
// BrowserAccessibilityCocoa). // BrowserAccessibilityCocoa).
// static
std::string AXElementWrapper::DOMIdOf(const id node) { std::string AXElementWrapper::DOMIdOf(const id node) {
return AXElementWrapper(node).DOMId(); return AXElementWrapper(node).DOMId();
} }
@@ -59,7 +70,7 @@ bool AXElementWrapper::IsNSAccessibilityElement() const {
} }
bool AXElementWrapper::IsAXUIElement() const { bool AXElementWrapper::IsAXUIElement() const {
return CFGetTypeID(node_) == AXUIElementGetTypeID(); return CFGetTypeID((__bridge CFTypeRef)node_) == AXUIElementGetTypeID();
} }
id AXElementWrapper::AsId() const { id AXElementWrapper::AsId() const {
@@ -76,11 +87,13 @@ NSArray* AXElementWrapper::Children() const {
return [node_ children]; return [node_ children];
if (IsAXUIElement()) { if (IsAXUIElement()) {
CFTypeRef children_ref; base::ScopedCFTypeRef<CFTypeRef> children_ref;
if ((AXUIElementCopyAttributeValue(static_cast<AXUIElementRef>(node_), if ((AXUIElementCopyAttributeValue(
kAXChildrenAttribute, &children_ref)) == (__bridge AXUIElementRef)node_, kAXChildrenAttribute,
kAXErrorSuccess) children_ref.InitializeInto())) == kAXErrorSuccess) {
return static_cast<NSArray*>(children_ref); return base::apple::CFToNSOwnershipCast(
(CFArrayRef)children_ref.release());
}
return nil; return nil;
} }
@@ -101,11 +114,11 @@ NSSize AXElementWrapper::Size() const {
} }
id value = *GetAttributeValue(NSAccessibilitySizeAttribute); id value = *GetAttributeValue(NSAccessibilitySizeAttribute);
if (value && CFGetTypeID(value) == AXValueGetTypeID()) { if (value && CFGetTypeID((__bridge CFTypeRef)value) == AXValueGetTypeID()) {
AXValueType type = AXValueGetType(static_cast<AXValueRef>(value)); AXValueType type = AXValueGetType((__bridge AXValueRef)value);
if (type == kAXValueCGSizeType) { if (type == kAXValueCGSizeType) {
NSSize size; NSSize size;
if (AXValueGetValue(static_cast<AXValueRef>(value), type, &size)) { if (AXValueGetValue((__bridge AXValueRef)value, type, &size)) {
return size; return size;
} }
} }
@@ -118,35 +131,36 @@ NSPoint AXElementWrapper::Position() const {
return [node_ accessibilityFrame].origin; return [node_ accessibilityFrame].origin;
} }
if (!IsAXUIElement()) { if (IsAXUIElement()) {
NOTREACHED() id value = *GetAttributeValue(NSAccessibilityPositionAttribute);
<< "Only AXUIElementRef and BrowserAccessibilityCocoa are supported."; if (value && CFGetTypeID((__bridge CFTypeRef)value) == AXValueGetTypeID()) {
return NSMakePoint(0, 0); AXValueType type = AXValueGetType((__bridge AXValueRef)value);
} if (type == kAXValueCGPointType) {
NSPoint point;
id value = *GetAttributeValue(NSAccessibilityPositionAttribute); if (AXValueGetValue((__bridge AXValueRef)value, type, &point)) {
if (value && CFGetTypeID(value) == AXValueGetTypeID()) { return point;
AXValueType type = AXValueGetType(static_cast<AXValueRef>(value)); }
if (type == kAXValueCGPointType) {
NSPoint point;
if (AXValueGetValue(static_cast<AXValueRef>(value), type, &point)) {
return point;
} }
} }
} }
NOTREACHED()
<< "Only AXUIElementRef and BrowserAccessibilityCocoa are supported.";
return NSMakePoint(0, 0); return NSMakePoint(0, 0);
} }
NSArray* AXElementWrapper::AttributeNames() const { NSArray* AXElementWrapper::AttributeNames() const {
if (IsNSAccessibilityElement()) if (IsNSAccessibilityElement()) {
return [node_ accessibilityAttributeNames]; return [node_ accessibilityAttributeNames];
}
if (IsAXUIElement()) { if (IsAXUIElement()) {
CFArrayRef attributes_ref; base::ScopedCFTypeRef<CFArrayRef> attributes_ref;
AXError result = AXUIElementCopyAttributeNames( AXError result = AXUIElementCopyAttributeNames(
static_cast<AXUIElementRef>(node_), &attributes_ref); (__bridge AXUIElementRef)node_, attributes_ref.InitializeInto());
if (AXSuccess(result, "AXAttributeNamesOf")) if (AXSuccess(result, "AXAttributeNamesOf")) {
return static_cast<NSArray*>(attributes_ref); return base::apple::CFToNSOwnershipCast(attributes_ref.release());
}
return nil; return nil;
} }
@@ -156,15 +170,17 @@ NSArray* AXElementWrapper::AttributeNames() const {
} }
NSArray* AXElementWrapper::ParameterizedAttributeNames() const { NSArray* AXElementWrapper::ParameterizedAttributeNames() const {
if (IsNSAccessibilityElement()) if (IsNSAccessibilityElement()) {
return [node_ accessibilityParameterizedAttributeNames]; return [node_ accessibilityParameterizedAttributeNames];
}
if (IsAXUIElement()) { if (IsAXUIElement()) {
CFArrayRef attributes_ref; base::ScopedCFTypeRef<CFArrayRef> attributes_ref;
AXError result = AXUIElementCopyParameterizedAttributeNames( AXError result = AXUIElementCopyParameterizedAttributeNames(
static_cast<AXUIElementRef>(node_), &attributes_ref); (__bridge AXUIElementRef)node_, attributes_ref.InitializeInto());
if (AXSuccess(result, "AXParameterizedAttributeNamesOf")) if (AXSuccess(result, "AXParameterizedAttributeNamesOf")) {
return static_cast<NSArray*>(attributes_ref); return base::apple::CFToNSOwnershipCast(attributes_ref.release());
}
return nil; return nil;
} }
@@ -175,16 +191,17 @@ NSArray* AXElementWrapper::ParameterizedAttributeNames() const {
AXOptionalNSObject AXElementWrapper::GetAttributeValue( AXOptionalNSObject AXElementWrapper::GetAttributeValue(
NSString* attribute) const { NSString* attribute) const {
if (IsNSAccessibilityElement()) if (IsNSAccessibilityElement()) {
return AXOptionalNSObject([node_ accessibilityAttributeValue:attribute]); return AXOptionalNSObject([node_ accessibilityAttributeValue:attribute]);
}
if (IsAXUIElement()) { if (IsAXUIElement()) {
CFTypeRef value_ref; base::ScopedCFTypeRef<CFTypeRef> value_ref;
AXError result = AXUIElementCopyAttributeValue( AXError result = AXUIElementCopyAttributeValue(
static_cast<AXUIElementRef>(node_), static_cast<CFStringRef>(attribute), (__bridge AXUIElementRef)node_, (__bridge CFStringRef)attribute,
&value_ref); value_ref.InitializeInto());
return ToOptional( return ToOptional(
static_cast<id>(value_ref), result, (__bridge id)value_ref.get(), result,
"AXGetAttributeValue(" + base::SysNSStringToUTF8(attribute) + ")"); "AXGetAttributeValue(" + base::SysNSStringToUTF8(attribute) + ")");
} }
@@ -199,22 +216,20 @@ AXOptionalNSObject AXElementWrapper::GetParameterizedAttributeValue(
forParameter:parameter]); forParameter:parameter]);
if (IsAXUIElement()) { if (IsAXUIElement()) {
// Convert NSValue parameter to CFTypeRef if needed. base::ScopedCFTypeRef<CFTypeRef> parameter_ref(CFBridgingRetain(parameter));
CFTypeRef parameter_ref = static_cast<CFTypeRef>(parameter);
if ([parameter isKindOfClass:[NSValue class]] && if ([parameter isKindOfClass:[NSValue class]] &&
!strcmp([static_cast<NSValue*>(parameter) objCType], !strcmp([parameter objCType], @encode(NSRange))) {
@encode(NSRange))) { NSRange range = [parameter rangeValue];
NSRange range = [static_cast<NSValue*>(parameter) rangeValue]; parameter_ref.reset(AXValueCreate(kAXValueTypeCFRange, &range));
parameter_ref = AXValueCreate(kAXValueTypeCFRange, &range);
} }
// Get value. // Get value.
CFTypeRef value_ref; base::ScopedCFTypeRef<CFTypeRef> value_ref;
AXError result = AXUIElementCopyParameterizedAttributeValue( AXError result = AXUIElementCopyParameterizedAttributeValue(
static_cast<AXUIElementRef>(node_), static_cast<CFStringRef>(attribute), (__bridge AXUIElementRef)node_, (__bridge CFStringRef)attribute,
parameter_ref, &value_ref); parameter_ref, value_ref.InitializeInto());
return ToOptional(static_cast<id>(value_ref), result, return ToOptional((__bridge id)value_ref.get(), result,
"GetParameterizedAttributeValue(" + "GetParameterizedAttributeValue(" +
base::SysNSStringToUTF8(attribute) + ")"); base::SysNSStringToUTF8(attribute) + ")");
} }
@@ -245,8 +260,11 @@ absl::optional<id> AXElementWrapper::PerformSelector(
NSSelectorFromString(base::SysUTF8ToNSString(selector_string + ":")); NSSelectorFromString(base::SysUTF8ToNSString(selector_string + ":"));
NSString* argument = base::SysUTF8ToNSString(argument_string); NSString* argument = base::SysUTF8ToNSString(argument_string);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
if ([node_ respondsToSelector:selector]) if ([node_ respondsToSelector:selector])
return [node_ performSelector:selector withObject:argument]; return [node_ performSelector:selector withObject:argument];
#pragma clang diagnostic pop
return absl::nullopt; return absl::nullopt;
} }
@@ -257,9 +275,9 @@ void AXElementWrapper::SetAttributeValue(NSString* attribute, id value) const {
} }
if (IsAXUIElement()) { if (IsAXUIElement()) {
AXUIElementSetAttributeValue(static_cast<AXUIElementRef>(node_), AXUIElementSetAttributeValue((__bridge AXUIElementRef)node_,
static_cast<CFStringRef>(attribute), (__bridge CFStringRef)attribute,
static_cast<CFTypeRef>(value)); (__bridge CFTypeRef)value);
return; return;
} }
@@ -272,10 +290,12 @@ NSArray* AXElementWrapper::ActionNames() const {
return [node_ accessibilityActionNames]; return [node_ accessibilityActionNames];
if (IsAXUIElement()) { if (IsAXUIElement()) {
CFArrayRef attributes_ref; base::ScopedCFTypeRef<CFArrayRef> attributes_ref;
if ((AXUIElementCopyActionNames(static_cast<AXUIElementRef>(node_), if ((AXUIElementCopyActionNames((__bridge AXUIElementRef)node_,
&attributes_ref)) == kAXErrorSuccess) attributes_ref.InitializeInto())) ==
return static_cast<NSArray*>(attributes_ref); kAXErrorSuccess) {
return base::apple::CFToNSOwnershipCast(attributes_ref.release());
}
return nil; return nil;
} }
@@ -291,8 +311,8 @@ void AXElementWrapper::PerformAction(NSString* action) const {
} }
if (IsAXUIElement()) { if (IsAXUIElement()) {
AXUIElementPerformAction(static_cast<AXUIElementRef>(node_), AXUIElementPerformAction((__bridge AXUIElementRef)node_,
static_cast<CFStringRef>(action)); (__bridge CFStringRef)action);
return; return;
} }

@@ -9,6 +9,7 @@
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include "base/apple/bridging.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/mac/foundation_util.h" #include "base/mac/foundation_util.h"
#include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_cftyperef.h"
@@ -19,6 +20,10 @@
#include "ui/accessibility/platform/inspect/ax_inspect_utils_mac.h" #include "ui/accessibility/platform/inspect/ax_inspect_utils_mac.h"
#include "ui/accessibility/platform/inspect/ax_tree_formatter_mac.h" #include "ui/accessibility/platform/inspect/ax_tree_formatter_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace ui { namespace ui {
// Callback function registered using AXObserverCreate. // Callback function registered using AXObserverCreate.
@@ -34,9 +39,9 @@ static void EventReceivedThunk(AXObserverRef observer_ref,
AXEventRecorderMac::AXEventRecorderMac(base::ProcessId pid, AXEventRecorderMac::AXEventRecorderMac(base::ProcessId pid,
const AXTreeSelector& selector) const AXTreeSelector& selector)
: observer_run_loop_source_(nullptr) { : observer_run_loop_source_(nullptr) {
AXUIElementRef node = nil; base::ScopedCFTypeRef<AXUIElementRef> node;
if (pid) { if (pid) {
node = AXUIElementCreateApplication(pid); node.reset(AXUIElementCreateApplication(pid));
if (!node) { if (!node) {
LOG(FATAL) << "Failed to get AXUIElement for pid " << pid; LOG(FATAL) << "Failed to get AXUIElement for pid " << pid;
} }
@@ -54,12 +59,12 @@ AXEventRecorderMac::AXEventRecorderMac(base::ProcessId pid,
} }
// Get an AXUIElement for the Chrome application. // Get an AXUIElement for the Chrome application.
application_.reset(node); application_ = std::move(node);
if (!application_.get()) if (!application_.get())
LOG(FATAL) << "Failed to create AXUIElement for application."; LOG(FATAL) << "Failed to create AXUIElement for application.";
// Add the notifications we care about to the observer. // Add the notifications we care about to the observer.
static NSArray* notifications = [@[ static NSArray* notifications = @[
@"AXAutocorrectionOccurred", @"AXAutocorrectionOccurred",
@"AXElementBusyChanged", @"AXElementBusyChanged",
@"AXExpandedChanged", @"AXExpandedChanged",
@@ -103,7 +108,7 @@ AXEventRecorderMac::AXEventRecorderMac(base::ProcessId pid,
NSAccessibilityWindowMiniaturizedNotification, NSAccessibilityWindowMiniaturizedNotification,
NSAccessibilityWindowMovedNotification, NSAccessibilityWindowMovedNotification,
NSAccessibilityWindowResizedNotification, NSAccessibilityWindowResizedNotification,
] retain]; ];
for (NSString* notification : notifications) { for (NSString* notification : notifications) {
AddNotification(notification); AddNotification(notification);
@@ -122,7 +127,7 @@ AXEventRecorderMac::~AXEventRecorderMac() {
void AXEventRecorderMac::AddNotification(NSString* notification) { void AXEventRecorderMac::AddNotification(NSString* notification) {
AXObserverAddNotification(observer_ref_, application_, AXObserverAddNotification(observer_ref_, application_,
base::mac::NSToCFCast(notification), this); base::apple::NSToCFPtrCast(notification), this);
} }
void AXEventRecorderMac::EventReceived(AXUIElementRef element, void AXEventRecorderMac::EventReceived(AXUIElementRef element,
@@ -135,7 +140,7 @@ void AXEventRecorderMac::EventReceived(AXUIElementRef element,
AXTreeFormatter::kFiltersDefaultSet); AXTreeFormatter::kFiltersDefaultSet);
std::string element_str = std::string element_str =
formatter.FormatTree(formatter.BuildNode(static_cast<id>(element))); formatter.FormatTree(formatter.BuildNode((__bridge id)element));
// Element dumps contain a new line character at the end, remove it. // Element dumps contain a new line character at the end, remove it.
if (!element_str.empty() && element_str.back() == '\n') { if (!element_str.empty() && element_str.back() == '\n') {
@@ -157,33 +162,27 @@ std::string AXEventRecorderMac::SerializeTextSelectionChangedProperties(
if (user_info == nil) if (user_info == nil)
return {}; return {};
std::vector<std::string> serialized_info; __block std::vector<std::string> serialized_info;
CFDictionaryApplyFunction( [base::apple::CFToNSPtrCast(user_info) enumerateKeysAndObjectsUsingBlock:^(
user_info, id key, id value, BOOL* stop) {
[](const void* raw_key, const void* raw_value, void* context) { std::string value_string;
auto* key = static_cast<NSString*>(raw_key); if ([key isEqual:NSAccessibilityTextStateChangeTypeKey]) {
auto* value = static_cast<NSObject*>(raw_value); value_string =
auto* serialized_info = static_cast<std::vector<std::string>*>(context); ToString(static_cast<AXTextStateChangeType>([value intValue]));
std::string value_string; } else if ([key isEqual:NSAccessibilityTextSelectionDirection]) {
if ([key isEqual:NSAccessibilityTextStateChangeTypeKey]) { value_string =
value_string = ToString(static_cast<AXTextStateChangeType>( ToString(static_cast<AXTextSelectionDirection>([value intValue]));
[static_cast<NSNumber*>(value) intValue])); } else if ([key isEqual:NSAccessibilityTextSelectionGranularity]) {
} else if ([key isEqual:NSAccessibilityTextSelectionDirection]) { value_string =
value_string = ToString(static_cast<AXTextSelectionDirection>( ToString(static_cast<AXTextSelectionGranularity>([value intValue]));
[static_cast<NSNumber*>(value) intValue])); } else if ([key isEqual:NSAccessibilityTextEditType]) {
} else if ([key isEqual:NSAccessibilityTextSelectionGranularity]) { value_string = ToString(static_cast<AXTextEditType>([value intValue]));
value_string = ToString(static_cast<AXTextSelectionGranularity>( } else {
[static_cast<NSNumber*>(value) intValue])); return;
} else if ([key isEqual:NSAccessibilityTextEditType]) { }
value_string = ToString(static_cast<AXTextEditType>( serialized_info.push_back(base::SysNSStringToUTF8(key) + "=" +
[static_cast<NSNumber*>(value) intValue])); value_string);
} else { }];
return;
}
serialized_info->push_back(base::SysNSStringToUTF8(key) + "=" +
value_string);
},
&serialized_info);
// Always sort the info so that we don't depend on CFDictionary for // Always sort the info so that we don't depend on CFDictionary for
// consistent output ordering. // consistent output ordering.

@@ -5,10 +5,11 @@
#ifndef UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_INSPECT_UTILS_MAC_H_ #ifndef UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_INSPECT_UTILS_MAC_H_
#define UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_INSPECT_UTILS_MAC_H_ #define UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_INSPECT_UTILS_MAC_H_
#import <Cocoa/Cocoa.h> #include <ApplicationServices/ApplicationServices.h>
#include "base/component_export.h" #include "base/component_export.h"
#include "base/functional/callback_forward.h" #include "base/functional/callback_forward.h"
#include "base/mac/scoped_cftyperef.h"
#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/accessibility/platform/inspect/ax_inspect.h" #include "ui/accessibility/platform/inspect/ax_inspect.h"
@@ -24,17 +25,20 @@ bool IsValidAXAttribute(const std::string& attribute);
// Return AXElement in a tree by a given criteria. // Return AXElement in a tree by a given criteria.
using AXFindCriteria = base::RepeatingCallback<bool(const AXUIElementRef)>; using AXFindCriteria = base::RepeatingCallback<bool(const AXUIElementRef)>;
COMPONENT_EXPORT(AX_PLATFORM) COMPONENT_EXPORT(AX_PLATFORM)
AXUIElementRef FindAXUIElement(const AXUIElementRef node, base::ScopedCFTypeRef<AXUIElementRef> FindAXUIElement(
const AXFindCriteria& criteria); const AXUIElementRef node,
const AXFindCriteria& criteria);
// Returns AXUIElement and its application process id by a given tree selector. // Returns AXUIElement and its application process id by a given tree selector.
COMPONENT_EXPORT(AX_PLATFORM) COMPONENT_EXPORT(AX_PLATFORM)
std::pair<AXUIElementRef, int> FindAXUIElement(const AXTreeSelector&); std::pair<base::ScopedCFTypeRef<AXUIElementRef>, int> FindAXUIElement(
const AXTreeSelector&);
// Returns AXUIElement for a window having title matching the given pattern. // Returns AXUIElement for a window having title matching the given pattern.
COMPONENT_EXPORT(AX_PLATFORM) COMPONENT_EXPORT(AX_PLATFORM)
AXUIElementRef FindAXWindowChild(AXUIElementRef parent, base::ScopedCFTypeRef<AXUIElementRef> FindAXWindowChild(
const std::string& pattern); AXUIElementRef parent,
const std::string& pattern);
} // namespace ui } // namespace ui

@@ -4,17 +4,26 @@
#include "ui/accessibility/platform/inspect/ax_inspect_utils_mac.h" #include "ui/accessibility/platform/inspect/ax_inspect_utils_mac.h"
#include <CoreGraphics/CoreGraphics.h>
#include <ostream> #include <ostream>
#include "base/apple/bridging.h"
#include "base/containers/fixed_flat_set.h" #include "base/containers/fixed_flat_set.h"
#include "base/debug/stack_trace.h" #include "base/debug/stack_trace.h"
#include "base/functional/callback.h" #include "base/functional/callback.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/mac/foundation_util.h"
#include "base/memory/scoped_policy.h"
#include "base/strings/pattern.h" #include "base/strings/pattern.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "ui/accessibility/platform/ax_private_attributes_mac.h" #include "ui/accessibility/platform/ax_private_attributes_mac.h"
#include "ui/accessibility/platform/inspect/ax_element_wrapper_mac.h" #include "ui/accessibility/platform/inspect/ax_element_wrapper_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// error: 'accessibilityAttributeNames' is deprecated: first deprecated in // error: 'accessibilityAttributeNames' is deprecated: first deprecated in
// macOS 10.10 - Use the NSAccessibility protocol methods instead (see // macOS 10.10 - Use the NSAccessibility protocol methods instead (see
// NSAccessibilityProtocols.h // NSAccessibilityProtocols.h
@@ -23,112 +32,106 @@
namespace ui { namespace ui {
using base::SysNSStringToUTF8; namespace {
const char kChromeTitle[] = "Google Chrome"; const char kChromeTitle[] = "Google Chrome";
const char kChromiumTitle[] = "Chromium"; const char kChromiumTitle[] = "Chromium";
const char kFirefoxTitle[] = "Firefox"; const char kFirefoxTitle[] = "Firefox";
const char kSafariTitle[] = "Safari"; const char kSafariTitle[] = "Safari";
struct NSStringComparator {
bool operator()(NSString* lhs, NSString* rhs) const {
return [lhs compare:rhs] == NSOrderedAscending;
}
};
bool IsValidAXAttribute(const std::string& attribute) {
// static local to avoid a global static constructor.
static auto kValidAttributes = base::MakeFixedFlatSet<NSString*>(
{NSAccessibilityAccessKeyAttribute,
NSAccessibilityARIAAtomicAttribute,
NSAccessibilityARIABusyAttribute,
NSAccessibilityARIAColumnCountAttribute,
NSAccessibilityARIAColumnIndexAttribute,
NSAccessibilityARIACurrentAttribute,
NSAccessibilityARIALiveAttribute,
NSAccessibilityARIAPosInSetAttribute,
NSAccessibilityARIARelevantAttribute,
NSAccessibilityARIARowCountAttribute,
NSAccessibilityARIARowIndexAttribute,
NSAccessibilityARIASetSizeAttribute,
NSAccessibilityAutocompleteValueAttribute,
NSAccessibilityBlockQuoteLevelAttribute,
NSAccessibilityBrailleLabelAttribute,
NSAccessibilityBrailleRoleDescription,
NSAccessibilityChromeAXNodeIdAttribute,
NSAccessibilityColumnHeaderUIElementsAttribute,
NSAccessibilityDescriptionAttribute,
NSAccessibilityDetailsElementsAttribute,
NSAccessibilityDOMClassList,
NSAccessibilityDropEffectsAttribute,
NSAccessibilityElementBusyAttribute,
NSAccessibilityFocusableAncestorAttribute,
NSAccessibilityGrabbedAttribute,
NSAccessibilityHasPopupAttribute,
NSAccessibilityInvalidAttribute,
NSAccessibilityIsMultiSelectable,
NSAccessibilityKeyShortcutsValueAttribute,
NSAccessibilityLoadedAttribute,
NSAccessibilityLoadingProgressAttribute,
NSAccessibilityMathFractionNumeratorAttribute,
NSAccessibilityMathFractionDenominatorAttribute,
NSAccessibilityMathRootRadicandAttribute,
NSAccessibilityMathRootIndexAttribute,
NSAccessibilityMathBaseAttribute,
NSAccessibilityMathSubscriptAttribute,
NSAccessibilityMathSuperscriptAttribute,
NSAccessibilityMathUnderAttribute,
NSAccessibilityMathOverAttribute,
NSAccessibilityMathPostscriptsAttribute,
NSAccessibilityMathPrescriptsAttribute,
NSAccessibilityOwnsAttribute,
NSAccessibilityPopupValueAttribute,
NSAccessibilityRequiredAttribute,
NSAccessibilityRoleDescriptionAttribute,
NSAccessibilitySelectedAttribute,
NSAccessibilitySizeAttribute,
NSAccessibilityTitleAttribute,
NSAccessibilityTitleUIElementAttribute,
NSAccessibilityURLAttribute,
NSAccessibilityVisitedAttribute},
NSStringComparator());
return kValidAttributes.contains(base::SysUTF8ToNSString(attribute));
}
NSArray* AXChildrenOf(const id node) { NSArray* AXChildrenOf(const id node) {
return AXElementWrapper(node).Children(); return AXElementWrapper(node).Children();
} }
std::string GetDOMId(const id node) { } // namespace
return AXElementWrapper(node).DOMId();
bool IsValidAXAttribute(const std::string& attribute) {
static NSSet<NSString*>* valid_attributes = [NSSet setWithArray:@[
NSAccessibilityAccessKeyAttribute,
NSAccessibilityARIAAtomicAttribute,
NSAccessibilityARIABusyAttribute,
NSAccessibilityARIAColumnCountAttribute,
NSAccessibilityARIAColumnIndexAttribute,
NSAccessibilityARIACurrentAttribute,
NSAccessibilityARIALiveAttribute,
NSAccessibilityARIAPosInSetAttribute,
NSAccessibilityARIARelevantAttribute,
NSAccessibilityARIARowCountAttribute,
NSAccessibilityARIARowIndexAttribute,
NSAccessibilityARIASetSizeAttribute,
NSAccessibilityAutocompleteValueAttribute,
NSAccessibilityBlockQuoteLevelAttribute,
NSAccessibilityBrailleLabelAttribute,
NSAccessibilityBrailleRoleDescription,
NSAccessibilityChromeAXNodeIdAttribute,
NSAccessibilityColumnHeaderUIElementsAttribute,
NSAccessibilityDescriptionAttribute,
NSAccessibilityDetailsElementsAttribute,
NSAccessibilityDOMClassList,
NSAccessibilityDropEffectsAttribute,
NSAccessibilityElementBusyAttribute,
NSAccessibilityFocusableAncestorAttribute,
NSAccessibilityGrabbedAttribute,
NSAccessibilityHasPopupAttribute,
NSAccessibilityInvalidAttribute,
NSAccessibilityIsMultiSelectable,
NSAccessibilityKeyShortcutsValueAttribute,
NSAccessibilityLoadedAttribute,
NSAccessibilityLoadingProgressAttribute,
NSAccessibilityMathFractionNumeratorAttribute,
NSAccessibilityMathFractionDenominatorAttribute,
NSAccessibilityMathRootRadicandAttribute,
NSAccessibilityMathRootIndexAttribute,
NSAccessibilityMathBaseAttribute,
NSAccessibilityMathSubscriptAttribute,
NSAccessibilityMathSuperscriptAttribute,
NSAccessibilityMathUnderAttribute,
NSAccessibilityMathOverAttribute,
NSAccessibilityMathPostscriptsAttribute,
NSAccessibilityMathPrescriptsAttribute,
NSAccessibilityOwnsAttribute,
NSAccessibilityPopupValueAttribute,
NSAccessibilityRequiredAttribute,
NSAccessibilityRoleDescriptionAttribute,
NSAccessibilitySelectedAttribute,
NSAccessibilitySizeAttribute,
NSAccessibilityTitleAttribute,
NSAccessibilityTitleUIElementAttribute,
NSAccessibilityURLAttribute,
NSAccessibilityVisitedAttribute,
]];
return [valid_attributes containsObject:base::SysUTF8ToNSString(attribute)];
} }
AXUIElementRef FindAXUIElement(const AXUIElementRef node, base::ScopedCFTypeRef<AXUIElementRef> FindAXUIElement(
const AXFindCriteria& criteria) { const AXUIElementRef node,
if (criteria.Run(node)) const AXFindCriteria& criteria) {
return node; if (criteria.Run(node)) {
return base::ScopedCFTypeRef<AXUIElementRef>(node,
base::scoped_policy::RETAIN);
}
NSArray* children = AXChildrenOf(static_cast<id>(node)); NSArray* children = AXChildrenOf((__bridge id)node);
for (id child in children) { for (id child in children) {
AXUIElementRef found = base::ScopedCFTypeRef<AXUIElementRef> found =
FindAXUIElement(static_cast<AXUIElementRef>(child), criteria); FindAXUIElement((__bridge AXUIElementRef)child, criteria);
if (found != nil) if (found != nil) {
return found; return found;
}
} }
return nil; return base::ScopedCFTypeRef<AXUIElementRef>();
} }
std::pair<AXUIElementRef, int> FindAXUIElement(const AXTreeSelector& selector) { std::pair<base::ScopedCFTypeRef<AXUIElementRef>, int> FindAXUIElement(
const AXTreeSelector& selector) {
if (selector.widget) { if (selector.widget) {
return {AXUIElementCreateApplication(selector.widget), selector.widget}; return {base::ScopedCFTypeRef<AXUIElementRef>(
AXUIElementCreateApplication(selector.widget)),
selector.widget};
} }
NSArray* windows = static_cast<NSArray*>(CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
kCGNullWindowID));
std::string title; std::string title;
if (selector.types & AXTreeSelector::Chrome) if (selector.types & AXTreeSelector::Chrome)
title = kChromeTitle; title = kChromeTitle;
@@ -139,32 +142,40 @@ std::pair<AXUIElementRef, int> FindAXUIElement(const AXTreeSelector& selector) {
else if (selector.types & AXTreeSelector::Safari) else if (selector.types & AXTreeSelector::Safari)
title = kSafariTitle; title = kSafariTitle;
else else
return {nil, 0}; return {base::ScopedCFTypeRef<AXUIElementRef>(), 0};
NSArray* windows =
base::apple::CFToNSOwnershipCast(CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
kCGNullWindowID));
for (NSDictionary* window_info in windows) { for (NSDictionary* window_info in windows) {
int pid = int pid = base::mac::ObjCCast<NSNumber>(window_info[@"kCGWindowOwnerPID"])
[static_cast<NSNumber*>([window_info objectForKey:@"kCGWindowOwnerPID"]) .intValue;
intValue]; std::string window_name = base::SysNSStringToUTF8(
std::string window_name = SysNSStringToUTF8(static_cast<NSString*>( base::mac::ObjCCast<NSString>(window_info[@"kCGWindowOwnerName"]));
[window_info objectForKey:@"kCGWindowOwnerName"]));
AXUIElementRef node = nil; base::ScopedCFTypeRef<AXUIElementRef> node;
// Application pre-defined selectors match or application title exact match. // Application pre-defined selectors match or application title exact match.
bool appTitleMatch = window_name == selector.pattern; bool app_title_match = window_name == selector.pattern;
if (window_name == title || appTitleMatch) if (window_name == title || app_title_match) {
node = AXUIElementCreateApplication(pid); node.reset(AXUIElementCreateApplication(pid));
}
// Window title match. Application contain an AXWindow accessible object as // Window title match. Application contain an AXWindow accessible object as
// a first child, which accessible name contain a window title. For example: // a first child, which accessible name contain a window title. For example:
// 'Inbox (2) - asurkov@igalia.com - Gmail'. // 'Inbox (2) - asurkov@igalia.com - Gmail'.
if (!selector.pattern.empty() && !appTitleMatch) { if (!selector.pattern.empty() && !app_title_match) {
if (!node) if (!node) {
node = AXUIElementCreateApplication(pid); node.reset(AXUIElementCreateApplication(pid));
}
AXUIElementRef window = FindAXWindowChild(node, selector.pattern); base::ScopedCFTypeRef<AXUIElementRef> window =
if (window) FindAXWindowChild(node, selector.pattern);
if (window) {
node = window; node = window;
}
} }
// ActiveTab selector. // ActiveTab selector.
@@ -173,10 +184,10 @@ std::pair<AXUIElementRef, int> FindAXUIElement(const AXTreeSelector& selector) {
node, base::BindRepeating([](const AXUIElementRef node) { node, base::BindRepeating([](const AXUIElementRef node) {
// Only active tab in exposed in browsers, thus find first // Only active tab in exposed in browsers, thus find first
// AXWebArea role. // AXWebArea role.
AXElementWrapper ax_node(static_cast<id>(node)); AXElementWrapper ax_node((__bridge id)node);
NSString* role = NSString* role =
*ax_node.GetAttributeValue(NSAccessibilityRoleAttribute); *ax_node.GetAttributeValue(NSAccessibilityRoleAttribute);
return SysNSStringToUTF8(role) == "AXWebArea"; return base::SysNSStringToUTF8(role) == "AXWebArea";
})); }));
} }
@@ -184,28 +195,33 @@ std::pair<AXUIElementRef, int> FindAXUIElement(const AXTreeSelector& selector) {
if (node) if (node)
return {node, pid}; return {node, pid};
} }
return {nil, 0}; return {base::ScopedCFTypeRef<AXUIElementRef>(), 0};
} }
AXUIElementRef FindAXWindowChild(AXUIElementRef parent, base::ScopedCFTypeRef<AXUIElementRef> FindAXWindowChild(
const std::string& pattern) { AXUIElementRef parent,
NSArray* children = AXChildrenOf(static_cast<id>(parent)); const std::string& pattern) {
if ([children count] == 0) NSArray* children = AXChildrenOf((__bridge id)parent);
return nil; if (children.count == 0) {
return base::ScopedCFTypeRef<AXUIElementRef>();
}
id window = [children objectAtIndex:0]; id window = children.firstObject;
AXElementWrapper ax_window(window); AXElementWrapper ax_window(window);
NSString* role = *ax_window.GetAttributeValue(NSAccessibilityRoleAttribute); NSString* role = *ax_window.GetAttributeValue(NSAccessibilityRoleAttribute);
if (SysNSStringToUTF8(role) != "AXWindow") if (base::SysNSStringToUTF8(role) != "AXWindow") {
return nil; return base::ScopedCFTypeRef<AXUIElementRef>();
}
NSString* window_title = NSString* window_title =
*ax_window.GetAttributeValue(NSAccessibilityTitleAttribute); *ax_window.GetAttributeValue(NSAccessibilityTitleAttribute);
if (base::MatchPattern(SysNSStringToUTF8(window_title), pattern)) if (base::MatchPattern(base::SysNSStringToUTF8(window_title), pattern)) {
return static_cast<AXUIElementRef>(window); return base::ScopedCFTypeRef<AXUIElementRef>(
(__bridge AXUIElementRef)window, base::scoped_policy::RETAIN);
}
return nil; return base::ScopedCFTypeRef<AXUIElementRef>();
} }
} // namespace ui } // namespace ui

@@ -4,6 +4,7 @@
#include "ui/accessibility/platform/inspect/ax_transform_mac.h" #include "ui/accessibility/platform/inspect/ax_transform_mac.h"
#include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "ui/accessibility/ax_range.h" #include "ui/accessibility/ax_range.h"
#include "ui/accessibility/platform/ax_platform_node.h" #include "ui/accessibility/platform/ax_platform_node.h"
@@ -14,6 +15,10 @@
#include "ui/accessibility/platform/inspect/ax_element_wrapper_mac.h" #include "ui/accessibility/platform/inspect/ax_element_wrapper_mac.h"
#include "ui/accessibility/platform/inspect/ax_inspect_utils.h" #include "ui/accessibility/platform/inspect/ax_inspect_utils.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace ui { namespace ui {
constexpr char kHeightDictKey[] = "h"; constexpr char kHeightDictKey[] = "h";
@@ -30,73 +35,75 @@ base::Value AXNSObjectToBaseValue(id value, const AXTreeIndexerMac* indexer) {
} }
// NSArray // NSArray
if ([value isKindOfClass:[NSArray class]]) { if (NSArray* array = base::mac::ObjCCast<NSArray>(value)) {
return base::Value(AXNSArrayToBaseValue((NSArray*)value, indexer)); return base::Value(AXNSArrayToBaseValue(value, indexer));
} }
// AXCustomContent // AXCustomContent
if (@available(macOS 11.0, *)) { if (@available(macOS 11.0, *)) {
if ([value isKindOfClass:[AXCustomContent class]]) { if (AXCustomContent* custom_content =
return base::Value(AXCustomContentToBaseValue((AXCustomContent*)value)); base::mac::ObjCCast<AXCustomContent>(value)) {
return base::Value(AXCustomContentToBaseValue(custom_content));
} }
} }
// NSDictionary // NSDictionary
if ([value isKindOfClass:[NSDictionary class]]) { if (NSDictionary* dictionary = base::mac::ObjCCast<NSDictionary>(value)) {
return base::Value( return base::Value(AXNSDictionaryToBaseValue(dictionary, indexer));
AXNSDictionaryToBaseValue((NSDictionary*)value, indexer));
} }
// NSNumber // NSNumber
if ([value isKindOfClass:[NSNumber class]]) { if (NSNumber* number = base::mac::ObjCCast<NSNumber>(value)) {
return base::Value([value intValue]); return base::Value(number.intValue);
} }
// NSRange, NSSize // NSRange, NSSize
if ([value isKindOfClass:[NSValue class]]) { if (NSValue* ns_value = base::mac::ObjCCast<NSValue>(value)) {
if (0 == strcmp([value objCType], @encode(NSRange))) { if (0 == strcmp(ns_value.objCType, @encode(NSRange))) {
return base::Value(AXNSRangeToBaseValue([value rangeValue])); return base::Value(AXNSRangeToBaseValue(ns_value.rangeValue));
} }
if (0 == strcmp([value objCType], @encode(NSSize))) { if (0 == strcmp(ns_value.objCType, @encode(NSSize))) {
return base::Value(AXNSSizeToBaseValue([value sizeValue])); return base::Value(AXNSSizeToBaseValue(ns_value.sizeValue));
} }
} }
// NSAttributedString // NSAttributedString
if ([value isKindOfClass:[NSAttributedString class]]) { if (NSAttributedString* attr_string =
return NSAttributedStringToBaseValue((NSAttributedString*)value, indexer); base::mac::ObjCCast<NSAttributedString>(value)) {
return NSAttributedStringToBaseValue(attr_string, indexer);
} }
// CGColorRef // CGColorRef
if (CFGetTypeID(value) == CGColorGetTypeID()) { if (CFGetTypeID((__bridge CFTypeRef)value) == CGColorGetTypeID()) {
return base::Value(CGColorRefToBaseValue(static_cast<CGColorRef>(value))); return base::Value(CGColorRefToBaseValue((__bridge CGColorRef)value));
} }
// AXValue // AXValue
if (CFGetTypeID(value) == AXValueGetTypeID()) { if (CFGetTypeID((__bridge CFTypeRef)value) == AXValueGetTypeID()) {
AXValueType type = AXValueGetType(static_cast<AXValueRef>(value)); AXValueRef ax_value = (__bridge AXValueRef)value;
AXValueType type = AXValueGetType(ax_value);
switch (type) { switch (type) {
case kAXValueCGPointType: { case kAXValueCGPointType: {
NSPoint point; NSPoint point;
if (AXValueGetValue(static_cast<AXValueRef>(value), type, &point)) { if (AXValueGetValue(ax_value, type, &point)) {
return base::Value(AXNSPointToBaseValue(point)); return base::Value(AXNSPointToBaseValue(point));
} }
} break; } break;
case kAXValueCGSizeType: { case kAXValueCGSizeType: {
NSSize size; NSSize size;
if (AXValueGetValue(static_cast<AXValueRef>(value), type, &size)) { if (AXValueGetValue(ax_value, type, &size)) {
return base::Value(AXNSSizeToBaseValue(size)); return base::Value(AXNSSizeToBaseValue(size));
} }
} break; } break;
case kAXValueCGRectType: { case kAXValueCGRectType: {
NSRect rect; NSRect rect;
if (AXValueGetValue(static_cast<AXValueRef>(value), type, &rect)) { if (AXValueGetValue(ax_value, type, &rect)) {
return base::Value(AXNSRectToBaseValue(rect)); return base::Value(AXNSRectToBaseValue(rect));
} }
} break; } break;
case kAXValueCFRangeType: { case kAXValueCFRangeType: {
NSRange range; NSRange range;
if (AXValueGetValue(static_cast<AXValueRef>(value), type, &range)) { if (AXValueGetValue(ax_value, type, &range)) {
return base::Value(AXNSRangeToBaseValue(range)); return base::Value(AXNSRangeToBaseValue(range));
} }
} break; } break;
@@ -111,8 +118,9 @@ base::Value AXNSObjectToBaseValue(id value, const AXTreeIndexerMac* indexer) {
} }
// AXTextMarkerRange // AXTextMarkerRange
if (IsAXTextMarkerRange(value)) if (IsAXTextMarkerRange(value)) {
return AXTextMarkerRangeToBaseValue(value, indexer); return AXTextMarkerRangeToBaseValue(value, indexer);
}
// Accessible object // Accessible object
if (AXElementWrapper::IsValidElement(value)) { if (AXElementWrapper::IsValidElement(value)) {
@@ -131,23 +139,27 @@ base::Value AXElementToBaseValue(id node, const AXTreeIndexerMac* indexer) {
base::Value AXPositionToBaseValue( base::Value AXPositionToBaseValue(
const AXPlatformNodeDelegate::AXPosition& position, const AXPlatformNodeDelegate::AXPosition& position,
const AXTreeIndexerMac* indexer) { const AXTreeIndexerMac* indexer) {
if (position->IsNullPosition()) if (position->IsNullPosition()) {
return AXNilToBaseValue(); return AXNilToBaseValue();
}
const AXPlatformTreeManager* manager = const AXPlatformTreeManager* manager =
static_cast<AXPlatformTreeManager*>(position->GetManager()); static_cast<AXPlatformTreeManager*>(position->GetManager());
if (!manager) if (!manager) {
return AXNilToBaseValue(); return AXNilToBaseValue();
}
AXPlatformNode* platform_node_anchor = AXPlatformNode* platform_node_anchor =
manager->GetPlatformNodeFromTree(position->anchor_id()); manager->GetPlatformNodeFromTree(position->anchor_id());
if (!platform_node_anchor) if (!platform_node_anchor) {
return AXNilToBaseValue(); return AXNilToBaseValue();
}
AXPlatformNodeCocoa* cocoa_anchor = static_cast<AXPlatformNodeCocoa*>( AXPlatformNodeCocoa* cocoa_anchor = static_cast<AXPlatformNodeCocoa*>(
platform_node_anchor->GetNativeViewAccessible()); platform_node_anchor->GetNativeViewAccessible());
if (!cocoa_anchor) if (!cocoa_anchor) {
return AXNilToBaseValue(); return AXNilToBaseValue();
}
std::string affinity; std::string affinity;
switch (position->affinity()) { switch (position->affinity()) {
@@ -162,13 +174,14 @@ base::Value AXPositionToBaseValue(
break; break;
} }
base::Value::Dict value; base::Value::Dict value =
value.Set(AXMakeSetKey(AXMakeOrderedKey("anchor", 0)), base::Value::Dict()
AXElementToBaseValue(static_cast<id>(cocoa_anchor), indexer)); .Set(AXMakeSetKey(AXMakeOrderedKey("anchor", 0)),
value.Set(AXMakeSetKey(AXMakeOrderedKey("offset", 1)), AXElementToBaseValue(static_cast<id>(cocoa_anchor), indexer))
position->text_offset()); .Set(AXMakeSetKey(AXMakeOrderedKey("offset", 1)),
value.Set(AXMakeSetKey(AXMakeOrderedKey("affinity", 2)), position->text_offset())
AXMakeConst(affinity)); .Set(AXMakeSetKey(AXMakeOrderedKey("affinity", 2)),
AXMakeConst(affinity));
return base::Value(std::move(value)); return base::Value(std::move(value));
} }
@@ -181,13 +194,16 @@ base::Value AXTextMarkerRangeToBaseValue(id text_marker_range,
const AXTreeIndexerMac* indexer) { const AXTreeIndexerMac* indexer) {
AXPlatformNodeDelegate::AXRange ax_range = AXPlatformNodeDelegate::AXRange ax_range =
AXTextMarkerRangeToAXRange(text_marker_range); AXTextMarkerRangeToAXRange(text_marker_range);
if (ax_range.IsNull()) if (ax_range.IsNull()) {
return AXNilToBaseValue(); return AXNilToBaseValue();
}
base::Value::Dict value; base::Value::Dict value =
value.Set("anchor", base::Value::Dict()
AXPositionToBaseValue(ax_range.anchor()->Clone(), indexer)); .Set("anchor",
value.Set("focus", AXPositionToBaseValue(ax_range.focus()->Clone(), indexer)); AXPositionToBaseValue(ax_range.anchor()->Clone(), indexer))
.Set("focus",
AXPositionToBaseValue(ax_range.focus()->Clone(), indexer));
return base::Value(std::move(value)); return base::Value(std::move(value));
} }
@@ -196,7 +212,7 @@ base::Value NSAttributedStringToBaseValue(NSAttributedString* attr_string,
__block base::Value::Dict result; __block base::Value::Dict result;
[attr_string [attr_string
enumerateAttributesInRange:NSMakeRange(0, [attr_string length]) enumerateAttributesInRange:NSMakeRange(0, attr_string.length)
options: options:
NSAttributedStringEnumerationLongestEffectiveRangeNotRequired NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
usingBlock:^(NSDictionary* attrs, NSRange nsRange, usingBlock:^(NSDictionary* attrs, NSRange nsRange,
@@ -210,7 +226,7 @@ base::Value NSAttributedStringToBaseValue(NSAttributedString* attr_string,
}]; }];
result.Set(std::string(base::SysNSStringToUTF8( result.Set(std::string(base::SysNSStringToUTF8(
[[attr_string string] [attr_string.string
substringWithRange:nsRange])), substringWithRange:nsRange])),
std::move(base_attrs)); std::move(base_attrs));
}]; }];
@@ -232,15 +248,17 @@ base::Value AXNilToBaseValue() {
base::Value::List AXNSArrayToBaseValue(NSArray* node_array, base::Value::List AXNSArrayToBaseValue(NSArray* node_array,
const AXTreeIndexerMac* indexer) { const AXTreeIndexerMac* indexer) {
base::Value::List list; base::Value::List list;
for (NSUInteger i = 0; i < [node_array count]; i++) for (id item in node_array) {
list.Append(AXNSObjectToBaseValue([node_array objectAtIndex:i], indexer)); list.Append(AXNSObjectToBaseValue(item, indexer));
}
return list; return list;
} }
base::Value::Dict AXCustomContentToBaseValue(AXCustomContent* content) { base::Value::Dict AXCustomContentToBaseValue(AXCustomContent* content) {
base::Value::Dict value; base::Value::Dict value =
value.Set("label", base::SysNSStringToUTF16(content.label)); base::Value::Dict()
value.Set("value", base::SysNSStringToUTF16(content.value)); .Set("label", base::SysNSStringToUTF16(content.label))
.Set("value", base::SysNSStringToUTF16(content.value));
return value; return value;
} }
@@ -256,40 +274,41 @@ base::Value::Dict AXNSDictionaryToBaseValue(NSDictionary* dictionary_value,
} }
base::Value::Dict AXNSPointToBaseValue(NSPoint point_value) { base::Value::Dict AXNSPointToBaseValue(NSPoint point_value) {
base::Value::Dict point; base::Value::Dict point =
point.Set(kXCoordDictKey, static_cast<int>(point_value.x)); base::Value::Dict()
point.Set(kYCoordDictKey, static_cast<int>(point_value.y)); .Set(kXCoordDictKey, static_cast<int>(point_value.x))
.Set(kYCoordDictKey, static_cast<int>(point_value.y));
return point; return point;
} }
base::Value::Dict AXNSSizeToBaseValue(NSSize size_value) { base::Value::Dict AXNSSizeToBaseValue(NSSize size_value) {
base::Value::Dict size; base::Value::Dict size = base::Value::Dict()
size.Set(AXMakeOrderedKey(kWidthDictKey, 0), .Set(AXMakeOrderedKey(kWidthDictKey, 0),
static_cast<int>(size_value.width)); static_cast<int>(size_value.width))
size.Set(AXMakeOrderedKey(kHeightDictKey, 1), .Set(AXMakeOrderedKey(kHeightDictKey, 1),
static_cast<int>(size_value.height)); static_cast<int>(size_value.height));
return size; return size;
} }
base::Value::Dict AXNSRectToBaseValue(NSRect rect_value) { base::Value::Dict AXNSRectToBaseValue(NSRect rect_value) {
base::Value::Dict rect; base::Value::Dict rect = base::Value::Dict()
rect.Set(AXMakeOrderedKey(kXCoordDictKey, 0), .Set(AXMakeOrderedKey(kXCoordDictKey, 0),
static_cast<int>(rect_value.origin.x)); static_cast<int>(rect_value.origin.x))
rect.Set(AXMakeOrderedKey(kYCoordDictKey, 1), .Set(AXMakeOrderedKey(kYCoordDictKey, 1),
static_cast<int>(rect_value.origin.y)); static_cast<int>(rect_value.origin.y))
rect.Set(AXMakeOrderedKey(kWidthDictKey, 2), .Set(AXMakeOrderedKey(kWidthDictKey, 2),
static_cast<int>(rect_value.size.width)); static_cast<int>(rect_value.size.width))
rect.Set(AXMakeOrderedKey(kHeightDictKey, 3), .Set(AXMakeOrderedKey(kHeightDictKey, 3),
static_cast<int>(rect_value.size.height)); static_cast<int>(rect_value.size.height));
return rect; return rect;
} }
base::Value::Dict AXNSRangeToBaseValue(NSRange node_range) { base::Value::Dict AXNSRangeToBaseValue(NSRange node_range) {
base::Value::Dict range; base::Value::Dict range = base::Value::Dict()
range.Set(AXMakeOrderedKey(kRangeLocDictKey, 0), .Set(AXMakeOrderedKey(kRangeLocDictKey, 0),
static_cast<int>(node_range.location)); static_cast<int>(node_range.location))
range.Set(AXMakeOrderedKey(kRangeLenDictKey, 1), .Set(AXMakeOrderedKey(kRangeLenDictKey, 1),
static_cast<int>(node_range.length)); static_cast<int>(node_range.length));
return range; return range;
} }

@@ -4,6 +4,8 @@
#include "ui/accessibility/platform/inspect/ax_tree_formatter_mac.h" #include "ui/accessibility/platform/inspect/ax_tree_formatter_mac.h"
#include <string>
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
@@ -19,16 +21,15 @@
#include "ui/accessibility/platform/inspect/ax_script_instruction.h" #include "ui/accessibility/platform/inspect/ax_script_instruction.h"
#include "ui/accessibility/platform/inspect/ax_transform_mac.h" #include "ui/accessibility/platform/inspect/ax_transform_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// This file uses the deprecated NSObject accessibility interface. // This file uses the deprecated NSObject accessibility interface.
// TODO(crbug.com/948844): Migrate to the new NSAccessibility interface. // TODO(crbug.com/948844): Migrate to the new NSAccessibility interface.
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations" #pragma clang diagnostic ignored "-Wdeprecated-declarations"
using base::StringPrintf;
using base::SysNSStringToUTF8;
using base::SysNSStringToUTF16;
using std::string;
namespace ui { namespace ui {
namespace { namespace {
@@ -47,13 +48,13 @@ AXTreeFormatterMac::~AXTreeFormatterMac() = default;
void AXTreeFormatterMac::AddDefaultFilters( void AXTreeFormatterMac::AddDefaultFilters(
std::vector<AXPropertyFilter>* property_filters) { std::vector<AXPropertyFilter>* property_filters) {
static NSArray* default_attributes = [@[ static NSArray* default_attributes = @[
@"AXAutocompleteValue", @"AXDescription", @"AXRole", @"AXSubrole", @"AXAutocompleteValue", @"AXDescription", @"AXRole", @"AXSubrole",
@"AXTitle", @"AXTitleUIElement", @"AXValue" @"AXTitle", @"AXTitleUIElement", @"AXValue"
] retain]; ];
for (NSString* attribute : default_attributes) { for (NSString* attribute : default_attributes) {
AddPropertyFilter(property_filters, SysNSStringToUTF8(attribute)); AddPropertyFilter(property_filters, base::SysNSStringToUTF8(attribute));
} }
if (show_ids()) { if (show_ids()) {
@@ -69,7 +70,7 @@ base::Value::Dict AXTreeFormatterMac::BuildTree(
base::Value::Dict AXTreeFormatterMac::BuildTreeForSelector( base::Value::Dict AXTreeFormatterMac::BuildTreeForSelector(
const AXTreeSelector& selector) const { const AXTreeSelector& selector) const {
AXUIElementRef node = nil; base::ScopedCFTypeRef<AXUIElementRef> node;
std::tie(node, std::ignore) = FindAXUIElement(selector); std::tie(node, std::ignore) = FindAXUIElement(selector);
if (node == nil) { if (node == nil) {
return base::Value::Dict(); return base::Value::Dict();
@@ -79,7 +80,7 @@ base::Value::Dict AXTreeFormatterMac::BuildTreeForSelector(
base::Value::Dict AXTreeFormatterMac::BuildTreeForAXUIElement( base::Value::Dict AXTreeFormatterMac::BuildTreeForAXUIElement(
AXUIElementRef node) const { AXUIElementRef node) const {
return BuildTree(static_cast<id>(node)); return BuildTree((__bridge id)node);
} }
base::Value::Dict AXTreeFormatterMac::BuildTree(const id root) const { base::Value::Dict AXTreeFormatterMac::BuildTree(const id root) const {
@@ -101,13 +102,13 @@ base::Value::Dict AXTreeFormatterMac::BuildTree(const id root) const {
std::string AXTreeFormatterMac::EvaluateScript( std::string AXTreeFormatterMac::EvaluateScript(
const AXTreeSelector& selector, const AXTreeSelector& selector,
const AXInspectScenario& scenario) const { const AXInspectScenario& scenario) const {
AXUIElementRef root = nil; base::ScopedCFTypeRef<AXUIElementRef> root;
std::tie(root, std::ignore) = FindAXUIElement(selector); std::tie(root, std::ignore) = FindAXUIElement(selector);
if (!root) if (!root)
return ""; return "";
std::string result = std::string result =
EvaluateScript(static_cast<id>(root), scenario.script_instructions, 0, EvaluateScript((__bridge id)root.get(), scenario.script_instructions, 0,
scenario.script_instructions.size()); scenario.script_instructions.size());
return result; return result;
@@ -207,7 +208,8 @@ void AXTreeFormatterMac::RecursiveBuildTree(const AXElementWrapper& ax_element,
base::Value::List child_dict_list; base::Value::List child_dict_list;
for (id child in children) { for (id child in children) {
base::Value::Dict child_dict; base::Value::Dict child_dict;
RecursiveBuildTree({child}, root_rect, indexer, &child_dict); RecursiveBuildTree(AXElementWrapper{child}, root_rect, indexer,
&child_dict);
child_dict_list.Append(std::move(child_dict)); child_dict_list.Append(std::move(child_dict));
} }
dict->Set(kChildrenDictAttr, std::move(child_dict_list)); dict->Set(kChildrenDictAttr, std::move(child_dict_list));
@@ -226,7 +228,7 @@ void AXTreeFormatterMac::AddProperties(const AXElementWrapper& ax_element,
NSArray* attributes = ax_element.AttributeNames(); NSArray* attributes = ax_element.AttributeNames();
for (NSString* attribute : attributes) { for (NSString* attribute : attributes) {
dict->SetByDottedPath( dict->SetByDottedPath(
SysNSStringToUTF8(attribute), base::SysNSStringToUTF8(attribute),
AXNSObjectToBaseValue(*ax_element.GetAttributeValue(attribute), AXNSObjectToBaseValue(*ax_element.GetAttributeValue(attribute),
indexer)); indexer));
} }
@@ -279,17 +281,19 @@ std::string AXTreeFormatterMac::ProcessTreeForOutput(
std::string line; std::string line;
// AXRole and AXSubrole have own formatting and should be listed upfront. // AXRole and AXSubrole have own formatting and should be listed upfront.
std::string role_attr = SysNSStringToUTF8(NSAccessibilityRoleAttribute); std::string role_attr = base::SysNSStringToUTF8(NSAccessibilityRoleAttribute);
const std::string* value = dict.FindString(role_attr); const std::string* value = dict.FindString(role_attr);
if (value) { if (value) {
WriteAttribute(true, *value, &line); WriteAttribute(true, *value, &line);
} }
std::string subrole_attr = SysNSStringToUTF8(NSAccessibilitySubroleAttribute); std::string subrole_attr =
base::SysNSStringToUTF8(NSAccessibilitySubroleAttribute);
value = dict.FindString(subrole_attr); value = dict.FindString(subrole_attr);
if (value) { if (value) {
WriteAttribute(false, WriteAttribute(
StringPrintf("%s=%s", subrole_attr.c_str(), value->c_str()), false,
&line); base::StringPrintf("%s=%s", subrole_attr.c_str(), value->c_str()),
&line);
} }
// Expose all other attributes. // Expose all other attributes.
@@ -308,10 +312,10 @@ std::string AXTreeFormatterMac::ProcessTreeForOutput(
// Write formatted value. // Write formatted value.
std::string formatted_value = AXFormatValue(item.second); std::string formatted_value = AXFormatValue(item.second);
WriteAttribute( WriteAttribute(false,
false, base::StringPrintf("%s=%s", item.first.c_str(),
StringPrintf("%s=%s", item.first.c_str(), formatted_value.c_str()), formatted_value.c_str()),
&line); &line);
} }
return line; return line;

@@ -10,14 +10,13 @@
namespace ui { namespace ui {
//
// NSAccessibilityElement or AXUIElement accessible node comparator. // NSAccessibilityElement or AXUIElement accessible node comparator.
struct AXNodeComparator { struct AXNodeComparator {
constexpr bool operator()(const gfx::NativeViewAccessible& lhs, constexpr bool operator()(const gfx::NativeViewAccessible& lhs,
const gfx::NativeViewAccessible& rhs) const { const gfx::NativeViewAccessible& rhs) const {
if (AXElementWrapper::IsAXUIElement(lhs)) { if (AXElementWrapper::IsAXUIElement(lhs)) {
DCHECK(AXElementWrapper::IsAXUIElement(rhs)); DCHECK(AXElementWrapper::IsAXUIElement(rhs));
return CFHash(lhs) < CFHash(rhs); return CFHash((__bridge CFTypeRef)lhs) < CFHash((__bridge CFTypeRef)rhs);
} }
DCHECK(AXElementWrapper::IsNSAccessibilityElement(lhs)); DCHECK(AXElementWrapper::IsNSAccessibilityElement(lhs));
DCHECK(AXElementWrapper::IsNSAccessibilityElement(rhs)); DCHECK(AXElementWrapper::IsNSAccessibilityElement(rhs));

@@ -1169,6 +1169,22 @@ source_set("test_support") {
} }
} }
if (is_mac) {
# TODO(https://crbug.com/1280317): Merge back into views_unittests once all
# .mm files are ARCed.
source_set("views_unittests_arc") {
testonly = true
sources = [ "widget/ax_native_widget_mac_unittest.mm" ]
configs += [ "//build/config/compiler:enable_arc" ]
deps = [
":views",
"//testing/gtest",
"//ui/accessibility",
"//ui/views:test_support",
]
}
}
test("views_unittests") { test("views_unittests") {
use_xvfb = use_xvfb_in_this_config use_xvfb = use_xvfb_in_this_config
@@ -1406,7 +1422,6 @@ test("views_unittests") {
"controls/native/native_view_host_mac_unittest.mm", "controls/native/native_view_host_mac_unittest.mm",
"controls/tabbed_pane/tabbed_pane_accessibility_mac_unittest.mm", "controls/tabbed_pane/tabbed_pane_accessibility_mac_unittest.mm",
"view_unittest_mac.mm", "view_unittest_mac.mm",
"widget/ax_native_widget_mac_unittest.mm",
"widget/native_widget_mac_unittest.mm", "widget/native_widget_mac_unittest.mm",
"widget/sublevel_manager_mac_unittest.mm", "widget/sublevel_manager_mac_unittest.mm",
] ]
@@ -1415,6 +1430,7 @@ test("views_unittests") {
sources -= [ "controls/native/native_view_host_unittest.cc" ] sources -= [ "controls/native/native_view_host_unittest.cc" ]
public_deps = [ public_deps = [
":views_unittests_arc",
"//components/remote_cocoa/app_shim", "//components/remote_cocoa/app_shim",
"//ui/accelerated_widget_mac", "//ui/accelerated_widget_mac",
] ]

@@ -24,6 +24,10 @@
#include "ui/views/test/widget_test.h" #include "ui/views/test/widget_test.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace views { namespace views {
namespace { namespace {
@@ -164,9 +168,8 @@ class AXNativeWidgetMacTest : public test::WidgetTest {
// on a retained accessibility object after the source view is deleted. // on a retained accessibility object after the source view is deleted.
TEST_F(AXNativeWidgetMacTest, Lifetime) { TEST_F(AXNativeWidgetMacTest, Lifetime) {
Textfield* view = AddChildTextfield(widget()->GetContentsView()->size()); Textfield* view = AddChildTextfield(widget()->GetContentsView()->size());
base::scoped_nsobject<NSObject> ax_node(view->GetNativeViewAccessible(), NSObject* ax_node = view->GetNativeViewAccessible();
base::scoped_policy::RETAIN); id<NSAccessibility> ax_obj = ToNSAccessibility(ax_node);
id<NSAccessibility> ax_obj = ToNSAccessibility(ax_node.get());
EXPECT_TRUE(AXObjectHandlesSelector(ax_obj, @selector(accessibilityValue))); EXPECT_TRUE(AXObjectHandlesSelector(ax_obj, @selector(accessibilityValue)));
EXPECT_NSEQ(kTestStringValue, ax_obj.accessibilityValue); EXPECT_NSEQ(kTestStringValue, ax_obj.accessibilityValue);
@@ -190,12 +193,11 @@ TEST_F(AXNativeWidgetMacTest, Lifetime) {
// The only usually available array attribute is AXChildren, so go up a level // The only usually available array attribute is AXChildren, so go up a level
// to the Widget to test that a bit. The default implementation just gets the // to the Widget to test that a bit. The default implementation just gets the
// attribute normally and returns its size (if it's an array). // attribute normally and returns its size (if it's an array).
base::scoped_nsprotocol<id<NSAccessibility>> ax_parent( id<NSAccessibility> ax_parent = ax_obj.accessibilityParent;
ax_obj.accessibilityParent, base::scoped_policy::RETAIN);
// There are two children: a NativeFrameView and the TextField. // There are two children: a NativeFrameView and the TextField.
EXPECT_EQ(2u, ax_parent.get().accessibilityChildren.count); EXPECT_EQ(2u, ax_parent.accessibilityChildren.count);
EXPECT_EQ(ax_node.get(), ax_parent.get().accessibilityChildren[1]); EXPECT_EQ(ax_node, ax_parent.accessibilityChildren[1]);
// If it is not an array, the default implementation throws an exception, so // If it is not an array, the default implementation throws an exception, so
// it's impossible to test these methods further on |ax_node|, apart from the // it's impossible to test these methods further on |ax_node|, apart from the
@@ -230,7 +232,7 @@ TEST_F(AXNativeWidgetMacTest, Lifetime) {
// The Widget is currently still around, but the TextField should be gone, // The Widget is currently still around, but the TextField should be gone,
// leaving just the NativeFrameView. // leaving just the NativeFrameView.
EXPECT_EQ(1u, ax_parent.get().accessibilityChildren.count); EXPECT_EQ(1u, ax_parent.accessibilityChildren.count);
} }
// Check that potentially keyboard-focusable elements are always leaf nodes. // Check that potentially keyboard-focusable elements are always leaf nodes.
@@ -687,8 +689,8 @@ TEST_F(AXNativeWidgetMacTest, ProtectedTextfields) {
EXPECT_TRUE(ax_node); EXPECT_TRUE(ax_node);
// Create a native Cocoa NSSecureTextField to compare against. // Create a native Cocoa NSSecureTextField to compare against.
base::scoped_nsobject<NSSecureTextField> cocoa_secure_textfield( NSSecureTextField* cocoa_secure_textfield =
[[NSSecureTextField alloc] initWithFrame:NSMakeRect(0, 0, 10, 10)]); [[NSSecureTextField alloc] initWithFrame:NSMakeRect(0, 0, 10, 10)];
const SEL expected_supported_selectors[] = { const SEL expected_supported_selectors[] = {
@selector(accessibilityValue), @selector(accessibilityValue),
@@ -702,7 +704,7 @@ TEST_F(AXNativeWidgetMacTest, ProtectedTextfields) {
for (auto* sel : expected_supported_selectors) { for (auto* sel : expected_supported_selectors) {
EXPECT_TRUE(AXObjectHandlesSelector(ax_node, sel)); EXPECT_TRUE(AXObjectHandlesSelector(ax_node, sel));
EXPECT_TRUE(AXObjectHandlesSelector(cocoa_secure_textfield.get(), sel)); EXPECT_TRUE(AXObjectHandlesSelector(cocoa_secure_textfield, sel));
} }
// TODO(https://crbug.com/939965): This should assert the same behavior of // TODO(https://crbug.com/939965): This should assert the same behavior of