1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/ax_selection.h"
#include "ui/accessibility/ax_node_position.h"
namespace ui {
namespace {
// Helper for GetUnignoredSelection. Creates a position using |node_id|,
// |offset| and |affinity|, and if it's ignored, updates these arguments so
// that they represent a non-null non-ignored position, according to
// |adjustment_behavior|. Returns true on success, false on failure. Note that
// if the position is initially null, it's not ignored and it's a success.
bool ComputeUnignoredSelectionEndpoint(
const AXTree* tree,
AXPositionAdjustmentBehavior adjustment_behavior,
AXNodeID& node_id,
int32_t& offset,
ax::mojom::TextAffinity& affinity) {
AXNode* node = tree ? tree->GetFromId(node_id) : nullptr;
if (!node) {
node_id = kInvalidAXNodeID;
offset = AXNodePosition::INVALID_OFFSET;
affinity = ax::mojom::TextAffinity::kDownstream;
return false;
}
AXNodePosition::AXPositionInstance position =
AXNodePosition::CreatePosition(*node, offset, affinity);
// Null positions are never ignored, but must be considered successful, or
// these Android tests would fail:
// org.chromium.content.browser.accessibility.AssistViewStructureTest#*
// The reason is that |position| becomes null because no AXTreeManager is
// registered for that |tree|'s AXTreeID.
// TODO(accessibility): investigate and fix this if needed.
if (!position->IsIgnored())
return true; // We assume that unignored positions are already valid.
position =
position->AsValidPosition()->AsUnignoredPosition(adjustment_behavior);
// Moving to an unignored position might have placed the position on a leaf
// node. Any selection endpoint that is inside a leaf node is expressed as a
// text position in AXTreeData. (Note that in this context "leaf node" means
// a node with no children or with only ignored children. This does not
// refer to a platform leaf.)
if (position->IsLeafTreePosition())
position = position->AsTextPosition();
// We do not expect the selection to have an endpoint on an inline text
// box as this will create issues with parts of the code that don't use
// inline text boxes.
if (position->IsTextPosition() &&
position->GetRole() == ax::mojom::Role::kInlineTextBox) {
position = position->CreateParentPosition();
}
switch (position->kind()) {
case AXPositionKind::NULL_POSITION:
node_id = kInvalidAXNodeID;
offset = AXNodePosition::INVALID_OFFSET;
affinity = ax::mojom::TextAffinity::kDownstream;
return false;
case AXPositionKind::TREE_POSITION:
node_id = position->anchor_id();
offset = position->child_index();
affinity = ax::mojom::TextAffinity::kDownstream;
return true;
case AXPositionKind::TEXT_POSITION:
node_id = position->anchor_id();
offset = position->text_offset();
affinity = position->affinity();
return true;
}
}
} // namespace
AXSelection::AXSelection() = default;
AXSelection::AXSelection(const AXSelection&) = default;
AXSelection::~AXSelection() = default;
AXSelection::AXSelection(const AXTree& tree)
: is_backward(tree.data().sel_is_backward),
anchor_object_id(tree.data().sel_anchor_object_id),
anchor_offset(tree.data().sel_anchor_offset),
anchor_affinity(tree.data().sel_anchor_affinity),
focus_object_id(tree.data().sel_focus_object_id),
focus_offset(tree.data().sel_focus_offset),
focus_affinity(tree.data().sel_focus_affinity),
tree_id_(tree.GetAXTreeID()) {}
AXSelection& AXSelection::ToUnignoredSelection() {
DCHECK_NE(tree_id_, AXTreeIDUnknown())
<< "Tree is not registered with a tree manager";
const AXTreeManager* manager = AXTreeManager::FromID(tree_id_);
if (!manager)
return *this;
// If one of the selection endpoints is invalid, then the other endpoint
// should also be unset.
if (!ComputeUnignoredSelectionEndpoint(
manager->ax_tree(),
is_backward ? AXPositionAdjustmentBehavior::kMoveForward
: AXPositionAdjustmentBehavior::kMoveBackward,
anchor_object_id, anchor_offset, anchor_affinity)) {
focus_object_id = kInvalidAXNodeID;
focus_offset = AXNodePosition::INVALID_OFFSET;
focus_affinity = ax::mojom::TextAffinity::kDownstream;
} else if (!ComputeUnignoredSelectionEndpoint(
manager->ax_tree(),
is_backward ? AXPositionAdjustmentBehavior::kMoveBackward
: AXPositionAdjustmentBehavior::kMoveForward,
focus_object_id, focus_offset, focus_affinity)) {
anchor_object_id = kInvalidAXNodeID;
anchor_offset = AXNodePosition::INVALID_OFFSET;
anchor_affinity = ax::mojom::TextAffinity::kDownstream;
}
return *this;
}
} // namespace ui
|