| 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
|
|
|