#include "ui/accessibility/test_ax_node_helper.h"
#include <map>
#include <utility>
#include "base/strings/utf_string_conversions.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_role_properties.h"
#include "ui/accessibility/ax_table_info.h"
#include "ui/accessibility/ax_tree_observer.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace ui {
std::unique_ptr<TestAXNodeHelper> TestAXNodeHelper::Create(AXTree* tree,
AXNode* node) {
if (!tree || !node) {
return nullptr;
}
auto helper =
std::unique_ptr<TestAXNodeHelper>(new TestAXNodeHelper(tree, node));
return helper;
}
TestAXNodeHelper::TestAXNodeHelper(AXTree* tree, AXNode* node)
: tree_(tree), node_(node) {}
TestAXNodeHelper::~TestAXNodeHelper() = default;
gfx::Rect TestAXNodeHelper::GetBoundsRect(
const AXCoordinateSystem coordinate_system,
const AXClippingBehavior clipping_behavior,
AXOffscreenResult* offscreen_result) const {
switch (coordinate_system) {
case AXCoordinateSystem::kScreenPhysicalPixels:
case AXCoordinateSystem::kScreenDIPs: {
gfx::RectF bounds = GetLocation();
if (offscreen_result) {
*offscreen_result = DetermineOffscreenResult(bounds);
}
return gfx::ToEnclosingRect(bounds);
}
case AXCoordinateSystem::kRootFrame:
case AXCoordinateSystem::kFrame:
NOTIMPLEMENTED();
return gfx::Rect();
}
}
gfx::Rect TestAXNodeHelper::GetInnerTextRangeBoundsRect(
const int start_offset,
const int end_offset,
const AXCoordinateSystem coordinate_system,
const AXClippingBehavior clipping_behavior,
AXOffscreenResult* offscreen_result) const {
switch (coordinate_system) {
case AXCoordinateSystem::kScreenPhysicalPixels:
case AXCoordinateSystem::kScreenDIPs: {
gfx::RectF bounds = GetLocation();
if (node_->GetRole() == ax::mojom::Role::kInlineTextBox) {
bounds = GetInlineTextRect(start_offset, end_offset);
} else if (node_->GetRole() == ax::mojom::Role::kStaticText &&
InternalChildCount() > 0) {
auto child = InternalGetChild(0);
if (child &&
child->node_->GetRole() == ax::mojom::Role::kInlineTextBox) {
bounds = child->GetInlineTextRect(start_offset, end_offset);
}
}
if (offscreen_result) {
*offscreen_result = DetermineOffscreenResult(bounds);
}
return gfx::ToEnclosingRect(bounds);
}
case AXCoordinateSystem::kRootFrame:
case AXCoordinateSystem::kFrame:
NOTIMPLEMENTED();
return gfx::Rect();
}
}
const AXNodeData& TestAXNodeHelper::GetData() const {
return node_->data();
}
gfx::RectF TestAXNodeHelper::GetLocation() const {
return GetData().relative_bounds.bounds;
}
int TestAXNodeHelper::InternalChildCount() const {
return static_cast<int>(node_->GetUnignoredChildCount());
}
std::unique_ptr<TestAXNodeHelper> TestAXNodeHelper::InternalGetChild(
int index) const {
CHECK_GE(index, 0);
CHECK_LT(index, InternalChildCount());
return Create(tree_,
node_->GetUnignoredChildAtIndex(static_cast<size_t>(index)));
}
gfx::RectF TestAXNodeHelper::GetInlineTextRect(const int start_offset,
const int end_offset) const {
DCHECK(start_offset >= 0 && end_offset >= 0 && start_offset <= end_offset);
const std::vector<int32_t>& character_offsets = node_->GetIntListAttribute(
ax::mojom::IntListAttribute::kCharacterOffsets);
gfx::RectF location = GetLocation();
gfx::RectF bounds;
switch (static_cast<ax::mojom::WritingDirection>(
node_->GetIntAttribute(ax::mojom::IntAttribute::kTextDirection))) {
case ax::mojom::WritingDirection::kNone:
case ax::mojom::WritingDirection::kLtr: {
int start_pixel_offset =
start_offset > 0 ? character_offsets[start_offset - 1] : location.x();
int end_pixel_offset =
end_offset > 0 ? character_offsets[end_offset - 1] : location.x();
bounds =
gfx::RectF(start_pixel_offset, location.y(),
end_pixel_offset - start_pixel_offset, location.height());
break;
}
default:
NOTIMPLEMENTED();
}
return bounds;
}
bool TestAXNodeHelper::Intersects(gfx::RectF rect1, gfx::RectF rect2) const {
return rect1.x() < rect2.right() && rect1.right() > rect2.x() &&
rect1.y() < rect2.bottom() && rect1.bottom() > rect2.y();
}
AXOffscreenResult TestAXNodeHelper::DetermineOffscreenResult(
gfx::RectF bounds) const {
if (!tree_ || !tree_->root())
return AXOffscreenResult::kOnscreen;
const AXNodeData& root_web_area_node_data = tree_->root()->data();
gfx::RectF root_web_area_bounds =
root_web_area_node_data.relative_bounds.bounds;
if (!root_web_area_bounds.IsEmpty() &&
!Intersects(bounds, root_web_area_bounds)) {
return AXOffscreenResult::kOffscreen;
}
return AXOffscreenResult::kOnscreen;
}
}