Index: content/browser/accessibility/browser_accessibility_cocoa.mm |
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm |
index bf5b5e2821bf9caeba7286cfb6972a1b00ea3d41..6ac825d1b12dfa5000e0fa1efac5d464272ff728 100644 |
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm |
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm |
@@ -28,14 +28,11 @@ |
#include "third_party/skia/include/core/SkColor.h" |
#include "ui/accessibility/ax_range.h" |
#import "ui/accessibility/platform/ax_platform_node_mac.h" |
+#import "ui/accessibility/platform/text_marker_helper_mac.h" |
-using AXAbstractPositionInstance = |
- content::AXPlatformPosition::AXPositionInstance; |
using AXPlatformPositionInstance = |
content::AXPlatformPosition::ConcreteInstance; |
using AXPlatformRange = ui::AXRange<AXPlatformPositionInstance::element_type>; |
-using AXTextMarkerRangeRef = CFTypeRef; |
-using AXTextMarkerRef = CFTypeRef; |
using StringAttribute = ui::AXStringAttribute; |
using content::AXPlatformPosition; |
using content::AccessibilityMatchPredicate; |
@@ -113,63 +110,30 @@ NSDictionary* attributeToMethodNameMap = nil; |
// VoiceOver uses -1 to mean "no limit" for AXResultsLimit. |
const int kAXResultsLimitNoLimit = -1; |
-extern "C" { |
- |
-// The following are private accessibility APIs required for cursor navigation |
-// and text selection. VoiceOver started relying on them in Mac OS X 10.11. |
-#if !defined(MAC_OS_X_VERSION_10_11) || \ |
- MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11 |
- |
-AXTextMarkerRef AXTextMarkerCreate(CFAllocatorRef allocator, |
- const UInt8* bytes, |
- CFIndex length); |
- |
-const UInt8* AXTextMarkerGetBytePtr(AXTextMarkerRef text_marker); |
- |
-size_t AXTextMarkerGetLength(AXTextMarkerRef text_marker); |
- |
-AXTextMarkerRangeRef AXTextMarkerRangeCreate(CFAllocatorRef allocator, |
- AXTextMarkerRef start_marker, |
- AXTextMarkerRef end_marker); |
- |
-AXTextMarkerRef AXTextMarkerRangeCopyStartMarker( |
- AXTextMarkerRangeRef text_marker_range); |
+static_assert(sizeof(AXPlatformPosition) == sizeof(ui::AXPositionBase), |
+ "AXPosition size mismatch"); |
+constexpr size_t kAXPositionSize = sizeof(ui::AXPositionBase); |
-AXTextMarkerRef AXTextMarkerRangeCopyEndMarker( |
- AXTextMarkerRangeRef text_marker_range); |
+class BrowserPositionFactory : public ui::PositionFactory { |
+ public: |
+ explicit BrowserPositionFactory( |
+ content::BrowserAccessibility* browser_accessibility) |
+ : browser_accessibility_(browser_accessibility) {} |
-#endif // MAC_OS_X_VERSION_10_11 |
+ std::unique_ptr<ui::AXPositionBase> GetRoot() const override; |
+ std::unique_ptr<ui::AXAbstractRange> GetSelection() const override; |
+ std::unique_ptr<ui::AXPositionBase> ExtractFromMarker( |
+ AXTextMarkerRef marker) const override; |
-} // extern "C" |
+ private: |
+ bool IsActive() const { |
+ return browser_accessibility_ && browser_accessibility_->instance_active(); |
+ } |
-static_assert(sizeof(AXPlatformPosition) == sizeof(ui::AXPositionBase), |
- "AXPosition size mismatch"); |
-constexpr size_t kAXPositionSize = sizeof(ui::AXPositionBase); |
+ content::BrowserAccessibility* browser_accessibility_; |
-// to call |release| on it to transfer ownership of the position to the text |
-// marker object. |
-id CreateTextMarker(AXAbstractPositionInstance position) { |
- AXTextMarkerRef text_marker = AXTextMarkerCreate( |
- kCFAllocatorDefault, reinterpret_cast<const UInt8*>(position.release()), |
- kAXPositionSize); |
- return static_cast<id>( |
- base::mac::CFTypeRefToNSObjectAutorelease(text_marker)); |
-} |
- |
-// |range| is destructed at the end of this method and ownership of its |anchor| |
-// and |focus| are transfered to the marker range object. |
-id CreateTextMarkerRange(const ui::AXAbstractRange range) { |
- AXTextMarkerRef start_marker = AXTextMarkerCreate( |
- kCFAllocatorDefault, reinterpret_cast<const UInt8*>(range.anchor()), |
- kAXPositionSize); |
- AXTextMarkerRef end_marker = AXTextMarkerCreate( |
- kCFAllocatorDefault, reinterpret_cast<const UInt8*>(range.focus()), |
- kAXPositionSize); |
- AXTextMarkerRangeRef marker_range = |
- AXTextMarkerRangeCreate(kCFAllocatorDefault, start_marker, end_marker); |
- return static_cast<id>( |
- base::mac::CFTypeRefToNSObjectAutorelease(marker_range)); |
-} |
+ DISALLOW_COPY_AND_ASSIGN(BrowserPositionFactory); |
+}; |
AXPlatformPositionInstance CreateConcretePositionFromTextMarker( |
AXTextMarkerRef text_marker) { |
@@ -189,12 +153,6 @@ AXPlatformPositionInstance CreateConcretePositionFromTextMarker( |
return position; |
} |
-AXAbstractPositionInstance CreatePositionFromTextMarker( |
- AXTextMarkerRef text_marker) { |
- return base::WrapUnique( |
- CreateConcretePositionFromTextMarker(text_marker).release()); |
-} |
- |
AXPlatformRange CreateRangeFromTextMarkerRange( |
AXTextMarkerRangeRef marker_range) { |
DCHECK(marker_range); |
@@ -536,7 +494,16 @@ NSString* const NSAccessibilityRequiredAttribute = @"AXRequired"; |
} |
#endif // MAC_OS_X_VERSION_10_12 |
-@implementation BrowserAccessibilityCocoa |
+@interface BrowserAccessibilityCocoa () |
+ |
+// Lazily creates a TextMarkerHelper and returns it. |
+- (TextMarkerHelperMac*)textMarkerHelper; |
+ |
+@end |
+ |
+@implementation BrowserAccessibilityCocoa { |
+ base::scoped_nsobject<TextMarkerHelperMac> textMarkerHelper_; |
+} |
+ (void)initialize { |
const struct { |
@@ -979,16 +946,12 @@ NSString* const NSAccessibilityRequiredAttribute = @"AXRequired"; |
!GetState(browserAccessibility_, ui::AX_STATE_DISABLED)]; |
} |
-// Returns a text marker that points to the last character in the document that |
-// can be selected with VoiceOver. |
- (id)endTextMarker { |
- const BrowserAccessibility* root = |
- browserAccessibility_->manager()->GetRoot(); |
- if (!root) |
- return nil; |
+ return [[self textMarkerHelper] endTextMarker]; |
+} |
- AXAbstractPositionInstance position = root->CreatePositionAt(0); |
- return CreateTextMarker(position->CreatePositionAtEndOfAnchor()); |
+- (id)startTextMarker { |
tapted
2017/06/19 11:44:30
(self review: swap order with endTextMarker)
|
+ return [[self textMarkerHelper] startTextMarker]; |
} |
- (NSNumber*)expanded { |
@@ -1682,35 +1645,7 @@ NSString* const NSAccessibilityRequiredAttribute = @"AXRequired"; |
} |
- (id)selectedTextMarkerRange { |
- if (![self instanceActive]) |
- return nil; |
- |
- BrowserAccessibilityManager* manager = browserAccessibility_->manager(); |
- if (!manager) |
- return nil; |
- |
- int32_t anchorId = manager->GetTreeData().sel_anchor_object_id; |
- const BrowserAccessibility* anchorObject = manager->GetFromID(anchorId); |
- if (!anchorObject) |
- return nil; |
- |
- int32_t focusId = manager->GetTreeData().sel_focus_object_id; |
- const BrowserAccessibility* focusObject = manager->GetFromID(focusId); |
- if (!focusObject) |
- return nil; |
- |
- int anchorOffset = manager->GetTreeData().sel_anchor_offset; |
- int focusOffset = manager->GetTreeData().sel_focus_offset; |
- if (anchorOffset < 0 || focusOffset < 0) |
- return nil; |
- |
- ui::AXTextAffinity anchorAffinity = |
- manager->GetTreeData().sel_anchor_affinity; |
- ui::AXTextAffinity focusAffinity = manager->GetTreeData().sel_focus_affinity; |
- |
- return CreateTextMarkerRange(CreateTextRange(*anchorObject, anchorOffset, |
- anchorAffinity, *focusObject, |
- focusOffset, focusAffinity)); |
+ return [[self textMarkerHelper] selectedTextMarkerRange]; |
} |
- (NSValue*)size { |
@@ -1744,18 +1679,6 @@ NSString* const NSAccessibilityRequiredAttribute = @"AXRequired"; |
return nil; |
} |
-// Returns a text marker that points to the first character in the document that |
-// can be selected with VoiceOver. |
-- (id)startTextMarker { |
- const BrowserAccessibility* root = |
- browserAccessibility_->manager()->GetRoot(); |
- if (!root) |
- return nil; |
- |
- AXAbstractPositionInstance position = root->CreatePositionAt(0); |
- return CreateTextMarker(position->CreatePositionAtStartOfAnchor()); |
-} |
- |
// Returns a subrole based upon the role. |
- (NSString*) subrole { |
if (![self instanceActive]) |
@@ -2026,6 +1949,15 @@ NSString* const NSAccessibilityRequiredAttribute = @"AXRequired"; |
return [attributedValue attributedSubstringFromRange:range]; |
} |
+- (TextMarkerHelperMac*)textMarkerHelper { |
+ if (!textMarkerHelper_) { |
+ textMarkerHelper_.reset([[TextMarkerHelperMac alloc] |
+ initWithFactory:base::MakeUnique<BrowserPositionFactory>( |
+ browserAccessibility_)]); |
+ } |
+ return textMarkerHelper_; |
+} |
+ |
// Returns the accessibility value for the given attribute. If the value isn't |
// supported this will return nil. |
- (id)accessibilityAttributeValue:(NSString*)attribute { |
@@ -2142,165 +2074,35 @@ NSString* const NSAccessibilityRequiredAttribute = @"AXRequired"; |
return nil; |
} |
+ SEL selector = NSSelectorFromString(attribute); |
tapted
2017/06/19 11:44:30
(self review: this needs a colon appended to the s
|
+ DCHECK(selector); |
+ if ([TextMarkerHelperMac instancesRespondToSelector:selector]) { |
+ return |
+ [[self textMarkerHelper] performSelector:selector withObject:parameter]; |
+ } |
+ |
if ([attribute isEqualToString:@"AXUIElementForTextMarker"]) { |
- AXPlatformPositionInstance position = |
- CreateConcretePositionFromTextMarker(parameter); |
+ AXPlatformPositionInstance position = CreateConcretePositionFromTextMarker( |
+ base::mac::CFCastStrict<AXTextMarkerRef>(parameter)); |
if (!position->IsNullPosition()) |
return ToBrowserAccessibilityCocoa(position->GetAnchor()); |
return nil; |
} |
- if ([attribute isEqualToString:@"AXTextMarkerRangeForUIElement"]) { |
- AXAbstractPositionInstance startPosition = |
- browserAccessibility_->CreatePositionAt(0); |
- AXAbstractPositionInstance endPosition = |
- startPosition->CreatePositionAtEndOfAnchor(); |
- ui::AXAbstractRange range = |
- ui::AXAbstractRange(std::move(startPosition), std::move(endPosition)); |
- return CreateTextMarkerRange(std::move(range)); |
+ if ([attribute isEqualToString:@"AXStringForTextMarkerRange"]) { |
+ return GetTextForTextMarkerRange( |
+ base::mac::CFCastStrict<AXTextMarkerRangeRef>(parameter)); |
} |
- if ([attribute isEqualToString:@"AXStringForTextMarkerRange"]) |
- return GetTextForTextMarkerRange(parameter); |
- |
- if ([attribute isEqualToString:@"AXAttributedStringForTextMarkerRange"]) |
- return GetAttributedTextForTextMarkerRange(parameter); |
- |
- if ([attribute isEqualToString:@"AXNextTextMarkerForTextMarker"]) { |
- AXAbstractPositionInstance position = |
- CreatePositionFromTextMarker(parameter); |
- if (position->IsNullPosition()) |
- return nil; |
- return CreateTextMarker(position->CreateNextCharacterPosition()); |
- } |
- |
- if ([attribute isEqualToString:@"AXPreviousTextMarkerForTextMarker"]) { |
- AXAbstractPositionInstance position = |
- CreatePositionFromTextMarker(parameter); |
- if (position->IsNullPosition()) |
- return nil; |
- return CreateTextMarker(position->CreatePreviousCharacterPosition()); |
- } |
- |
- if ([attribute isEqualToString:@"AXLeftWordTextMarkerRangeForTextMarker"]) { |
- AXAbstractPositionInstance endPosition = |
- CreatePositionFromTextMarker(parameter); |
- if (endPosition->IsNullPosition()) |
- return nil; |
- |
- AXAbstractPositionInstance startWordPosition = |
- endPosition->CreatePreviousWordStartPosition(); |
- AXAbstractPositionInstance endWordPosition = |
- endPosition->CreatePreviousWordEndPosition(); |
- AXAbstractPositionInstance startPosition = |
- *startWordPosition <= *endWordPosition ? std::move(endWordPosition) |
- : std::move(startWordPosition); |
- ui::AXAbstractRange range(std::move(startPosition), std::move(endPosition)); |
- return CreateTextMarkerRange(std::move(range)); |
- } |
- |
- if ([attribute isEqualToString:@"AXRightWordTextMarkerRangeForTextMarker"]) { |
- AXAbstractPositionInstance startPosition = |
- CreatePositionFromTextMarker(parameter); |
- if (startPosition->IsNullPosition()) |
- return nil; |
- |
- AXAbstractPositionInstance endWordPosition = |
- startPosition->CreateNextWordEndPosition(); |
- AXAbstractPositionInstance startWordPosition = |
- startPosition->CreateNextWordStartPosition(); |
- AXAbstractPositionInstance endPosition = |
- *startWordPosition <= *endWordPosition ? std::move(startWordPosition) |
- : std::move(endWordPosition); |
- ui::AXAbstractRange range(std::move(startPosition), std::move(endPosition)); |
- return CreateTextMarkerRange(std::move(range)); |
- } |
- |
- if ([attribute isEqualToString:@"AXNextWordEndTextMarkerForTextMarker"]) { |
- AXAbstractPositionInstance position = |
- CreatePositionFromTextMarker(parameter); |
- if (position->IsNullPosition()) |
- return nil; |
- return CreateTextMarker(position->CreateNextWordEndPosition()); |
- } |
- |
- if ([attribute |
- isEqualToString:@"AXPreviousWordStartTextMarkerForTextMarker"]) { |
- AXAbstractPositionInstance position = |
- CreatePositionFromTextMarker(parameter); |
- if (position->IsNullPosition()) |
- return nil; |
- return CreateTextMarker(position->CreatePreviousWordStartPosition()); |
- } |
- |
- if ([attribute isEqualToString:@"AXTextMarkerRangeForLine"]) { |
- AXAbstractPositionInstance position = |
- CreatePositionFromTextMarker(parameter); |
- if (position->IsNullPosition()) |
- return nil; |
- |
- AXAbstractPositionInstance startPosition = |
- position->CreatePreviousLineStartPosition(); |
- AXAbstractPositionInstance endPosition = |
- position->CreateNextLineEndPosition(); |
- ui::AXAbstractRange range(std::move(startPosition), std::move(endPosition)); |
- return CreateTextMarkerRange(std::move(range)); |
- } |
- |
- if ([attribute isEqualToString:@"AXLeftLineTextMarkerRangeForTextMarker"]) { |
- AXAbstractPositionInstance endPosition = |
- CreatePositionFromTextMarker(parameter); |
- if (endPosition->IsNullPosition()) |
- return nil; |
- |
- AXAbstractPositionInstance startLinePosition = |
- endPosition->CreatePreviousLineStartPosition(); |
- AXAbstractPositionInstance endLinePosition = |
- endPosition->CreatePreviousLineEndPosition(); |
- AXAbstractPositionInstance startPosition = |
- *startLinePosition <= *endLinePosition ? std::move(endLinePosition) |
- : std::move(startLinePosition); |
- ui::AXAbstractRange range(std::move(startPosition), std::move(endPosition)); |
- return CreateTextMarkerRange(std::move(range)); |
- } |
- |
- if ([attribute isEqualToString:@"AXRightLineTextMarkerRangeForTextMarker"]) { |
- AXAbstractPositionInstance startPosition = |
- CreatePositionFromTextMarker(parameter); |
- if (startPosition->IsNullPosition()) |
- return nil; |
- |
- AXAbstractPositionInstance startLinePosition = |
- startPosition->CreateNextLineStartPosition(); |
- AXAbstractPositionInstance endLinePosition = |
- startPosition->CreateNextLineEndPosition(); |
- AXAbstractPositionInstance endPosition = |
- *startLinePosition <= *endLinePosition ? std::move(startLinePosition) |
- : std::move(endLinePosition); |
- ui::AXAbstractRange range(std::move(startPosition), std::move(endPosition)); |
- return CreateTextMarkerRange(std::move(range)); |
- } |
- |
- if ([attribute isEqualToString:@"AXNextLineEndTextMarkerForTextMarker"]) { |
- AXAbstractPositionInstance position = |
- CreatePositionFromTextMarker(parameter); |
- if (position->IsNullPosition()) |
- return nil; |
- return CreateTextMarker(position->CreateNextLineEndPosition()); |
- } |
- |
- if ([attribute |
- isEqualToString:@"AXPreviousLineStartTextMarkerForTextMarker"]) { |
- AXAbstractPositionInstance position = |
- CreatePositionFromTextMarker(parameter); |
- if (position->IsNullPosition()) |
- return nil; |
- return CreateTextMarker(position->CreatePreviousLineStartPosition()); |
+ if ([attribute isEqualToString:@"AXAttributedStringForTextMarkerRange"]) { |
+ return GetAttributedTextForTextMarkerRange( |
+ base::mac::CFCastStrict<AXTextMarkerRangeRef>(parameter)); |
} |
if ([attribute isEqualToString:@"AXLengthForTextMarkerRange"]) { |
- NSString* text = GetTextForTextMarkerRange(parameter); |
+ NSString* text = GetTextForTextMarkerRange( |
+ base::mac::CFCastStrict<AXTextMarkerRangeRef>(parameter)); |
return [NSNumber numberWithInt:[text length]]; |
} |
@@ -2340,24 +2142,14 @@ NSString* const NSAccessibilityRequiredAttribute = @"AXRequired"; |
return nil; |
} |
- if ([attribute isEqualToString: |
- NSAccessibilityLineTextMarkerRangeForTextMarkerParameterizedAttribute]) { |
- AXAbstractPositionInstance position = |
- CreatePositionFromTextMarker(parameter); |
- if (position->IsNullPosition()) |
- return nil; |
- |
- ui::AXAbstractRange range(position->CreatePreviousLineStartPosition(), |
- position->CreateNextLineEndPosition()); |
- return CreateTextMarkerRange(std::move(range)); |
- } |
- |
+ // TODO(tapted): Move this to TextMarkerHelper. |
if ([attribute isEqualToString: |
NSAccessibilityBoundsForTextMarkerRangeParameterizedAttribute]) { |
BrowserAccessibility* startObject; |
BrowserAccessibility* endObject; |
int startOffset, endOffset; |
- AXPlatformRange range = CreateRangeFromTextMarkerRange(parameter); |
+ AXPlatformRange range = CreateRangeFromTextMarkerRange( |
+ base::mac::CFCastStrict<AXTextMarkerRangeRef>(parameter)); |
if (range.IsNull()) |
return nil; |
@@ -2380,28 +2172,6 @@ NSString* const NSAccessibilityRequiredAttribute = @"AXRequired"; |
} |
if ([attribute isEqualToString: |
- NSAccessibilityTextMarkerRangeForUnorderedTextMarkersParameterizedAttribute]) { |
- if (![parameter isKindOfClass:[NSArray class]]) |
- return nil; |
- |
- NSArray* text_marker_array = parameter; |
- if ([text_marker_array count] != 2) |
- return nil; |
- |
- AXAbstractPositionInstance startPosition = |
- CreatePositionFromTextMarker([text_marker_array objectAtIndex:0]); |
- AXAbstractPositionInstance endPosition = |
- CreatePositionFromTextMarker([text_marker_array objectAtIndex:1]); |
- if (*startPosition <= *endPosition) { |
- return CreateTextMarkerRange(ui::AXAbstractRange(std::move(startPosition), |
- std::move(endPosition))); |
- } else { |
- return CreateTextMarkerRange(ui::AXAbstractRange( |
- std::move(endPosition), std::move(startPosition))); |
- } |
- } |
- |
- if ([attribute isEqualToString: |
NSAccessibilityIndexForChildUIElementParameterizedAttribute]) { |
if (![parameter isKindOfClass:[BrowserAccessibilityCocoa class]]) |
return nil; |
@@ -2898,3 +2668,51 @@ NSString* const NSAccessibilityRequiredAttribute = @"AXRequired"; |
} |
@end |
+ |
+std::unique_ptr<ui::AXPositionBase> BrowserPositionFactory::GetRoot() const { |
+ const BrowserAccessibility* root = |
+ browser_accessibility_->manager()->GetRoot(); |
+ if (!root) |
+ return nullptr; |
+ |
+ return root->CreatePositionAt(0); |
+} |
+ |
+std::unique_ptr<ui::AXAbstractRange> BrowserPositionFactory::GetSelection() |
+ const { |
+ if (!IsActive()) |
+ return nullptr; |
+ |
+ BrowserAccessibilityManager* manager = browser_accessibility_->manager(); |
+ if (!manager) |
+ return nullptr; |
+ |
+ int32_t anchorId = manager->GetTreeData().sel_anchor_object_id; |
tapted
2017/06/19 11:44:30
(self review: convert to hacker_case)
|
+ const BrowserAccessibility* anchorObject = manager->GetFromID(anchorId); |
+ if (!anchorObject) |
+ return nullptr; |
+ |
+ int32_t focusId = manager->GetTreeData().sel_focus_object_id; |
+ const BrowserAccessibility* focusObject = manager->GetFromID(focusId); |
+ if (!focusObject) |
+ return nullptr; |
+ |
+ int anchorOffset = manager->GetTreeData().sel_anchor_offset; |
+ int focusOffset = manager->GetTreeData().sel_focus_offset; |
+ if (anchorOffset < 0 || focusOffset < 0) |
+ return nullptr; |
+ |
+ ui::AXTextAffinity anchorAffinity = |
+ manager->GetTreeData().sel_anchor_affinity; |
+ ui::AXTextAffinity focusAffinity = manager->GetTreeData().sel_focus_affinity; |
+ |
+ return base::MakeUnique<ui::AXAbstractRange>( |
+ CreateTextRange(*anchorObject, anchorOffset, anchorAffinity, *focusObject, |
+ focusOffset, focusAffinity)); |
+} |
+ |
+std::unique_ptr<ui::AXPositionBase> BrowserPositionFactory::ExtractFromMarker( |
+ AXTextMarkerRef marker) const { |
+ return base::WrapUnique( |
+ CreateConcretePositionFromTextMarker(marker).release()); |
+} |