// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ui/accessibility/platform/browser_accessibility_ios.h"
#import "base/apple/foundation_util.h"
#import "base/memory/ptr_util.h"
#import "ui/accessibility/platform/browser_accessibility_manager_ios.h"
#import "ui/accessibility/platform/ax_platform_node_ios.h"
#import "ui/accessibility/platform/ax_platform_node_ui_kit_element.h"
namespace ui {
// static
std::unique_ptr<BrowserAccessibility> BrowserAccessibility::Create(
BrowserAccessibilityManager* manager,
AXNode* node) {
return base::WrapUnique(new BrowserAccessibilityIOS(manager, node));
}
BrowserAccessibilityIOS::BrowserAccessibilityIOS(
BrowserAccessibilityManager* manager,
AXNode* node)
: BrowserAccessibility(manager, node) {}
BrowserAccessibilityIOS::~BrowserAccessibilityIOS() {
if (platform_node_) {
// `Destroy()` also deletes the object.
platform_node_.ExtractAsDangling()->Destroy();
}
}
void BrowserAccessibilityIOS::OnDataChanged() {
BrowserAccessibility::OnDataChanged();
if (platform_node_) {
// TODO(crbug.com/336611337): Investigate why this needs to be called
// unconditionally rather than just for children changes.
[base::apple::ObjCCastStrict<AXPlatformNodeUIKitElement>(
platform_node_->GetNativeViewAccessible()) childrenChanged];
return;
}
CreatePlatformNode();
}
size_t BrowserAccessibilityIOS::PlatformChildCount() const {
size_t child_count = BrowserAccessibility::PlatformChildCount();
// If this is a table, include the extra fake nodes generated by
// AXTableInfo, for the column nodes and the table header container, all of
// which are important only on macOS and iOS.
// TODO(crbug.com/336611337): Find a way to share all the extra table nodes
// logic with BrowserAccessibilityMac.
const std::vector<raw_ptr<AXNode, VectorExperimental>>* extra_mac_nodes =
node()->GetExtraMacNodes();
if (!extra_mac_nodes) {
return child_count;
}
return child_count + extra_mac_nodes->size();
}
BrowserAccessibility* BrowserAccessibilityIOS::PlatformGetChild(
size_t child_index) const {
if (child_index < BrowserAccessibility::PlatformChildCount()) {
return BrowserAccessibility::PlatformGetChild(child_index);
}
if (child_index >= PlatformChildCount()) {
return nullptr;
}
// If this is a table, include the extra fake nodes generated by
// AXTableInfo, for the column nodes and the table header container, all of
// which are only important on macOS and iOS.
const std::vector<raw_ptr<AXNode, VectorExperimental>>* extra_mac_nodes =
node()->GetExtraMacNodes();
if (!extra_mac_nodes || extra_mac_nodes->empty()) {
return nullptr;
}
child_index -= BrowserAccessibility::PlatformChildCount();
if (child_index < extra_mac_nodes->size()) {
return manager_->GetFromAXNode((*extra_mac_nodes)[child_index]);
}
return nullptr;
}
BrowserAccessibility* BrowserAccessibilityIOS::PlatformGetFirstChild() const {
return PlatformGetChild(0);
}
BrowserAccessibility* BrowserAccessibilityIOS::PlatformGetLastChild() const {
const std::vector<raw_ptr<AXNode, VectorExperimental>>* extra_mac_nodes =
node()->GetExtraMacNodes();
if (extra_mac_nodes && !extra_mac_nodes->empty()) {
return manager_->GetFromAXNode(extra_mac_nodes->back());
}
return BrowserAccessibility::PlatformGetLastChild();
}
BrowserAccessibility* BrowserAccessibilityIOS::PlatformGetNextSibling() const {
BrowserAccessibility* parent = PlatformGetParent();
if (parent) {
size_t next_child_index = node()->GetUnignoredIndexInParent() + 1;
if (next_child_index >= parent->InternalChildCount() &&
next_child_index < parent->PlatformChildCount()) {
// Get the extra_mac_node.
return parent->PlatformGetChild(next_child_index);
} else if (next_child_index >= parent->PlatformChildCount()) {
return nullptr;
}
}
return BrowserAccessibility::PlatformGetNextSibling();
}
BrowserAccessibility* BrowserAccessibilityIOS::PlatformGetPreviousSibling()
const {
BrowserAccessibility* parent = PlatformGetParent();
if (parent) {
size_t child_index = node()->GetUnignoredIndexInParent();
if (child_index > parent->InternalChildCount() &&
child_index <= parent->PlatformChildCount()) {
// Get the extra_mac_node.
return parent->PlatformGetChild(child_index - 1);
} else if (child_index == 0) {
return nullptr;
}
}
return BrowserAccessibility::PlatformGetPreviousSibling();
}
gfx::NativeViewAccessible BrowserAccessibilityIOS::GetNativeViewAccessible() {
return platform_node_ ? platform_node_->GetNativeViewAccessible() : nullptr;
}
AXPlatformNode* BrowserAccessibilityIOS::GetAXPlatformNode() const {
return platform_node_;
}
float BrowserAccessibilityIOS::GetDeviceScaleFactor() const {
return manager_->device_scale_factor();
}
void BrowserAccessibilityIOS::CreatePlatformNode() {
CHECK(!platform_node_);
platform_node_ =
static_cast<AXPlatformNodeIOS*>(AXPlatformNode::Create(this));
platform_node_->SetIOSDelegate(this);
}
} // namespace ui