Chromium Code Reviews| 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()); |
| +} |