Flutter Engine
The Flutter Engine
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
ax_node.cc
Go to the documentation of this file.
1// Copyright 2013 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
5#include "ax_node.h"
6
7#include <algorithm>
8#include <utility>
9
10#include "ax_enums.h"
11#include "ax_role_properties.h"
12#include "ax_table_info.h"
13#include "ax_tree.h"
14#include "ax_tree_manager.h"
15#include "ax_tree_manager_map.h"
16#include "base/color_utils.h"
17#include "base/string_utils.h"
18
19namespace ui {
20
22
24 AXNode* parent,
25 int32_t id,
26 size_t index_in_parent,
27 size_t unignored_index_in_parent)
28 : tree_(tree),
29 index_in_parent_(index_in_parent),
30 unignored_index_in_parent_(unignored_index_in_parent),
31 parent_(parent) {
32 data_.id = id;
33}
34
35AXNode::~AXNode() = default;
36
38 // TODO(nektar): Should BASE_DCHECK if the node is not ignored.
40 return unignored_child_count_;
41}
42
44 return std::move(data_);
45}
46
49 size_t count = 0;
50 for (auto it = UnignoredChildrenBegin(); it != UnignoredChildrenEnd(); ++it) {
51 if (count == index)
52 return it.get();
53 ++count;
54 }
55 return nullptr;
56}
57
60 AXNode* result = parent();
61 while (result && result->IsIgnored())
62 result = result->parent();
63 return result;
64}
65
68 return unignored_index_in_parent_;
69}
70
73 return index_in_parent_;
74}
75
78 return ComputeFirstUnignoredChildRecursive();
79}
80
83 return ComputeLastUnignoredChildRecursive();
84}
85
88 return nullptr;
89
90 AXNode* deepest_child = GetFirstUnignoredChild();
91 while (deepest_child->GetUnignoredChildCount()) {
92 deepest_child = deepest_child->GetFirstUnignoredChild();
93 }
94
95 return deepest_child;
96}
97
100 return nullptr;
101
102 AXNode* deepest_child = GetLastUnignoredChild();
103 while (deepest_child->GetUnignoredChildCount()) {
104 deepest_child = deepest_child->GetLastUnignoredChild();
105 }
106
107 return deepest_child;
108}
109
110// Search for the next sibling of this node, skipping over any ignored nodes
111// encountered.
112//
113// In our search:
114// If we find an ignored sibling, we consider its children as our siblings.
115// If we run out of siblings, we consider an ignored parent's siblings as our
116// own siblings.
117//
118// Note: this behaviour of 'skipping over' an ignored node makes this subtly
119// different to finding the next (direct) sibling which is unignored.
120//
121// Consider a tree, where (i) marks a node as ignored:
122//
123// 1
124// ├── 2
125// ├── 3(i)
126// │ └── 5
127// └── 4
128//
129// The next sibling of node 2 is node 3, which is ignored.
130// The next unignored sibling of node 2 could be either:
131// 1) node 4 - next unignored sibling in the literal tree, or
132// 2) node 5 - next unignored sibling in the logical document.
133//
134// There is no next sibling of node 5.
135// The next unignored sibling of node 5 could be either:
136// 1) null - no next sibling in the literal tree, or
137// 2) node 4 - next unignored sibling in the logical document.
138//
139// In both cases, this method implements approach (2).
140//
141// TODO(chrishall): Can we remove this non-reflexive case by forbidding
142// GetNextUnignoredSibling calls on an ignored started node?
143// Note: this means that Next/Previous-UnignoredSibling are not reflexive if
144// either of the nodes in question are ignored. From above we get an example:
145// NextUnignoredSibling(3) is 4, but
146// PreviousUnignoredSibling(4) is 5.
147//
148// The view of unignored siblings for node 3 includes both node 2 and node 4:
149// 2 <-- [3(i)] --> 4
150//
151// Whereas nodes 2, 5, and 4 do not consider node 3 to be an unignored sibling:
152// null <-- [2] --> 5
153// 2 <-- [5] --> 4
154// 5 <-- [4] --> null
157 const AXNode* current = this;
158
159 // If there are children of the |current| node still to consider.
160 bool considerChildren = false;
161
162 while (current) {
163 // A |candidate| sibling to consider.
164 // If it is unignored then we have found our result.
165 // Otherwise promote it to |current| and consider its children.
166 AXNode* candidate;
167
168 if (considerChildren && (candidate = current->GetFirstChild())) {
169 if (!candidate->IsIgnored())
170 return candidate;
171 current = candidate;
172
173 } else if ((candidate = current->GetNextSibling())) {
174 if (!candidate->IsIgnored())
175 return candidate;
176 current = candidate;
177 // Look through the ignored candidate node to consider their children as
178 // though they were siblings.
179 considerChildren = true;
180
181 } else {
182 // Continue our search through a parent iff they are ignored.
183 //
184 // If |current| has an ignored parent, then we consider the parent's
185 // siblings as though they were siblings of |current|.
186 //
187 // Given a tree:
188 // 1
189 // ├── 2(?)
190 // │ └── [4]
191 // └── 3
192 //
193 // Node 4's view of siblings:
194 // literal tree: null <-- [4] --> null
195 //
196 // If node 2 is not ignored, then node 4's view doesn't change, and we
197 // have no more nodes to consider:
198 // unignored tree: null <-- [4] --> null
199 //
200 // If instead node 2 is ignored, then node 4's view of siblings grows to
201 // include node 3, and we have more nodes to consider:
202 // unignored tree: null <-- [4] --> 3
203 current = current->parent();
204 if (!current || !current->IsIgnored())
205 return nullptr;
206
207 // We have already considered all relevant descendants of |current|.
208 considerChildren = false;
209 }
210 }
211
212 return nullptr;
213}
214
215// Search for the previous sibling of this node, skipping over any ignored nodes
216// encountered.
217//
218// In our search for a sibling:
219// If we find an ignored sibling, we may consider its children as siblings.
220// If we run out of siblings, we may consider an ignored parent's siblings as
221// our own.
222//
223// See the documentation for |GetNextUnignoredSibling| for more details.
226 const AXNode* current = this;
227
228 // If there are children of the |current| node still to consider.
229 bool considerChildren = false;
230
231 while (current) {
232 // A |candidate| sibling to consider.
233 // If it is unignored then we have found our result.
234 // Otherwise promote it to |current| and consider its children.
235 AXNode* candidate;
236
237 if (considerChildren && (candidate = current->GetLastChild())) {
238 if (!candidate->IsIgnored())
239 return candidate;
240 current = candidate;
241
242 } else if ((candidate = current->GetPreviousSibling())) {
243 if (!candidate->IsIgnored())
244 return candidate;
245 current = candidate;
246 // Look through the ignored candidate node to consider their children as
247 // though they were siblings.
248 considerChildren = true;
249
250 } else {
251 // Continue our search through a parent iff they are ignored.
252 //
253 // If |current| has an ignored parent, then we consider the parent's
254 // siblings as though they were siblings of |current|.
255 //
256 // Given a tree:
257 // 1
258 // ├── 2
259 // └── 3(?)
260 // └── [4]
261 //
262 // Node 4's view of siblings:
263 // literal tree: null <-- [4] --> null
264 //
265 // If node 3 is not ignored, then node 4's view doesn't change, and we
266 // have no more nodes to consider:
267 // unignored tree: null <-- [4] --> null
268 //
269 // If instead node 3 is ignored, then node 4's view of siblings grows to
270 // include node 2, and we have more nodes to consider:
271 // unignored tree: 2 <-- [4] --> null
272 current = current->parent();
273 if (!current || !current->IsIgnored())
274 return nullptr;
275
276 // We have already considered all relevant descendants of |current|.
277 considerChildren = false;
278 }
279 }
280
281 return nullptr;
282}
283
286 return GetFirstUnignoredChild();
287
288 const AXNode* node = this;
289 while (node) {
290 AXNode* sibling = node->GetNextUnignoredSibling();
291 if (sibling)
292 return sibling;
293
294 node = node->GetUnignoredParent();
295 }
296
297 return nullptr;
298}
299
302 if (!sibling)
303 return GetUnignoredParent();
304
305 if (sibling->GetUnignoredChildCount())
306 return sibling->GetDeepestLastUnignoredChild();
307
308 return sibling;
309}
310
314}
315
318 return UnignoredChildIterator(this, nullptr);
319}
320
321// The first (direct) child, ignored or unignored.
323 if (children().empty())
324 return nullptr;
325 return children()[0];
326}
327
328// The last (direct) child, ignored or unignored.
330 size_t n = children().size();
331 if (n == 0)
332 return nullptr;
333 return children()[n - 1];
334}
335
336// The previous (direct) sibling, ignored or unignored.
338 // Root nodes lack a parent, their index_in_parent should be 0.
339 BASE_DCHECK(!parent() ? index_in_parent() == 0 : true);
340 size_t index = index_in_parent();
341 if (index == 0)
342 return nullptr;
343 return parent()->children()[index - 1];
344}
345
346// The next (direct) sibling, ignored or unignored.
348 if (!parent())
349 return nullptr;
350 size_t nextIndex = index_in_parent() + 1;
351 if (nextIndex >= parent()->children().size())
352 return nullptr;
353 return parent()->children()[nextIndex];
354}
355
356bool AXNode::IsText() const {
357 // In Legacy Layout, a list marker has no children and is thus represented on
358 // all platforms as a leaf node that exposes the marker itself, i.e., it forms
359 // part of the AX tree's text representation. In contrast, in Layout NG, a
360 // list marker has a static text child.
362 return !children().size();
363 return ui::IsText(data().role);
364}
365
371}
372
374 data_ = src;
375}
376
377void AXNode::SetLocation(int32_t offset_container_id,
378 const gfx::RectF& location,
380 data_.relative_bounds.offset_container_id = offset_container_id;
381 data_.relative_bounds.bounds = location;
382 if (transform) {
384 std::make_unique<gfx::Transform>(*transform);
385 } else {
386 data_.relative_bounds.transform.reset();
387 }
388}
389
390void AXNode::SetIndexInParent(size_t index_in_parent) {
391 index_in_parent_ = index_in_parent;
392}
393
395 if (!IsIgnored())
396 UpdateUnignoredCachedValuesRecursive(0);
397}
398
399void AXNode::SwapChildren(std::vector<AXNode*>* children) {
400 children->swap(children_);
401}
402
404 delete this;
405}
406
407bool AXNode::IsDescendantOf(const AXNode* ancestor) const {
408 if (this == ancestor)
409 return true;
410 if (parent())
411 return parent()->IsDescendantOf(ancestor);
412
413 return false;
414}
415
417 std::vector<int> line_offsets;
419 &line_offsets)) {
420 return line_offsets;
421 }
422
423 int start_offset = 0;
424 ComputeLineStartOffsets(&line_offsets, &start_offset);
426 line_offsets);
427 return line_offsets;
428}
429
430void AXNode::ComputeLineStartOffsets(std::vector<int>* line_offsets,
431 int* start_offset) const {
432 BASE_DCHECK(line_offsets);
433 BASE_DCHECK(start_offset);
434 for (const AXNode* child : children()) {
435 BASE_DCHECK(child);
436 if (!child->children().empty()) {
437 child->ComputeLineStartOffsets(line_offsets, start_offset);
438 continue;
439 }
440
441 // Don't report if the first piece of text starts a new line or not.
442 if (*start_offset && !child->data().HasIntAttribute(
444 // If there are multiple objects with an empty accessible label at the
445 // start of a line, only include a single line start offset.
446 if (line_offsets->empty() || line_offsets->back() != *start_offset)
447 line_offsets->push_back(*start_offset);
448 }
449
450 std::u16string text =
451 child->data().GetString16Attribute(ax::mojom::StringAttribute::kName);
452 *start_offset += static_cast<int>(text.length());
453 }
454}
455
457 ax::mojom::StringAttribute attribute) const {
458 const AXNode* current_node = this;
459 do {
460 if (current_node->data().HasStringAttribute(attribute))
461 return current_node->data().GetStringAttribute(attribute);
462 current_node = current_node->parent();
463 } while (current_node);
464 return base::EmptyString();
465}
466
468 ax::mojom::StringAttribute attribute) const {
470}
471
472std::string AXNode::GetInnerText() const {
473 // If a text field has no descendants, then we compute its inner text from its
474 // value or its placeholder. Otherwise we prefer to look at its descendant
475 // text nodes because Blink doesn't always add all trailing white space to the
476 // value attribute.
477 const bool is_plain_text_field_without_descendants =
479 if (is_plain_text_field_without_descendants) {
480 std::string value =
482 // If the value is empty, then there might be some placeholder text in the
483 // text field, or any other name that is derived from visible contents, even
484 // if the text field has no children.
485 if (!value.empty())
486 return value;
487 }
488
489 // Ordinarily, plain text fields are leaves. We need to exclude them from the
490 // set of leaf nodes when they expose any descendants. This is because we want
491 // to compute their inner text from their descendant text nodes as we don't
492 // always trust the "value" attribute provided by Blink.
493 const bool is_plain_text_field_with_descendants =
495 if (IsLeaf() && !is_plain_text_field_with_descendants) {
496 switch (data().GetNameFrom()) {
499 // The accessible name is not displayed on screen, e.g. aria-label, or is
500 // not displayed directly inside the node, e.g. an associated label
501 // element.
503 // The node's accessible name is explicitly empty.
505 // The accessible name does not represent the entirety of the node's inner
506 // text, e.g. a table's caption or a figure's figcaption.
509 // The accessible name is not displayed directly inside the node but is
510 // visible via e.g. a tooltip.
512 return std::string();
513
515 // The placeholder text is initially displayed inside the text field and
516 // takes the place of its value.
518 // The value attribute takes the place of the node's inner text, e.g. the
519 // value of a submit button is displayed inside the button itself.
522 }
523 }
524
525 std::string inner_text;
526 for (auto it = UnignoredChildrenBegin(); it != UnignoredChildrenEnd(); ++it) {
527 inner_text += it->GetInnerText();
528 }
529 return inner_text;
530}
531
532std::string AXNode::GetLanguage() const {
533 return std::string();
534}
535
536std::ostream& operator<<(std::ostream& stream, const AXNode& node) {
537 return stream << node.data().ToString();
538}
539
540bool AXNode::IsTable() const {
541 return IsTableLike(data().role);
542}
543
544std::optional<int> AXNode::GetTableColCount() const {
545 const AXTableInfo* table_info = GetAncestorTableInfo();
546 if (!table_info)
547 return std::nullopt;
548 return static_cast<int>(table_info->col_count);
549}
550
551std::optional<int> AXNode::GetTableRowCount() const {
552 const AXTableInfo* table_info = GetAncestorTableInfo();
553 if (!table_info)
554 return std::nullopt;
555 return static_cast<int>(table_info->row_count);
556}
557
558std::optional<int> AXNode::GetTableAriaColCount() const {
559 const AXTableInfo* table_info = GetAncestorTableInfo();
560 if (!table_info)
561 return std::nullopt;
562 return std::make_optional(table_info->aria_col_count);
563}
564
565std::optional<int> AXNode::GetTableAriaRowCount() const {
566 const AXTableInfo* table_info = GetAncestorTableInfo();
567 if (!table_info)
568 return std::nullopt;
569 return std::make_optional(table_info->aria_row_count);
570}
571
572std::optional<int> AXNode::GetTableCellCount() const {
573 const AXTableInfo* table_info = GetAncestorTableInfo();
574 if (!table_info)
575 return std::nullopt;
576
577 return static_cast<int>(table_info->unique_cell_ids.size());
578}
579
580std::optional<bool> AXNode::GetTableHasColumnOrRowHeaderNode() const {
581 const AXTableInfo* table_info = GetAncestorTableInfo();
582 if (!table_info)
583 return std::nullopt;
584
585 return !table_info->all_headers.empty();
586}
587
589 const AXTableInfo* table_info = GetAncestorTableInfo();
590 if (!table_info)
591 return nullptr;
592
593 // There is a table but there is no cell with the given index.
594 if (index < 0 ||
595 static_cast<size_t>(index) >= table_info->unique_cell_ids.size()) {
596 return nullptr;
597 }
598
599 return tree_->GetFromId(
600 table_info->unique_cell_ids[static_cast<size_t>(index)]);
601}
602
604 const AXTableInfo* table_info = GetAncestorTableInfo();
605 if (!table_info)
606 return nullptr;
607
608 return tree_->GetFromId(table_info->caption_id);
609}
610
611AXNode* AXNode::GetTableCellFromCoords(int row_index, int col_index) const {
612 const AXTableInfo* table_info = GetAncestorTableInfo();
613 if (!table_info)
614 return nullptr;
615
616 // There is a table but the given coordinates are outside the table.
617 if (row_index < 0 ||
618 static_cast<size_t>(row_index) >= table_info->row_count ||
619 col_index < 0 ||
620 static_cast<size_t>(col_index) >= table_info->col_count) {
621 return nullptr;
622 }
623
624 return tree_->GetFromId(table_info->cell_ids[static_cast<size_t>(row_index)]
625 [static_cast<size_t>(col_index)]);
626}
627
628std::vector<AXNode::AXID> AXNode::GetTableColHeaderNodeIds() const {
629 const AXTableInfo* table_info = GetAncestorTableInfo();
630 if (!table_info)
631 return std::vector<AXNode::AXID>();
632
633 std::vector<AXNode::AXID> col_header_ids;
634 // Flatten and add column header ids of each column to |col_header_ids|.
635 for (std::vector<AXNode::AXID> col_headers_at_index :
636 table_info->col_headers) {
637 col_header_ids.insert(col_header_ids.end(), col_headers_at_index.begin(),
638 col_headers_at_index.end());
639 }
640
641 return col_header_ids;
642}
643
644std::vector<AXNode::AXID> AXNode::GetTableColHeaderNodeIds(
645 int col_index) const {
646 const AXTableInfo* table_info = GetAncestorTableInfo();
647 if (!table_info)
648 return std::vector<AXNode::AXID>();
649
650 if (col_index < 0 || static_cast<size_t>(col_index) >= table_info->col_count)
651 return std::vector<AXNode::AXID>();
652
653 return std::vector<AXNode::AXID>(
654 table_info->col_headers[static_cast<size_t>(col_index)]);
655}
656
657std::vector<AXNode::AXID> AXNode::GetTableRowHeaderNodeIds(
658 int row_index) const {
659 const AXTableInfo* table_info = GetAncestorTableInfo();
660 if (!table_info)
661 return std::vector<AXNode::AXID>();
662
663 if (row_index < 0 || static_cast<size_t>(row_index) >= table_info->row_count)
664 return std::vector<AXNode::AXID>();
665
666 return std::vector<AXNode::AXID>(
667 table_info->row_headers[static_cast<size_t>(row_index)]);
668}
669
670std::vector<AXNode::AXID> AXNode::GetTableUniqueCellIds() const {
671 const AXTableInfo* table_info = GetAncestorTableInfo();
672 if (!table_info)
673 return std::vector<AXNode::AXID>();
674
675 return std::vector<AXNode::AXID>(table_info->unique_cell_ids);
676}
677
678const std::vector<AXNode*>* AXNode::GetExtraMacNodes() const {
679 // Should only be available on the table node itself, not any of its children.
680 const AXTableInfo* table_info = tree_->GetTableInfo(this);
681 if (!table_info)
682 return nullptr;
683
684 return &table_info->extra_mac_nodes;
685}
686
687//
688// Table row-like nodes.
689//
690
691bool AXNode::IsTableRow() const {
692 return ui::IsTableRow(data().role);
693}
694
695std::optional<int> AXNode::GetTableRowRowIndex() const {
696 if (!IsTableRow())
697 return std::nullopt;
698
699 const AXTableInfo* table_info = GetAncestorTableInfo();
700 if (!table_info)
701 return std::nullopt;
702
703 const auto& iter = table_info->row_id_to_index.find(id());
704 if (iter == table_info->row_id_to_index.end())
705 return std::nullopt;
706 return static_cast<int>(iter->second);
707}
708
709std::vector<AXNode::AXID> AXNode::GetTableRowNodeIds() const {
710 std::vector<AXNode::AXID> row_node_ids;
711 const AXTableInfo* table_info = GetAncestorTableInfo();
712 if (!table_info)
713 return row_node_ids;
714
715 for (AXNode* node : table_info->row_nodes)
716 row_node_ids.push_back(node->data().id);
717
718 return row_node_ids;
719}
720
721#if defined(OS_APPLE)
722
723//
724// Table column-like nodes. These nodes are only present on macOS.
725//
726
727bool AXNode::IsTableColumn() const {
728 return ui::IsTableColumn(data().role);
729}
730
731std::optional<int> AXNode::GetTableColColIndex() const {
732 if (!IsTableColumn())
733 return std::nullopt;
734
735 const AXTableInfo* table_info = GetAncestorTableInfo();
736 if (!table_info)
737 return std::nullopt;
738
739 int index = 0;
740 for (const AXNode* node : table_info->extra_mac_nodes) {
741 if (node == this)
742 break;
743 index++;
744 }
745 return index;
746}
747
748#endif // defined(OS_APPLE)
749
750//
751// Table cell-like nodes.
752//
753
755 return IsCellOrTableHeader(data().role);
756}
757
758std::optional<int> AXNode::GetTableCellIndex() const {
759 if (!IsTableCellOrHeader())
760 return std::nullopt;
761
762 const AXTableInfo* table_info = GetAncestorTableInfo();
763 if (!table_info)
764 return std::nullopt;
765
766 const auto& iter = table_info->cell_id_to_index.find(id());
767 if (iter != table_info->cell_id_to_index.end())
768 return static_cast<int>(iter->second);
769 return std::nullopt;
770}
771
772std::optional<int> AXNode::GetTableCellColIndex() const {
773 const AXTableInfo* table_info = GetAncestorTableInfo();
774 if (!table_info)
775 return std::nullopt;
776
777 std::optional<int> index = GetTableCellIndex();
778 if (!index)
779 return std::nullopt;
780
781 return static_cast<int>(table_info->cell_data_vector[*index].col_index);
782}
783
784std::optional<int> AXNode::GetTableCellRowIndex() const {
785 const AXTableInfo* table_info = GetAncestorTableInfo();
786 if (!table_info)
787 return std::nullopt;
788
789 std::optional<int> index = GetTableCellIndex();
790 if (!index)
791 return std::nullopt;
792
793 return static_cast<int>(table_info->cell_data_vector[*index].row_index);
794}
795
796std::optional<int> AXNode::GetTableCellColSpan() const {
797 // If it's not a table cell, don't return a col span.
798 if (!IsTableCellOrHeader())
799 return std::nullopt;
800
801 // Otherwise, try to return a colspan, with 1 as the default if it's not
802 // specified.
803 int col_span;
805 return col_span;
806 return 1;
807}
808
809std::optional<int> AXNode::GetTableCellRowSpan() const {
810 // If it's not a table cell, don't return a row span.
811 if (!IsTableCellOrHeader())
812 return std::nullopt;
813
814 // Otherwise, try to return a row span, with 1 as the default if it's not
815 // specified.
816 int row_span;
818 return row_span;
819 return 1;
820}
821
822std::optional<int> AXNode::GetTableCellAriaColIndex() const {
823 const AXTableInfo* table_info = GetAncestorTableInfo();
824 if (!table_info)
825 return std::nullopt;
826
827 std::optional<int> index = GetTableCellIndex();
828 if (!index)
829 return std::nullopt;
830
831 return static_cast<int>(table_info->cell_data_vector[*index].aria_col_index);
832}
833
834std::optional<int> AXNode::GetTableCellAriaRowIndex() const {
835 const AXTableInfo* table_info = GetAncestorTableInfo();
836 if (!table_info)
837 return std::nullopt;
838
839 std::optional<int> index = GetTableCellIndex();
840 if (!index)
841 return std::nullopt;
842
843 return static_cast<int>(table_info->cell_data_vector[*index].aria_row_index);
844}
845
846std::vector<AXNode::AXID> AXNode::GetTableCellColHeaderNodeIds() const {
847 const AXTableInfo* table_info = GetAncestorTableInfo();
848 if (!table_info || table_info->col_count <= 0)
849 return std::vector<AXNode::AXID>();
850
851 // If this node is not a cell, then return the headers for the first column.
852 int col_index = GetTableCellColIndex().value_or(0);
853
854 return std::vector<AXNode::AXID>(table_info->col_headers[col_index]);
855}
856
857void AXNode::GetTableCellColHeaders(std::vector<AXNode*>* col_headers) const {
858 BASE_DCHECK(col_headers);
859
860 std::vector<int32_t> col_header_ids = GetTableCellColHeaderNodeIds();
861 IdVectorToNodeVector(col_header_ids, col_headers);
862}
863
864std::vector<AXNode::AXID> AXNode::GetTableCellRowHeaderNodeIds() const {
865 const AXTableInfo* table_info = GetAncestorTableInfo();
866 if (!table_info || table_info->row_count <= 0)
867 return std::vector<AXNode::AXID>();
868
869 // If this node is not a cell, then return the headers for the first row.
870 int row_index = GetTableCellRowIndex().value_or(0);
871
872 return std::vector<AXNode::AXID>(table_info->row_headers[row_index]);
873}
874
875void AXNode::GetTableCellRowHeaders(std::vector<AXNode*>* row_headers) const {
876 BASE_DCHECK(row_headers);
877
878 std::vector<int32_t> row_header_ids = GetTableCellRowHeaderNodeIds();
879 IdVectorToNodeVector(row_header_ids, row_headers);
880}
881
883 if (!IsTableCellOrHeader())
884 return false;
885
886 const AXNode* node = this;
887 while (node && !node->IsTable())
888 node = node->parent();
889 if (!node)
890 return false;
891
892 return node->data().role == ax::mojom::Role::kTable;
893}
894
896 if (!IsTableCellOrHeader())
897 return false;
898
899 const AXNode* node = this;
900 while (node && !node->IsTable())
901 node = node->parent();
902 if (!node)
903 return false;
904
905 return node->data().role == ax::mojom::Role::kGrid ||
907}
908
909AXTableInfo* AXNode::GetAncestorTableInfo() const {
910 const AXNode* node = this;
911 while (node && !node->IsTable())
912 node = node->parent();
913 if (node)
914 return tree_->GetTableInfo(node);
915 return nullptr;
916}
917
918void AXNode::IdVectorToNodeVector(const std::vector<int32_t>& ids,
919 std::vector<AXNode*>* nodes) const {
920 for (int32_t id : ids) {
921 AXNode* node = tree_->GetFromId(id);
922 if (node)
923 nodes->push_back(node);
924 }
925}
926
927std::optional<int> AXNode::GetHierarchicalLevel() const {
928 int hierarchical_level =
930
931 // According to the WAI_ARIA spec, a defined hierarchical level value is
932 // greater than 0.
933 // https://www.w3.org/TR/wai-aria-1.1/#aria-level
934 if (hierarchical_level > 0)
935 return hierarchical_level;
936
937 return std::nullopt;
938}
939
941 return ui::IsItemLike(data().role);
942}
943
945 return ui::IsSetLike(data().role);
946}
947
948// Uses AXTree's cache to calculate node's PosInSet.
949std::optional<int> AXNode::GetPosInSet() {
950 return tree_->GetPosInSet(*this);
951}
952
953// Uses AXTree's cache to calculate node's SetSize.
954std::optional<int> AXNode::GetSetSize() {
955 return tree_->GetSetSize(*this);
956}
957
958// Returns true if the role of ordered set matches the role of item.
959// Returns false otherwise.
960bool AXNode::SetRoleMatchesItemRole(const AXNode* ordered_set) const {
961 ax::mojom::Role item_role = data().role;
962 // Switch on role of ordered set
963 switch (ordered_set->data().role) {
965 return item_role == ax::mojom::Role::kArticle;
967 return item_role == ax::mojom::Role::kListItem;
969 return item_role == ax::mojom::Role::kComment ||
970 item_role == ax::mojom::Role::kListItem ||
971 item_role == ax::mojom::Role::kMenuItem ||
972 item_role == ax::mojom::Role::kMenuItemRadio ||
973 item_role == ax::mojom::Role::kListBoxOption ||
974 item_role == ax::mojom::Role::kTreeItem;
976 return item_role == ax::mojom::Role::kMenuItem ||
977 item_role == ax::mojom::Role::kMenuItemRadio ||
980 return item_role == ax::mojom::Role::kMenuItem ||
981 item_role == ax::mojom::Role::kMenuItemRadio ||
984 return item_role == ax::mojom::Role::kTab;
986 return item_role == ax::mojom::Role::kTreeItem;
988 return item_role == ax::mojom::Role::kListBoxOption;
990 return item_role == ax::mojom::Role::kMenuListOption ||
991 item_role == ax::mojom::Role::kMenuItem ||
992 item_role == ax::mojom::Role::kMenuItemRadio ||
995 return item_role == ax::mojom::Role::kRadioButton;
997 // Only the term for each description list entry should receive posinset
998 // and setsize.
999 return item_role == ax::mojom::Role::kDescriptionListTerm ||
1000 item_role == ax::mojom::Role::kTerm;
1002 // kPopUpButtons can wrap a kMenuListPopUp.
1003 return item_role == ax::mojom::Role::kMenuListPopup;
1004 default:
1005 return false;
1006 }
1007}
1008
1010 return IsIgnored() || IsEmbeddedGroup() ||
1014}
1015
1016int AXNode::UpdateUnignoredCachedValuesRecursive(int startIndex) {
1017 int count = 0;
1018 for (AXNode* child : children_) {
1019 if (child->IsIgnored()) {
1020 child->unignored_index_in_parent_ = 0;
1021 count += child->UpdateUnignoredCachedValuesRecursive(startIndex + count);
1022 } else {
1023 child->unignored_index_in_parent_ = startIndex + count++;
1024 }
1025 }
1026 unignored_child_count_ = count;
1027 return count;
1028}
1029
1030// Finds ordered set that contains node.
1031// Is not required for set's role to match node's role.
1033 AXNode* result = parent();
1034 // Continue walking up while parent is invalid, ignored, a generic container,
1035 // unknown, or embedded group.
1036 while (result && result->IsIgnoredContainerForOrderedSet()) {
1037 result = result->parent();
1038 }
1039
1040 return result;
1041}
1042
1043AXNode* AXNode::ComputeLastUnignoredChildRecursive() const {
1045 if (children().empty())
1046 return nullptr;
1047
1048 for (int i = static_cast<int>(children().size()) - 1; i >= 0; --i) {
1049 AXNode* child = children_[i];
1050 if (!child->IsIgnored())
1051 return child;
1052
1053 AXNode* descendant = child->ComputeLastUnignoredChildRecursive();
1054 if (descendant)
1055 return descendant;
1056 }
1057 return nullptr;
1058}
1059
1060AXNode* AXNode::ComputeFirstUnignoredChildRecursive() const {
1062 for (size_t i = 0; i < children().size(); i++) {
1063 AXNode* child = children_[i];
1064 if (!child->IsIgnored())
1065 return child;
1066
1067 AXNode* descendant = child->ComputeFirstUnignoredChildRecursive();
1068 if (descendant)
1069 return descendant;
1070 }
1071 return nullptr;
1072}
1073
1074bool AXNode::IsIgnored() const {
1075 return data().IsIgnored();
1076}
1077
1079 const AXNode* ancestor = GetUnignoredParent();
1080 while (ancestor) {
1081 if (ancestor->IsLeaf())
1082 return true;
1083 ancestor = ancestor->GetUnignoredParent();
1084 }
1085 return false;
1086}
1087
1088bool AXNode::IsLeaf() const {
1089 // A node is also a leaf if all of it's descendants are ignored.
1091 return true;
1092
1093#if defined(OS_WIN)
1094 // On Windows, we want to hide the subtree of a collapsed <select> element.
1095 // Otherwise, ATs are always going to announce its options whether it's
1096 // collapsed or expanded. In the AXTree, this element corresponds to a node
1097 // with role ax::mojom::Role::kPopUpButton that is the parent of a node with
1098 // role ax::mojom::Role::kMenuListPopup.
1100 return true;
1101#endif // defined(OS_WIN)
1102
1103 // These types of objects may have children that we use as internal
1104 // implementation details, but we want to expose them as leaves to platform
1105 // accessibility APIs because screen readers might be confused if they find
1106 // any children.
1107 if (data().IsPlainTextField() || IsText())
1108 return true;
1109
1110 // Roles whose children are only presentational according to the ARIA and
1111 // HTML5 Specs should be hidden from screen readers.
1112 switch (data().role) {
1113 // According to the ARIA and Core-AAM specs:
1114 // https://w3c.github.io/aria/#button,
1115 // https://www.w3.org/TR/core-aam-1.1/#exclude_elements
1116 // buttons' children are presentational only and should be hidden from
1117 // screen readers. However, we cannot enforce the leafiness of buttons
1118 // because they may contain many rich, interactive descendants such as a day
1119 // in a calendar, and screen readers will need to interact with these
1120 // contents. See https://crbug.com/689204.
1121 // So we decided to not enforce the leafiness of buttons and expose all
1122 // children.
1124 return false;
1133 return true;
1134 default:
1135 return false;
1136 }
1137}
1138
1140 if (data().role == ax::mojom::Role::kListMarker)
1141 return true;
1142
1143 // List marker node's children can only be text elements.
1144 if (!IsText())
1145 return false;
1146
1147 // There is no need to iterate over all the ancestors of the current anchor
1148 // since a list marker node only has children on 2 levels.
1149 // i.e.:
1150 // AXLayoutObject role=kListMarker
1151 // ++StaticText
1152 // ++++InlineTextBox
1153 AXNode* parent_node = GetUnignoredParent();
1154 if (parent_node && parent_node->data().role == ax::mojom::Role::kListMarker)
1155 return true;
1156
1157 AXNode* grandparent_node = parent_node->GetUnignoredParent();
1158 return grandparent_node &&
1159 grandparent_node->data().role == ax::mojom::Role::kListMarker;
1160}
1161
1163 if (data().role != ax::mojom::Role::kPopUpButton ||
1164 !data().HasState(ax::mojom::State::kCollapsed)) {
1165 return false;
1166 }
1167
1168 // When a popup button contains a menu list popup, its only child is unignored
1169 // and is a menu list popup.
1171 if (!node)
1172 return false;
1173
1174 return node->data().role == ax::mojom::Role::kMenuListPopup;
1175}
1176
1178 AXNode* node = GetOrderedSet();
1179
1180 if (!node)
1181 return nullptr;
1182
1183 // The ordered set returned is either the popup element child of the popup
1184 // button (e.g., the AXMenuListPopup) or the popup button itself. We need
1185 // |node| to point to the popup button itself.
1186 if (node->data().role != ax::mojom::Role::kPopUpButton) {
1187 node = node->parent();
1188 if (!node)
1189 return nullptr;
1190 }
1191
1192 return node->IsCollapsedMenuListPopUpButton() ? node : nullptr;
1193}
1194
1196 if (data().role != ax::mojom::Role::kGroup || !parent())
1197 return false;
1198
1199 return ui::IsSetLike(parent()->data().role);
1200}
1201
1203 AXNode* current_node = const_cast<AXNode*>(this);
1204 AXNode* lowest_unignored_node = current_node;
1205 for (; lowest_unignored_node && lowest_unignored_node->IsIgnored();
1206 lowest_unignored_node = lowest_unignored_node->parent()) {
1207 }
1208
1209 // `highest_leaf_node` could be nullptr.
1210 AXNode* highest_leaf_node = lowest_unignored_node;
1211 // For the purposes of this method, a leaf node does not include leaves in the
1212 // internal accessibility tree, only in the platform exposed tree.
1213 for (AXNode* ancestor_node = lowest_unignored_node; ancestor_node;
1214 ancestor_node = ancestor_node->GetUnignoredParent()) {
1215 if (ancestor_node->IsLeaf())
1216 highest_leaf_node = ancestor_node;
1217 }
1218 if (highest_leaf_node)
1219 return highest_leaf_node;
1220
1221 if (lowest_unignored_node)
1222 return lowest_unignored_node;
1223 return current_node;
1224}
1225
1228
1231 return parent;
1232
1234 }
1235
1236 return nullptr;
1237}
1238
1240 if (!ancestor)
1241 return false;
1242 if (this == ancestor)
1243 return true;
1246 return false;
1247}
1248
1251 if (parent_)
1252 return parent_;
1253 const AXTreeManager* manager =
1255 if (manager)
1256 return manager->GetParentNodeFromParentTreeAsAXNode();
1257 return nullptr;
1258}
1259
1261 BASE_DCHECK(tree())
1262 << "Cannot retrieve the current selection if the node is not "
1263 "attached to an accessibility tree.\n"
1264 << *this;
1266
1267 // "selection.anchor_offset" and "selection.focus_ofset" might need to be
1268 // adjusted if the anchor or the focus nodes include ignored children.
1269 //
1270 // TODO(nektar): Move this logic into its own "AXSelection" class and cache
1271 // the result for faster reuse.
1272 const AXNode* anchor = tree()->GetFromId(selection.anchor_object_id);
1273 if (anchor && !anchor->IsLeaf()) {
1274 BASE_DCHECK(selection.anchor_offset >= 0);
1275 if (static_cast<size_t>(selection.anchor_offset) <
1276 anchor->children().size()) {
1277 const AXNode* anchor_child = anchor->children()[selection.anchor_offset];
1278 BASE_DCHECK(anchor_child);
1279 selection.anchor_offset =
1280 static_cast<int>(anchor_child->GetUnignoredIndexInParent());
1281 } else {
1282 selection.anchor_offset =
1283 static_cast<int>(anchor->GetUnignoredChildCount());
1284 }
1285 }
1286
1287 const AXNode* focus = tree()->GetFromId(selection.focus_object_id);
1288 if (focus && !focus->IsLeaf()) {
1289 BASE_DCHECK(selection.focus_offset >= 0);
1290 if (static_cast<size_t>(selection.focus_offset) <
1291 focus->children().size()) {
1292 const AXNode* focus_child = focus->children()[selection.focus_offset];
1293 BASE_DCHECK(focus_child);
1294 selection.focus_offset =
1295 static_cast<int>(focus_child->GetUnignoredIndexInParent());
1296 } else {
1297 selection.focus_offset =
1298 static_cast<int>(focus->GetUnignoredChildCount());
1299 }
1300 }
1301 return selection;
1302}
1303
1304} // namespace ui
int count
Definition: FontMgrTest.cpp:50
virtual AXTableInfo * GetTableInfo(const AXNode *table_node) const =0
virtual bool GetTreeUpdateInProgressState() const =0
virtual std::optional< int > GetSetSize(const AXNode &node)=0
virtual AXNode * GetFromId(int32_t id) const =0
virtual AXTreeID GetAXTreeID() const =0
virtual std::optional< int > GetPosInSet(const AXNode &node)=0
virtual Selection GetUnignoredSelection() const =0
std::optional< int > GetSetSize()
Definition: ax_node.cc:954
AXNode * GetCollapsedMenuListPopUpButtonAncestor() const
Definition: ax_node.cc:1177
std::vector< AXNode::AXID > GetTableCellColHeaderNodeIds() const
Definition: ax_node.cc:846
void Destroy()
Definition: ax_node.cc:403
void UpdateUnignoredCachedValues()
Definition: ax_node.cc:394
AXNode * GetDeepestLastUnignoredChild() const
Definition: ax_node.cc:98
size_t GetUnignoredChildCount() const
Definition: ax_node.cc:37
ChildIteratorBase< AXNode, &AXNode::GetNextUnignoredSibling, &AXNode::GetPreviousUnignoredSibling, &AXNode::GetFirstUnignoredChild, &AXNode::GetLastUnignoredChild > UnignoredChildIterator
Definition: ax_node.h:143
std::vector< AXNode::AXID > GetTableColHeaderNodeIds() const
Definition: ax_node.cc:628
std::vector< AXNode::AXID > GetTableCellRowHeaderNodeIds() const
Definition: ax_node.cc:864
UnignoredChildIterator UnignoredChildrenEnd() const
Definition: ax_node.cc:316
bool IsCellOrHeaderOfARIATable() const
Definition: ax_node.cc:882
std::vector< AXNode::AXID > GetTableUniqueCellIds() const
Definition: ax_node.cc:670
size_t GetIndexInParent() const
Definition: ax_node.cc:71
AXNode * GetLowestPlatformAncestor() const
Definition: ax_node.cc:1202
bool IsOrderedSetItem() const
Definition: ax_node.cc:940
std::optional< int > GetTableCellIndex() const
Definition: ax_node.cc:758
AXID id() const
Definition: ax_node.h:110
void SetData(const AXNodeData &src)
Definition: ax_node.cc:373
std::optional< int > GetTableCellRowIndex() const
Definition: ax_node.cc:784
void SetIndexInParent(size_t index_in_parent)
Definition: ax_node.cc:390
AXNode * GetParentCrossingTreeBoundary() const
Definition: ax_node.cc:1249
bool IsEmbeddedGroup() const
Definition: ax_node.cc:1195
size_t GetUnignoredIndexInParent() const
Definition: ax_node.cc:66
AXNode * GetLastUnignoredChild() const
Definition: ax_node.cc:81
bool IsCellOrHeaderOfARIAGrid() const
Definition: ax_node.cc:895
bool IsTableCellOrHeader() const
Definition: ax_node.cc:754
bool IsIgnoredContainerForOrderedSet() const
Definition: ax_node.cc:1009
std::optional< int > GetTableCellAriaColIndex() const
Definition: ax_node.cc:822
OwnerTree * tree() const
Definition: ax_node.h:109
int32_t AXID
Definition: ax_node.h:36
static constexpr AXID kInvalidAXID
Definition: ax_node.h:41
std::vector< AXNode::AXID > GetTableRowHeaderNodeIds(int row_index) const
Definition: ax_node.cc:657
AXNode * GetNextUnignoredInTreeOrder() const
Definition: ax_node.cc:284
OwnerTree::Selection GetUnignoredSelection() const
Definition: ax_node.cc:1260
std::optional< int > GetTableRowCount() const
Definition: ax_node.cc:551
std::optional< bool > GetTableHasColumnOrRowHeaderNode() const
Definition: ax_node.cc:580
AXNode * GetUnignoredParent() const
Definition: ax_node.cc:58
bool IsInListMarker() const
Definition: ax_node.cc:1139
AXNode * GetFirstUnignoredChild() const
Definition: ax_node.cc:76
virtual ~AXNode()
AXNode * GetTextFieldAncestor() const
Definition: ax_node.cc:1226
std::optional< int > GetTableRowRowIndex() const
Definition: ax_node.cc:695
std::vector< int > GetOrComputeLineStartOffsets()
Definition: ax_node.cc:416
AXNode * GetNextSibling() const
Definition: ax_node.cc:347
void GetTableCellColHeaders(std::vector< AXNode * > *col_headers) const
Definition: ax_node.cc:857
bool IsLeaf() const
Definition: ax_node.cc:1088
const std::vector< int32_t > & GetIntListAttribute(ax::mojom::IntListAttribute attribute) const
Definition: ax_node.h:263
std::vector< AXNode::AXID > GetTableRowNodeIds() const
Definition: ax_node.cc:709
AXNode * GetLastChild() const
Definition: ax_node.cc:329
AXNode * GetDeepestFirstUnignoredChild() const
Definition: ax_node.cc:86
void SetLocation(int32_t offset_container_id, const gfx::RectF &location, gfx::Transform *transform)
Definition: ax_node.cc:377
AXNode * GetUnignoredChildAtIndex(size_t index) const
Definition: ax_node.cc:47
AXNode * GetTableCaption() const
Definition: ax_node.cc:603
std::optional< int > GetTableCellAriaRowIndex() const
Definition: ax_node.cc:834
std::optional< int > GetTableAriaColCount() const
Definition: ax_node.cc:558
bool IsText() const
Definition: ax_node.cc:356
std::optional< int > GetTableColCount() const
Definition: ax_node.cc:544
bool IsTableRow() const
Definition: ax_node.cc:691
const std::vector< AXNode * > & children() const
Definition: ax_node.h:113
std::string GetLanguage() const
Definition: ax_node.cc:532
std::optional< int > GetHierarchicalLevel() const
Definition: ax_node.cc:927
void GetTableCellRowHeaders(std::vector< AXNode * > *row_headers) const
Definition: ax_node.cc:875
std::optional< int > GetTableCellColSpan() const
Definition: ax_node.cc:796
AXNode * GetPreviousUnignoredSibling() const
Definition: ax_node.cc:224
AXNode * parent() const
Definition: ax_node.h:111
UnignoredChildIterator UnignoredChildrenBegin() const
Definition: ax_node.cc:311
std::optional< int > GetTableCellRowSpan() const
Definition: ax_node.cc:809
bool IsTable() const
Definition: ax_node.cc:540
AXNodeData && TakeData()
Definition: ax_node.cc:43
const std::string & GetInheritedStringAttribute(ax::mojom::StringAttribute attribute) const
Definition: ax_node.cc:456
std::u16string GetInheritedString16Attribute(ax::mojom::StringAttribute attribute) const
Definition: ax_node.cc:467
bool SetRoleMatchesItemRole(const AXNode *ordered_set) const
Definition: ax_node.cc:960
bool IsCollapsedMenuListPopUpButton() const
Definition: ax_node.cc:1162
AXNode(OwnerTree *tree, AXNode *parent, int32_t id, size_t index_in_parent, size_t unignored_index_in_parent=0)
Definition: ax_node.cc:23
int GetIntAttribute(ax::mojom::IntAttribute attribute) const
Definition: ax_node.h:232
bool IsDescendantOfCrossingTreeBoundary(const AXNode *ancestor) const
Definition: ax_node.cc:1239
bool IsChildOfLeaf() const
Definition: ax_node.cc:1078
void SwapChildren(std::vector< AXNode * > *children)
Definition: ax_node.cc:399
AXNode * GetFirstChild() const
Definition: ax_node.cc:322
bool IsLineBreak() const
Definition: ax_node.cc:366
AXNode * GetPreviousSibling() const
Definition: ax_node.cc:337
std::optional< int > GetTableCellCount() const
Definition: ax_node.cc:572
bool IsOrderedSet() const
Definition: ax_node.cc:944
std::optional< int > GetTableCellColIndex() const
Definition: ax_node.cc:772
bool IsIgnored() const
Definition: ax_node.cc:1074
AXNode * GetPreviousUnignoredInTreeOrder() const
Definition: ax_node.cc:300
AXNode * GetNextUnignoredSibling() const
Definition: ax_node.cc:155
size_t index_in_parent() const
Definition: ax_node.h:114
bool IsDescendantOf(const AXNode *ancestor) const
Definition: ax_node.cc:407
AXNode * GetTableCellFromIndex(int index) const
Definition: ax_node.cc:588
std::optional< int > GetPosInSet()
Definition: ax_node.cc:949
AXNode * GetOrderedSet() const
Definition: ax_node.cc:1032
AXNode * GetTableCellFromCoords(int row_index, int col_index) const
Definition: ax_node.cc:611
const AXNodeData & data() const
Definition: ax_node.h:112
const std::vector< AXNode * > * GetExtraMacNodes() const
Definition: ax_node.cc:678
std::string GetInnerText() const
Definition: ax_node.cc:472
std::optional< int > GetTableAriaRowCount() const
Definition: ax_node.cc:565
std::vector< std::vector< int32_t > > col_headers
Definition: ax_table_info.h:59
std::vector< CellData > cell_data_vector
Definition: ax_table_info.h:77
std::vector< std::vector< int32_t > > cell_ids
Definition: ax_table_info.h:74
std::unordered_map< int32_t, size_t > row_id_to_index
Definition: ax_table_info.h:91
std::vector< AXNode * > extra_mac_nodes
Definition: ax_table_info.h:85
std::unordered_map< int32_t, size_t > cell_id_to_index
Definition: ax_table_info.h:88
std::vector< AXNode * > row_nodes
Definition: ax_table_info.h:94
std::vector< int32_t > all_headers
Definition: ax_table_info.h:65
std::vector< std::vector< int32_t > > row_headers
Definition: ax_table_info.h:62
int32_t caption_id
Definition: ax_table_info.h:68
std::vector< int32_t > unique_cell_ids
Definition: ax_table_info.h:80
AXTreeManager * GetManager(AXTreeID tree_id)
static AXTreeManagerMap & GetInstance()
EMSCRIPTEN_KEEPALIVE void empty()
uint8_t value
GAsyncResult * result
std::u16string text
StringAttribute
Definition: ax_enums.h:521
const std::string & EmptyString()
std::u16string UTF8ToUTF16(std::string src)
Definition: string_utils.cc:67
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font manager
Definition: switches.h:218
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
bool IsTableColumn(ax::mojom::Role role)
bool IsCellOrTableHeader(const ax::mojom::Role role)
bool IsTableRow(ax::mojom::Role role)
bool IsItemLike(const ax::mojom::Role role)
bool IsTableLike(const ax::mojom::Role role)
std::ostream & operator<<(std::ostream &os, AXEventGenerator::Event event)
bool IsSetLike(const ax::mojom::Role role)
bool IsText(ax::mojom::Role role)
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition: p3.cpp:47
AXRelativeBounds relative_bounds
Definition: ax_node_data.h:293
bool IsPlainTextField() const
bool HasState(ax::mojom::State state) const
void AddIntListAttribute(ax::mojom::IntListAttribute attribute, const std::vector< int32_t > &value)
bool HasStringAttribute(ax::mojom::StringAttribute attribute) const
bool IsTextField() const
ax::mojom::NameFrom GetNameFrom() const
bool IsRichTextField() const
virtual std::string ToString() const
bool IsIgnored() const
bool GetBoolAttribute(ax::mojom::BoolAttribute attribute) const
ax::mojom::Role role
Definition: ax_node_data.h:277
const std::string & GetStringAttribute(ax::mojom::StringAttribute attribute) const
std::unique_ptr< gfx::Transform > transform
#define BASE_DCHECK(condition)
Definition: logging.h:63