Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
ax_event_generator.cc
Go to the documentation of this file.
1// Copyright 2017 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 <algorithm>
8
9#include "ax_enums.h"
10#include "ax_node.h"
11#include "ax_role_properties.h"
13
14namespace ui {
15namespace {
16
17bool IsActiveLiveRegion(const AXTreeObserver::Change& change) {
18 return change.node->data().HasStringAttribute(
20 change.node->data().GetStringAttribute(
22}
23
24bool IsContainedInLiveRegion(const AXTreeObserver::Change& change) {
25 return change.node->data().HasStringAttribute(
27 change.node->data().HasStringAttribute(
29}
30
31bool HasEvent(const std::set<AXEventGenerator::EventParams>& node_events,
33 for (auto& iter : node_events) {
34 if (iter.event == event)
35 return true;
36 }
37 return false;
38}
39
40void RemoveEvent(std::set<AXEventGenerator::EventParams>* node_events,
42 for (auto& iter : *node_events) {
43 if (iter.event == event) {
44 node_events->erase(iter);
45 return;
46 }
47 }
48}
49
50// If a node toggled its ignored state, don't also fire children-changed because
51// platforms likely will do that in response to ignored-changed.
52// Suppress name- and description-changed because those can be emitted as a side
53// effect of calculating alternative text values for a newly-displayed object.
54// Ditto for text attributes such as foreground and background colors, or
55// display changing from "none" to "block."
56void RemoveEventsDueToIgnoredChanged(
57 std::set<AXEventGenerator::EventParams>* node_events) {
58 RemoveEvent(node_events,
59 AXEventGenerator::Event::ATK_TEXT_OBJECT_ATTRIBUTE_CHANGED);
60 RemoveEvent(node_events, AXEventGenerator::Event::CHILDREN_CHANGED);
61 RemoveEvent(node_events, AXEventGenerator::Event::DESCRIPTION_CHANGED);
62 RemoveEvent(node_events, AXEventGenerator::Event::NAME_CHANGED);
63 RemoveEvent(node_events, AXEventGenerator::Event::OBJECT_ATTRIBUTE_CHANGED);
64 RemoveEvent(node_events, AXEventGenerator::Event::SORT_CHANGED);
65 RemoveEvent(node_events, AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED);
66 RemoveEvent(node_events,
67 AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED);
68}
69
70// Add a particular AXEventGenerator::IgnoredChangedState to
71// |ignored_changed_states|.
72void AddIgnoredChangedState(
75 ignored_changed_states.set(static_cast<size_t>(state));
76}
77
78// Returns true if |ignored_changed_states| contains a particular
79// AXEventGenerator::IgnoredChangedState.
80bool HasIgnoredChangedState(
83 return ignored_changed_states[static_cast<size_t>(state)];
84}
85
86} // namespace
87
90 ax::mojom::EventFrom event_from,
91 const std::vector<AXEventIntent>& event_intents)
92 : event(event), event_from(event_from), event_intents(event_intents) {}
93
95
97 const EventParams& event_params)
98 : node(node), event_params(event_params) {
100}
101
103 return rhs.event == event;
104}
105
107 return event < rhs.event;
108}
109
111 const std::map<AXNode*, std::set<EventParams>>& map,
112 const std::map<AXNode*, std::set<EventParams>>::const_iterator& head)
113 : map_(map), map_iter_(head) {
114 if (map_iter_ != map.end())
115 set_iter_ = map_iter_->second.begin();
116}
117
119 default;
120
122
124 const AXEventGenerator::Iterator& rhs) const {
125 return map_iter_ != rhs.map_iter_ ||
126 (map_iter_ != map_.end() && set_iter_ != rhs.set_iter_);
127}
128
130 if (map_iter_ == map_.end())
131 return *this;
132
133 set_iter_++;
134 while (map_iter_ != map_.end() && set_iter_ == map_iter_->second.end()) {
135 map_iter_++;
136 if (map_iter_ != map_.end())
137 set_iter_ = map_iter_->second.begin();
138 }
139
140 return *this;
141}
142
144 BASE_DCHECK(map_iter_ != map_.end() && set_iter_ != map_iter_->second.end());
145 return AXEventGenerator::TargetedEvent(map_iter_->first, *set_iter_);
146}
147
149
151 if (tree_)
152 tree_->AddObserver(this);
153}
154
156
158 if (tree_)
159 tree_->RemoveObserver(this);
160 tree_ = new_tree;
161 if (tree_)
162 tree_->AddObserver(this);
163}
164
166 tree_->RemoveObserver(this);
167 tree_ = nullptr;
168}
169
171 tree_events_.clear();
172}
173
175 BASE_DCHECK(node);
176
178 return;
179
180 std::set<EventParams>& node_events = tree_events_[node];
181 node_events.emplace(event, ax::mojom::EventFrom::kNone,
182 tree_->event_intents());
183}
184
186 const AXNodeData& old_node_data,
187 const AXNodeData& new_node_data) {
188 BASE_DCHECK(tree_ == tree);
189 // Fire CHILDREN_CHANGED events when the list of children updates.
190 // Internally we store inline text box nodes as children of a static text
191 // node or a line break node, which enables us to determine character bounds
192 // and line layout. We don't expose those to platform APIs, though, so
193 // suppress CHILDREN_CHANGED events on static text nodes.
194 if (new_node_data.child_ids != old_node_data.child_ids &&
195 !ui::IsText(new_node_data.role)) {
196 AXNode* node = tree_->GetFromId(new_node_data.id);
197 tree_events_[node].emplace(Event::CHILDREN_CHANGED,
199 tree_->event_intents());
200 }
201}
202
204 AXNode* node,
205 ax::mojom::Role old_role,
206 ax::mojom::Role new_role) {
207 BASE_DCHECK(tree_ == tree);
208 AddEvent(node, Event::ROLE_CHANGED);
209}
210
212 AXNode* node,
214 bool new_value) {
215 BASE_DCHECK(tree_ == tree);
216
218 AddEvent(node, Event::STATE_CHANGED);
219 AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
220 }
221
222 switch (state) {
224 AddEvent(node, new_value ? Event::EXPANDED : Event::COLLAPSED);
225
226 if (node->data().role == ax::mojom::Role::kRow ||
228 AXNode* container = node;
229 while (container && !IsRowContainer(container->data().role))
230 container = container->parent();
231 if (container)
232 AddEvent(container, Event::ROW_COUNT_CHANGED);
233 }
234 break;
236 AXNode* unignored_parent = node->GetUnignoredParent();
237 if (unignored_parent)
238 AddEvent(unignored_parent, Event::CHILDREN_CHANGED);
239 AddEvent(node, Event::IGNORED_CHANGED);
240 if (!new_value)
241 AddEvent(node, Event::SUBTREE_CREATED);
242 break;
243 }
245 AddEvent(node, Event::MULTILINE_STATE_CHANGED);
246 break;
248 AddEvent(node, Event::MULTISELECTABLE_STATE_CHANGED);
249 break;
251 AddEvent(node, Event::REQUIRED_STATE_CHANGED);
252 break;
253 default:
254 break;
255 }
256}
257
259 AXNode* node,
261 const std::string& old_value,
262 const std::string& new_value) {
263 BASE_DCHECK(tree_ == tree);
264
265 switch (attr) {
267 AddEvent(node, Event::ACCESS_KEY_CHANGED);
268 break;
270 AddEvent(node, Event::INVALID_STATUS_CHANGED);
271 break;
273 AddEvent(node, Event::AUTO_COMPLETE_CHANGED);
274 break;
276 AddEvent(node, Event::CLASS_NAME_CHANGED);
277 break;
279 AddEvent(node, Event::DESCRIPTION_CHANGED);
280 break;
282 AddEvent(node, Event::KEY_SHORTCUTS_CHANGED);
283 break;
285 AddEvent(node, Event::LANGUAGE_CHANGED);
286 break;
288 AddEvent(node, Event::LIVE_RELEVANT_CHANGED);
289 break;
291 AddEvent(node, Event::LIVE_STATUS_CHANGED);
292
293 // Fire a LIVE_REGION_CREATED if the previous value was off, and the new
294 // value is not-off.
295 if (!IsAlert(node->data().role)) {
296 bool old_state = !old_value.empty() && old_value != "off";
297 bool new_state = !new_value.empty() && new_value != "off";
298 if (!old_state && new_state)
299 AddEvent(node, Event::LIVE_REGION_CREATED);
300 }
301 break;
303 // If the name of the root node changes, we expect OnTreeDataChanged to
304 // add a DOCUMENT_TITLE_CHANGED event instead.
305 if (node != tree->root())
306 AddEvent(node, Event::NAME_CHANGED);
307
308 if (node->data().HasStringAttribute(
310 FireLiveRegionEvents(node);
311 }
312 break;
314 AddEvent(node, Event::PLACEHOLDER_CHANGED);
315 break;
317 AddEvent(node, Event::VALUE_CHANGED);
318 break;
320 // The image annotation is reported as part of the accessible name.
321 AddEvent(node, Event::IMAGE_ANNOTATION_CHANGED);
322 break;
324 AddEvent(node, Event::TEXT_ATTRIBUTE_CHANGED);
325 break;
326 default:
327 AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED);
328 break;
329 }
330}
331
333 AXNode* node,
335 int32_t old_value,
336 int32_t new_value) {
337 BASE_DCHECK(tree_ == tree);
338
339 switch (attr) {
341 // Don't fire on invisible containers, as it confuses some screen readers,
342 // such as NVDA.
344 AddEvent(node, Event::ACTIVE_DESCENDANT_CHANGED);
345 active_descendant_changed_.push_back(node);
346 }
347 break;
349 AddEvent(node, Event::CHECKED_STATE_CHANGED);
350 AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
351 break;
353 AddEvent(node, Event::DROPEFFECT_CHANGED);
354 break;
356 AddEvent(node, Event::HASPOPUP_CHANGED);
357 AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
358 break;
360 AddEvent(node, Event::HIERARCHICAL_LEVEL_CHANGED);
361 break;
363 AddEvent(node, Event::INVALID_STATUS_CHANGED);
364 break;
366 AddEvent(node, Event::POSITION_IN_SET_CHANGED);
367 break;
369 bool was_enabled;
370 bool was_readonly;
371 GetRestrictionStates(static_cast<ax::mojom::Restriction>(old_value),
372 &was_enabled, &was_readonly);
373 bool is_enabled;
374 bool is_readonly;
375 GetRestrictionStates(static_cast<ax::mojom::Restriction>(new_value),
376 &is_enabled, &is_readonly);
377
378 if (was_enabled != is_enabled) {
379 AddEvent(node, Event::ENABLED_CHANGED);
380 AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
381 }
382 if (was_readonly != is_readonly) {
383 AddEvent(node, Event::READONLY_CHANGED);
384 AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
385 }
386 break;
387 }
389 AddEvent(node, Event::SCROLL_HORIZONTAL_POSITION_CHANGED);
390 break;
392 AddEvent(node, Event::SCROLL_VERTICAL_POSITION_CHANGED);
393 break;
395 // Ignore sort direction changes on roles other than table headers and
396 // grid headers.
397 if (IsTableHeader(node->data().role))
398 AddEvent(node, Event::SORT_CHANGED);
399 break;
401 // The image annotation is reported as part of the accessible name.
402 AddEvent(node, Event::IMAGE_ANNOTATION_CHANGED);
403 break;
405 AddEvent(node, Event::SET_SIZE_CHANGED);
406 break;
415 AddEvent(node, Event::TEXT_ATTRIBUTE_CHANGED);
416 break;
418 // Alignment is exposed as an object attribute because it cannot apply to
419 // a substring. However, for some platforms (e.g. ATK), alignment is a
420 // text attribute. Therefore fire both events to ensure platforms get the
421 // expected notifications.
422 AddEvent(node, Event::ATK_TEXT_OBJECT_ATTRIBUTE_CHANGED);
423 AddEvent(node, Event::OBJECT_ATTRIBUTE_CHANGED);
424 break;
425 default:
426 AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED);
427 break;
428 }
429}
430
432 AXNode* node,
434 float old_value,
435 float new_value) {
436 BASE_DCHECK(tree_ == tree);
437
438 switch (attr) {
440 AddEvent(node, Event::VALUE_MAX_CHANGED);
441 break;
443 AddEvent(node, Event::VALUE_MIN_CHANGED);
444 break;
446 AddEvent(node, Event::VALUE_STEP_CHANGED);
447 break;
449 AddEvent(node, Event::VALUE_CHANGED);
450 break;
453 AddEvent(node, Event::TEXT_ATTRIBUTE_CHANGED);
454 break;
456 // Indentation is exposed as an object attribute because it cannot apply
457 // to a substring. However, for some platforms (e.g. ATK), alignment is a
458 // text attribute. Therefore fire both events to ensure platforms get the
459 // expected notifications.
460 AddEvent(node, Event::ATK_TEXT_OBJECT_ATTRIBUTE_CHANGED);
461 AddEvent(node, Event::OBJECT_ATTRIBUTE_CHANGED);
462 break;
463 default:
464 AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED);
465 break;
466 }
467}
468
470 AXNode* node,
472 bool new_value) {
473 BASE_DCHECK(tree_ == tree);
474
475 switch (attr) {
477 AddEvent(node, Event::BUSY_CHANGED);
478 AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
479 // Fire an 'invalidated' event when aria-busy becomes false
480 if (!new_value)
481 AddEvent(node, Event::LAYOUT_INVALIDATED);
482 break;
484 AddEvent(node, Event::GRABBED_CHANGED);
485 break;
487 AddEvent(node, Event::ATOMIC_CHANGED);
488 break;
490 AddEvent(node, Event::SELECTED_CHANGED);
491 AddEvent(node, Event::WIN_IACCESSIBLE_STATE_CHANGED);
492 AXNode* container = node;
493 while (container &&
495 container = container->parent();
496 if (container)
497 AddEvent(container, Event::SELECTED_CHILDREN_CHANGED);
498 break;
499 }
500 default:
501 AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED);
502 break;
503 }
504}
505
507 AXTree* tree,
508 AXNode* node,
510 const std::vector<int32_t>& old_value,
511 const std::vector<int32_t>& new_value) {
512 BASE_DCHECK(tree_ == tree);
513
514 switch (attr) {
516 AddEvent(node, Event::CONTROLS_CHANGED);
517 break;
519 AddEvent(node, Event::DESCRIBED_BY_CHANGED);
520 break;
522 AddEvent(node, Event::FLOW_TO_CHANGED);
523
524 // Fire FLOW_FROM_CHANGED for all nodes added or removed
525 for (int32_t id : ComputeIntListDifference(old_value, new_value)) {
526 if (auto* target_node = tree->GetFromId(id))
527 AddEvent(target_node, Event::FLOW_FROM_CHANGED);
528 }
529 break;
530 }
532 AddEvent(node, Event::LABELED_BY_CHANGED);
533 break;
537 // On a native text field, the spelling- and grammar-error markers are
538 // associated with children not exposed on any platform. Therefore, we
539 // adjust the node we fire that event on here.
540 if (AXNode* text_field = node->GetTextFieldAncestor())
541 AddEvent(text_field, Event::TEXT_ATTRIBUTE_CHANGED);
542 else
543 AddEvent(node, Event::TEXT_ATTRIBUTE_CHANGED);
544 break;
545 default:
546 AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED);
547 break;
548 }
549}
550
552 const AXTreeData& old_tree_data,
553 const AXTreeData& new_tree_data) {
554 BASE_DCHECK(tree_ == tree);
555
556 if (new_tree_data.loaded && !old_tree_data.loaded &&
557 ShouldFireLoadEvents(tree->root())) {
558 AddEvent(tree->root(), Event::LOAD_COMPLETE);
559 }
560
561 if (new_tree_data.sel_is_backward != old_tree_data.sel_is_backward ||
562 new_tree_data.sel_anchor_object_id !=
563 old_tree_data.sel_anchor_object_id ||
564 new_tree_data.sel_anchor_offset != old_tree_data.sel_anchor_offset ||
565 new_tree_data.sel_anchor_affinity != old_tree_data.sel_anchor_affinity ||
566 new_tree_data.sel_focus_object_id != old_tree_data.sel_focus_object_id ||
567 new_tree_data.sel_focus_offset != old_tree_data.sel_focus_offset ||
568 new_tree_data.sel_focus_affinity != old_tree_data.sel_focus_affinity) {
569 AddEvent(tree->root(), Event::DOCUMENT_SELECTION_CHANGED);
570 }
571 if (new_tree_data.title != old_tree_data.title)
572 AddEvent(tree->root(), Event::DOCUMENT_TITLE_CHANGED);
573 if (new_tree_data.focus_id != old_tree_data.focus_id) {
574 AXNode* focus_node = tree->GetFromId(new_tree_data.focus_id);
575 if (focus_node) {
576 AddEvent(focus_node, Event::FOCUS_CHANGED);
577 }
578 }
579}
580
582 BASE_DCHECK(tree_ == tree);
583 tree_events_.erase(node);
584}
585
587 BASE_DCHECK(tree_ == tree);
588}
589
591 BASE_DCHECK(tree_ == tree);
592 tree_events_.erase(node);
593}
594
596 BASE_DCHECK(tree_ == tree);
597}
598
600 AXTree* tree,
601 bool root_changed,
602 const std::vector<Change>& changes) {
603 BASE_DCHECK(tree_ == tree);
604
605 if (root_changed && ShouldFireLoadEvents(tree->root())) {
606 if (tree->data().loaded)
607 AddEvent(tree->root(), Event::LOAD_COMPLETE);
608 else
609 AddEvent(tree->root(), Event::LOAD_START);
610 }
611
612 for (const auto& change : changes) {
613 if (change.type == SUBTREE_CREATED) {
614 AddEvent(change.node, Event::SUBTREE_CREATED);
615 } else if (change.type != NODE_CREATED) {
616 FireRelationSourceEvents(tree, change.node);
617 continue;
618 }
619
620 if (IsAlert(change.node->data().role))
621 AddEvent(change.node, Event::ALERT);
622 else if (IsActiveLiveRegion(change))
623 AddEvent(change.node, Event::LIVE_REGION_CREATED);
624 else if (IsContainedInLiveRegion(change))
625 FireLiveRegionEvents(change.node);
626 }
627
628 FireActiveDescendantEvents();
629
630 PostprocessEvents();
631}
632
633void AXEventGenerator::FireLiveRegionEvents(AXNode* node) {
634 AXNode* live_root = node;
635 while (live_root && !live_root->data().HasStringAttribute(
637 live_root = live_root->parent();
638
639 if (live_root &&
641 live_root->data().GetStringAttribute(
643 // Fire LIVE_REGION_NODE_CHANGED on each node that changed.
644 if (!node->data()
646 .empty())
647 AddEvent(node, Event::LIVE_REGION_NODE_CHANGED);
648 // Fire LIVE_REGION_NODE_CHANGED on the root of the live region.
649 AddEvent(live_root, Event::LIVE_REGION_CHANGED);
650 }
651}
652
653void AXEventGenerator::FireActiveDescendantEvents() {
654 for (AXNode* node : active_descendant_changed_) {
655 AXNode* descendant = tree_->GetFromId(node->data().GetIntAttribute(
657 if (!descendant)
658 continue;
659 switch (descendant->data().role) {
664 AddEvent(descendant, Event::MENU_ITEM_SELECTED);
665 break;
666 default:
667 break;
668 }
669 }
670 active_descendant_changed_.clear();
671}
672
673void AXEventGenerator::FireRelationSourceEvents(AXTree* tree,
674 AXNode* target_node) {
675 int32_t target_id = target_node->id();
676 std::set<AXNode*> source_nodes;
677 auto callback = [&](const auto& entry) {
678 const auto& target_to_sources = entry.second;
679 auto sources_it = target_to_sources.find(target_id);
680 if (sources_it == target_to_sources.end())
681 return;
682
683 auto sources = sources_it->second;
684 std::for_each(sources.begin(), sources.end(), [&](int32_t source_id) {
685 AXNode* source_node = tree->GetFromId(source_id);
686
687 if (!source_node || source_nodes.count(source_node) > 0)
688 return;
689
690 source_nodes.insert(source_node);
691
692 // GCC < 6.4 requires this pointer when calling a member
693 // function in anonymous function
694 this->AddEvent(source_node, Event::RELATED_NODE_CHANGED);
695 });
696 };
697
698 std::for_each(tree->int_reverse_relations().begin(),
699 tree->int_reverse_relations().end(), callback);
700 std::for_each(
701 tree->intlist_reverse_relations().begin(),
702 tree->intlist_reverse_relations().end(), [&](auto& entry) {
703 // Explicitly exclude relationships for which an additional event on the
704 // source node would cause extra noise. For example, kRadioGroupIds
705 // forms relations among all radio buttons and serves little value for
706 // AT to get events on the previous radio button in the group.
707 if (entry.first != ax::mojom::IntListAttribute::kRadioGroupIds)
708 callback(entry);
709 });
710}
711
712// Attempts to suppress load-related events that we presume no AT will be
713// interested in under any circumstances, such as pages which have no size.
714bool AXEventGenerator::ShouldFireLoadEvents(AXNode* node) {
715 if (always_fire_load_complete_)
716 return true;
717
718 const AXNodeData& data = node->data();
719 return data.relative_bounds.bounds.width() ||
720 data.relative_bounds.bounds.height();
721}
722
723void AXEventGenerator::TrimEventsDueToAncestorIgnoredChanged(
724 AXNode* node,
725 std::map<AXNode*, IgnoredChangedStatesBitset>&
726 ancestor_ignored_changed_map) {
727 BASE_DCHECK(node);
728
729 // Recursively compute and cache ancestor ignored changed results in
730 // |ancestor_ignored_changed_map|, if |node|'s ancestors have become ignored
731 // and the ancestor's ignored changed results have not been cached.
732 if (node->parent() &&
733 !base::Contains(ancestor_ignored_changed_map, node->parent())) {
734 TrimEventsDueToAncestorIgnoredChanged(node->parent(),
735 ancestor_ignored_changed_map);
736 }
737
738 // If an ancestor of |node| changed to ignored state (hide), append hide state
739 // to the corresponding entry in the map for |node|. Similarly, if an ancestor
740 // of |node| removed its ignored state (show), we append show state to the
741 // corresponding entry in map for |node| as well. If |node| flipped its
742 // ignored state as well, we want to remove various events related to
743 // IGNORED_CHANGED event.
744 const auto& parent_map_iter =
745 ancestor_ignored_changed_map.find(node->parent());
746 const auto& curr_events_iter = tree_events_.find(node);
747
748 // Initialize |ancestor_ignored_changed_map[node]| with an empty bitset,
749 // representing neither |node| nor its ancestor has IGNORED_CHANGED.
750 IgnoredChangedStatesBitset& ancestor_ignored_changed_states =
751 ancestor_ignored_changed_map[node];
752
753 // If |ancestor_ignored_changed_map| contains an entry for |node|'s
754 // ancestor's and the ancestor has either show/hide state, we want to populate
755 // |node|'s show/hide state in the map based on its cached ancestor result.
756 // An empty entry in |ancestor_ignored_changed_map| for |node| means that
757 // neither |node| nor its ancestor has IGNORED_CHANGED.
758 if (parent_map_iter != ancestor_ignored_changed_map.end()) {
759 // Propagate ancestor's show/hide states to |node|'s entry in the map.
760 if (HasIgnoredChangedState(parent_map_iter->second,
762 AddIgnoredChangedState(ancestor_ignored_changed_states,
764 }
765 if (HasIgnoredChangedState(parent_map_iter->second,
767 AddIgnoredChangedState(ancestor_ignored_changed_states,
769 }
770
771 // If |node| has IGNORED changed with show/hide state that matches one of
772 // its ancestors' IGNORED changed show/hide states, we want to remove
773 // |node|'s IGNORED_CHANGED related events.
774 if (curr_events_iter != tree_events_.end() &&
775 HasEvent(curr_events_iter->second, Event::IGNORED_CHANGED)) {
776 if ((HasIgnoredChangedState(parent_map_iter->second,
778 node->IsIgnored()) ||
779 (HasIgnoredChangedState(parent_map_iter->second,
781 !node->IsIgnored())) {
782 RemoveEvent(&(curr_events_iter->second), Event::IGNORED_CHANGED);
783 RemoveEventsDueToIgnoredChanged(&(curr_events_iter->second));
784 }
785
786 if (node->IsIgnored()) {
787 AddIgnoredChangedState(ancestor_ignored_changed_states,
789 } else {
790 AddIgnoredChangedState(ancestor_ignored_changed_states,
792 }
793 }
794
795 return;
796 }
797
798 // If ignored changed results for ancestors are not cached, calculate the
799 // corresponding entry for |node| in the map using the ignored states and
800 // events of |node|.
801 if (curr_events_iter != tree_events_.end() &&
802 HasEvent(curr_events_iter->second, Event::IGNORED_CHANGED)) {
803 if (node->IsIgnored()) {
804 AddIgnoredChangedState(ancestor_ignored_changed_states,
806 } else {
807 AddIgnoredChangedState(ancestor_ignored_changed_states,
809 }
810
811 return;
812 }
813}
814
815void AXEventGenerator::PostprocessEvents() {
816 std::map<AXNode*, IgnoredChangedStatesBitset> ancestor_ignored_changed_map;
817 std::set<AXNode*> removed_subtree_created_nodes;
818 auto iter = tree_events_.begin();
819 while (iter != tree_events_.end()) {
820 AXNode* node = iter->first;
821 std::set<EventParams>& node_events = iter->second;
822
823 // A newly created live region or alert should not *also* fire a
824 // live region changed event.
825 if (HasEvent(node_events, Event::ALERT) ||
826 HasEvent(node_events, Event::LIVE_REGION_CREATED)) {
827 RemoveEvent(&node_events, Event::LIVE_REGION_CHANGED);
828 }
829
830 if (HasEvent(node_events, Event::IGNORED_CHANGED)) {
831 // If a node toggled its ignored state, we only want to fire
832 // IGNORED_CHANGED event on the top most ancestor where this ignored state
833 // change takes place and suppress all the descendants's IGNORED_CHANGED
834 // events.
835 TrimEventsDueToAncestorIgnoredChanged(node, ancestor_ignored_changed_map);
836 RemoveEventsDueToIgnoredChanged(&node_events);
837 }
838
839 // When the selected option in an expanded select element changes, the
840 // foreground and background colors change. But we don't want to treat
841 // those as text attribute changes. This can also happen when a widget
842 // such as a button becomes enabled/disabled.
843 if (HasEvent(node_events, Event::SELECTED_CHANGED) ||
844 HasEvent(node_events, Event::ENABLED_CHANGED)) {
845 RemoveEvent(&node_events, Event::TEXT_ATTRIBUTE_CHANGED);
846 }
847
848 AXNode* parent = node->GetUnignoredParent();
849
850 // Don't fire text attribute changed on this node if its immediate parent
851 // also has text attribute changed.
852 if (parent && HasEvent(node_events, Event::TEXT_ATTRIBUTE_CHANGED) &&
853 tree_events_.find(parent) != tree_events_.end() &&
854 HasEvent(tree_events_[parent], Event::TEXT_ATTRIBUTE_CHANGED)) {
855 RemoveEvent(&node_events, Event::TEXT_ATTRIBUTE_CHANGED);
856 }
857
858 // Don't fire subtree created on this node if any of its ancestors also has
859 // subtree created.
860 if (HasEvent(node_events, Event::SUBTREE_CREATED)) {
861 while (parent &&
862 (tree_events_.find(parent) != tree_events_.end() ||
863 base::Contains(removed_subtree_created_nodes, parent))) {
864 if (base::Contains(removed_subtree_created_nodes, parent) ||
865 HasEvent(tree_events_[parent], Event::SUBTREE_CREATED)) {
866 RemoveEvent(&node_events, Event::SUBTREE_CREATED);
867 removed_subtree_created_nodes.insert(node);
868 break;
869 }
870 parent = parent->GetUnignoredParent();
871 }
872 }
873
874 // If this was the only event, remove the node entirely from the
875 // tree events.
876 if (node_events.empty())
877 iter = tree_events_.erase(iter);
878 else
879 ++iter;
880 }
881}
882
883// static
884void AXEventGenerator::GetRestrictionStates(ax::mojom::Restriction restriction,
885 bool* is_enabled,
886 bool* is_readonly) {
887 switch (restriction) {
889 *is_enabled = false;
890 *is_readonly = true;
891 break;
893 *is_enabled = true;
894 *is_readonly = true;
895 break;
897 *is_enabled = true;
898 *is_readonly = false;
899 break;
900 }
901}
902
903// static
904std::vector<int32_t> AXEventGenerator::ComputeIntListDifference(
905 const std::vector<int32_t>& lhs,
906 const std::vector<int32_t>& rhs) {
907 std::set<int32_t> sorted_lhs(lhs.cbegin(), lhs.cend());
908 std::set<int32_t> sorted_rhs(rhs.cbegin(), rhs.cend());
909
910 std::vector<int32_t> result;
911 std::set_symmetric_difference(sorted_lhs.cbegin(), sorted_lhs.cend(),
912 sorted_rhs.cbegin(), sorted_rhs.cend(),
913 std::back_inserter(result));
914 return result;
915}
916
917std::ostream& operator<<(std::ostream& os, AXEventGenerator::Event event) {
918 return os << ToString(event);
919}
920
922 switch (event) {
923 case AXEventGenerator::Event::ACCESS_KEY_CHANGED:
924 return "ACCESS_KEY_CHANGED";
925 case AXEventGenerator::Event::ATOMIC_CHANGED:
926 return "ATOMIC_CHANGED";
927 case AXEventGenerator::Event::ACTIVE_DESCENDANT_CHANGED:
928 return "ACTIVE_DESCENDANT_CHANGED";
929 case AXEventGenerator::Event::ALERT:
930 return "ALERT";
931 case AXEventGenerator::Event::ATK_TEXT_OBJECT_ATTRIBUTE_CHANGED:
932 return "ATK_TEXT_OBJECT_ATTRIBUTE_CHANGED";
933 case AXEventGenerator::Event::BUSY_CHANGED:
934 return "BUSY_CHANGED";
935 case AXEventGenerator::Event::CHECKED_STATE_CHANGED:
936 return "CHECKED_STATE_CHANGED";
937 case AXEventGenerator::Event::CHILDREN_CHANGED:
938 return "CHILDREN_CHANGED";
939 case AXEventGenerator::Event::CLASS_NAME_CHANGED:
940 return "CLASS_NAME_CHANGED";
941 case AXEventGenerator::Event::COLLAPSED:
942 return "COLLAPSED";
943 case AXEventGenerator::Event::CONTROLS_CHANGED:
944 return "CONTROLS_CHANGED";
945 case AXEventGenerator::Event::DESCRIBED_BY_CHANGED:
946 return "DESCRIBED_BY_CHANGED";
947 case AXEventGenerator::Event::DESCRIPTION_CHANGED:
948 return "DESCRIPTION_CHANGED";
949 case AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED:
950 return "DOCUMENT_SELECTION_CHANGED";
951 case AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED:
952 return "DOCUMENT_TITLE_CHANGED";
953 case AXEventGenerator::Event::DROPEFFECT_CHANGED:
954 return "DROPEFFECT_CHANGED";
955 case AXEventGenerator::Event::ENABLED_CHANGED:
956 return "ENABLED_CHANGED";
957 case AXEventGenerator::Event::EXPANDED:
958 return "EXPANDED";
959 case AXEventGenerator::Event::FLOW_FROM_CHANGED:
960 return "FLOW_FROM_CHANGED";
961 case AXEventGenerator::Event::FLOW_TO_CHANGED:
962 return "FLOW_TO_CHANGED";
963 case AXEventGenerator::Event::GRABBED_CHANGED:
964 return "GRABBED_CHANGED";
965 case AXEventGenerator::Event::HASPOPUP_CHANGED:
966 return "HASPOPUP_CHANGED";
967 case AXEventGenerator::Event::HIERARCHICAL_LEVEL_CHANGED:
968 return "HIERARCHICAL_LEVEL_CHANGED";
969 case ui::AXEventGenerator::Event::IGNORED_CHANGED:
970 return "IGNORED_CHANGED";
971 case AXEventGenerator::Event::IMAGE_ANNOTATION_CHANGED:
972 return "IMAGE_ANNOTATION_CHANGED";
973 case AXEventGenerator::Event::INVALID_STATUS_CHANGED:
974 return "INVALID_STATUS_CHANGED";
975 case AXEventGenerator::Event::KEY_SHORTCUTS_CHANGED:
976 return "KEY_SHORTCUTS_CHANGED";
977 case AXEventGenerator::Event::LABELED_BY_CHANGED:
978 return "LABELED_BY_CHANGED";
979 case AXEventGenerator::Event::LANGUAGE_CHANGED:
980 return "LANGUAGE_CHANGED";
981 case AXEventGenerator::Event::LAYOUT_INVALIDATED:
982 return "LAYOUT_INVALIDATED";
983 case AXEventGenerator::Event::LIVE_REGION_CHANGED:
984 return "LIVE_REGION_CHANGED";
985 case AXEventGenerator::Event::LIVE_REGION_CREATED:
986 return "LIVE_REGION_CREATED";
987 case AXEventGenerator::Event::LIVE_REGION_NODE_CHANGED:
988 return "LIVE_REGION_NODE_CHANGED";
989 case AXEventGenerator::Event::LIVE_RELEVANT_CHANGED:
990 return "LIVE_RELEVANT_CHANGED";
991 case AXEventGenerator::Event::LIVE_STATUS_CHANGED:
992 return "LIVE_STATUS_CHANGED";
993 case AXEventGenerator::Event::LOAD_COMPLETE:
994 return "LOAD_COMPLETE";
995 case AXEventGenerator::Event::LOAD_START:
996 return "LOAD_START";
997 case AXEventGenerator::Event::MENU_ITEM_SELECTED:
998 return "MENU_ITEM_SELECTED";
999 case AXEventGenerator::Event::MULTILINE_STATE_CHANGED:
1000 return "MULTILINE_STATE_CHANGED";
1001 case AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED:
1002 return "MULTISELECTABLE_STATE_CHANGED";
1003 case AXEventGenerator::Event::NAME_CHANGED:
1004 return "NAME_CHANGED";
1005 case AXEventGenerator::Event::OBJECT_ATTRIBUTE_CHANGED:
1006 return "OBJECT_ATTRIBUTE_CHANGED";
1007 case AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED:
1008 return "OTHER_ATTRIBUTE_CHANGED";
1009 case AXEventGenerator::Event::PLACEHOLDER_CHANGED:
1010 return "PLACEHOLDER_CHANGED";
1011 case AXEventGenerator::Event::PORTAL_ACTIVATED:
1012 return "PORTAL_ACTIVATED";
1013 case AXEventGenerator::Event::POSITION_IN_SET_CHANGED:
1014 return "POSITION_IN_SET_CHANGED";
1015 case AXEventGenerator::Event::READONLY_CHANGED:
1016 return "READONLY_CHANGED";
1017 case AXEventGenerator::Event::RELATED_NODE_CHANGED:
1018 return "RELATED_NODE_CHANGED";
1019 case AXEventGenerator::Event::REQUIRED_STATE_CHANGED:
1020 return "REQUIRED_STATE_CHANGED";
1021 case AXEventGenerator::Event::ROLE_CHANGED:
1022 return "ROLE_CHANGED";
1023 case AXEventGenerator::Event::ROW_COUNT_CHANGED:
1024 return "ROW_COUNT_CHANGED";
1025 case AXEventGenerator::Event::SCROLL_HORIZONTAL_POSITION_CHANGED:
1026 return "SCROLL_HORIZONTAL_POSITION_CHANGED";
1027 case AXEventGenerator::Event::SCROLL_VERTICAL_POSITION_CHANGED:
1028 return "SCROLL_VERTICAL_POSITION_CHANGED";
1029 case AXEventGenerator::Event::SELECTED_CHANGED:
1030 return "SELECTED_CHANGED";
1031 case AXEventGenerator::Event::SELECTED_CHILDREN_CHANGED:
1032 return "SELECTED_CHILDREN_CHANGED";
1033 case AXEventGenerator::Event::SET_SIZE_CHANGED:
1034 return "SET_SIZE_CHANGED";
1035 case AXEventGenerator::Event::STATE_CHANGED:
1036 return "STATE_CHANGED";
1037 case AXEventGenerator::Event::SUBTREE_CREATED:
1038 return "SUBTREE_CREATED";
1039 case AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED:
1040 return "TEXT_ATTRIBUTE_CHANGED";
1041 case AXEventGenerator::Event::VALUE_CHANGED:
1042 return "VALUE_CHANGED";
1043 case AXEventGenerator::Event::VALUE_MAX_CHANGED:
1044 return "VALUE_MAX_CHANGED";
1045 case AXEventGenerator::Event::VALUE_MIN_CHANGED:
1046 return "VALUE_MIN_CHANGED";
1047 case AXEventGenerator::Event::VALUE_STEP_CHANGED:
1048 return "VALUE_STEP_CHANGED";
1049 case AXEventGenerator::Event::AUTO_COMPLETE_CHANGED:
1050 return "AUTO_COMPLETE_CHANGED";
1051 case AXEventGenerator::Event::FOCUS_CHANGED:
1052 return "FOCUS_CHANGED";
1053 case AXEventGenerator::Event::SORT_CHANGED:
1054 return "SORT_CHANGED";
1055 case AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED:
1056 return "WIN_IACCESSIBLE_STATE_CHANGED";
1057 }
1059}
1060
1061} // namespace ui
Iterator(const std::map< AXNode *, std::set< EventParams > > &map, const std::map< AXNode *, std::set< EventParams > >::const_iterator &head)
bool operator!=(const Iterator &rhs) const
void AddEvent(ui::AXNode *node, Event event)
void OnIntAttributeChanged(AXTree *tree, AXNode *node, ax::mojom::IntAttribute attr, int32_t old_value, int32_t new_value) override
void OnRoleChanged(AXTree *tree, AXNode *node, ax::mojom::Role old_role, ax::mojom::Role new_role) override
~AXEventGenerator() override
void OnNodeWillBeDeleted(AXTree *tree, AXNode *node) override
std::bitset< static_cast< size_t >(IgnoredChangedState::kCount)> IgnoredChangedStatesBitset
void OnStateChanged(AXTree *tree, AXNode *node, ax::mojom::State state, bool new_value) override
void OnBoolAttributeChanged(AXTree *tree, AXNode *node, ax::mojom::BoolAttribute attr, bool new_value) override
void OnTreeDataChanged(AXTree *tree, const ui::AXTreeData &old_data, const ui::AXTreeData &new_data) override
void OnNodeWillBeReparented(AXTree *tree, AXNode *node) override
void OnIntListAttributeChanged(AXTree *tree, AXNode *node, ax::mojom::IntListAttribute attr, const std::vector< int32_t > &old_value, const std::vector< int32_t > &new_value) override
void OnSubtreeWillBeDeleted(AXTree *tree, AXNode *node) override
void OnNodeDataChanged(AXTree *tree, const AXNodeData &old_node_data, const AXNodeData &new_node_data) override
void SetTree(AXTree *new_tree)
void OnAtomicUpdateFinished(AXTree *tree, bool root_changed, const std::vector< Change > &changes) override
void OnFloatAttributeChanged(AXTree *tree, AXNode *node, ax::mojom::FloatAttribute attr, float old_value, float new_value) override
void OnSubtreeWillBeReparented(AXTree *tree, AXNode *node) override
void OnStringAttributeChanged(AXTree *tree, AXNode *node, ax::mojom::StringAttribute attr, const std::string &old_value, const std::string &new_value) override
AXNode * GetUnignoredParent() const
Definition ax_node.cc:58
AXNode * GetTextFieldAncestor() const
Definition ax_node.cc:1226
AXNode * parent() const
Definition ax_node.h:111
const AXNodeData & data() const
Definition ax_node.h:112
const AXTreeData & data() const
Definition ax_tree.h:59
AXNode * GetFromId(int32_t id) const override
Definition ax_tree.cc:728
void AddObserver(AXTreeObserver *observer)
Definition ax_tree.cc:708
void RemoveObserver(AXTreeObserver *observer)
Definition ax_tree.cc:717
AXNode * root() const
Definition ax_tree.h:57
const std::vector< AXEventIntent > & event_intents() const
Definition ax_tree.h:167
AtkStateType state
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
GAsyncResult * result
bool Contains(const Container &container, const Value &value)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
bool IsContainerWithSelectableChildren(const ax::mojom::Role role)
const char * ToString(ax::mojom::Event event)
bool IsRowContainer(const ax::mojom::Role role)
std::ostream & operator<<(std::ostream &os, AXEventGenerator::Event event)
bool IsTableHeader(ax::mojom::Role role)
bool IsText(ax::mojom::Role role)
bool IsAlert(const ax::mojom::Role role)
static Target * is_enabled(Benchmark *bench, const Config &config)
bool operator<(const EventParams &rhs) const
bool operator==(const EventParams &rhs)
EventParams(Event event, ax::mojom::EventFrom event_from, const std::vector< AXEventIntent > &event_intents)
TargetedEvent(ui::AXNode *node, const EventParams &event_params)
bool HasState(ax::mojom::State state) const
std::vector< int32_t > child_ids
bool HasStringAttribute(ax::mojom::StringAttribute attribute) const
bool GetBoolAttribute(ax::mojom::BoolAttribute attribute) const
ax::mojom::Role role
int GetIntAttribute(ax::mojom::IntAttribute attribute) const
const std::string & GetStringAttribute(ax::mojom::StringAttribute attribute) const
AXNode::AXID sel_anchor_object_id
ax::mojom::TextAffinity sel_focus_affinity
AXNode::AXID sel_focus_object_id
AXNode::AXID focus_id
int32_t sel_focus_offset
ax::mojom::TextAffinity sel_anchor_affinity
std::string title
int32_t sel_anchor_offset
#define BASE_DCHECK(condition)
Definition logging.h:63
#define BASE_UNREACHABLE()
Definition logging.h:69