Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ax_ui: move selection based AXTree methods under AXSelection
Extract selection logic out of AXTree and move it under AXSelection umbrella. Make AXSelection keeping a AXTreeID instance as it allows to retrieve AXNode instance by node id. It is needed to compute unignored selection, as well it will be helpful to enclose anchor/focus AXNode object getting into AXSelection. Later, we may switch to an architecture of a single AXSelection instance per an AXTree object, which will allow to implement lazily computed and live unignored selection. Change-Id: Ib3446b12e7ebb40c70520499ab3a59e200f26d7d Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3863567 Commit-Queue: Alexander Surkkov <asurkov@igalia.com> Reviewed-by: Nektarios Paisios <nektar@chromium.org> Cr-Commit-Position: refs/heads/main@{#1054178}
- Loading branch information
Showing
7 changed files
with
152 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
// 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() { | ||
const AXTreeManager* manager = AXTreeManager::FromID(tree_id_); | ||
DCHECK(manager); | ||
|
||
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters