| Index: ui/accessibility/platform/text_marker_helper_mac.mm | 
| diff --git a/ui/accessibility/platform/text_marker_helper_mac.mm b/ui/accessibility/platform/text_marker_helper_mac.mm | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..a4d3025b1cb39370aa1c335b37649aa8ca99df28 | 
| --- /dev/null | 
| +++ b/ui/accessibility/platform/text_marker_helper_mac.mm | 
| @@ -0,0 +1,293 @@ | 
| +// Copyright 2017 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#import "ui/accessibility/platform/text_marker_helper_mac.h" | 
| + | 
| +#import <Cocoa/Cocoa.h> | 
| + | 
| +#import "base/mac/foundation_util.h" | 
| +#include "base/mac/scoped_cftyperef.h" | 
| +#include "ui/accessibility/ax_position.h" | 
| +#include "ui/accessibility/ax_range.h" | 
| + | 
| +using ui::AXPositionPointer; | 
| +using ui::AXRangePointer; | 
| +using ui::AXPositionData; | 
| + | 
| +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. | 
| +AXTextMarkerRef AXTextMarkerCreate(CFAllocatorRef allocator, | 
| +                                   const UInt8* bytes, | 
| +                                   CFIndex length); | 
| + | 
| +AXTextMarkerRangeRef AXTextMarkerRangeCreate(CFAllocatorRef allocator, | 
| +                                             AXTextMarkerRef start_marker, | 
| +                                             AXTextMarkerRef end_marker); | 
| +const UInt8* AXTextMarkerGetBytePtr(AXTextMarkerRef text_marker); | 
| +size_t AXTextMarkerGetLength(AXTextMarkerRef text_marker); | 
| +AXTextMarkerRef AXTextMarkerRangeCopyStartMarker( | 
| +    AXTextMarkerRangeRef text_marker_range); | 
| +AXTextMarkerRef AXTextMarkerRangeCopyEndMarker( | 
| +    AXTextMarkerRangeRef text_marker_range); | 
| + | 
| +}  // extern "C" | 
| + | 
| +namespace { | 
| + | 
| +constexpr size_t kDataSize = sizeof(AXPositionData); | 
| + | 
| +// AXTextMarkerCreate copies from data buffer given to it. | 
| +id CreateTextMarker(AXPositionPointer position) { | 
| +  AXPositionData data; | 
| +  position->ToData(&data); | 
| + | 
| +  AXTextMarkerRef text_marker = AXTextMarkerCreate( | 
| +      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&data), kDataSize); | 
| +  return static_cast<id>( | 
| +      base::mac::CFTypeRefToNSObjectAutorelease(text_marker)); | 
| +} | 
| + | 
| +// |range| is destructed at the end of this method. |anchor| and |focus| are | 
| +// copied into the individual text markers. | 
| +id CreateTextMarkerRange(AXRangePointer range) { | 
| +  AXPositionData anchor, focus; | 
| +  range.first->ToData(&anchor); | 
| +  range.second->ToData(&focus); | 
| + | 
| +  base::ScopedCFTypeRef<AXTextMarkerRef> start_marker(AXTextMarkerCreate( | 
| +      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&anchor), kDataSize)); | 
| +  base::ScopedCFTypeRef<AXTextMarkerRef> end_marker(AXTextMarkerCreate( | 
| +      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&focus), kDataSize)); | 
| +  AXTextMarkerRangeRef marker_range = | 
| +      AXTextMarkerRangeCreate(kCFAllocatorDefault, start_marker, end_marker); | 
| +  return static_cast<id>( | 
| +      base::mac::CFTypeRefToNSObjectAutorelease(marker_range)); | 
| +} | 
| + | 
| +bool ExtractData(AXTextMarkerRef text_marker, AXPositionData* data) { | 
| +  DCHECK(text_marker); | 
| + | 
| +  if (AXTextMarkerGetLength(text_marker) != kDataSize) | 
| +    return false; | 
| + | 
| +  const UInt8* source_buffer = AXTextMarkerGetBytePtr(text_marker); | 
| +  if (!source_buffer) | 
| +    return false; | 
| + | 
| +  *data = *reinterpret_cast<const AXPositionData*>(source_buffer); | 
| +  return true; | 
| +} | 
| + | 
| +// Of two positions, returns the one that comes first (destroys the other). | 
| +AXPositionPointer FirstOf(AXPositionPointer a, AXPositionPointer b) { | 
| +  return b->Compare(a) ? std::move(b) : std::move(a); | 
| +} | 
| + | 
| +}  // namespace | 
| + | 
| +@interface TextMarkerHelperMac () | 
| +- (AXPositionPointer)extractFrom:(id)parameter; | 
| +@end | 
| + | 
| +@implementation TextMarkerHelperMac { | 
| +  std::unique_ptr<ui::PositionFactory> factory_; | 
| +} | 
| + | 
| +- (instancetype)initWithFactory:(std::unique_ptr<ui::PositionFactory>)factory { | 
| +  if ((self = [super init])) { | 
| +    factory_ = std::move(factory); | 
| +  } | 
| +  return self; | 
| +} | 
| + | 
| +- (id)startTextMarker { | 
| +  AXPositionPointer root = factory_->GetRoot(); | 
| +  return root ? CreateTextMarker(root->PositionAtStartOfAnchor()) : nil; | 
| +} | 
| + | 
| +- (id)endTextMarker { | 
| +  AXPositionPointer root = factory_->GetRoot(); | 
| +  return root ? CreateTextMarker(root->PositionAtEndOfAnchor()) : nil; | 
| +} | 
| + | 
| +- (id)selectedTextMarkerRange { | 
| +  AXRangePointer selection = factory_->GetSelection(); | 
| +  if (!selection.first || !selection.second) | 
| +    return nil; | 
| +  return CreateTextMarkerRange(std::move(selection)); | 
| +} | 
| + | 
| +- (AXPositionPointer)extractFrom:(id)parameter { | 
| +  AXPositionData data; | 
| +  if (ExtractData(base::mac::CFCastStrict<AXTextMarkerRef>(parameter), &data)) | 
| +    return factory_->GetFromData(data); | 
| +  return factory_->GetFromData(ui::AXAbstractPosition::kNullData); | 
| +} | 
| + | 
| +- (id)AXTextMarkerRangeForUIElement:(id)parameter { | 
| +  AXPositionPointer startPosition = factory_->GetRoot(); | 
| +  AXPositionPointer endPosition = startPosition->PositionAtEndOfAnchor(); | 
| +  AXRangePointer range = | 
| +      AXRangePointer(std::move(startPosition), std::move(endPosition)); | 
| +  return CreateTextMarkerRange(std::move(range)); | 
| +} | 
| + | 
| +- (id)AXUIElementForTextMarker:(id)parameter { | 
| +  AXPositionData data; | 
| +  if (ExtractData(base::mac::CFCastStrict<AXTextMarkerRef>(parameter), &data)) | 
| +    return factory_->GetAccessibilityObject(data); | 
| +  return nil; | 
| +} | 
| + | 
| +- (id)AXNextTextMarkerForTextMarker:(id)parameter { | 
| +  AXPositionPointer position = [self extractFrom:parameter]; | 
| +  if (position->IsNull()) | 
| +    return nil; | 
| +  return CreateTextMarker(position->NextCharacterPosition()); | 
| +} | 
| + | 
| +- (id)AXPreviousTextMarkerForTextMarker:(id)parameter { | 
| +  AXPositionPointer position = [self extractFrom:parameter]; | 
| +  if (position->IsNull()) | 
| +    return nil; | 
| +  return CreateTextMarker(position->PreviousCharacterPosition()); | 
| +} | 
| + | 
| +- (id)AXLeftWordTextMarkerRangeForTextMarker:(id)parameter { | 
| +  AXPositionPointer endPosition = [self extractFrom:parameter]; | 
| +  if (endPosition->IsNull()) | 
| +    return nil; | 
| + | 
| +  AXPositionPointer startPosition = | 
| +      FirstOf(endPosition->PreviousWordStartPosition(), | 
| +              endPosition->PreviousWordEndPosition()); | 
| +  AXRangePointer range(std::move(startPosition), std::move(endPosition)); | 
| +  return CreateTextMarkerRange(std::move(range)); | 
| +} | 
| + | 
| +- (id)AXRightWordTextMarkerRangeForTextMarker:(id)parameter { | 
| +  AXPositionPointer startPosition = [self extractFrom:parameter]; | 
| +  if (startPosition->IsNull()) | 
| +    return nil; | 
| + | 
| +  AXPositionPointer endPosition = | 
| +      FirstOf(startPosition->NextWordEndPosition(), | 
| +              startPosition->NextWordStartPosition()); | 
| +  AXRangePointer range(std::move(startPosition), std::move(endPosition)); | 
| +  return CreateTextMarkerRange(std::move(range)); | 
| +} | 
| + | 
| +- (id)AXNextWordEndTextMarkerForTextMarker:(id)parameter { | 
| +  AXPositionPointer position = [self extractFrom:parameter]; | 
| +  if (position->IsNull()) | 
| +    return nil; | 
| +  return CreateTextMarker(position->NextWordEndPosition()); | 
| +} | 
| + | 
| +- (id)AXPreviousWordStartTextMarkerForTextMarker:(id)parameter { | 
| +  AXPositionPointer position = [self extractFrom:parameter]; | 
| +  if (position->IsNull()) | 
| +    return nil; | 
| +  return CreateTextMarker(position->PreviousWordStartPosition()); | 
| +} | 
| + | 
| +- (id)AXTextMarkerRangeForLine:(id)parameter { | 
| +  AXPositionPointer position = [self extractFrom:parameter]; | 
| +  if (position->IsNull()) | 
| +    return nil; | 
| + | 
| +  AXPositionPointer startPosition = position->PreviousLineStartPosition(); | 
| +  AXPositionPointer endPosition = position->NextLineEndPosition(); | 
| +  AXRangePointer range(std::move(startPosition), std::move(endPosition)); | 
| +  return CreateTextMarkerRange(std::move(range)); | 
| +} | 
| + | 
| +- (id)AXLeftLineTextMarkerRangeForTextMarker:(id)parameter { | 
| +  AXPositionPointer endPosition = [self extractFrom:parameter]; | 
| +  if (endPosition->IsNull()) | 
| +    return nil; | 
| + | 
| +  AXPositionPointer startPosition = | 
| +      FirstOf(endPosition->PreviousLineStartPosition(), | 
| +              endPosition->PreviousLineEndPosition()); | 
| +  AXRangePointer range(std::move(startPosition), std::move(endPosition)); | 
| +  return CreateTextMarkerRange(std::move(range)); | 
| +} | 
| + | 
| +- (id)AXRightLineTextMarkerRangeForTextMarker:(id)parameter { | 
| +  AXPositionPointer startPosition = [self extractFrom:parameter]; | 
| +  if (startPosition->IsNull()) | 
| +    return nil; | 
| + | 
| +  AXPositionPointer endPosition = | 
| +      FirstOf(startPosition->NextLineStartPosition(), | 
| +              startPosition->NextLineEndPosition()); | 
| +  AXRangePointer range(std::move(startPosition), std::move(endPosition)); | 
| +  return CreateTextMarkerRange(std::move(range)); | 
| +} | 
| + | 
| +- (id)AXNextLineEndTextMarkerForTextMarker:(id)parameter { | 
| +  AXPositionPointer position = [self extractFrom:parameter]; | 
| +  if (position->IsNull()) | 
| +    return nil; | 
| +  return CreateTextMarker(position->NextLineEndPosition()); | 
| +} | 
| + | 
| +- (id)AXPreviousLineStartTextMarkerForTextMarker:(id)parameter { | 
| +  AXPositionPointer position = [self extractFrom:parameter]; | 
| +  if (position->IsNull()) | 
| +    return nil; | 
| +  return CreateTextMarker(position->PreviousLineStartPosition()); | 
| +} | 
| + | 
| +- (id)AXLineTextMarkerRangeForTextMarker:(id)parameter { | 
| +  AXPositionPointer position = [self extractFrom:parameter]; | 
| +  if (position->IsNull()) | 
| +    return nil; | 
| + | 
| +  AXRangePointer range(position->PreviousLineStartPosition(), | 
| +                       position->NextLineEndPosition()); | 
| +  return CreateTextMarkerRange(std::move(range)); | 
| +} | 
| + | 
| +- (id)AXTextMarkerRangeForUnorderedTextMarkers:(id)parameter { | 
| +  if (![parameter isKindOfClass:[NSArray class]]) | 
| +    return nil; | 
| + | 
| +  NSArray* textMarkerArray = parameter; | 
| +  if ([textMarkerArray count] != 2) | 
| +    return nil; | 
| + | 
| +  AXPositionPointer startPosition = | 
| +      [self extractFrom:[textMarkerArray objectAtIndex:0]]; | 
| +  AXPositionPointer endPosition = | 
| +      [self extractFrom:[textMarkerArray objectAtIndex:1]]; | 
| +  if (endPosition->Compare(startPosition)) { | 
| +    return CreateTextMarkerRange( | 
| +        AXRangePointer(std::move(endPosition), std::move(startPosition))); | 
| +  } | 
| +  return CreateTextMarkerRange( | 
| +      AXRangePointer(std::move(startPosition), std::move(endPosition))); | 
| +} | 
| + | 
| ++ (BOOL)getRangeDataFromMarkerRange:(id)parameter | 
| +                              start:(ui::AXPositionData*)start | 
| +                                end:(ui::AXPositionData*)end { | 
| +  AXTextMarkerRangeRef markerRange = | 
| +      base::mac::CFCastStrict<AXTextMarkerRangeRef>(parameter); | 
| +  DCHECK(markerRange); | 
| + | 
| +  base::ScopedCFTypeRef<AXTextMarkerRef> startMarker( | 
| +      AXTextMarkerRangeCopyStartMarker(markerRange)); | 
| +  base::ScopedCFTypeRef<AXTextMarkerRef> endMarker( | 
| +      AXTextMarkerRangeCopyEndMarker(markerRange)); | 
| +  if (!startMarker || !endMarker) | 
| +    return NO; | 
| + | 
| +  return ExtractData(startMarker, start) && ExtractData(endMarker, end); | 
| +} | 
| + | 
| +@end | 
|  |