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:

committed by
Chromium LUCI CQ

parent
4756d043c4
commit
c41a68c1d0
@@ -42,14 +42,6 @@ struct CONTENT_EXPORT AXTextEdit {
|
||||
// Returns true if the given object is an NSRange instance.
|
||||
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
|
||||
|
||||
// BrowserAccessibilityCocoa is a cocoa wrapper around the BrowserAccessibility
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#import "content/browser/accessibility/browser_accessibility_mac.h"
|
||||
|
||||
#include "base/debug/stack_trace.h"
|
||||
#include "base/memory/scoped_policy.h"
|
||||
#import "base/task/single_thread_task_runner.h"
|
||||
#include "base/task/single_thread_task_runner.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
|
||||
// to add it to the new one, e.g. its list of children.
|
||||
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
|
||||
// created, but keep a null check just in case.
|
||||
|
@@ -611,6 +611,10 @@ source_set("browser_sources") {
|
||||
sources += [ "context_factory.h" ]
|
||||
}
|
||||
|
||||
if (is_apple) {
|
||||
configs += [ "//build/config/compiler:enable_arc" ]
|
||||
}
|
||||
|
||||
if (is_mac) {
|
||||
sources += [
|
||||
"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_tree_formatter_mac.h"
|
||||
|
||||
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
||||
#error "This file requires ARC support."
|
||||
#endif
|
||||
|
||||
namespace content {
|
||||
|
||||
// static
|
||||
@@ -22,8 +26,8 @@ std::unique_ptr<ui::AXEventRecorder> AXInspectFactory::CreatePlatformRecorder(
|
||||
BrowserAccessibilityManager*,
|
||||
base::ProcessId pid,
|
||||
const ui::AXTreeSelector& selector) {
|
||||
return AXInspectFactory::CreateRecorder(ui::AXApiType::kMac, nullptr, pid,
|
||||
selector);
|
||||
return AXInspectFactory::CreateRecorder(ui::AXApiType::kMac,
|
||||
/*manager=*/nullptr, pid, selector);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@@ -6,6 +6,10 @@
|
||||
|
||||
#include "base/observer_list.h"
|
||||
|
||||
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
||||
#error "This file requires ARC support."
|
||||
#endif
|
||||
|
||||
namespace content {
|
||||
|
||||
ScopedNotifyNativeEventProcessorObserver::
|
||||
@@ -15,13 +19,13 @@ ScopedNotifyNativeEventProcessorObserver::
|
||||
NSEvent* event)
|
||||
: observer_list_(observer_list), event_(event) {
|
||||
for (auto& observer : *observer_list_)
|
||||
observer.WillRunNativeEvent(event_);
|
||||
observer.WillRunNativeEvent((__bridge const void*)event_);
|
||||
}
|
||||
|
||||
ScopedNotifyNativeEventProcessorObserver::
|
||||
~ScopedNotifyNativeEventProcessorObserver() {
|
||||
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",
|
||||
]
|
||||
|
||||
configs += [ "//build/config/compiler:enable_arc" ]
|
||||
|
||||
frameworks = [
|
||||
"AppKit.framework",
|
||||
"Foundation.framework",
|
||||
|
@@ -135,7 +135,7 @@ struct COMPONENT_EXPORT(AX_PLATFORM) AXTextStateChangeIntent final {
|
||||
AXTextSelection selection);
|
||||
|
||||
// Constructs an editing intent.
|
||||
AXTextStateChangeIntent(AXTextEditType edit);
|
||||
explicit AXTextStateChangeIntent(AXTextEditType edit);
|
||||
|
||||
AXTextStateChangeIntent(const AXTextStateChangeIntent& intent);
|
||||
|
||||
|
@@ -7,6 +7,10 @@
|
||||
#include "ui/accessibility/ax_enums.mojom.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 {
|
||||
|
||||
// static
|
||||
@@ -107,7 +111,7 @@ AXTextSelection AXTextSelection::FromDirectionAndGranularity(
|
||||
break;
|
||||
}
|
||||
|
||||
return AXTextSelection(direction, granularity, /* focus_change */ false);
|
||||
return AXTextSelection(direction, granularity, /*focus_change=*/false);
|
||||
}
|
||||
|
||||
AXTextSelection::AXTextSelection() = default;
|
||||
@@ -133,7 +137,7 @@ AXTextStateChangeIntent::DefaultFocusTextStateChangeIntent() {
|
||||
AXTextStateChangeType::kSelectionMove,
|
||||
AXTextSelection(AXTextSelectionDirection::kDiscontiguous,
|
||||
AXTextSelectionGranularity::kUnknown,
|
||||
/* focus_change */ true));
|
||||
/*focus_change=*/true));
|
||||
}
|
||||
|
||||
// static
|
||||
@@ -143,7 +147,7 @@ AXTextStateChangeIntent::DefaultSelectionChangeIntent() {
|
||||
AXTextStateChangeType::kSelectionMove,
|
||||
AXTextSelection(AXTextSelectionDirection::kDiscontiguous,
|
||||
AXTextSelectionGranularity::kUnknown,
|
||||
/* focus_change */ false));
|
||||
/*focus_change=*/false));
|
||||
}
|
||||
|
||||
AXTextStateChangeIntent::AXTextStateChangeIntent() = default;
|
||||
|
@@ -5,31 +5,24 @@
|
||||
#ifndef UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_COCOA_H_
|
||||
#define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_COCOA_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#import <Accessibility/Accessibility.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "base/component_export.h"
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
#include "ui/accessibility/ax_enums.mojom-forward.h"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class AXPlatformNodeBase;
|
||||
class AXPlatformNodeDelegate;
|
||||
|
||||
struct AXAnnouncementSpec {
|
||||
AXAnnouncementSpec();
|
||||
~AXAnnouncementSpec();
|
||||
|
||||
base::scoped_nsobject<NSString> announcement;
|
||||
base::scoped_nsobject<NSWindow> window;
|
||||
bool is_polite;
|
||||
};
|
||||
|
||||
} // 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)
|
||||
@interface AXPlatformNodeCocoa
|
||||
: NSAccessibilityElement <NSAccessibility, AXCustomContentProvider>
|
||||
@@ -75,17 +68,15 @@ COMPONENT_EXPORT(AX_PLATFORM)
|
||||
@property(nonatomic, readonly) ui::AXPlatformNodeDelegate* nodeDelegate;
|
||||
|
||||
// Returns the data necessary to queue an NSAccessibility announcement if
|
||||
// |eventType| should be announced, or nullptr otherwise.
|
||||
- (std::unique_ptr<ui::AXAnnouncementSpec>)announcementForEvent:
|
||||
(ax::mojom::Event)eventType;
|
||||
// |eventType| should be announced, or nil otherwise.
|
||||
- (AXAnnouncementSpec*)announcementForEvent:(ax::mojom::Event)eventType;
|
||||
|
||||
// Ask the system to announce |announcementText|. This is debounced to happen
|
||||
// at most every |kLiveRegionDebounceMillis| per node, with only the most
|
||||
// recent announcement text read, to account for situations with multiple
|
||||
// notifications happening one after another (for example, results for
|
||||
// find-in-page updating rapidly as they come in from subframes).
|
||||
- (void)scheduleLiveRegionAnnouncement:
|
||||
(std::unique_ptr<ui::AXAnnouncementSpec>)announcement;
|
||||
- (void)scheduleLiveRegionAnnouncement:(AXAnnouncementSpec*)announcement;
|
||||
|
||||
- (id)AXWindow;
|
||||
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#import "ui/accessibility/platform/ax_platform_node_cocoa.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/foundation_util.h"
|
||||
@@ -27,19 +28,32 @@
|
||||
#import "ui/gfx/mac/coordinate_conversion.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;
|
||||
|
||||
namespace ui {
|
||||
@interface AXAnnouncementSpec ()
|
||||
|
||||
AXAnnouncementSpec::AXAnnouncementSpec() = default;
|
||||
AXAnnouncementSpec::~AXAnnouncementSpec() = default;
|
||||
@property(nonatomic, strong) NSString* announcement;
|
||||
@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 {
|
||||
|
||||
// Same length as web content/WebKit.
|
||||
static int kLiveRegionDebounceMillis = 20;
|
||||
int kLiveRegionDebounceMillis = 20;
|
||||
|
||||
using RoleMap = std::map<ax::mojom::Role, NSString*>;
|
||||
using EventMap = std::map<ax::mojom::Event, NSString*>;
|
||||
@@ -236,7 +250,7 @@ void CollectAncestorRoles(
|
||||
@implementation AXPlatformNodeCocoa {
|
||||
// This field is not a raw_ptr<> because it requires @property rewrite.
|
||||
RAW_PTR_EXCLUSION ui::AXPlatformNodeBase* _node; // Weak. Retains us.
|
||||
std::unique_ptr<ui::AXAnnouncementSpec> _pendingAnnouncement;
|
||||
AXAnnouncementSpec* __strong _pendingAnnouncement;
|
||||
}
|
||||
|
||||
@synthesize node = _node;
|
||||
@@ -329,9 +343,9 @@ void CollectAncestorRoles(
|
||||
break;
|
||||
}
|
||||
|
||||
// On Mac OS X, the accessible name of an object is exposed as its
|
||||
// title if it comes from visible text, and as its description
|
||||
// otherwise, but never both.
|
||||
// On macOS, the accessible name of an object is exposed as its title if it
|
||||
// comes from visible text, and as its description otherwise, but never both.
|
||||
//
|
||||
// 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
|
||||
// accessibilityTitle or in accessibilityLabel.
|
||||
@@ -785,7 +799,7 @@ void CollectAncestorRoles(
|
||||
// Add annotation information
|
||||
int leafTextLength = leafTextRange.GetText().length();
|
||||
DCHECK_LE(static_cast<unsigned long>(anchorStartOffset + leafTextLength),
|
||||
[attributedString length]);
|
||||
attributedString.length);
|
||||
NSRange leafRange = NSMakeRange(anchorStartOffset, leafTextLength);
|
||||
|
||||
CollectAncestorRoles(*anchor, ancestor_roles);
|
||||
@@ -821,16 +835,15 @@ void CollectAncestorRoles(
|
||||
// TODO(crbug.com/958811): Implement NSAccessibilityVisibleNameKey.
|
||||
|
||||
if (text_attrs.font_size != ui::AXTextAttributes::kUnsetValue) {
|
||||
[fontAttributes setValue:@(text_attrs.font_size)
|
||||
forKey:NSAccessibilityFontSizeKey];
|
||||
fontAttributes[NSAccessibilityFontSizeKey] = @(text_attrs.font_size);
|
||||
}
|
||||
|
||||
if (text_attrs.HasTextStyle(ax::mojom::TextStyle::kBold)) {
|
||||
[fontAttributes setValue:@YES forKey:@"AXFontBold"];
|
||||
fontAttributes[@"AXFontBold"] = @YES;
|
||||
}
|
||||
|
||||
if (text_attrs.HasTextStyle(ax::mojom::TextStyle::kItalic)) {
|
||||
[fontAttributes setValue:@YES forKey:@"AXFontItalic"];
|
||||
fontAttributes[@"AXFontItalic"] = @YES;
|
||||
}
|
||||
|
||||
[attributedString addAttribute:NSAccessibilityFontTextAttribute
|
||||
@@ -913,24 +926,24 @@ void CollectAncestorRoles(
|
||||
return base::SysUTF8ToNSString(_node->GetName());
|
||||
}
|
||||
|
||||
- (std::unique_ptr<ui::AXAnnouncementSpec>)announcementForEvent:
|
||||
(ax::mojom::Event)eventType {
|
||||
- (AXAnnouncementSpec*)announcementForEvent:(ax::mojom::Event)eventType {
|
||||
// Only alerts and live region changes should be announced.
|
||||
DCHECK(eventType == ax::mojom::Event::kAlert ||
|
||||
eventType == ax::mojom::Event::kLiveRegionChanged);
|
||||
std::string liveStatus =
|
||||
_node->GetStringAttribute(ax::mojom::StringAttribute::kLiveStatus);
|
||||
// If live status is explicitly set to off, don't announce.
|
||||
if (liveStatus == "off")
|
||||
return nullptr;
|
||||
if (liveStatus == "off") {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString* name = [self getName];
|
||||
NSString* announcementText =
|
||||
[name length] > 0
|
||||
? name
|
||||
: base::SysUTF16ToNSString(_node->GetTextContentUTF16());
|
||||
if ([announcementText length] == 0)
|
||||
return nullptr;
|
||||
name.length > 0 ? name
|
||||
: base::SysUTF16ToNSString(_node->GetTextContentUTF16());
|
||||
if (announcementText.length == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
const std::string& description =
|
||||
_node->GetStringAttribute(ax::mojom::StringAttribute::kDescription);
|
||||
@@ -942,34 +955,31 @@ void CollectAncestorRoles(
|
||||
base::SysUTF8ToNSString(description)];
|
||||
}
|
||||
|
||||
auto announcement = std::make_unique<ui::AXAnnouncementSpec>();
|
||||
announcement->announcement =
|
||||
base::scoped_nsobject<NSString>([announcementText retain]);
|
||||
announcement->window =
|
||||
base::scoped_nsobject<NSWindow>([[self AXWindow] retain]);
|
||||
announcement->is_polite = liveStatus != "assertive";
|
||||
return announcement;
|
||||
AXAnnouncementSpec* spec = [[AXAnnouncementSpec alloc] init];
|
||||
spec.announcement = announcementText;
|
||||
spec.window = [self AXWindow];
|
||||
spec.polite = liveStatus != "assertive";
|
||||
return spec;
|
||||
}
|
||||
|
||||
- (void)scheduleLiveRegionAnnouncement:
|
||||
(std::unique_ptr<ui::AXAnnouncementSpec>)announcement {
|
||||
- (void)scheduleLiveRegionAnnouncement:(AXAnnouncementSpec*)announcement {
|
||||
if (_pendingAnnouncement) {
|
||||
// An announcement is already in flight, so just reset the contents. This is
|
||||
// threadsafe because the dispatch is on the main queue.
|
||||
_pendingAnnouncement = std::move(announcement);
|
||||
_pendingAnnouncement = announcement;
|
||||
return;
|
||||
}
|
||||
|
||||
_pendingAnnouncement = std::move(announcement);
|
||||
_pendingAnnouncement = announcement;
|
||||
dispatch_after(
|
||||
kLiveRegionDebounceMillis * NSEC_PER_MSEC, dispatch_get_main_queue(), ^{
|
||||
if (!_pendingAnnouncement) {
|
||||
if (!self->_pendingAnnouncement) {
|
||||
return;
|
||||
}
|
||||
PostAnnouncementNotification(_pendingAnnouncement->announcement,
|
||||
_pendingAnnouncement->window,
|
||||
_pendingAnnouncement->is_polite);
|
||||
_pendingAnnouncement.reset();
|
||||
PostAnnouncementNotification(self->_pendingAnnouncement.announcement,
|
||||
self->_pendingAnnouncement.window,
|
||||
self->_pendingAnnouncement.polite);
|
||||
self->_pendingAnnouncement = nil;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -983,8 +993,9 @@ void CollectAncestorRoles(
|
||||
}
|
||||
|
||||
- (id)accessibilityHitTest:(NSPoint)point {
|
||||
if (!NSPointInRect(point, [self boundsInScreen]))
|
||||
if (!NSPointInRect(point, self.boundsInScreen)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
for (id child in [[self AXChildren] reverseObjectEnumerator]) {
|
||||
if (!NSPointInRect(point, [child accessibilityFrame]))
|
||||
@@ -1012,8 +1023,7 @@ void CollectAncestorRoles(
|
||||
if (!_node)
|
||||
return @[];
|
||||
|
||||
base::scoped_nsobject<NSMutableArray> axActions(
|
||||
[[NSMutableArray alloc] init]);
|
||||
NSMutableArray* axActions = [NSMutableArray array];
|
||||
const ActionList& action_list = GetActionList();
|
||||
|
||||
// VoiceOver expects the "press" action to be first. Note that some roles
|
||||
@@ -1027,7 +1037,7 @@ void CollectAncestorRoles(
|
||||
if (AlsoUseShowMenuActionForDefaultAction(*_node))
|
||||
[axActions addObject:NSAccessibilityShowMenuAction];
|
||||
|
||||
return axActions.autorelease();
|
||||
return axActions;
|
||||
}
|
||||
|
||||
- (void)accessibilityPerformAction:(NSString*)action {
|
||||
@@ -1092,10 +1102,9 @@ void CollectAncestorRoles(
|
||||
];
|
||||
// Required for all text, including protected textfields.
|
||||
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();
|
||||
switch (role) {
|
||||
case ax::mojom::Role::kTextField:
|
||||
@@ -1292,7 +1301,7 @@ void CollectAncestorRoles(
|
||||
if ([self titleUIElement])
|
||||
[axAttributes addObject:NSAccessibilityTitleUIElementAttribute];
|
||||
|
||||
return axAttributes.autorelease();
|
||||
return axAttributes;
|
||||
}
|
||||
|
||||
- (NSArray*)accessibilityParameterizedAttributeNames {
|
||||
@@ -1342,8 +1351,11 @@ void CollectAncestorRoles(
|
||||
return nil; // Return nil when detached. Even for ax::mojom::Role.
|
||||
|
||||
SEL selector = NSSelectorFromString(attribute);
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
if ([self respondsToSelector:selector])
|
||||
return [self performSelector:selector];
|
||||
#pragma clang diagnostic pop
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -1353,8 +1365,11 @@ void CollectAncestorRoles(
|
||||
return nil;
|
||||
|
||||
SEL selector = NSSelectorFromString([attribute stringByAppendingString:@":"]);
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
if ([self respondsToSelector:selector])
|
||||
return [self performSelector:selector withObject:parameter];
|
||||
#pragma clang diagnostic pop
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -1524,7 +1539,7 @@ void CollectAncestorRoles(
|
||||
if (![self instanceActive])
|
||||
return nil;
|
||||
|
||||
NSMutableArray* elements = [[[NSMutableArray alloc] init] autorelease];
|
||||
NSMutableArray* elements = [NSMutableArray array];
|
||||
for (ui::AXNodeID id :
|
||||
_node->GetIntListAttribute(ax::mojom::IntListAttribute::kDetailsIds)) {
|
||||
AXPlatformNodeCocoa* node = [self fromNodeID:id];
|
||||
@@ -1532,14 +1547,14 @@ void CollectAncestorRoles(
|
||||
[elements addObject:node];
|
||||
}
|
||||
|
||||
return [elements count] ? elements : nil;
|
||||
return elements.count ? elements : nil;
|
||||
}
|
||||
|
||||
- (NSArray*)AXDOMClassList {
|
||||
if (![self instanceActive])
|
||||
return nil;
|
||||
|
||||
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
|
||||
NSMutableArray* ret = [NSMutableArray array];
|
||||
|
||||
std::string classes;
|
||||
if (_node->GetStringAttribute(ax::mojom::StringAttribute::kClassName,
|
||||
@@ -1673,9 +1688,7 @@ void CollectAncestorRoles(
|
||||
if (!container)
|
||||
return nil;
|
||||
|
||||
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
|
||||
[ret addObject:container->GetNativeViewAccessible()];
|
||||
return ret;
|
||||
return @[ container->GetNativeViewAccessible() ];
|
||||
}
|
||||
|
||||
- (NSString*)AXPopupValue {
|
||||
@@ -1877,7 +1890,7 @@ void CollectAncestorRoles(
|
||||
const auto checkedState = static_cast<ax::mojom::CheckedState>(
|
||||
_node->GetIntAttribute(ax::mojom::IntAttribute::kCheckedState));
|
||||
if (checkedState == ax::mojom::CheckedState::kTrue) {
|
||||
return @"\xE2\x9C\x93"; // UTF-8 for unicode 0x2713, "check mark"
|
||||
return @"\u2713"; // "check mark"
|
||||
}
|
||||
|
||||
return @"";
|
||||
@@ -2009,8 +2022,8 @@ void CollectAncestorRoles(
|
||||
// We potentially need to add text attributes to the whole text content
|
||||
// because a spelling mistake might start or end outside the given range.
|
||||
NSMutableAttributedString* attributedTextContent =
|
||||
[[[NSMutableAttributedString alloc]
|
||||
initWithString:base::SysUTF16ToNSString(textContent)] autorelease];
|
||||
[[NSMutableAttributedString alloc]
|
||||
initWithString:base::SysUTF16ToNSString(textContent)];
|
||||
if (!_node->IsText()) {
|
||||
AXRange axRange(_node->GetDelegate()->CreateTextPositionAt(0),
|
||||
_node->GetDelegate()->CreateTextPositionAt(
|
||||
@@ -2027,11 +2040,12 @@ void CollectAncestorRoles(
|
||||
return nil;
|
||||
|
||||
NSString* text = base::SysUTF16ToNSString(axRange.GetText());
|
||||
if ([text length] == 0)
|
||||
if (text.length == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableAttributedString* attributedText =
|
||||
[[[NSMutableAttributedString alloc] initWithString:text] autorelease];
|
||||
[[NSMutableAttributedString alloc] initWithString:text];
|
||||
// Currently, we only decorate the attributed string with misspelling
|
||||
// and annotation information.
|
||||
[self addTextAnnotationsIn:&axRange to:attributedText];
|
||||
@@ -2094,7 +2108,7 @@ void CollectAncestorRoles(
|
||||
if (![self instanceActive])
|
||||
return nil;
|
||||
|
||||
// Mac OS X wants static text exposed in AXValue.
|
||||
// macOS wants static text exposed in AXValue.
|
||||
if (ui::IsNameExposedInAXValueForRole([self internalRole]))
|
||||
return @"";
|
||||
|
||||
@@ -2345,7 +2359,7 @@ void CollectAncestorRoles(
|
||||
NSArray* rows = [self accessibilityRows];
|
||||
// accessibilityRows returns an empty array unless instanceActive does,
|
||||
// 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) {
|
||||
if ([[row accessibilitySelected] boolValue]) {
|
||||
[selectedRows addObject:row];
|
||||
@@ -2362,7 +2376,7 @@ void CollectAncestorRoles(
|
||||
ui::AXPlatformNodeDelegate* delegate = _node->GetDelegate();
|
||||
DCHECK(delegate);
|
||||
|
||||
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
|
||||
NSMutableArray* ret = [NSMutableArray array];
|
||||
|
||||
// If this is a table, return all column headers.
|
||||
ax::mojom::Role role = _node->GetRole();
|
||||
@@ -2522,30 +2536,28 @@ void CollectAncestorRoles(
|
||||
if (!_node)
|
||||
return 0;
|
||||
|
||||
return
|
||||
[[self AXLineForIndex:[NSNumber numberWithInteger:index]] integerValue];
|
||||
return [[self AXLineForIndex:@(index)] integerValue];
|
||||
}
|
||||
|
||||
- (NSRange)accessibilityRangeForIndex:(NSInteger)index {
|
||||
if (!_node)
|
||||
return NSMakeRange(0, 0);
|
||||
|
||||
return [[self AXRangeForIndex:[NSNumber numberWithInteger:index]] rangeValue];
|
||||
return [[self AXRangeForIndex:@(index)] rangeValue];
|
||||
}
|
||||
|
||||
- (NSRange)accessibilityStyleRangeForIndex:(NSInteger)index {
|
||||
if (!_node)
|
||||
return NSMakeRange(0, 0);
|
||||
|
||||
return [[self AXStyleRangeForIndex:[NSNumber numberWithInteger:index]]
|
||||
rangeValue];
|
||||
return [[self AXStyleRangeForIndex:@(index)] rangeValue];
|
||||
}
|
||||
|
||||
- (NSRange)accessibilityRangeForLine:(NSInteger)line {
|
||||
if (!_node)
|
||||
return NSMakeRange(0, 0);
|
||||
|
||||
return [[self AXRangeForLine:[NSNumber numberWithInteger:line]] rangeValue];
|
||||
return [[self AXRangeForLine:@(line)] rangeValue];
|
||||
}
|
||||
|
||||
- (NSRange)accessibilityRangeForPosition:(NSPoint)point {
|
||||
@@ -2746,30 +2758,27 @@ void CollectAncestorRoles(
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSDictionary* createMathSubSupScriptsPair(
|
||||
AXPlatformNodeCocoa* subscript,
|
||||
AXPlatformNodeCocoa* superscript) {
|
||||
AXPlatformNodeCocoa* nodes[2];
|
||||
NSString* keys[2];
|
||||
NSUInteger count = 0;
|
||||
namespace {
|
||||
|
||||
NSDictionary* CreateMathSubSupScriptsPair(AXPlatformNodeCocoa* subscript,
|
||||
AXPlatformNodeCocoa* superscript) {
|
||||
NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
|
||||
if (subscript) {
|
||||
nodes[count] = subscript;
|
||||
keys[count] = NSAccessibilityMathSubscriptAttribute;
|
||||
count++;
|
||||
dictionary[NSAccessibilityMathSubscriptAttribute] = subscript;
|
||||
}
|
||||
if (superscript) {
|
||||
nodes[count] = superscript;
|
||||
keys[count] = NSAccessibilityMathSuperscriptAttribute;
|
||||
count++;
|
||||
dictionary[NSAccessibilityMathSuperscriptAttribute] = superscript;
|
||||
}
|
||||
return [[NSDictionary alloc] initWithObjects:nodes forKeys:keys count:count];
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
- (NSArray*)AXMathPostscripts {
|
||||
if (![self instanceActive] ||
|
||||
_node->GetRole() != ax::mojom::Role::kMathMLMultiscripts)
|
||||
return nil;
|
||||
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
|
||||
NSMutableArray* ret = [NSMutableArray array];
|
||||
bool foundBaseElement = false;
|
||||
AXPlatformNodeCocoa* subscript = nullptr;
|
||||
for (AXPlatformNodeCocoa* child in [self AXChildren]) {
|
||||
@@ -2784,7 +2793,7 @@ static NSDictionary* createMathSubSupScriptsPair(
|
||||
continue;
|
||||
}
|
||||
AXPlatformNodeCocoa* superscript = child;
|
||||
[ret addObject:createMathSubSupScriptsPair(subscript, superscript)];
|
||||
[ret addObject:CreateMathSubSupScriptsPair(subscript, superscript)];
|
||||
subscript = nullptr;
|
||||
}
|
||||
return [ret count] ? ret : nil;
|
||||
@@ -2794,7 +2803,7 @@ static NSDictionary* createMathSubSupScriptsPair(
|
||||
if (![self instanceActive] ||
|
||||
_node->GetRole() != ax::mojom::Role::kMathMLMultiscripts)
|
||||
return nil;
|
||||
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
|
||||
NSMutableArray* ret = [NSMutableArray array];
|
||||
bool foundPrescriptDelimiter = false;
|
||||
AXPlatformNodeCocoa* subscript = nullptr;
|
||||
for (AXPlatformNodeCocoa* child in [self AXChildren]) {
|
||||
@@ -2808,7 +2817,7 @@ static NSDictionary* createMathSubSupScriptsPair(
|
||||
continue;
|
||||
}
|
||||
AXPlatformNodeCocoa* superscript = child;
|
||||
[ret addObject:createMathSubSupScriptsPair(subscript, superscript)];
|
||||
[ret addObject:CreateMathSubSupScriptsPair(subscript, superscript)];
|
||||
subscript = nullptr;
|
||||
}
|
||||
return [ret count] ? ret : nil;
|
||||
|
@@ -7,15 +7,17 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/component_export.h"
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
#include "ui/accessibility/platform/ax_platform_node_base.h"
|
||||
|
||||
@class AXPlatformNodeCocoa;
|
||||
|
||||
namespace ui {
|
||||
|
||||
class AXPlatformNodeMac : public AXPlatformNodeBase {
|
||||
class COMPONENT_EXPORT(AX_PLATFORM) AXPlatformNodeMac
|
||||
: public AXPlatformNodeBase {
|
||||
public:
|
||||
~AXPlatformNodeMac() override;
|
||||
AXPlatformNodeMac(const AXPlatformNodeMac&) = delete;
|
||||
@@ -30,15 +32,9 @@ class AXPlatformNodeMac : public AXPlatformNodeBase {
|
||||
void Destroy() override;
|
||||
bool IsPlatformCheckable() const override;
|
||||
|
||||
AXPlatformNodeCocoa* GetNativeWrapper() const { return native_node_.get(); }
|
||||
|
||||
base::scoped_nsobject<AXPlatformNodeCocoa> ReleaseNativeWrapper() {
|
||||
return std::move(native_node_);
|
||||
}
|
||||
|
||||
void SetNativeWrapper(AXPlatformNodeCocoa* native_node) {
|
||||
return native_node_.reset(native_node);
|
||||
}
|
||||
AXPlatformNodeCocoa* GetNativeWrapper() const;
|
||||
AXPlatformNodeCocoa* ReleaseNativeWrapper();
|
||||
void SetNativeWrapper(AXPlatformNodeCocoa* native_node);
|
||||
|
||||
protected:
|
||||
AXPlatformNodeMac();
|
||||
@@ -48,10 +44,11 @@ class AXPlatformNodeMac : public AXPlatformNodeBase {
|
||||
PlatformAttributeList* attributes) override;
|
||||
|
||||
private:
|
||||
base::scoped_nsobject<AXPlatformNodeCocoa> native_node_;
|
||||
|
||||
friend AXPlatformNode* AXPlatformNode::Create(
|
||||
AXPlatformNodeDelegate* delegate);
|
||||
|
||||
struct ObjCStorage;
|
||||
std::unique_ptr<ObjCStorage> objc_storage_;
|
||||
};
|
||||
|
||||
// Convenience function to determine whether an internal object role should
|
||||
|
@@ -7,6 +7,10 @@
|
||||
#include "base/strings/sys_string_conversions.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 {
|
||||
|
||||
using RoleMap = std::map<ax::mojom::Role, NSString*>;
|
||||
@@ -59,15 +63,19 @@ AXPlatformNode* AXPlatformNode::FromNativeViewAccessible(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AXPlatformNodeMac::AXPlatformNodeMac() = default;
|
||||
struct AXPlatformNodeMac::ObjCStorage {
|
||||
AXPlatformNodeCocoa* __strong native_node;
|
||||
};
|
||||
|
||||
AXPlatformNodeMac::AXPlatformNodeMac()
|
||||
: objc_storage_(std::make_unique<ObjCStorage>()) {}
|
||||
AXPlatformNodeMac::~AXPlatformNodeMac() = default;
|
||||
|
||||
void AXPlatformNodeMac::Destroy() {
|
||||
if (native_node_) {
|
||||
[native_node_ detach];
|
||||
// Also, nullify smart pointer to make accidental use-after-free impossible.
|
||||
native_node_.reset();
|
||||
if (objc_storage_->native_node) {
|
||||
[objc_storage_->native_node detach];
|
||||
// Also, clear the pointer to make accidental use-after-free impossible.
|
||||
objc_storage_->native_node = nil;
|
||||
}
|
||||
AXPlatformNodeBase::Destroy();
|
||||
}
|
||||
@@ -83,10 +91,26 @@ bool AXPlatformNodeMac::IsPlatformCheckable() const {
|
||||
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() {
|
||||
if (!native_node_)
|
||||
native_node_.reset([[AXPlatformNodeCocoa alloc] initWithNode:this]);
|
||||
return native_node_.get();
|
||||
if (!objc_storage_->native_node) {
|
||||
objc_storage_->native_node =
|
||||
[[AXPlatformNodeCocoa alloc] initWithNode:this];
|
||||
}
|
||||
return objc_storage_->native_node;
|
||||
}
|
||||
|
||||
void AXPlatformNodeMac::NotifyAccessibilityEvent(ax::mojom::Event event_type) {
|
||||
@@ -98,8 +122,9 @@ void AXPlatformNodeMac::NotifyAccessibilityEvent(ax::mojom::Event event_type) {
|
||||
// regular NSAccessibility notification system.
|
||||
if (event_type == ax::mojom::Event::kAlert ||
|
||||
event_type == ax::mojom::Event::kLiveRegionChanged) {
|
||||
if (auto announcement = [native_node_ announcementForEvent:event_type]) {
|
||||
[native_node_ scheduleLiveRegionAnnouncement:std::move(announcement)];
|
||||
if (AXAnnouncementSpec* announcement =
|
||||
[objc_storage_->native_node announcementForEvent:event_type]) {
|
||||
[objc_storage_->native_node scheduleLiveRegionAnnouncement:announcement];
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -107,14 +132,14 @@ void AXPlatformNodeMac::NotifyAccessibilityEvent(ax::mojom::Event event_type) {
|
||||
ax::mojom::Role role = GetRole();
|
||||
if (ui::IsMenuItem(role)) {
|
||||
// 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;
|
||||
} else if (ui::IsListItem(role)) {
|
||||
if (const AXPlatformNodeBase* container = GetSelectionContainer()) {
|
||||
if (container->GetRole() == ax::mojom::Role::kListBox &&
|
||||
!container->HasState(ax::mojom::State::kMultiselectable) &&
|
||||
GetDelegate()->GetFocus() == GetNativeViewAccessible()) {
|
||||
NotifyMacEvent(native_node_, ax::mojom::Event::kFocus);
|
||||
NotifyMacEvent(objc_storage_->native_node, ax::mojom::Event::kFocus);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -123,12 +148,12 @@ void AXPlatformNodeMac::NotifyAccessibilityEvent(ax::mojom::Event event_type) {
|
||||
|
||||
// Otherwise, use mappings between ax::mojom::Event and NSAccessibility
|
||||
// notifications from the EventMap above.
|
||||
NotifyMacEvent(native_node_, event_type);
|
||||
NotifyMacEvent(objc_storage_->native_node, event_type);
|
||||
}
|
||||
|
||||
void AXPlatformNodeMac::AnnounceText(const std::u16string& text) {
|
||||
PostAnnouncementNotification(base::SysUTF16ToNSString(text),
|
||||
[native_node_ AXWindow], false);
|
||||
[objc_storage_->native_node AXWindow], false);
|
||||
}
|
||||
|
||||
bool IsNameExposedInAXValueForRole(ax::mojom::Role role) {
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#define UI_ACCESSIBILITY_PLATFORM_AX_PRIVATE_WEBKIT_CONSTANTS_MAC_H_
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "base/component_export.h"
|
||||
|
||||
namespace ui {
|
||||
|
@@ -4,6 +4,10 @@
|
||||
|
||||
#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 {
|
||||
|
||||
const char* ToString(AXTextStateChangeType type) {
|
||||
|
@@ -10,9 +10,9 @@
|
||||
#include "base/component_export.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
|
||||
// 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.
|
||||
COMPONENT_EXPORT(AX_PLATFORM)
|
||||
id AXTextMarkerFrom(const AXPlatformNodeCocoa* anchor,
|
||||
id AXTextMarkerFrom(AXPlatformNodeCocoa* anchor,
|
||||
int offset,
|
||||
ax::mojom::TextAffinity affinity);
|
||||
|
||||
|
@@ -4,45 +4,51 @@
|
||||
|
||||
#include "ui/accessibility/platform/ax_utils_mac.h"
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include "base/mac/scoped_cftyperef.h"
|
||||
#include "ui/accessibility/ax_range.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_delegate.h"
|
||||
|
||||
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
||||
#error "This file requires ARC support."
|
||||
#endif
|
||||
|
||||
namespace ui {
|
||||
|
||||
bool IsAXTextMarker(id object) {
|
||||
if (object == nil)
|
||||
if (object == nil) {
|
||||
return false;
|
||||
|
||||
AXTextMarkerRef cf_text_marker = static_cast<AXTextMarkerRef>(object);
|
||||
DCHECK(cf_text_marker);
|
||||
return CFGetTypeID(cf_text_marker) == AXTextMarkerGetTypeID();
|
||||
}
|
||||
return CFGetTypeID((__bridge CFTypeRef)object) == AXTextMarkerGetTypeID();
|
||||
}
|
||||
|
||||
bool IsAXTextMarkerRange(id object) {
|
||||
if (object == nil)
|
||||
if (object == nil) {
|
||||
return false;
|
||||
|
||||
AXTextMarkerRangeRef cf_marker_range =
|
||||
static_cast<AXTextMarkerRangeRef>(object);
|
||||
DCHECK(cf_marker_range);
|
||||
return CFGetTypeID(cf_marker_range) == AXTextMarkerRangeGetTypeID();
|
||||
}
|
||||
return CFGetTypeID((__bridge CFTypeRef)object) ==
|
||||
AXTextMarkerRangeGetTypeID();
|
||||
}
|
||||
|
||||
AXPlatformNodeDelegate::AXPosition AXTextMarkerToAXPosition(id text_marker) {
|
||||
if (!IsAXTextMarker(text_marker))
|
||||
if (!IsAXTextMarker(text_marker)) {
|
||||
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) !=
|
||||
sizeof(AXPlatformNodeDelegate::SerializedPosition))
|
||||
sizeof(AXPlatformNodeDelegate::SerializedPosition)) {
|
||||
return AXNodePosition::CreateNullPosition();
|
||||
}
|
||||
|
||||
const UInt8* source_buffer = AXTextMarkerGetBytePtr(cf_text_marker);
|
||||
if (!source_buffer)
|
||||
if (!source_buffer) {
|
||||
return AXNodePosition::CreateNullPosition();
|
||||
}
|
||||
|
||||
return AXNodePosition::Unserialize(
|
||||
*reinterpret_cast<const AXPlatformNodeDelegate::SerializedPosition*>(
|
||||
@@ -56,20 +62,21 @@ AXPlatformNodeDelegate::AXRange AXTextMarkerRangeToAXRange(
|
||||
}
|
||||
|
||||
AXTextMarkerRangeRef cf_marker_range =
|
||||
static_cast<AXTextMarkerRangeRef>(text_marker_range);
|
||||
(__bridge AXTextMarkerRangeRef)text_marker_range;
|
||||
|
||||
base::ScopedCFTypeRef<AXTextMarkerRef> start_marker(
|
||||
AXTextMarkerRangeCopyStartMarker(cf_marker_range));
|
||||
base::ScopedCFTypeRef<AXTextMarkerRef> end_marker(
|
||||
AXTextMarkerRangeCopyEndMarker(cf_marker_range));
|
||||
if (!start_marker.get() || !end_marker.get())
|
||||
id start_marker =
|
||||
CFBridgingRelease(AXTextMarkerRangeCopyStartMarker(cf_marker_range));
|
||||
id end_marker =
|
||||
CFBridgingRelease(AXTextMarkerRangeCopyEndMarker(cf_marker_range));
|
||||
if (!start_marker || !end_marker) {
|
||||
return AXPlatformNodeDelegate::AXRange();
|
||||
}
|
||||
|
||||
// |AXPlatformNodeDelegate::AXRange| takes ownership of its anchor and focus.
|
||||
AXPlatformNodeDelegate::AXPosition anchor =
|
||||
AXTextMarkerToAXPosition(static_cast<id>(start_marker.get()));
|
||||
AXTextMarkerToAXPosition(start_marker);
|
||||
AXPlatformNodeDelegate::AXPosition focus =
|
||||
AXTextMarkerToAXPosition(static_cast<id>(end_marker.get()));
|
||||
AXTextMarkerToAXPosition(end_marker);
|
||||
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
|
||||
// buffer given to it.
|
||||
AXPlatformNodeDelegate::SerializedPosition serialized = position->Serialize();
|
||||
AXTextMarkerRef cf_text_marker = AXTextMarkerCreate(
|
||||
return CFBridgingRelease(AXTextMarkerCreate(
|
||||
kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized),
|
||||
sizeof(AXPlatformNodeDelegate::SerializedPosition));
|
||||
return [static_cast<id>(cf_text_marker) autorelease];
|
||||
sizeof(AXPlatformNodeDelegate::SerializedPosition)));
|
||||
}
|
||||
|
||||
id AXRangeToAXTextMarkerRange(AXPlatformNodeDelegate::AXRange range) {
|
||||
@@ -96,15 +102,14 @@ id AXRangeToAXTextMarkerRange(AXPlatformNodeDelegate::AXRange range) {
|
||||
kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized_focus),
|
||||
sizeof(AXPlatformNodeDelegate::SerializedPosition)));
|
||||
|
||||
AXTextMarkerRangeRef cf_marker_range =
|
||||
AXTextMarkerRangeCreate(kCFAllocatorDefault, start_marker, end_marker);
|
||||
return [static_cast<id>(cf_marker_range) autorelease];
|
||||
return CFBridgingRelease(
|
||||
AXTextMarkerRangeCreate(kCFAllocatorDefault, start_marker, end_marker));
|
||||
}
|
||||
|
||||
id AXTextMarkerFrom(const AXPlatformNodeCocoa* anchor,
|
||||
id AXTextMarkerFrom(AXPlatformNodeCocoa* anchor,
|
||||
int offset,
|
||||
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::AXPosition position =
|
||||
anchor_node->CreateTextPositionAt(offset, affinity);
|
||||
@@ -112,20 +117,19 @@ id AXTextMarkerFrom(const AXPlatformNodeCocoa* anchor,
|
||||
}
|
||||
|
||||
id AXTextMarkerRangeFrom(id start_textmarker, id end_textmarker) {
|
||||
AXTextMarkerRangeRef cf_marker_range = AXTextMarkerRangeCreate(
|
||||
kCFAllocatorDefault, static_cast<AXTextMarkerRef>(start_textmarker),
|
||||
static_cast<AXTextMarkerRef>(end_textmarker));
|
||||
return [static_cast<id>(cf_marker_range) autorelease];
|
||||
return CFBridgingRelease(AXTextMarkerRangeCreate(
|
||||
kCFAllocatorDefault, (__bridge AXTextMarkerRef)start_textmarker,
|
||||
(__bridge AXTextMarkerRef)end_textmarker));
|
||||
}
|
||||
|
||||
id AXTextMarkerRangeStart(id text_marker_range) {
|
||||
return static_cast<id>(AXTextMarkerRangeCopyStartMarker(
|
||||
static_cast<AXTextMarkerRangeRef>(text_marker_range)));
|
||||
return CFBridgingRelease(AXTextMarkerRangeCopyStartMarker(
|
||||
(__bridge AXTextMarkerRangeRef)text_marker_range));
|
||||
}
|
||||
|
||||
id AXTextMarkerRangeEnd(id text_marker_range) {
|
||||
return static_cast<id>(AXTextMarkerRangeCopyEndMarker(
|
||||
static_cast<AXTextMarkerRangeRef>(text_marker_range)));
|
||||
return CFBridgingRelease(AXTextMarkerRangeCopyEndMarker(
|
||||
(__bridge AXTextMarkerRangeRef)text_marker_range));
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
@@ -9,6 +9,10 @@
|
||||
#include "base/memory/raw_ptr.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 {
|
||||
|
||||
class AXElementWrapper;
|
||||
@@ -107,7 +111,7 @@ class COMPONENT_EXPORT(AX_PLATFORM) AXCallStatementInvoker final {
|
||||
gfx::NativeViewAccessible LineIndexToNode(
|
||||
const std::u16string line_index) const;
|
||||
|
||||
const id node;
|
||||
id __strong node;
|
||||
|
||||
// Map between AXUIElement objects and their DOMIds/accessible tree
|
||||
// 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_property_node.h"
|
||||
|
||||
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
||||
#error "This file requires ARC support."
|
||||
#endif
|
||||
|
||||
namespace ui {
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// accesible elements and their DOM ids and line numbers.
|
||||
// accessible elements and their DOM ids and line numbers.
|
||||
if (!target)
|
||||
target = indexer_->NodeBy(property_node.name_or_value);
|
||||
|
||||
@@ -165,7 +169,7 @@ AXOptionalNSObject AXCallStatementInvoker::InvokeFor(
|
||||
}
|
||||
|
||||
if (AXElementWrapper::IsValidElement(target))
|
||||
return InvokeForAXElement({target}, property_node);
|
||||
return InvokeForAXElement(AXElementWrapper{target}, property_node);
|
||||
|
||||
if (IsAXTextMarkerRange(target)) {
|
||||
return InvokeForAXTextMarkerRange(target, property_node);
|
||||
@@ -283,7 +287,7 @@ AXOptionalNSObject AXCallStatementInvoker::InvokeForAXElement(
|
||||
optional_arg_selector
|
||||
? ax_element.Invoke<BOOL, SEL>(selector, *optional_arg_selector)
|
||||
: ax_element.Invoke<BOOL>(selector);
|
||||
return AXOptionalNSObject([NSNumber numberWithBool:return_value]);
|
||||
return AXOptionalNSObject(@(return_value));
|
||||
}
|
||||
|
||||
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_optional.h"
|
||||
|
||||
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
||||
#error "This file requires ARC support."
|
||||
#endif
|
||||
|
||||
namespace ui {
|
||||
|
||||
// Optional tri-state id object.
|
||||
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 {
|
||||
public:
|
||||
// Returns true if the object is either NSAccessibilityElement or
|
||||
@@ -39,7 +43,7 @@ class COMPONENT_EXPORT(AX_PLATFORM) AXElementWrapper final {
|
||||
// BrowserAccessibilityCocoa).
|
||||
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
|
||||
// AXUIElement.
|
||||
@@ -150,7 +154,7 @@ class COMPONENT_EXPORT(AX_PLATFORM) AXElementWrapper final {
|
||||
// Converts the given value and the error object into AXOptional object.
|
||||
AXOptionalNSObject ToOptional(id, AXError, const std::string& message) const;
|
||||
|
||||
const id node_;
|
||||
id __strong node_;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
@@ -4,16 +4,25 @@
|
||||
|
||||
#include "ui/accessibility/platform/inspect/ax_element_wrapper_mac.h"
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "base/apple/bridging.h"
|
||||
#include "base/containers/fixed_flat_set.h"
|
||||
#include "base/debug/stack_trace.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/scoped_cftyperef.h"
|
||||
#include "base/strings/pattern.h"
|
||||
#include "base/strings/sys_string_conversions.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
|
||||
// macOS 10.10 - Use the NSAccessibility protocol methods instead (see
|
||||
// NSAccessibilityProtocols.h
|
||||
@@ -22,8 +31,6 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
using base::SysNSStringToUTF8;
|
||||
|
||||
constexpr char kUnsupportedObject[] =
|
||||
"Only AXUIElementRef and BrowserAccessibilityCocoa are supported.";
|
||||
|
||||
@@ -32,20 +39,24 @@ bool AXElementWrapper::IsValidElement(const id node) {
|
||||
return AXElementWrapper(node).IsValidElement();
|
||||
}
|
||||
|
||||
// static
|
||||
bool AXElementWrapper::IsNSAccessibilityElement(const id node) {
|
||||
return AXElementWrapper(node).IsNSAccessibilityElement();
|
||||
}
|
||||
|
||||
// static
|
||||
bool AXElementWrapper::IsAXUIElement(const id node) {
|
||||
return AXElementWrapper(node).IsAXUIElement();
|
||||
}
|
||||
|
||||
// static
|
||||
NSArray* AXElementWrapper::ChildrenOf(const id node) {
|
||||
return AXElementWrapper(node).Children();
|
||||
}
|
||||
|
||||
// Returns DOM id of a given node (either AXUIElement or
|
||||
// BrowserAccessibilityCocoa).
|
||||
// static
|
||||
std::string AXElementWrapper::DOMIdOf(const id node) {
|
||||
return AXElementWrapper(node).DOMId();
|
||||
}
|
||||
@@ -59,7 +70,7 @@ bool AXElementWrapper::IsNSAccessibilityElement() const {
|
||||
}
|
||||
|
||||
bool AXElementWrapper::IsAXUIElement() const {
|
||||
return CFGetTypeID(node_) == AXUIElementGetTypeID();
|
||||
return CFGetTypeID((__bridge CFTypeRef)node_) == AXUIElementGetTypeID();
|
||||
}
|
||||
|
||||
id AXElementWrapper::AsId() const {
|
||||
@@ -76,11 +87,13 @@ NSArray* AXElementWrapper::Children() const {
|
||||
return [node_ children];
|
||||
|
||||
if (IsAXUIElement()) {
|
||||
CFTypeRef children_ref;
|
||||
if ((AXUIElementCopyAttributeValue(static_cast<AXUIElementRef>(node_),
|
||||
kAXChildrenAttribute, &children_ref)) ==
|
||||
kAXErrorSuccess)
|
||||
return static_cast<NSArray*>(children_ref);
|
||||
base::ScopedCFTypeRef<CFTypeRef> children_ref;
|
||||
if ((AXUIElementCopyAttributeValue(
|
||||
(__bridge AXUIElementRef)node_, kAXChildrenAttribute,
|
||||
children_ref.InitializeInto())) == kAXErrorSuccess) {
|
||||
return base::apple::CFToNSOwnershipCast(
|
||||
(CFArrayRef)children_ref.release());
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -101,11 +114,11 @@ NSSize AXElementWrapper::Size() const {
|
||||
}
|
||||
|
||||
id value = *GetAttributeValue(NSAccessibilitySizeAttribute);
|
||||
if (value && CFGetTypeID(value) == AXValueGetTypeID()) {
|
||||
AXValueType type = AXValueGetType(static_cast<AXValueRef>(value));
|
||||
if (value && CFGetTypeID((__bridge CFTypeRef)value) == AXValueGetTypeID()) {
|
||||
AXValueType type = AXValueGetType((__bridge AXValueRef)value);
|
||||
if (type == kAXValueCGSizeType) {
|
||||
NSSize size;
|
||||
if (AXValueGetValue(static_cast<AXValueRef>(value), type, &size)) {
|
||||
if (AXValueGetValue((__bridge AXValueRef)value, type, &size)) {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
@@ -118,35 +131,36 @@ NSPoint AXElementWrapper::Position() const {
|
||||
return [node_ accessibilityFrame].origin;
|
||||
}
|
||||
|
||||
if (!IsAXUIElement()) {
|
||||
NOTREACHED()
|
||||
<< "Only AXUIElementRef and BrowserAccessibilityCocoa are supported.";
|
||||
return NSMakePoint(0, 0);
|
||||
}
|
||||
|
||||
id value = *GetAttributeValue(NSAccessibilityPositionAttribute);
|
||||
if (value && CFGetTypeID(value) == AXValueGetTypeID()) {
|
||||
AXValueType type = AXValueGetType(static_cast<AXValueRef>(value));
|
||||
if (type == kAXValueCGPointType) {
|
||||
NSPoint point;
|
||||
if (AXValueGetValue(static_cast<AXValueRef>(value), type, &point)) {
|
||||
return point;
|
||||
if (IsAXUIElement()) {
|
||||
id value = *GetAttributeValue(NSAccessibilityPositionAttribute);
|
||||
if (value && CFGetTypeID((__bridge CFTypeRef)value) == AXValueGetTypeID()) {
|
||||
AXValueType type = AXValueGetType((__bridge AXValueRef)value);
|
||||
if (type == kAXValueCGPointType) {
|
||||
NSPoint point;
|
||||
if (AXValueGetValue((__bridge AXValueRef)value, type, &point)) {
|
||||
return point;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NOTREACHED()
|
||||
<< "Only AXUIElementRef and BrowserAccessibilityCocoa are supported.";
|
||||
return NSMakePoint(0, 0);
|
||||
}
|
||||
|
||||
NSArray* AXElementWrapper::AttributeNames() const {
|
||||
if (IsNSAccessibilityElement())
|
||||
if (IsNSAccessibilityElement()) {
|
||||
return [node_ accessibilityAttributeNames];
|
||||
}
|
||||
|
||||
if (IsAXUIElement()) {
|
||||
CFArrayRef attributes_ref;
|
||||
base::ScopedCFTypeRef<CFArrayRef> attributes_ref;
|
||||
AXError result = AXUIElementCopyAttributeNames(
|
||||
static_cast<AXUIElementRef>(node_), &attributes_ref);
|
||||
if (AXSuccess(result, "AXAttributeNamesOf"))
|
||||
return static_cast<NSArray*>(attributes_ref);
|
||||
(__bridge AXUIElementRef)node_, attributes_ref.InitializeInto());
|
||||
if (AXSuccess(result, "AXAttributeNamesOf")) {
|
||||
return base::apple::CFToNSOwnershipCast(attributes_ref.release());
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -156,15 +170,17 @@ NSArray* AXElementWrapper::AttributeNames() const {
|
||||
}
|
||||
|
||||
NSArray* AXElementWrapper::ParameterizedAttributeNames() const {
|
||||
if (IsNSAccessibilityElement())
|
||||
if (IsNSAccessibilityElement()) {
|
||||
return [node_ accessibilityParameterizedAttributeNames];
|
||||
}
|
||||
|
||||
if (IsAXUIElement()) {
|
||||
CFArrayRef attributes_ref;
|
||||
base::ScopedCFTypeRef<CFArrayRef> attributes_ref;
|
||||
AXError result = AXUIElementCopyParameterizedAttributeNames(
|
||||
static_cast<AXUIElementRef>(node_), &attributes_ref);
|
||||
if (AXSuccess(result, "AXParameterizedAttributeNamesOf"))
|
||||
return static_cast<NSArray*>(attributes_ref);
|
||||
(__bridge AXUIElementRef)node_, attributes_ref.InitializeInto());
|
||||
if (AXSuccess(result, "AXParameterizedAttributeNamesOf")) {
|
||||
return base::apple::CFToNSOwnershipCast(attributes_ref.release());
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -175,16 +191,17 @@ NSArray* AXElementWrapper::ParameterizedAttributeNames() const {
|
||||
|
||||
AXOptionalNSObject AXElementWrapper::GetAttributeValue(
|
||||
NSString* attribute) const {
|
||||
if (IsNSAccessibilityElement())
|
||||
if (IsNSAccessibilityElement()) {
|
||||
return AXOptionalNSObject([node_ accessibilityAttributeValue:attribute]);
|
||||
}
|
||||
|
||||
if (IsAXUIElement()) {
|
||||
CFTypeRef value_ref;
|
||||
base::ScopedCFTypeRef<CFTypeRef> value_ref;
|
||||
AXError result = AXUIElementCopyAttributeValue(
|
||||
static_cast<AXUIElementRef>(node_), static_cast<CFStringRef>(attribute),
|
||||
&value_ref);
|
||||
(__bridge AXUIElementRef)node_, (__bridge CFStringRef)attribute,
|
||||
value_ref.InitializeInto());
|
||||
return ToOptional(
|
||||
static_cast<id>(value_ref), result,
|
||||
(__bridge id)value_ref.get(), result,
|
||||
"AXGetAttributeValue(" + base::SysNSStringToUTF8(attribute) + ")");
|
||||
}
|
||||
|
||||
@@ -199,22 +216,20 @@ AXOptionalNSObject AXElementWrapper::GetParameterizedAttributeValue(
|
||||
forParameter:parameter]);
|
||||
|
||||
if (IsAXUIElement()) {
|
||||
// Convert NSValue parameter to CFTypeRef if needed.
|
||||
CFTypeRef parameter_ref = static_cast<CFTypeRef>(parameter);
|
||||
base::ScopedCFTypeRef<CFTypeRef> parameter_ref(CFBridgingRetain(parameter));
|
||||
if ([parameter isKindOfClass:[NSValue class]] &&
|
||||
!strcmp([static_cast<NSValue*>(parameter) objCType],
|
||||
@encode(NSRange))) {
|
||||
NSRange range = [static_cast<NSValue*>(parameter) rangeValue];
|
||||
parameter_ref = AXValueCreate(kAXValueTypeCFRange, &range);
|
||||
!strcmp([parameter objCType], @encode(NSRange))) {
|
||||
NSRange range = [parameter rangeValue];
|
||||
parameter_ref.reset(AXValueCreate(kAXValueTypeCFRange, &range));
|
||||
}
|
||||
|
||||
// Get value.
|
||||
CFTypeRef value_ref;
|
||||
base::ScopedCFTypeRef<CFTypeRef> value_ref;
|
||||
AXError result = AXUIElementCopyParameterizedAttributeValue(
|
||||
static_cast<AXUIElementRef>(node_), static_cast<CFStringRef>(attribute),
|
||||
parameter_ref, &value_ref);
|
||||
(__bridge AXUIElementRef)node_, (__bridge CFStringRef)attribute,
|
||||
parameter_ref, value_ref.InitializeInto());
|
||||
|
||||
return ToOptional(static_cast<id>(value_ref), result,
|
||||
return ToOptional((__bridge id)value_ref.get(), result,
|
||||
"GetParameterizedAttributeValue(" +
|
||||
base::SysNSStringToUTF8(attribute) + ")");
|
||||
}
|
||||
@@ -245,8 +260,11 @@ absl::optional<id> AXElementWrapper::PerformSelector(
|
||||
NSSelectorFromString(base::SysUTF8ToNSString(selector_string + ":"));
|
||||
NSString* argument = base::SysUTF8ToNSString(argument_string);
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
if ([node_ respondsToSelector:selector])
|
||||
return [node_ performSelector:selector withObject:argument];
|
||||
#pragma clang diagnostic pop
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
@@ -257,9 +275,9 @@ void AXElementWrapper::SetAttributeValue(NSString* attribute, id value) const {
|
||||
}
|
||||
|
||||
if (IsAXUIElement()) {
|
||||
AXUIElementSetAttributeValue(static_cast<AXUIElementRef>(node_),
|
||||
static_cast<CFStringRef>(attribute),
|
||||
static_cast<CFTypeRef>(value));
|
||||
AXUIElementSetAttributeValue((__bridge AXUIElementRef)node_,
|
||||
(__bridge CFStringRef)attribute,
|
||||
(__bridge CFTypeRef)value);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -272,10 +290,12 @@ NSArray* AXElementWrapper::ActionNames() const {
|
||||
return [node_ accessibilityActionNames];
|
||||
|
||||
if (IsAXUIElement()) {
|
||||
CFArrayRef attributes_ref;
|
||||
if ((AXUIElementCopyActionNames(static_cast<AXUIElementRef>(node_),
|
||||
&attributes_ref)) == kAXErrorSuccess)
|
||||
return static_cast<NSArray*>(attributes_ref);
|
||||
base::ScopedCFTypeRef<CFArrayRef> attributes_ref;
|
||||
if ((AXUIElementCopyActionNames((__bridge AXUIElementRef)node_,
|
||||
attributes_ref.InitializeInto())) ==
|
||||
kAXErrorSuccess) {
|
||||
return base::apple::CFToNSOwnershipCast(attributes_ref.release());
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -291,8 +311,8 @@ void AXElementWrapper::PerformAction(NSString* action) const {
|
||||
}
|
||||
|
||||
if (IsAXUIElement()) {
|
||||
AXUIElementPerformAction(static_cast<AXUIElementRef>(node_),
|
||||
static_cast<CFStringRef>(action));
|
||||
AXUIElementPerformAction((__bridge AXUIElementRef)node_,
|
||||
(__bridge CFStringRef)action);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include "base/apple/bridging.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/foundation_util.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_tree_formatter_mac.h"
|
||||
|
||||
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
||||
#error "This file requires ARC support."
|
||||
#endif
|
||||
|
||||
namespace ui {
|
||||
|
||||
// Callback function registered using AXObserverCreate.
|
||||
@@ -34,9 +39,9 @@ static void EventReceivedThunk(AXObserverRef observer_ref,
|
||||
AXEventRecorderMac::AXEventRecorderMac(base::ProcessId pid,
|
||||
const AXTreeSelector& selector)
|
||||
: observer_run_loop_source_(nullptr) {
|
||||
AXUIElementRef node = nil;
|
||||
base::ScopedCFTypeRef<AXUIElementRef> node;
|
||||
if (pid) {
|
||||
node = AXUIElementCreateApplication(pid);
|
||||
node.reset(AXUIElementCreateApplication(pid));
|
||||
if (!node) {
|
||||
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.
|
||||
application_.reset(node);
|
||||
application_ = std::move(node);
|
||||
if (!application_.get())
|
||||
LOG(FATAL) << "Failed to create AXUIElement for application.";
|
||||
|
||||
// Add the notifications we care about to the observer.
|
||||
static NSArray* notifications = [@[
|
||||
static NSArray* notifications = @[
|
||||
@"AXAutocorrectionOccurred",
|
||||
@"AXElementBusyChanged",
|
||||
@"AXExpandedChanged",
|
||||
@@ -103,7 +108,7 @@ AXEventRecorderMac::AXEventRecorderMac(base::ProcessId pid,
|
||||
NSAccessibilityWindowMiniaturizedNotification,
|
||||
NSAccessibilityWindowMovedNotification,
|
||||
NSAccessibilityWindowResizedNotification,
|
||||
] retain];
|
||||
];
|
||||
|
||||
for (NSString* notification : notifications) {
|
||||
AddNotification(notification);
|
||||
@@ -122,7 +127,7 @@ AXEventRecorderMac::~AXEventRecorderMac() {
|
||||
|
||||
void AXEventRecorderMac::AddNotification(NSString* notification) {
|
||||
AXObserverAddNotification(observer_ref_, application_,
|
||||
base::mac::NSToCFCast(notification), this);
|
||||
base::apple::NSToCFPtrCast(notification), this);
|
||||
}
|
||||
|
||||
void AXEventRecorderMac::EventReceived(AXUIElementRef element,
|
||||
@@ -135,7 +140,7 @@ void AXEventRecorderMac::EventReceived(AXUIElementRef element,
|
||||
AXTreeFormatter::kFiltersDefaultSet);
|
||||
|
||||
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.
|
||||
if (!element_str.empty() && element_str.back() == '\n') {
|
||||
@@ -157,33 +162,27 @@ std::string AXEventRecorderMac::SerializeTextSelectionChangedProperties(
|
||||
if (user_info == nil)
|
||||
return {};
|
||||
|
||||
std::vector<std::string> serialized_info;
|
||||
CFDictionaryApplyFunction(
|
||||
user_info,
|
||||
[](const void* raw_key, const void* raw_value, void* context) {
|
||||
auto* key = static_cast<NSString*>(raw_key);
|
||||
auto* value = static_cast<NSObject*>(raw_value);
|
||||
auto* serialized_info = static_cast<std::vector<std::string>*>(context);
|
||||
std::string value_string;
|
||||
if ([key isEqual:NSAccessibilityTextStateChangeTypeKey]) {
|
||||
value_string = ToString(static_cast<AXTextStateChangeType>(
|
||||
[static_cast<NSNumber*>(value) intValue]));
|
||||
} else if ([key isEqual:NSAccessibilityTextSelectionDirection]) {
|
||||
value_string = ToString(static_cast<AXTextSelectionDirection>(
|
||||
[static_cast<NSNumber*>(value) intValue]));
|
||||
} else if ([key isEqual:NSAccessibilityTextSelectionGranularity]) {
|
||||
value_string = ToString(static_cast<AXTextSelectionGranularity>(
|
||||
[static_cast<NSNumber*>(value) intValue]));
|
||||
} else if ([key isEqual:NSAccessibilityTextEditType]) {
|
||||
value_string = ToString(static_cast<AXTextEditType>(
|
||||
[static_cast<NSNumber*>(value) intValue]));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
serialized_info->push_back(base::SysNSStringToUTF8(key) + "=" +
|
||||
value_string);
|
||||
},
|
||||
&serialized_info);
|
||||
__block std::vector<std::string> serialized_info;
|
||||
[base::apple::CFToNSPtrCast(user_info) enumerateKeysAndObjectsUsingBlock:^(
|
||||
id key, id value, BOOL* stop) {
|
||||
std::string value_string;
|
||||
if ([key isEqual:NSAccessibilityTextStateChangeTypeKey]) {
|
||||
value_string =
|
||||
ToString(static_cast<AXTextStateChangeType>([value intValue]));
|
||||
} else if ([key isEqual:NSAccessibilityTextSelectionDirection]) {
|
||||
value_string =
|
||||
ToString(static_cast<AXTextSelectionDirection>([value intValue]));
|
||||
} else if ([key isEqual:NSAccessibilityTextSelectionGranularity]) {
|
||||
value_string =
|
||||
ToString(static_cast<AXTextSelectionGranularity>([value intValue]));
|
||||
} else if ([key isEqual:NSAccessibilityTextEditType]) {
|
||||
value_string = ToString(static_cast<AXTextEditType>([value intValue]));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
serialized_info.push_back(base::SysNSStringToUTF8(key) + "=" +
|
||||
value_string);
|
||||
}];
|
||||
|
||||
// Always sort the info so that we don't depend on CFDictionary for
|
||||
// consistent output ordering.
|
||||
|
@@ -5,10 +5,11 @@
|
||||
#ifndef 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/functional/callback_forward.h"
|
||||
#include "base/mac/scoped_cftyperef.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.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.
|
||||
using AXFindCriteria = base::RepeatingCallback<bool(const AXUIElementRef)>;
|
||||
COMPONENT_EXPORT(AX_PLATFORM)
|
||||
AXUIElementRef FindAXUIElement(const AXUIElementRef node,
|
||||
const AXFindCriteria& criteria);
|
||||
base::ScopedCFTypeRef<AXUIElementRef> FindAXUIElement(
|
||||
const AXUIElementRef node,
|
||||
const AXFindCriteria& criteria);
|
||||
|
||||
// Returns AXUIElement and its application process id by a given tree selector.
|
||||
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.
|
||||
COMPONENT_EXPORT(AX_PLATFORM)
|
||||
AXUIElementRef FindAXWindowChild(AXUIElementRef parent,
|
||||
const std::string& pattern);
|
||||
base::ScopedCFTypeRef<AXUIElementRef> FindAXWindowChild(
|
||||
AXUIElementRef parent,
|
||||
const std::string& pattern);
|
||||
|
||||
} // namespace ui
|
||||
|
||||
|
@@ -4,17 +4,26 @@
|
||||
|
||||
#include "ui/accessibility/platform/inspect/ax_inspect_utils_mac.h"
|
||||
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "base/apple/bridging.h"
|
||||
#include "base/containers/fixed_flat_set.h"
|
||||
#include "base/debug/stack_trace.h"
|
||||
#include "base/functional/callback.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/sys_string_conversions.h"
|
||||
#include "ui/accessibility/platform/ax_private_attributes_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
|
||||
// macOS 10.10 - Use the NSAccessibility protocol methods instead (see
|
||||
// NSAccessibilityProtocols.h
|
||||
@@ -23,112 +32,106 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
using base::SysNSStringToUTF8;
|
||||
namespace {
|
||||
|
||||
const char kChromeTitle[] = "Google Chrome";
|
||||
const char kChromiumTitle[] = "Chromium";
|
||||
const char kFirefoxTitle[] = "Firefox";
|
||||
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) {
|
||||
return AXElementWrapper(node).Children();
|
||||
}
|
||||
|
||||
std::string GetDOMId(const id node) {
|
||||
return AXElementWrapper(node).DOMId();
|
||||
} // namespace
|
||||
|
||||
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,
|
||||
const AXFindCriteria& criteria) {
|
||||
if (criteria.Run(node))
|
||||
return node;
|
||||
base::ScopedCFTypeRef<AXUIElementRef> FindAXUIElement(
|
||||
const AXUIElementRef node,
|
||||
const AXFindCriteria& criteria) {
|
||||
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) {
|
||||
AXUIElementRef found =
|
||||
FindAXUIElement(static_cast<AXUIElementRef>(child), criteria);
|
||||
if (found != nil)
|
||||
base::ScopedCFTypeRef<AXUIElementRef> found =
|
||||
FindAXUIElement((__bridge AXUIElementRef)child, criteria);
|
||||
if (found != nil) {
|
||||
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) {
|
||||
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;
|
||||
if (selector.types & AXTreeSelector::Chrome)
|
||||
title = kChromeTitle;
|
||||
@@ -139,32 +142,40 @@ std::pair<AXUIElementRef, int> FindAXUIElement(const AXTreeSelector& selector) {
|
||||
else if (selector.types & AXTreeSelector::Safari)
|
||||
title = kSafariTitle;
|
||||
else
|
||||
return {nil, 0};
|
||||
return {base::ScopedCFTypeRef<AXUIElementRef>(), 0};
|
||||
|
||||
NSArray* windows =
|
||||
base::apple::CFToNSOwnershipCast(CGWindowListCopyWindowInfo(
|
||||
kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
|
||||
kCGNullWindowID));
|
||||
|
||||
for (NSDictionary* window_info in windows) {
|
||||
int pid =
|
||||
[static_cast<NSNumber*>([window_info objectForKey:@"kCGWindowOwnerPID"])
|
||||
intValue];
|
||||
std::string window_name = SysNSStringToUTF8(static_cast<NSString*>(
|
||||
[window_info objectForKey:@"kCGWindowOwnerName"]));
|
||||
int pid = base::mac::ObjCCast<NSNumber>(window_info[@"kCGWindowOwnerPID"])
|
||||
.intValue;
|
||||
std::string window_name = base::SysNSStringToUTF8(
|
||||
base::mac::ObjCCast<NSString>(window_info[@"kCGWindowOwnerName"]));
|
||||
|
||||
AXUIElementRef node = nil;
|
||||
base::ScopedCFTypeRef<AXUIElementRef> node;
|
||||
|
||||
// Application pre-defined selectors match or application title exact match.
|
||||
bool appTitleMatch = window_name == selector.pattern;
|
||||
if (window_name == title || appTitleMatch)
|
||||
node = AXUIElementCreateApplication(pid);
|
||||
bool app_title_match = window_name == selector.pattern;
|
||||
if (window_name == title || app_title_match) {
|
||||
node.reset(AXUIElementCreateApplication(pid));
|
||||
}
|
||||
|
||||
// Window title match. Application contain an AXWindow accessible object as
|
||||
// a first child, which accessible name contain a window title. For example:
|
||||
// 'Inbox (2) - asurkov@igalia.com - Gmail'.
|
||||
if (!selector.pattern.empty() && !appTitleMatch) {
|
||||
if (!node)
|
||||
node = AXUIElementCreateApplication(pid);
|
||||
if (!selector.pattern.empty() && !app_title_match) {
|
||||
if (!node) {
|
||||
node.reset(AXUIElementCreateApplication(pid));
|
||||
}
|
||||
|
||||
AXUIElementRef window = FindAXWindowChild(node, selector.pattern);
|
||||
if (window)
|
||||
base::ScopedCFTypeRef<AXUIElementRef> window =
|
||||
FindAXWindowChild(node, selector.pattern);
|
||||
if (window) {
|
||||
node = window;
|
||||
}
|
||||
}
|
||||
|
||||
// ActiveTab selector.
|
||||
@@ -173,10 +184,10 @@ std::pair<AXUIElementRef, int> FindAXUIElement(const AXTreeSelector& selector) {
|
||||
node, base::BindRepeating([](const AXUIElementRef node) {
|
||||
// Only active tab in exposed in browsers, thus find first
|
||||
// AXWebArea role.
|
||||
AXElementWrapper ax_node(static_cast<id>(node));
|
||||
AXElementWrapper ax_node((__bridge id)node);
|
||||
NSString* role =
|
||||
*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)
|
||||
return {node, pid};
|
||||
}
|
||||
return {nil, 0};
|
||||
return {base::ScopedCFTypeRef<AXUIElementRef>(), 0};
|
||||
}
|
||||
|
||||
AXUIElementRef FindAXWindowChild(AXUIElementRef parent,
|
||||
const std::string& pattern) {
|
||||
NSArray* children = AXChildrenOf(static_cast<id>(parent));
|
||||
if ([children count] == 0)
|
||||
return nil;
|
||||
base::ScopedCFTypeRef<AXUIElementRef> FindAXWindowChild(
|
||||
AXUIElementRef parent,
|
||||
const std::string& pattern) {
|
||||
NSArray* children = AXChildrenOf((__bridge id)parent);
|
||||
if (children.count == 0) {
|
||||
return base::ScopedCFTypeRef<AXUIElementRef>();
|
||||
}
|
||||
|
||||
id window = [children objectAtIndex:0];
|
||||
id window = children.firstObject;
|
||||
|
||||
AXElementWrapper ax_window(window);
|
||||
NSString* role = *ax_window.GetAttributeValue(NSAccessibilityRoleAttribute);
|
||||
if (SysNSStringToUTF8(role) != "AXWindow")
|
||||
return nil;
|
||||
if (base::SysNSStringToUTF8(role) != "AXWindow") {
|
||||
return base::ScopedCFTypeRef<AXUIElementRef>();
|
||||
}
|
||||
|
||||
NSString* window_title =
|
||||
*ax_window.GetAttributeValue(NSAccessibilityTitleAttribute);
|
||||
if (base::MatchPattern(SysNSStringToUTF8(window_title), pattern))
|
||||
return static_cast<AXUIElementRef>(window);
|
||||
if (base::MatchPattern(base::SysNSStringToUTF8(window_title), pattern)) {
|
||||
return base::ScopedCFTypeRef<AXUIElementRef>(
|
||||
(__bridge AXUIElementRef)window, base::scoped_policy::RETAIN);
|
||||
}
|
||||
|
||||
return nil;
|
||||
return base::ScopedCFTypeRef<AXUIElementRef>();
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "ui/accessibility/platform/inspect/ax_transform_mac.h"
|
||||
|
||||
#include "base/mac/foundation_util.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "ui/accessibility/ax_range.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_inspect_utils.h"
|
||||
|
||||
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
||||
#error "This file requires ARC support."
|
||||
#endif
|
||||
|
||||
namespace ui {
|
||||
|
||||
constexpr char kHeightDictKey[] = "h";
|
||||
@@ -30,73 +35,75 @@ base::Value AXNSObjectToBaseValue(id value, const AXTreeIndexerMac* indexer) {
|
||||
}
|
||||
|
||||
// NSArray
|
||||
if ([value isKindOfClass:[NSArray class]]) {
|
||||
return base::Value(AXNSArrayToBaseValue((NSArray*)value, indexer));
|
||||
if (NSArray* array = base::mac::ObjCCast<NSArray>(value)) {
|
||||
return base::Value(AXNSArrayToBaseValue(value, indexer));
|
||||
}
|
||||
|
||||
// AXCustomContent
|
||||
if (@available(macOS 11.0, *)) {
|
||||
if ([value isKindOfClass:[AXCustomContent class]]) {
|
||||
return base::Value(AXCustomContentToBaseValue((AXCustomContent*)value));
|
||||
if (AXCustomContent* custom_content =
|
||||
base::mac::ObjCCast<AXCustomContent>(value)) {
|
||||
return base::Value(AXCustomContentToBaseValue(custom_content));
|
||||
}
|
||||
}
|
||||
|
||||
// NSDictionary
|
||||
if ([value isKindOfClass:[NSDictionary class]]) {
|
||||
return base::Value(
|
||||
AXNSDictionaryToBaseValue((NSDictionary*)value, indexer));
|
||||
if (NSDictionary* dictionary = base::mac::ObjCCast<NSDictionary>(value)) {
|
||||
return base::Value(AXNSDictionaryToBaseValue(dictionary, indexer));
|
||||
}
|
||||
|
||||
// NSNumber
|
||||
if ([value isKindOfClass:[NSNumber class]]) {
|
||||
return base::Value([value intValue]);
|
||||
if (NSNumber* number = base::mac::ObjCCast<NSNumber>(value)) {
|
||||
return base::Value(number.intValue);
|
||||
}
|
||||
|
||||
// NSRange, NSSize
|
||||
if ([value isKindOfClass:[NSValue class]]) {
|
||||
if (0 == strcmp([value objCType], @encode(NSRange))) {
|
||||
return base::Value(AXNSRangeToBaseValue([value rangeValue]));
|
||||
if (NSValue* ns_value = base::mac::ObjCCast<NSValue>(value)) {
|
||||
if (0 == strcmp(ns_value.objCType, @encode(NSRange))) {
|
||||
return base::Value(AXNSRangeToBaseValue(ns_value.rangeValue));
|
||||
}
|
||||
if (0 == strcmp([value objCType], @encode(NSSize))) {
|
||||
return base::Value(AXNSSizeToBaseValue([value sizeValue]));
|
||||
if (0 == strcmp(ns_value.objCType, @encode(NSSize))) {
|
||||
return base::Value(AXNSSizeToBaseValue(ns_value.sizeValue));
|
||||
}
|
||||
}
|
||||
|
||||
// NSAttributedString
|
||||
if ([value isKindOfClass:[NSAttributedString class]]) {
|
||||
return NSAttributedStringToBaseValue((NSAttributedString*)value, indexer);
|
||||
if (NSAttributedString* attr_string =
|
||||
base::mac::ObjCCast<NSAttributedString>(value)) {
|
||||
return NSAttributedStringToBaseValue(attr_string, indexer);
|
||||
}
|
||||
|
||||
// CGColorRef
|
||||
if (CFGetTypeID(value) == CGColorGetTypeID()) {
|
||||
return base::Value(CGColorRefToBaseValue(static_cast<CGColorRef>(value)));
|
||||
if (CFGetTypeID((__bridge CFTypeRef)value) == CGColorGetTypeID()) {
|
||||
return base::Value(CGColorRefToBaseValue((__bridge CGColorRef)value));
|
||||
}
|
||||
|
||||
// AXValue
|
||||
if (CFGetTypeID(value) == AXValueGetTypeID()) {
|
||||
AXValueType type = AXValueGetType(static_cast<AXValueRef>(value));
|
||||
if (CFGetTypeID((__bridge CFTypeRef)value) == AXValueGetTypeID()) {
|
||||
AXValueRef ax_value = (__bridge AXValueRef)value;
|
||||
AXValueType type = AXValueGetType(ax_value);
|
||||
switch (type) {
|
||||
case kAXValueCGPointType: {
|
||||
NSPoint point;
|
||||
if (AXValueGetValue(static_cast<AXValueRef>(value), type, &point)) {
|
||||
if (AXValueGetValue(ax_value, type, &point)) {
|
||||
return base::Value(AXNSPointToBaseValue(point));
|
||||
}
|
||||
} break;
|
||||
case kAXValueCGSizeType: {
|
||||
NSSize size;
|
||||
if (AXValueGetValue(static_cast<AXValueRef>(value), type, &size)) {
|
||||
if (AXValueGetValue(ax_value, type, &size)) {
|
||||
return base::Value(AXNSSizeToBaseValue(size));
|
||||
}
|
||||
} break;
|
||||
case kAXValueCGRectType: {
|
||||
NSRect rect;
|
||||
if (AXValueGetValue(static_cast<AXValueRef>(value), type, &rect)) {
|
||||
if (AXValueGetValue(ax_value, type, &rect)) {
|
||||
return base::Value(AXNSRectToBaseValue(rect));
|
||||
}
|
||||
} break;
|
||||
case kAXValueCFRangeType: {
|
||||
NSRange range;
|
||||
if (AXValueGetValue(static_cast<AXValueRef>(value), type, &range)) {
|
||||
if (AXValueGetValue(ax_value, type, &range)) {
|
||||
return base::Value(AXNSRangeToBaseValue(range));
|
||||
}
|
||||
} break;
|
||||
@@ -111,8 +118,9 @@ base::Value AXNSObjectToBaseValue(id value, const AXTreeIndexerMac* indexer) {
|
||||
}
|
||||
|
||||
// AXTextMarkerRange
|
||||
if (IsAXTextMarkerRange(value))
|
||||
if (IsAXTextMarkerRange(value)) {
|
||||
return AXTextMarkerRangeToBaseValue(value, indexer);
|
||||
}
|
||||
|
||||
// Accessible object
|
||||
if (AXElementWrapper::IsValidElement(value)) {
|
||||
@@ -131,23 +139,27 @@ base::Value AXElementToBaseValue(id node, const AXTreeIndexerMac* indexer) {
|
||||
base::Value AXPositionToBaseValue(
|
||||
const AXPlatformNodeDelegate::AXPosition& position,
|
||||
const AXTreeIndexerMac* indexer) {
|
||||
if (position->IsNullPosition())
|
||||
if (position->IsNullPosition()) {
|
||||
return AXNilToBaseValue();
|
||||
}
|
||||
|
||||
const AXPlatformTreeManager* manager =
|
||||
static_cast<AXPlatformTreeManager*>(position->GetManager());
|
||||
if (!manager)
|
||||
if (!manager) {
|
||||
return AXNilToBaseValue();
|
||||
}
|
||||
|
||||
AXPlatformNode* platform_node_anchor =
|
||||
manager->GetPlatformNodeFromTree(position->anchor_id());
|
||||
if (!platform_node_anchor)
|
||||
if (!platform_node_anchor) {
|
||||
return AXNilToBaseValue();
|
||||
}
|
||||
|
||||
AXPlatformNodeCocoa* cocoa_anchor = static_cast<AXPlatformNodeCocoa*>(
|
||||
platform_node_anchor->GetNativeViewAccessible());
|
||||
if (!cocoa_anchor)
|
||||
if (!cocoa_anchor) {
|
||||
return AXNilToBaseValue();
|
||||
}
|
||||
|
||||
std::string affinity;
|
||||
switch (position->affinity()) {
|
||||
@@ -162,13 +174,14 @@ base::Value AXPositionToBaseValue(
|
||||
break;
|
||||
}
|
||||
|
||||
base::Value::Dict value;
|
||||
value.Set(AXMakeSetKey(AXMakeOrderedKey("anchor", 0)),
|
||||
AXElementToBaseValue(static_cast<id>(cocoa_anchor), indexer));
|
||||
value.Set(AXMakeSetKey(AXMakeOrderedKey("offset", 1)),
|
||||
position->text_offset());
|
||||
value.Set(AXMakeSetKey(AXMakeOrderedKey("affinity", 2)),
|
||||
AXMakeConst(affinity));
|
||||
base::Value::Dict value =
|
||||
base::Value::Dict()
|
||||
.Set(AXMakeSetKey(AXMakeOrderedKey("anchor", 0)),
|
||||
AXElementToBaseValue(static_cast<id>(cocoa_anchor), indexer))
|
||||
.Set(AXMakeSetKey(AXMakeOrderedKey("offset", 1)),
|
||||
position->text_offset())
|
||||
.Set(AXMakeSetKey(AXMakeOrderedKey("affinity", 2)),
|
||||
AXMakeConst(affinity));
|
||||
return base::Value(std::move(value));
|
||||
}
|
||||
|
||||
@@ -181,13 +194,16 @@ base::Value AXTextMarkerRangeToBaseValue(id text_marker_range,
|
||||
const AXTreeIndexerMac* indexer) {
|
||||
AXPlatformNodeDelegate::AXRange ax_range =
|
||||
AXTextMarkerRangeToAXRange(text_marker_range);
|
||||
if (ax_range.IsNull())
|
||||
if (ax_range.IsNull()) {
|
||||
return AXNilToBaseValue();
|
||||
}
|
||||
|
||||
base::Value::Dict value;
|
||||
value.Set("anchor",
|
||||
AXPositionToBaseValue(ax_range.anchor()->Clone(), indexer));
|
||||
value.Set("focus", AXPositionToBaseValue(ax_range.focus()->Clone(), indexer));
|
||||
base::Value::Dict value =
|
||||
base::Value::Dict()
|
||||
.Set("anchor",
|
||||
AXPositionToBaseValue(ax_range.anchor()->Clone(), indexer))
|
||||
.Set("focus",
|
||||
AXPositionToBaseValue(ax_range.focus()->Clone(), indexer));
|
||||
return base::Value(std::move(value));
|
||||
}
|
||||
|
||||
@@ -196,7 +212,7 @@ base::Value NSAttributedStringToBaseValue(NSAttributedString* attr_string,
|
||||
__block base::Value::Dict result;
|
||||
|
||||
[attr_string
|
||||
enumerateAttributesInRange:NSMakeRange(0, [attr_string length])
|
||||
enumerateAttributesInRange:NSMakeRange(0, attr_string.length)
|
||||
options:
|
||||
NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
|
||||
usingBlock:^(NSDictionary* attrs, NSRange nsRange,
|
||||
@@ -210,7 +226,7 @@ base::Value NSAttributedStringToBaseValue(NSAttributedString* attr_string,
|
||||
}];
|
||||
|
||||
result.Set(std::string(base::SysNSStringToUTF8(
|
||||
[[attr_string string]
|
||||
[attr_string.string
|
||||
substringWithRange:nsRange])),
|
||||
std::move(base_attrs));
|
||||
}];
|
||||
@@ -232,15 +248,17 @@ base::Value AXNilToBaseValue() {
|
||||
base::Value::List AXNSArrayToBaseValue(NSArray* node_array,
|
||||
const AXTreeIndexerMac* indexer) {
|
||||
base::Value::List list;
|
||||
for (NSUInteger i = 0; i < [node_array count]; i++)
|
||||
list.Append(AXNSObjectToBaseValue([node_array objectAtIndex:i], indexer));
|
||||
for (id item in node_array) {
|
||||
list.Append(AXNSObjectToBaseValue(item, indexer));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
base::Value::Dict AXCustomContentToBaseValue(AXCustomContent* content) {
|
||||
base::Value::Dict value;
|
||||
value.Set("label", base::SysNSStringToUTF16(content.label));
|
||||
value.Set("value", base::SysNSStringToUTF16(content.value));
|
||||
base::Value::Dict value =
|
||||
base::Value::Dict()
|
||||
.Set("label", base::SysNSStringToUTF16(content.label))
|
||||
.Set("value", base::SysNSStringToUTF16(content.value));
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -256,40 +274,41 @@ base::Value::Dict AXNSDictionaryToBaseValue(NSDictionary* dictionary_value,
|
||||
}
|
||||
|
||||
base::Value::Dict AXNSPointToBaseValue(NSPoint point_value) {
|
||||
base::Value::Dict point;
|
||||
point.Set(kXCoordDictKey, static_cast<int>(point_value.x));
|
||||
point.Set(kYCoordDictKey, static_cast<int>(point_value.y));
|
||||
base::Value::Dict point =
|
||||
base::Value::Dict()
|
||||
.Set(kXCoordDictKey, static_cast<int>(point_value.x))
|
||||
.Set(kYCoordDictKey, static_cast<int>(point_value.y));
|
||||
return point;
|
||||
}
|
||||
|
||||
base::Value::Dict AXNSSizeToBaseValue(NSSize size_value) {
|
||||
base::Value::Dict size;
|
||||
size.Set(AXMakeOrderedKey(kWidthDictKey, 0),
|
||||
static_cast<int>(size_value.width));
|
||||
size.Set(AXMakeOrderedKey(kHeightDictKey, 1),
|
||||
static_cast<int>(size_value.height));
|
||||
base::Value::Dict size = base::Value::Dict()
|
||||
.Set(AXMakeOrderedKey(kWidthDictKey, 0),
|
||||
static_cast<int>(size_value.width))
|
||||
.Set(AXMakeOrderedKey(kHeightDictKey, 1),
|
||||
static_cast<int>(size_value.height));
|
||||
return size;
|
||||
}
|
||||
|
||||
base::Value::Dict AXNSRectToBaseValue(NSRect rect_value) {
|
||||
base::Value::Dict rect;
|
||||
rect.Set(AXMakeOrderedKey(kXCoordDictKey, 0),
|
||||
static_cast<int>(rect_value.origin.x));
|
||||
rect.Set(AXMakeOrderedKey(kYCoordDictKey, 1),
|
||||
static_cast<int>(rect_value.origin.y));
|
||||
rect.Set(AXMakeOrderedKey(kWidthDictKey, 2),
|
||||
static_cast<int>(rect_value.size.width));
|
||||
rect.Set(AXMakeOrderedKey(kHeightDictKey, 3),
|
||||
static_cast<int>(rect_value.size.height));
|
||||
base::Value::Dict rect = base::Value::Dict()
|
||||
.Set(AXMakeOrderedKey(kXCoordDictKey, 0),
|
||||
static_cast<int>(rect_value.origin.x))
|
||||
.Set(AXMakeOrderedKey(kYCoordDictKey, 1),
|
||||
static_cast<int>(rect_value.origin.y))
|
||||
.Set(AXMakeOrderedKey(kWidthDictKey, 2),
|
||||
static_cast<int>(rect_value.size.width))
|
||||
.Set(AXMakeOrderedKey(kHeightDictKey, 3),
|
||||
static_cast<int>(rect_value.size.height));
|
||||
return rect;
|
||||
}
|
||||
|
||||
base::Value::Dict AXNSRangeToBaseValue(NSRange node_range) {
|
||||
base::Value::Dict range;
|
||||
range.Set(AXMakeOrderedKey(kRangeLocDictKey, 0),
|
||||
static_cast<int>(node_range.location));
|
||||
range.Set(AXMakeOrderedKey(kRangeLenDictKey, 1),
|
||||
static_cast<int>(node_range.length));
|
||||
base::Value::Dict range = base::Value::Dict()
|
||||
.Set(AXMakeOrderedKey(kRangeLocDictKey, 0),
|
||||
static_cast<int>(node_range.location))
|
||||
.Set(AXMakeOrderedKey(kRangeLenDictKey, 1),
|
||||
static_cast<int>(node_range.length));
|
||||
return range;
|
||||
}
|
||||
|
||||
|
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "ui/accessibility/platform/inspect/ax_tree_formatter_mac.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/strings/string_number_conversions.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_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.
|
||||
// TODO(crbug.com/948844): Migrate to the new NSAccessibility interface.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
using base::StringPrintf;
|
||||
using base::SysNSStringToUTF8;
|
||||
using base::SysNSStringToUTF16;
|
||||
using std::string;
|
||||
|
||||
namespace ui {
|
||||
|
||||
namespace {
|
||||
@@ -47,13 +48,13 @@ AXTreeFormatterMac::~AXTreeFormatterMac() = default;
|
||||
|
||||
void AXTreeFormatterMac::AddDefaultFilters(
|
||||
std::vector<AXPropertyFilter>* property_filters) {
|
||||
static NSArray* default_attributes = [@[
|
||||
static NSArray* default_attributes = @[
|
||||
@"AXAutocompleteValue", @"AXDescription", @"AXRole", @"AXSubrole",
|
||||
@"AXTitle", @"AXTitleUIElement", @"AXValue"
|
||||
] retain];
|
||||
];
|
||||
|
||||
for (NSString* attribute : default_attributes) {
|
||||
AddPropertyFilter(property_filters, SysNSStringToUTF8(attribute));
|
||||
AddPropertyFilter(property_filters, base::SysNSStringToUTF8(attribute));
|
||||
}
|
||||
|
||||
if (show_ids()) {
|
||||
@@ -69,7 +70,7 @@ base::Value::Dict AXTreeFormatterMac::BuildTree(
|
||||
|
||||
base::Value::Dict AXTreeFormatterMac::BuildTreeForSelector(
|
||||
const AXTreeSelector& selector) const {
|
||||
AXUIElementRef node = nil;
|
||||
base::ScopedCFTypeRef<AXUIElementRef> node;
|
||||
std::tie(node, std::ignore) = FindAXUIElement(selector);
|
||||
if (node == nil) {
|
||||
return base::Value::Dict();
|
||||
@@ -79,7 +80,7 @@ base::Value::Dict AXTreeFormatterMac::BuildTreeForSelector(
|
||||
|
||||
base::Value::Dict AXTreeFormatterMac::BuildTreeForAXUIElement(
|
||||
AXUIElementRef node) const {
|
||||
return BuildTree(static_cast<id>(node));
|
||||
return BuildTree((__bridge id)node);
|
||||
}
|
||||
|
||||
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(
|
||||
const AXTreeSelector& selector,
|
||||
const AXInspectScenario& scenario) const {
|
||||
AXUIElementRef root = nil;
|
||||
base::ScopedCFTypeRef<AXUIElementRef> root;
|
||||
std::tie(root, std::ignore) = FindAXUIElement(selector);
|
||||
if (!root)
|
||||
return "";
|
||||
|
||||
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());
|
||||
|
||||
return result;
|
||||
@@ -207,7 +208,8 @@ void AXTreeFormatterMac::RecursiveBuildTree(const AXElementWrapper& ax_element,
|
||||
base::Value::List child_dict_list;
|
||||
for (id child in children) {
|
||||
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));
|
||||
}
|
||||
dict->Set(kChildrenDictAttr, std::move(child_dict_list));
|
||||
@@ -226,7 +228,7 @@ void AXTreeFormatterMac::AddProperties(const AXElementWrapper& ax_element,
|
||||
NSArray* attributes = ax_element.AttributeNames();
|
||||
for (NSString* attribute : attributes) {
|
||||
dict->SetByDottedPath(
|
||||
SysNSStringToUTF8(attribute),
|
||||
base::SysNSStringToUTF8(attribute),
|
||||
AXNSObjectToBaseValue(*ax_element.GetAttributeValue(attribute),
|
||||
indexer));
|
||||
}
|
||||
@@ -279,17 +281,19 @@ std::string AXTreeFormatterMac::ProcessTreeForOutput(
|
||||
std::string line;
|
||||
|
||||
// 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);
|
||||
if (value) {
|
||||
WriteAttribute(true, *value, &line);
|
||||
}
|
||||
std::string subrole_attr = SysNSStringToUTF8(NSAccessibilitySubroleAttribute);
|
||||
std::string subrole_attr =
|
||||
base::SysNSStringToUTF8(NSAccessibilitySubroleAttribute);
|
||||
value = dict.FindString(subrole_attr);
|
||||
if (value) {
|
||||
WriteAttribute(false,
|
||||
StringPrintf("%s=%s", subrole_attr.c_str(), value->c_str()),
|
||||
&line);
|
||||
WriteAttribute(
|
||||
false,
|
||||
base::StringPrintf("%s=%s", subrole_attr.c_str(), value->c_str()),
|
||||
&line);
|
||||
}
|
||||
|
||||
// Expose all other attributes.
|
||||
@@ -308,10 +312,10 @@ std::string AXTreeFormatterMac::ProcessTreeForOutput(
|
||||
|
||||
// Write formatted value.
|
||||
std::string formatted_value = AXFormatValue(item.second);
|
||||
WriteAttribute(
|
||||
false,
|
||||
StringPrintf("%s=%s", item.first.c_str(), formatted_value.c_str()),
|
||||
&line);
|
||||
WriteAttribute(false,
|
||||
base::StringPrintf("%s=%s", item.first.c_str(),
|
||||
formatted_value.c_str()),
|
||||
&line);
|
||||
}
|
||||
|
||||
return line;
|
||||
|
@@ -10,14 +10,13 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
//
|
||||
// NSAccessibilityElement or AXUIElement accessible node comparator.
|
||||
struct AXNodeComparator {
|
||||
constexpr bool operator()(const gfx::NativeViewAccessible& lhs,
|
||||
const gfx::NativeViewAccessible& rhs) const {
|
||||
if (AXElementWrapper::IsAXUIElement(lhs)) {
|
||||
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(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") {
|
||||
use_xvfb = use_xvfb_in_this_config
|
||||
|
||||
@@ -1406,7 +1422,6 @@ test("views_unittests") {
|
||||
"controls/native/native_view_host_mac_unittest.mm",
|
||||
"controls/tabbed_pane/tabbed_pane_accessibility_mac_unittest.mm",
|
||||
"view_unittest_mac.mm",
|
||||
"widget/ax_native_widget_mac_unittest.mm",
|
||||
"widget/native_widget_mac_unittest.mm",
|
||||
"widget/sublevel_manager_mac_unittest.mm",
|
||||
]
|
||||
@@ -1415,6 +1430,7 @@ test("views_unittests") {
|
||||
sources -= [ "controls/native/native_view_host_unittest.cc" ]
|
||||
|
||||
public_deps = [
|
||||
":views_unittests_arc",
|
||||
"//components/remote_cocoa/app_shim",
|
||||
"//ui/accelerated_widget_mac",
|
||||
]
|
||||
|
@@ -24,6 +24,10 @@
|
||||
#include "ui/views/test/widget_test.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 {
|
||||
@@ -164,9 +168,8 @@ class AXNativeWidgetMacTest : public test::WidgetTest {
|
||||
// on a retained accessibility object after the source view is deleted.
|
||||
TEST_F(AXNativeWidgetMacTest, Lifetime) {
|
||||
Textfield* view = AddChildTextfield(widget()->GetContentsView()->size());
|
||||
base::scoped_nsobject<NSObject> ax_node(view->GetNativeViewAccessible(),
|
||||
base::scoped_policy::RETAIN);
|
||||
id<NSAccessibility> ax_obj = ToNSAccessibility(ax_node.get());
|
||||
NSObject* ax_node = view->GetNativeViewAccessible();
|
||||
id<NSAccessibility> ax_obj = ToNSAccessibility(ax_node);
|
||||
|
||||
EXPECT_TRUE(AXObjectHandlesSelector(ax_obj, @selector(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
|
||||
// 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).
|
||||
base::scoped_nsprotocol<id<NSAccessibility>> ax_parent(
|
||||
ax_obj.accessibilityParent, base::scoped_policy::RETAIN);
|
||||
id<NSAccessibility> ax_parent = ax_obj.accessibilityParent;
|
||||
|
||||
// There are two children: a NativeFrameView and the TextField.
|
||||
EXPECT_EQ(2u, ax_parent.get().accessibilityChildren.count);
|
||||
EXPECT_EQ(ax_node.get(), ax_parent.get().accessibilityChildren[1]);
|
||||
EXPECT_EQ(2u, ax_parent.accessibilityChildren.count);
|
||||
EXPECT_EQ(ax_node, ax_parent.accessibilityChildren[1]);
|
||||
|
||||
// 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
|
||||
@@ -230,7 +232,7 @@ TEST_F(AXNativeWidgetMacTest, Lifetime) {
|
||||
|
||||
// The Widget is currently still around, but the TextField should be gone,
|
||||
// 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.
|
||||
@@ -687,8 +689,8 @@ TEST_F(AXNativeWidgetMacTest, ProtectedTextfields) {
|
||||
EXPECT_TRUE(ax_node);
|
||||
|
||||
// Create a native Cocoa NSSecureTextField to compare against.
|
||||
base::scoped_nsobject<NSSecureTextField> cocoa_secure_textfield(
|
||||
[[NSSecureTextField alloc] initWithFrame:NSMakeRect(0, 0, 10, 10)]);
|
||||
NSSecureTextField* cocoa_secure_textfield =
|
||||
[[NSSecureTextField alloc] initWithFrame:NSMakeRect(0, 0, 10, 10)];
|
||||
|
||||
const SEL expected_supported_selectors[] = {
|
||||
@selector(accessibilityValue),
|
||||
@@ -702,7 +704,7 @@ TEST_F(AXNativeWidgetMacTest, ProtectedTextfields) {
|
||||
|
||||
for (auto* sel : expected_supported_selectors) {
|
||||
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
|
||||
|
Reference in New Issue
Block a user