Commit c8a899f4 authored by Hiroki Sato's avatar Hiroki Sato Committed by Commit Bot

arc-a11y: prevent crash with a wrong source node id

Previously we had an assumption that the event source from Android is
serialized in event node data. But it can be incorrect in some rare
cases.
This change prevents Chrome crash on such a case.

AX-Relnotes: n/a.
Bug: b:159861631
Test: AXTreeSourceArcTest
Change-Id: I22308ba29e23ff1de9271661443ceb7f67e79275
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2265895
Commit-Queue: Hiroki Sato <hirokisato@chromium.org>
Reviewed-by: default avatarSara Kato <sarakato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#782784}
parent d951b4de
...@@ -50,9 +50,7 @@ ax::mojom::Event ToAXEvent( ...@@ -50,9 +50,7 @@ ax::mojom::Event ToAXEvent(
case mojom::AccessibilityEventType::VIEW_TEXT_SELECTION_CHANGED: case mojom::AccessibilityEventType::VIEW_TEXT_SELECTION_CHANGED:
return ax::mojom::Event::kTextSelectionChanged; return ax::mojom::Event::kTextSelectionChanged;
case mojom::AccessibilityEventType::WINDOW_STATE_CHANGED: { case mojom::AccessibilityEventType::WINDOW_STATE_CHANGED: {
if (arc_content_change_types.has_value()) { if (source_node && arc_content_change_types.has_value()) {
DCHECK(source_node); // Should be not null because it's dropped in
// Android side.
const base::Optional<ax::mojom::Event> event_or_null = const base::Optional<ax::mojom::Event> event_or_null =
FromContentChangeTypesToAXEvent(arc_content_change_types.value(), FromContentChangeTypesToAXEvent(arc_content_change_types.value(),
*source_node); *source_node);
...@@ -68,9 +66,7 @@ ax::mojom::Event ToAXEvent( ...@@ -68,9 +66,7 @@ ax::mojom::Event ToAXEvent(
case mojom::AccessibilityEventType::NOTIFICATION_STATE_CHANGED: case mojom::AccessibilityEventType::NOTIFICATION_STATE_CHANGED:
return ax::mojom::Event::kLayoutComplete; return ax::mojom::Event::kLayoutComplete;
case mojom::AccessibilityEventType::WINDOW_CONTENT_CHANGED: case mojom::AccessibilityEventType::WINDOW_CONTENT_CHANGED:
if (arc_content_change_types.has_value()) { if (source_node && arc_content_change_types.has_value()) {
DCHECK(source_node); // Should be not null because it's dropped in
// Android side.
const base::Optional<ax::mojom::Event> event_or_null = const base::Optional<ax::mojom::Event> event_or_null =
FromContentChangeTypesToAXEvent(arc_content_change_types.value(), FromContentChangeTypesToAXEvent(arc_content_change_types.value(),
*source_node); *source_node);
......
...@@ -124,7 +124,8 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) { ...@@ -124,7 +124,8 @@ void AXTreeSourceArc::NotifyAccessibilityEvent(AXEventData* event_data) {
event_data->event_text) { event_data->event_text) {
AccessibilityInfoDataWrapper* source_node = AccessibilityInfoDataWrapper* source_node =
GetFromId(event_data->source_id); GetFromId(event_data->source_id);
UpdateAXNameCache(source_node, *event_data->event_text); if (IsValid(source_node))
UpdateAXNameCache(source_node, *event_data->event_text);
} }
ApplyCachedProperties(); ApplyCachedProperties();
...@@ -371,12 +372,11 @@ AXTreeSourceArc::GetSelectedNodeInfoFromAdapterView( ...@@ -371,12 +372,11 @@ AXTreeSourceArc::GetSelectedNodeInfoFromAdapterView(
bool AXTreeSourceArc::UpdateAndroidFocusedId(const AXEventData& event_data) { bool AXTreeSourceArc::UpdateAndroidFocusedId(const AXEventData& event_data) {
if (event_data.event_type == AXEventType::VIEW_FOCUSED) { if (event_data.event_type == AXEventType::VIEW_FOCUSED) {
AccessibilityInfoDataWrapper* focused_node = AccessibilityInfoDataWrapper* source_node = GetFromId(event_data.source_id);
GetFromId(event_data.source_id); if (source_node && source_node->IsVisibleToUser()) {
if (focused_node && focused_node->IsVisibleToUser()) {
// Sometimes Android sets focus on unfocusable node, e.g. ListView. // Sometimes Android sets focus on unfocusable node, e.g. ListView.
AccessibilityInfoDataWrapper* adjusted_node = AccessibilityInfoDataWrapper* adjusted_node =
FindFirstFocusableNode(focused_node); FindFirstFocusableNode(source_node);
android_focused_id_ = IsValid(adjusted_node) ? adjusted_node->GetId() android_focused_id_ = IsValid(adjusted_node) ? adjusted_node->GetId()
: event_data.source_id; : event_data.source_id;
} }
...@@ -385,8 +385,9 @@ bool AXTreeSourceArc::UpdateAndroidFocusedId(const AXEventData& event_data) { ...@@ -385,8 +385,9 @@ bool AXTreeSourceArc::UpdateAndroidFocusedId(const AXEventData& event_data) {
// 1. Changing a value in ProgressBar or TimePicker in ARC P. // 1. Changing a value in ProgressBar or TimePicker in ARC P.
// 2. Selecting an item in the context of an AdapterView. // 2. Selecting an item in the context of an AdapterView.
AccessibilityInfoDataWrapper* source_node = GetFromId(event_data.source_id); AccessibilityInfoDataWrapper* source_node = GetFromId(event_data.source_id);
DCHECK(source_node); if (!source_node || !source_node->IsNode())
DCHECK(source_node->IsNode()); return false;
AXNodeInfoData* node_info = source_node->GetNode(); AXNodeInfoData* node_info = source_node->GetNode();
DCHECK(node_info); DCHECK(node_info);
...@@ -436,16 +437,16 @@ bool AXTreeSourceArc::UpdateAndroidFocusedId(const AXEventData& event_data) { ...@@ -436,16 +437,16 @@ bool AXTreeSourceArc::UpdateAndroidFocusedId(const AXEventData& event_data) {
} }
void AXTreeSourceArc::UpdateAXNameCache( void AXTreeSourceArc::UpdateAXNameCache(
AccessibilityInfoDataWrapper* focused_node, AccessibilityInfoDataWrapper* source_node,
const std::vector<std::string>& event_text) { const std::vector<std::string>& event_text) {
if (IsDrawerLayout(focused_node->GetNode())) { if (IsDrawerLayout(source_node->GetNode())) {
// When drawer menu opened, make the menu title announced. // When drawer menu opened, make the menu title announced.
// When focus is changed, ChromeVox computes the diff in ancestry between // When focus is changed, ChromeVox computes the diff in ancestry between
// the previously focused and new focused node. // the previously focused and new focused node.
// As the DrawerLayout is LCA of them, set the new title to be the first // As the DrawerLayout is LCA of them, set the new title to be the first
// visible child node (which is usually drawer menu). // visible child node (which is usually drawer menu).
std::vector<AccessibilityInfoDataWrapper*> children; std::vector<AccessibilityInfoDataWrapper*> children;
focused_node->GetChildren(&children); source_node->GetChildren(&children);
for (auto* child : children) { for (auto* child : children) {
if (child->IsNode() && child->IsVisibleToUser() && if (child->IsNode() && child->IsVisibleToUser() &&
GetBooleanProperty(child->GetNode(), AXBooleanProperty::IMPORTANCE)) { GetBooleanProperty(child->GetNode(), AXBooleanProperty::IMPORTANCE)) {
......
...@@ -123,7 +123,7 @@ class AXTreeSourceArc : public ui::AXTreeSource<AccessibilityInfoDataWrapper*, ...@@ -123,7 +123,7 @@ class AXTreeSourceArc : public ui::AXTreeSource<AccessibilityInfoDataWrapper*,
// automation. // automation.
bool UpdateAndroidFocusedId(const mojom::AccessibilityEventData& event_data); bool UpdateAndroidFocusedId(const mojom::AccessibilityEventData& event_data);
void UpdateAXNameCache(AccessibilityInfoDataWrapper* focused_node, void UpdateAXNameCache(AccessibilityInfoDataWrapper* source_node,
const std::vector<std::string>& event_text); const std::vector<std::string>& event_text);
void ApplyCachedProperties(); void ApplyCachedProperties();
......
...@@ -1178,4 +1178,37 @@ TEST_F(AXTreeSourceArcTest, StateDescriptionChangedEvent) { ...@@ -1178,4 +1178,37 @@ TEST_F(AXTreeSourceArcTest, StateDescriptionChangedEvent) {
// TODO(sahok): add test when source_node is not a range widget. // TODO(sahok): add test when source_node is not a range widget.
} }
TEST_F(AXTreeSourceArcTest, EventWithWrongSourceId) {
auto event = AXEventData::New();
event->source_id = 99999; // This doesn't exist in serialized nodes.
event->task_id = 1;
event->window_data = std::vector<mojom::AccessibilityWindowInfoDataPtr>();
event->window_data->push_back(AXWindowInfoData::New());
AXWindowInfoData* root_window = event->window_data->back().get();
root_window->window_id = 100;
root_window->root_node_id = 10;
event->node_data.push_back(AXNodeInfoData::New());
AXNodeInfoData* node = event->node_data.back().get();
node->id = 10;
// This test only verifies that wrong source id won't make Chrome crash.
event->event_type = AXEventType::VIEW_FOCUSED;
CallNotifyAccessibilityEvent(event.get());
event->event_type = AXEventType::VIEW_SELECTED;
CallNotifyAccessibilityEvent(event.get());
event->event_type = AXEventType::WINDOW_STATE_CHANGED;
event->event_text = std::vector<std::string>({"test text."});
SetProperty(event.get(), AXEventIntListProperty::CONTENT_CHANGE_TYPES,
{static_cast<int>(mojom::ContentChangeType::STATE_DESCRIPTION)});
CallNotifyAccessibilityEvent(event.get());
event->event_type = AXEventType::WINDOW_CONTENT_CHANGED;
CallNotifyAccessibilityEvent(event.get());
}
} // namespace arc } // namespace arc
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment