Index: ui/accessibility/platform/ax_platform_node_mac.mm |
diff --git a/ui/accessibility/platform/ax_platform_node_mac.mm b/ui/accessibility/platform/ax_platform_node_mac.mm |
index 240240e6180bc11f40e56de4fa36fdafd9e65b81..7532f158966fa8cada5cc1043385cccc1791cb2b 100644 |
--- a/ui/accessibility/platform/ax_platform_node_mac.mm |
+++ b/ui/accessibility/platform/ax_platform_node_mac.mm |
@@ -8,12 +8,16 @@ |
#include <stddef.h> |
#include "base/macros.h" |
+#include "base/memory/ptr_util.h" |
#include "base/strings/sys_string_conversions.h" |
#include "ui/accessibility/ax_action_data.h" |
#include "ui/accessibility/ax_node_data.h" |
+#include "ui/accessibility/ax_node_position.h" |
+#include "ui/accessibility/ax_range.h" |
#include "ui/accessibility/ax_role_properties.h" |
#include "ui/accessibility/platform/ax_platform_node.h" |
#include "ui/accessibility/platform/ax_platform_node_delegate.h" |
+#import "ui/accessibility/platform/text_marker_helper_mac.h" |
#include "ui/base/l10n/l10n_util.h" |
#import "ui/gfx/mac/coordinate_conversion.h" |
#include "ui/strings/grit/ui_strings.h" |
@@ -221,21 +225,70 @@ EventMap BuildEventMap() { |
} |
void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) { |
+ NSLog(@"Notify: %@, %@", target, |
+ [AXPlatformNodeCocoa nativeNotificationFromAXEvent:event_type]); |
NSAccessibilityPostNotification( |
target, [AXPlatformNodeCocoa nativeNotificationFromAXEvent:event_type]); |
} |
} // namespace |
+namespace ui { |
+namespace { |
+ |
+using AXNodePositionInstance = AXNodePosition::AXPositionInstance; |
+using AXNodeRange = AXRange<AXNodePositionInstance::element_type>; |
+ |
+class NodePositionFactory : public ui::PositionFactory { |
+ public: |
+ explicit NodePositionFactory(AXPlatformNodeCocoa* node) : node_(node) {} |
+ |
+ ui::AXPositionPointer GetRoot() const override; |
+ ui::AXRangePointer GetSelection() const override; |
+ ui::AXPositionPointer GetFromData( |
+ const ui::AXPositionData& data) const override; |
+ id GetAccessibilityObject(const ui::AXPositionData& data) const override; |
+ |
+ private: |
+ AXPlatformNodeCocoa* node_; // Weak. Transitively owns |this|. |
+ |
+ DISALLOW_COPY_AND_ASSIGN(NodePositionFactory); |
+}; |
+ |
+AXNodeRange CreateRangeFromTextMarkerRange(id marker_range) { |
+ ui::AXPositionData start, end; |
+ if (![TextMarkerHelperMac getRangeDataFromMarkerRange:marker_range |
+ start:&start |
+ end:&end]) { |
+ return AXNodeRange(); |
+ } |
+ |
+ return AXNodeRange(AXNodePosition::CreateFromData(start), |
+ AXNodePosition::CreateFromData(end)); |
+} |
+ |
+NSString* GetTextForTextMarkerRange(id marker_range) { |
+ AXNodeRange range = CreateRangeFromTextMarkerRange(marker_range); |
+ if (range.IsNull()) |
+ return nil; |
+ return base::SysUTF16ToNSString(range.GetText()); |
+} |
+ |
+} // namespace |
+} // namespace ui |
+ |
@interface AXPlatformNodeCocoa () |
// Helper function for string attributes that don't require extra processing. |
- (NSString*)getStringAttribute:(ui::AXStringAttribute)attribute; |
// Returns AXValue, or nil if AXValue isn't an NSString. |
- (NSString*)getAXValueAsString; |
+// Lazily creates a TextMarkerHelper and returns it. |
+- (TextMarkerHelperMac*)textMarkerHelper; |
@end |
@implementation AXPlatformNodeCocoa { |
ui::AXPlatformNodeBase* node_; // Weak. Retains us. |
+ base::scoped_nsobject<TextMarkerHelperMac> textMarkerHelper_; |
} |
@synthesize node = node_; |
@@ -274,6 +327,9 @@ void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) { |
NSAccessibilityPostNotification( |
self, NSAccessibilityUIElementDestroyedNotification); |
node_ = nil; |
+ |
+ // Nothing retains |textMarkerHelper_|, so this should always dealloc. |
+ textMarkerHelper_.reset(); |
} |
- (NSRect)boundsInScreen { |
@@ -294,6 +350,16 @@ void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) { |
return [value isKindOfClass:[NSString class]] ? value : nil; |
} |
+- (TextMarkerHelperMac*)textMarkerHelper { |
+ if (!textMarkerHelper_ && node_) { |
+ DLOG(INFO) << "bail"; |
+ return nil; |
+ textMarkerHelper_.reset([[TextMarkerHelperMac alloc] |
+ initWithFactory:base::MakeUnique<ui::NodePositionFactory>(self)]); |
+ } |
+ return textMarkerHelper_; |
+} |
+ |
// NSAccessibility informal protocol implementation. |
- (BOOL)accessibilityIsIgnored { |
@@ -393,6 +459,8 @@ void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) { |
[axAttributes addObject:kTextAttributes]; |
if (!node_->GetData().HasState(ui::AX_STATE_PROTECTED)) |
[axAttributes addObjectsFromArray:kUnprotectedTextAttributes]; |
+ [axAttributes |
+ addObjectsFromArray:[TextMarkerHelperMac getSupportedAttributes]]; |
// Fallthrough. |
case ui::AX_ROLE_CHECK_BOX: |
case ui::AX_ROLE_COMBO_BOX: |
@@ -409,6 +477,32 @@ void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) { |
default: |
break; |
} |
+ if (node_->GetData().role == ui::AX_ROLE_TEXT_FIELD) { |
+ NSLog(@"%@ reports %@", [self AXValue], axAttributes.get()); |
+ } |
+ return axAttributes.autorelease(); |
+} |
+ |
+- (NSArray*)accessibilityParameterizedAttributeNames { |
+ static NSArray* const kEditableTextAttributes = [@[ |
+ NSAccessibilityLineForIndexParameterizedAttribute, |
+ NSAccessibilityRangeForLineParameterizedAttribute, |
+ NSAccessibilityStringForRangeParameterizedAttribute, |
+ NSAccessibilityRangeForPositionParameterizedAttribute, |
+ NSAccessibilityRangeForIndexParameterizedAttribute, |
+ NSAccessibilityBoundsForRangeParameterizedAttribute, |
+ NSAccessibilityRTFForRangeParameterizedAttribute, |
+ NSAccessibilityAttributedStringForRangeParameterizedAttribute, |
+ NSAccessibilityStyleRangeForIndexParameterizedAttribute, |
+ ] retain]; |
+ |
+ base::scoped_nsobject<NSMutableArray> axAttributes( |
+ [[NSMutableArray alloc] init]); |
+ [axAttributes addObjectsFromArray:kEditableTextAttributes]; |
+ [axAttributes |
+ addObjectsFromArray:[TextMarkerHelperMac |
+ getSupportedParameterizedAttributesExtended:YES]]; |
+ NSLog(@"PARAM!: %@ reports %@", [self AXValue], axAttributes.get()); |
return axAttributes.autorelease(); |
} |
@@ -489,8 +583,53 @@ void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) { |
- (id)accessibilityAttributeValue:(NSString*)attribute { |
SEL selector = NSSelectorFromString(attribute); |
+ if ([TextMarkerHelperMac instancesRespondToSelector:selector]) { |
+ DCHECK(![self respondsToSelector:selector]); |
+ NSLog(@"supported: %@", attribute); |
+ return [[self textMarkerHelper] performSelector:selector]; |
+ } |
+ if ([self respondsToSelector:selector]) { |
+ id result = [self performSelector:selector]; |
+ if (![attribute isEqualToString:@"AXRole"]) { |
+ NSLog(@"Trying: %@ (%@) -> %@", attribute, [self AXValue], result); |
+ } |
+ return result; |
+ } |
+ NSLog(@"UNsupported: %@", attribute); |
+ return nil; |
+} |
+ |
+- (id)accessibilityAttributeValue:(NSString*)attribute |
+ forParameter:(id)parameter { |
+ SEL selector = NSSelectorFromString([attribute stringByAppendingString:@":"]); |
+ DCHECK(selector); |
+ NSLog(@"Trying: %@ for %@", attribute, parameter); |
+ if ([TextMarkerHelperMac instancesRespondToSelector:selector]) { |
+ DCHECK(![self respondsToSelector:selector]); |
+ return |
+ [[self textMarkerHelper] performSelector:selector withObject:parameter]; |
+ } |
+ |
if ([self respondsToSelector:selector]) |
- return [self performSelector:selector]; |
+ return [self performSelector:selector withObject:parameter]; |
+ |
+ // TODO(tapted): Move these to TextMarkerHelper. |
+ if ([attribute isEqualToString:@"AXStringForTextMarkerRange"]) |
+ return ui::GetTextForTextMarkerRange(parameter); |
+ |
+ if ([attribute isEqualToString:@"AXAttributedStringForTextMarkerRange"]) { |
+ if (NSString* str = ui::GetTextForTextMarkerRange(parameter)) { |
+ base::scoped_nsobject<NSAttributedString> attrString( |
+ [[NSAttributedString alloc] initWithString:str]); |
+ return attrString.autorelease(); |
+ } |
+ } |
+ |
+ if ([attribute isEqualToString:@"AXLengthForTextMarkerRange"]) { |
+ NSString* text = ui::GetTextForTextMarkerRange(parameter); |
+ return [NSNumber numberWithInt:[text length]]; |
+ } |
+ NSLog(@"Usupported: %@ for %@", attribute, parameter); |
return nil; |
} |
@@ -533,6 +672,10 @@ void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) { |
return [self getStringAttribute:ui::AX_ATTR_DESCRIPTION]; |
} |
+- (NSString*)AXDescription { |
+ return [self getStringAttribute:ui::AX_ATTR_DESCRIPTION]; |
+} |
+ |
- (id)AXValue { |
switch (node_->GetData().role) { |
case ui::AX_ROLE_TAB: |
@@ -620,6 +763,10 @@ void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) { |
return [NSValue valueWithRange:{std::min(start, end), abs(end - start)}]; |
} |
+- (NSArray*)AXSelectedTextRanges { |
+ return @[ [self AXSelectedTextRange] ]; |
+} |
+ |
- (NSNumber*)AXNumberOfCharacters { |
return @([[self getAXValueAsString] length]); |
} |
@@ -633,6 +780,43 @@ void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) { |
return @0; |
} |
+- (id)AXStringForRange:(id)parameter { |
+ DCHECK([parameter isKindOfClass:[NSValue class]]); |
+ return [[self getAXValueAsString] substringWithRange:[parameter rangeValue]]; |
+} |
+ |
+- (id)AXAttributedStringForRange:(id)parameter { |
+ base::scoped_nsobject<NSAttributedString> attributedString( |
+ [[NSAttributedString alloc] |
+ initWithString:[self AXStringForRange:parameter]]); |
+ return attributedString.autorelease(); |
+} |
+ |
+- (id)AXRangeForLine:(id)parameter { |
+ DCHECK([parameter isKindOfClass:[NSNumber class]]); |
+ DCHECK_EQ(0, [parameter intValue]); |
+ id r = [NSValue valueWithRange:{0, [[self getAXValueAsString] length]}]; |
+ NSLog(@"-> %@", r); |
+ return r; |
+} |
+ |
+- (id)AXLineForIndex:(id)parameter { |
+ DCHECK([parameter isKindOfClass:[NSNumber class]]); |
+ return @0; |
+} |
+ |
+- (id)AXOwns { |
+ return @[]; |
+} |
+ |
+- (id)AXSharedTextUIElements { |
+ return @[ self ]; |
+} |
+ |
+- (id)AXSharedCharacterRange { |
+ return [NSValue valueWithRange:{0, [[self getAXValueAsString] length]}]; |
+} |
+ |
@end |
namespace ui { |
@@ -693,4 +877,26 @@ int AXPlatformNodeMac::GetIndexInParent() { |
return -1; |
} |
+AXPositionPointer NodePositionFactory::GetRoot() const { |
+ if (![node_ node]) |
+ return nullptr; |
+ |
+ return AXNodePosition::CreateTextPosition(1, [node_ node]->unique_id(), 0, |
+ AX_TEXT_AFFINITY_DOWNSTREAM); |
+} |
+ |
+AXRangePointer NodePositionFactory::GetSelection() const { |
+ return AXRangePointer(nullptr, nullptr); |
+} |
+ |
+AXPositionPointer NodePositionFactory::GetFromData( |
+ const AXPositionData& data) const { |
+ return AXNodePosition::CreateFromData(data); |
+} |
+ |
+id NodePositionFactory::GetAccessibilityObject( |
+ const AXPositionData& data) const { |
+ return node_; |
+} |
+ |
} // namespace ui |