// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/platform/ax_utils_mac.h"
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
#include "base/apple/scoped_cftyperef.h"
#include "ui/accessibility/ax_range.h"
#include "ui/accessibility/platform/ax_platform_node_base.h"
#include "ui/accessibility/platform/ax_platform_node_cocoa.h"
#include "ui/accessibility/platform/ax_platform_node_delegate.h"
namespace ui {
bool IsAXTextMarker(id object) {
if (object == nil) {
return false;
}
return CFGetTypeID((__bridge CFTypeRef)object) == AXTextMarkerGetTypeID();
}
bool IsAXTextMarkerRange(id object) {
if (object == nil) {
return false;
}
return CFGetTypeID((__bridge CFTypeRef)object) ==
AXTextMarkerRangeGetTypeID();
}
AXPlatformNodeDelegate::AXPosition AXTextMarkerToAXPosition(id text_marker) {
if (!IsAXTextMarker(text_marker)) {
return AXNodePosition::CreateNullPosition();
}
AXTextMarkerRef cf_text_marker = (__bridge AXTextMarkerRef)text_marker;
if (AXTextMarkerGetLength(cf_text_marker) !=
sizeof(AXPlatformNodeDelegate::SerializedPosition)) {
return AXNodePosition::CreateNullPosition();
}
const UInt8* source_buffer = AXTextMarkerGetBytePtr(cf_text_marker);
if (!source_buffer) {
return AXNodePosition::CreateNullPosition();
}
return AXNodePosition::Unserialize(
*reinterpret_cast<const AXPlatformNodeDelegate::SerializedPosition*>(
source_buffer));
}
AXPlatformNodeDelegate::AXRange AXTextMarkerRangeToAXRange(
id text_marker_range) {
if (!IsAXTextMarkerRange(text_marker_range)) {
return AXPlatformNodeDelegate::AXRange();
}
AXTextMarkerRangeRef cf_marker_range =
(__bridge AXTextMarkerRangeRef)text_marker_range;
id start_marker =
CFBridgingRelease(AXTextMarkerRangeCopyStartMarker(cf_marker_range));
id end_marker =
CFBridgingRelease(AXTextMarkerRangeCopyEndMarker(cf_marker_range));
if (!start_marker || !end_marker) {
return AXPlatformNodeDelegate::AXRange();
}
// |AXPlatformNodeDelegate::AXRange| takes ownership of its anchor and focus.
AXPlatformNodeDelegate::AXPosition anchor =
AXTextMarkerToAXPosition(start_marker);
AXPlatformNodeDelegate::AXPosition focus =
AXTextMarkerToAXPosition(end_marker);
return AXPlatformNodeDelegate::AXRange(std::move(anchor), std::move(focus));
}
id AXPositionToAXTextMarker(AXPlatformNodeDelegate::AXPosition position) {
// AXTextMarkerCreate is a system function that makes a copy of the data
// buffer given to it.
AXPlatformNodeDelegate::SerializedPosition serialized = position->Serialize();
return CFBridgingRelease(AXTextMarkerCreate(
kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized),
sizeof(AXPlatformNodeDelegate::SerializedPosition)));
}
id AXRangeToAXTextMarkerRange(AXPlatformNodeDelegate::AXRange range) {
AXPlatformNodeDelegate::SerializedPosition serialized_anchor =
range.anchor()->Serialize();
AXPlatformNodeDelegate::SerializedPosition serialized_focus =
range.focus()->Serialize();
base::apple::ScopedCFTypeRef<AXTextMarkerRef> start_marker(AXTextMarkerCreate(
kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized_anchor),
sizeof(AXPlatformNodeDelegate::SerializedPosition)));
base::apple::ScopedCFTypeRef<AXTextMarkerRef> end_marker(AXTextMarkerCreate(
kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized_focus),
sizeof(AXPlatformNodeDelegate::SerializedPosition)));
return CFBridgingRelease(AXTextMarkerRangeCreate(
kCFAllocatorDefault, start_marker.get(), end_marker.get()));
}
id AXTextMarkerFrom(AXPlatformNodeCocoa* anchor,
int offset,
ax::mojom::TextAffinity affinity) {
AXPlatformNode* anchor_platform_node = anchor.node;
AXPlatformNodeDelegate* anchor_node = anchor_platform_node->GetDelegate();
AXPlatformNodeDelegate::AXPosition position =
anchor_node->CreateTextPositionAt(offset, affinity);
return AXPositionToAXTextMarker(std::move(position));
}
id AXTextMarkerRangeFrom(id start_textmarker, id end_textmarker) {
return CFBridgingRelease(AXTextMarkerRangeCreate(
kCFAllocatorDefault, (__bridge AXTextMarkerRef)start_textmarker,
(__bridge AXTextMarkerRef)end_textmarker));
}
id AXTextMarkerRangeStart(id text_marker_range) {
return CFBridgingRelease(AXTextMarkerRangeCopyStartMarker(
(__bridge AXTextMarkerRangeRef)text_marker_range));
}
id AXTextMarkerRangeEnd(id text_marker_range) {
return CFBridgingRelease(AXTextMarkerRangeCopyEndMarker(
(__bridge AXTextMarkerRangeRef)text_marker_range));
}
} // namespace ui