Flutter Engine
The Flutter Engine
test_ax_node_helper.cc
Go to the documentation of this file.
1// Copyright 2020 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <map>
8#include <utility>
9
10#include "ax_action_data.h"
11#include "ax_role_properties.h"
12#include "ax_table_info.h"
13#include "ax_tree_observer.h"
16
17namespace ui {
18
19namespace {
20
21// A global map from AXNodes to TestAXNodeHelpers.
22std::map<AXNode::AXID, TestAXNodeHelper*> g_node_id_to_helper_map;
23
24// A simple implementation of AXTreeObserver to catch when AXNodes are
25// deleted so we can delete their helpers.
26class TestAXTreeObserver : public AXTreeObserver {
27 private:
28 void OnNodeDeleted(AXTree* tree, int32_t node_id) override {
29 const auto iter = g_node_id_to_helper_map.find(node_id);
30 if (iter != g_node_id_to_helper_map.end()) {
31 TestAXNodeHelper* helper = iter->second;
32 delete helper;
33 g_node_id_to_helper_map.erase(node_id);
34 }
35 }
36};
37
38TestAXTreeObserver g_ax_tree_observer;
39
40} // namespace
41
42// static
44 if (!tree || !node)
45 return nullptr;
46
47 if (!tree->HasObserver(&g_ax_tree_observer))
48 tree->AddObserver(&g_ax_tree_observer);
49 auto iter = g_node_id_to_helper_map.find(node->id());
50 if (iter != g_node_id_to_helper_map.end())
51 return iter->second;
52 TestAXNodeHelper* helper = new TestAXNodeHelper(tree, node);
53 g_node_id_to_helper_map[node->id()] = helper;
54 return helper;
55}
56
57TestAXNodeHelper::TestAXNodeHelper(AXTree* tree, AXNode* node)
58 : tree_(tree), node_(node) {}
59
61
63 const AXCoordinateSystem coordinate_system,
64 const AXClippingBehavior clipping_behavior,
65 AXOffscreenResult* offscreen_result) const {
66 switch (coordinate_system) {
68 // For unit testing purposes, assume a device scale factor of 1 and fall
69 // through.
71 // We could optionally add clipping here if ever needed.
72 gfx::RectF bounds = GetLocation();
73
74 // For test behavior only, for bounds that are offscreen we currently do
75 // not apply clipping to the bounds but we still return the offscreen
76 // status.
77 if (offscreen_result) {
78 *offscreen_result = DetermineOffscreenResult(bounds);
79 }
80
82 }
86 return gfx::Rect();
87 }
88}
89
91 const int start_offset,
92 const int end_offset,
93 const AXCoordinateSystem coordinate_system,
94 const AXClippingBehavior clipping_behavior,
95 AXOffscreenResult* offscreen_result) const {
96 switch (coordinate_system) {
98 // For unit testing purposes, assume a device scale factor of 1 and fall
99 // through.
101 gfx::RectF bounds = GetLocation();
102 // This implementation currently only deals with text node that has role
103 // kInlineTextBox and kStaticText.
104 // For test purposes, assume node with kStaticText always has a single
105 // child with role kInlineTextBox.
106 if (GetData().role == ax::mojom::Role::kInlineTextBox) {
107 bounds = GetInlineTextRect(start_offset, end_offset);
108 } else if (GetData().role == ax::mojom::Role::kStaticText &&
109 InternalChildCount() > 0) {
110 TestAXNodeHelper* child = InternalGetChild(0);
111 if (child != nullptr &&
112 child->GetData().role == ax::mojom::Role::kInlineTextBox) {
113 bounds = child->GetInlineTextRect(start_offset, end_offset);
114 }
115 }
116
117 // For test behavior only, for bounds that are offscreen we currently do
118 // not apply clipping to the bounds but we still return the offscreen
119 // status.
120 if (offscreen_result) {
121 *offscreen_result = DetermineOffscreenResult(bounds);
122 }
123
125 }
129 return gfx::Rect();
130 }
131}
132
133const AXNodeData& TestAXNodeHelper::GetData() const {
134 return node_->data();
135}
136
137gfx::RectF TestAXNodeHelper::GetLocation() const {
138 return GetData().relative_bounds.bounds;
139}
140
141int TestAXNodeHelper::InternalChildCount() const {
142 return static_cast<int>(node_->GetUnignoredChildCount());
143}
144
145TestAXNodeHelper* TestAXNodeHelper::InternalGetChild(int index) const {
146 BASE_CHECK(index >= 0);
147 BASE_CHECK(index < InternalChildCount());
148 return GetOrCreate(
149 tree_, node_->GetUnignoredChildAtIndex(static_cast<size_t>(index)));
150}
151
152gfx::RectF TestAXNodeHelper::GetInlineTextRect(const int start_offset,
153 const int end_offset) const {
154 BASE_DCHECK(start_offset >= 0 && end_offset >= 0 &&
155 start_offset <= end_offset);
156 const std::vector<int32_t>& character_offsets = GetData().GetIntListAttribute(
158 gfx::RectF location = GetLocation();
160
161 switch (static_cast<ax::mojom::WritingDirection>(
162 GetData().GetIntAttribute(ax::mojom::IntAttribute::kTextDirection))) {
163 // Currently only kNone and kLtr are supported text direction.
166 int start_pixel_offset =
167 start_offset > 0 ? character_offsets[start_offset - 1] : location.x();
168 int end_pixel_offset =
169 end_offset > 0 ? character_offsets[end_offset - 1] : location.x();
170 bounds =
171 gfx::RectF(start_pixel_offset, location.y(),
172 end_pixel_offset - start_pixel_offset, location.height());
173 break;
174 }
175 default:
177 }
178 return bounds;
179}
180
181AXOffscreenResult TestAXNodeHelper::DetermineOffscreenResult(
182 gfx::RectF bounds) const {
183 if (!tree_ || !tree_->root())
185
186 const AXNodeData& root_web_area_node_data = tree_->root()->data();
187 gfx::RectF root_web_area_bounds =
188 root_web_area_node_data.relative_bounds.bounds;
189
190 // For testing, we only look at the current node's bound relative to the root
191 // web area bounds to determine offscreen status. We currently do not look at
192 // the bounds of the immediate parent of the node for determining offscreen
193 // status.
194 // We only determine offscreen result if the root web area bounds is actually
195 // set in the test. We default the offscreen result of every other situation
196 // to AXOffscreenResult::kOnscreen.
197 if (!root_web_area_bounds.IsEmpty()) {
198 bounds.Intersect(root_web_area_bounds);
199 if (bounds.IsEmpty())
201 }
203}
204
205} // namespace ui
bool IsEmpty() const
Definition: rect_f.h:104
constexpr float y() const
Definition: rect_f.h:50
constexpr float height() const
Definition: rect_f.h:56
constexpr float x() const
Definition: rect_f.h:47
Definition: rect.h:36
size_t GetUnignoredChildCount() const
Definition: ax_node.cc:37
AXID id() const
Definition: ax_node.h:110
AXNode * GetUnignoredChildAtIndex(size_t index) const
Definition: ax_node.cc:47
const AXNodeData & data() const
Definition: ax_node.h:112
void AddObserver(AXTreeObserver *observer)
Definition: ax_tree.cc:708
AXNode * root() const
Definition: ax_tree.h:57
bool HasObserver(AXTreeObserver *observer)
Definition: ax_tree.cc:712
gfx::Rect GetBoundsRect(const AXCoordinateSystem coordinate_system, const AXClippingBehavior clipping_behavior, AXOffscreenResult *offscreen_result) const
gfx::Rect GetInnerTextRangeBoundsRect(const int start_offset, const int end_offset, const AXCoordinateSystem coordinate_system, const AXClippingBehavior clipping_behavior, AXOffscreenResult *offscreen_result) const
static TestAXNodeHelper * GetOrCreate(AXTree *tree, AXNode *node)
Optional< SkRect > bounds
Definition: SkRecords.h:189
WritingDirection
Definition: ax_enums.h:964
Rect ToEnclosingRect(const RectF &r)
TRect< Scalar > Rect
Definition: rect.h:769
AXRelativeBounds relative_bounds
Definition: ax_node_data.h:293
ax::mojom::Role role
Definition: ax_node_data.h:277
const std::vector< int32_t > & GetIntListAttribute(ax::mojom::IntListAttribute attribute) const
#define BASE_DCHECK(condition)
Definition: logging.h:63
#define BASE_UNREACHABLE()
Definition: logging.h:69
#define BASE_CHECK(condition)
Definition: logging.h:56