Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 681f359

Browse files
Formatting
1 parent 2facb3a commit 681f359

4 files changed

+301
-3
lines changed

third_party/accessibility/ax/platform/ax_platform_node_textrangeprovider_win.cc

+4-2
Original file line numberDiff line numberDiff line change
@@ -1550,14 +1550,16 @@ void AXPlatformNodeTextRangeProviderWin::TextRangeEndpoints::SetEnd(
15501550

15511551
void AXPlatformNodeTextRangeProviderWin::TextRangeEndpoints::AddObserver(
15521552
const AXTreeID tree_id) {
1553-
AXTreeManager* ax_tree_manager = AXTreeManagerMap::GetInstance().GetManager(tree_id);
1553+
AXTreeManager* ax_tree_manager =
1554+
AXTreeManagerMap::GetInstance().GetManager(tree_id);
15541555
BASE_DCHECK(ax_tree_manager);
15551556
ax_tree_manager->GetTree()->AddObserver(this);
15561557
}
15571558

15581559
void AXPlatformNodeTextRangeProviderWin::TextRangeEndpoints::RemoveObserver(
15591560
const AXTreeID tree_id) {
1560-
AXTreeManager* ax_tree_manager = AXTreeManagerMap::GetInstance().GetManager(tree_id);
1561+
AXTreeManager* ax_tree_manager =
1562+
AXTreeManagerMap::GetInstance().GetManager(tree_id);
15611563
if (ax_tree_manager)
15621564
ax_tree_manager->GetTree()->RemoveObserver(this);
15631565
}

third_party/accessibility/ax/platform/ax_platform_node_textrangeprovider_win.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class AX_EXPORT __declspec(uuid("3071e40d-a10d-45ff-a59f-6e8e1138e2c1"))
3434
AXNodePosition::AXPositionInstance start,
3535
AXNodePosition::AXPositionInstance end);
3636

37-
// Creates an instance of the class for unit tests, where AXPlatformNodes
37+
// Creates an instance of the class for unit tests, where AXPlatformNodes
3838
// cannot be queried automatically from endpoints.
3939
static ITextRangeProvider* CreateTextRangeProviderForTesting(
4040
AXPlatformNodeWin* owner,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
// Copyright 2019 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+
#ifndef UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_TEXTRANGEPROVIDER_WIN_H_
6+
#define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_TEXTRANGEPROVIDER_WIN_H_
7+
8+
#include <atlbase.h>
9+
#include <atlcom.h>
10+
11+
#include <UIAutomationCore.h>
12+
13+
#include "ax/ax_node_position.h"
14+
#include "ax/ax_tree_observer.h"
15+
#include "ax/platform/ax_platform_node_delegate.h"
16+
#include "ax/platform/ax_platform_node_win.h"
17+
18+
namespace ui {
19+
20+
class AX_EXPORT __declspec(uuid("3071e40d-a10d-45ff-a59f-6e8e1138e2c1"))
21+
AXPlatformNodeTextRangeProviderWin
22+
: public CComObjectRootEx<CComMultiThreadModel>,
23+
public ITextRangeProvider {
24+
public:
25+
BEGIN_COM_MAP(AXPlatformNodeTextRangeProviderWin)
26+
COM_INTERFACE_ENTRY(ITextRangeProvider)
27+
COM_INTERFACE_ENTRY(AXPlatformNodeTextRangeProviderWin)
28+
END_COM_MAP()
29+
30+
AXPlatformNodeTextRangeProviderWin();
31+
~AXPlatformNodeTextRangeProviderWin();
32+
33+
static ITextRangeProvider* CreateTextRangeProvider(
34+
AXNodePosition::AXPositionInstance start,
35+
AXNodePosition::AXPositionInstance end);
36+
37+
// Creates an instance of the class for unit tests, where AXPlatformNodes
38+
// cannot be queried automatically from endpoints.
39+
static ITextRangeProvider* CreateTextRangeProviderForTesting(
40+
AXPlatformNodeWin* owner,
41+
AXNodePosition::AXPositionInstance start,
42+
AXNodePosition::AXPositionInstance end);
43+
44+
//
45+
// ITextRangeProvider methods.
46+
//
47+
48+
IFACEMETHODIMP Clone(ITextRangeProvider** clone) override;
49+
IFACEMETHODIMP Compare(ITextRangeProvider* other, BOOL* result) override;
50+
IFACEMETHODIMP
51+
CompareEndpoints(TextPatternRangeEndpoint this_endpoint,
52+
ITextRangeProvider* other,
53+
TextPatternRangeEndpoint other_endpoint,
54+
int* result) override;
55+
IFACEMETHODIMP ExpandToEnclosingUnit(TextUnit unit) override;
56+
IFACEMETHODIMP
57+
FindAttribute(TEXTATTRIBUTEID attribute_id,
58+
VARIANT attribute_val,
59+
BOOL is_backward,
60+
ITextRangeProvider** result) override;
61+
IFACEMETHODIMP
62+
FindText(BSTR string,
63+
BOOL backwards,
64+
BOOL ignore_case,
65+
ITextRangeProvider** result) override;
66+
IFACEMETHODIMP GetAttributeValue(TEXTATTRIBUTEID attribute_id,
67+
VARIANT* value) override;
68+
IFACEMETHODIMP
69+
GetBoundingRectangles(SAFEARRAY** screen_physical_pixel_rectangles) override;
70+
IFACEMETHODIMP
71+
GetEnclosingElement(IRawElementProviderSimple** element) override;
72+
IFACEMETHODIMP GetText(int max_count, BSTR* text) override;
73+
IFACEMETHODIMP Move(TextUnit unit, int count, int* units_moved) override;
74+
IFACEMETHODIMP
75+
MoveEndpointByUnit(TextPatternRangeEndpoint endpoint,
76+
TextUnit unit,
77+
int count,
78+
int* units_moved) override;
79+
IFACEMETHODIMP
80+
MoveEndpointByRange(TextPatternRangeEndpoint this_endpoint,
81+
ITextRangeProvider* other,
82+
TextPatternRangeEndpoint other_endpoint) override;
83+
IFACEMETHODIMP Select() override;
84+
IFACEMETHODIMP AddToSelection() override;
85+
IFACEMETHODIMP RemoveFromSelection() override;
86+
IFACEMETHODIMP ScrollIntoView(BOOL align_to_top) override;
87+
IFACEMETHODIMP GetChildren(SAFEARRAY** children) override;
88+
89+
AXPlatformNodeWin* GetOwner() const;
90+
void SetOwnerForTesting(AXPlatformNodeWin* owner);
91+
92+
private:
93+
using AXPositionInstance = AXNodePosition::AXPositionInstance;
94+
using AXPositionInstanceType = typename AXPositionInstance::element_type;
95+
using AXNodeRange = AXRange<AXPositionInstanceType>;
96+
97+
friend class AXPlatformNodeTextRangeProviderTest;
98+
friend class AXPlatformNodeTextProviderTest;
99+
friend class AXRangePhysicalPixelRectDelegate;
100+
101+
static bool AtStartOfLinePredicate(const AXPositionInstance& position);
102+
static bool AtEndOfLinePredicate(const AXPositionInstance& position);
103+
104+
static AXPositionInstance GetNextTextBoundaryPosition(
105+
const AXPositionInstance& position,
106+
ax::mojom::TextBoundary boundary_type,
107+
AXBoundaryBehavior options,
108+
ax::mojom::MoveDirection boundary_direction);
109+
110+
// Prefer these *Impl methods when functionality is needed internally. We
111+
// should avoid calling external APIs internally as it will cause the
112+
// histograms to become innaccurate.
113+
HRESULT MoveEndpointByUnitImpl(TextPatternRangeEndpoint endpoint,
114+
TextUnit unit,
115+
int count,
116+
int* units_moved);
117+
118+
IFACEMETHODIMP ExpandToEnclosingUnitImpl(TextUnit unit);
119+
120+
std::u16string GetString(int max_count,
121+
size_t* appended_newlines_count = nullptr);
122+
const AXPositionInstance& start() const { return endpoints_.GetStart(); }
123+
const AXPositionInstance& end() const { return endpoints_.GetEnd(); }
124+
AXPlatformNodeDelegate* GetDelegate(
125+
const AXPositionInstanceType* position) const;
126+
AXPlatformNodeDelegate* GetDelegate(const AXTreeID tree_id,
127+
const AXNode::AXID node_id) const;
128+
129+
template <typename AnchorIterator, typename ExpandMatchLambda>
130+
HRESULT FindAttributeRange(const TEXTATTRIBUTEID text_attribute_id,
131+
VARIANT attribute_val,
132+
const AnchorIterator first,
133+
const AnchorIterator last,
134+
ExpandMatchLambda expand_match);
135+
136+
AXPositionInstance MoveEndpointByCharacter(const AXPositionInstance& endpoint,
137+
const int count,
138+
int* units_moved);
139+
AXPositionInstance MoveEndpointByWord(const AXPositionInstance& endpoint,
140+
const int count,
141+
int* units_moved);
142+
AXPositionInstance MoveEndpointByLine(const AXPositionInstance& endpoint,
143+
bool is_start_endpoint,
144+
const int count,
145+
int* units_moved);
146+
AXPositionInstance MoveEndpointByParagraph(const AXPositionInstance& endpoint,
147+
const bool is_start_endpoint,
148+
const int count,
149+
int* units_moved);
150+
AXPositionInstance MoveEndpointByPage(const AXPositionInstance& endpoint,
151+
const bool is_start_endpoint,
152+
const int count,
153+
int* units_moved);
154+
AXPositionInstance MoveEndpointByDocument(const AXPositionInstance& endpoint,
155+
const int count,
156+
int* units_moved);
157+
158+
AXPositionInstance MoveEndpointByUnitHelper(
159+
const AXPositionInstance& endpoint,
160+
const ax::mojom::TextBoundary boundary_type,
161+
const int count,
162+
int* units_moved);
163+
164+
// A text range normalization is necessary to prevent a |start_| endpoint to
165+
// be positioned at the end of an anchor when it can be at the start of the
166+
// next anchor. After normalization, it is guaranteed that:
167+
// * both endpoints passed by parameter are always positioned on unignored
168+
// anchors;
169+
// * both endpoints passed by parameter are never between a grapheme cluster;
170+
// * if the endpoints passed by parameter create a degenerate range, both
171+
// endpoints are on the same anchor.
172+
// Normalization never updates the internal endpoints directly. Instead, it
173+
// normalizes the endpoints passed by parameter.
174+
void NormalizeTextRange(AXPositionInstance& start, AXPositionInstance& end);
175+
static void NormalizeAsUnignoredPosition(AXPositionInstance& position);
176+
static void NormalizeAsUnignoredTextRange(AXPositionInstance& start,
177+
AXPositionInstance& end);
178+
179+
AXPlatformNodeDelegate* GetRootDelegate(const ui::AXTreeID tree_id);
180+
AXNode* GetSelectionCommonAnchor();
181+
void RemoveFocusFromPreviousSelectionIfNeeded(
182+
const AXNodeRange& new_selection);
183+
AXPlatformNodeWin* GetPlatformNodeFromAXNode(const AXNode* node) const;
184+
AXPlatformNodeWin* GetLowestAccessibleCommonPlatformNode() const;
185+
bool HasTextRangeOrSelectionInAtomicTextField(
186+
const AXPositionInstance& start_position,
187+
const AXPositionInstance& end_position) const;
188+
189+
void SetStart(AXPositionInstance start);
190+
void SetEnd(AXPositionInstance end);
191+
192+
static bool TextAttributeIsArrayType(TEXTATTRIBUTEID attribute_id);
193+
static bool TextAttributeIsUiaReservedValue(
194+
const base::win::VariantVector& vector);
195+
static bool ShouldReleaseTextAttributeAsSafearray(
196+
TEXTATTRIBUTEID attribute_id,
197+
const base::win::VariantVector& vector);
198+
199+
Microsoft::WRL::ComPtr<AXPlatformNodeWin> owner_for_test_;
200+
201+
// The TextRangeEndpoints class has the responsibility of keeping the
202+
// endpoints of the range valid or nullify them when it can't find a valid
203+
// alternative.
204+
//
205+
// An endpoint can become invalid when
206+
// A. the node it's on gets deleted,
207+
// B. when an ancestor node gets deleted, deleting the subtree our endpoint
208+
// is on, or
209+
// C. when a descendant node gets deleted, potentially rendering the
210+
// position invalid due to a smaller MaxTextOffset value (for a text
211+
// position) or fewer child nodes (for a tree position).
212+
//
213+
// In all cases, our approach to resolve the endpoints to valid positions
214+
// takes two steps:
215+
// 1. Move the endpoint to an equivalent ancestor position before the node
216+
// gets deleted - we can't move the position once the node it's on is
217+
// deleted since this position would already be considered invalid.
218+
// 2. Call AsValidPosition on that new position once the node is deleted -
219+
// calling this function before the node gets deleted wouldn't do much
220+
// since our position would still be considered valid at this point.
221+
//
222+
// Because AsValidPosition can potentially be expensive, we only want to run
223+
// it when necessary. For this reason, we store the node ID and tree ID that
224+
// causes the first step to happen and only run the second step in
225+
// OnNodeDeleted for the corresponding node deletion. When OnNodeDeleted is
226+
// called, the |start_| and |end_| endpoints have already been moved up to an
227+
// ancestor that is still part of the tree. This is to ensure that we don't
228+
// have to read the node/tree structure of the deleted node in that function -
229+
// which would likely result in a crash.
230+
//
231+
// Both scenarios A and B are fixed by this approach (by the implementation of
232+
// OnSubtreeWillBeDeleted), but we still have work to do to fix scenario C.
233+
// This case, in theory, would only require the second step to ensure that the
234+
// position is always valid but computing whether node is part of the subtree
235+
// of the endpoint we're on would be very expensive. Furthermore, because the
236+
// endpoints are generally on leaf nodes, the scenario is unlikely - we
237+
// haven't heard of issues caused by this scenario yet. Eventually, we might
238+
// be able to scope the fix to specific use cases, like when the range is on
239+
// UIA embedded object (e.g. button, select, etc.)
240+
//
241+
// ***
242+
//
243+
// Why we can't use a ScopedObserver here:
244+
// We tried using a ScopedObserver instead of a simple observer in this case,
245+
// but there appears to be a problem with the lifetime of the referenced
246+
// AXTreeManager in the ScopedObserver. The AXTreeManager can get deleted
247+
// before the TextRangeEndpoints does, so when the destructor of the
248+
// ScopedObserver calls ScopedObserver::RemoveAll on an already deleted
249+
// AXTreeManager, it crashes.
250+
class TextRangeEndpoints : public AXTreeObserver {
251+
public:
252+
TextRangeEndpoints();
253+
~TextRangeEndpoints() override;
254+
const AXPositionInstance& GetStart() const { return start_; }
255+
const AXPositionInstance& GetEnd() const { return end_; }
256+
void SetStart(AXPositionInstance new_start);
257+
void SetEnd(AXPositionInstance new_end);
258+
259+
void AddObserver(const AXTreeID tree_id);
260+
void RemoveObserver(const AXTreeID tree_id);
261+
void OnSubtreeWillBeDeleted(AXTree* tree, AXNode* node) override;
262+
void OnNodeDeleted(AXTree* tree, AXNode::AXID node_id) override;
263+
264+
private:
265+
struct DeletionOfInterest {
266+
AXTreeID tree_id;
267+
AXNode::AXID node_id;
268+
};
269+
270+
void AdjustEndpointForSubtreeDeletion(AXTree* tree,
271+
const AXNode* const node,
272+
bool is_start_endpoint);
273+
274+
AXPositionInstance start_;
275+
AXPositionInstance end_;
276+
277+
std::optional<DeletionOfInterest> validation_necessary_for_start_;
278+
std::optional<DeletionOfInterest> validation_necessary_for_end_;
279+
};
280+
TextRangeEndpoints endpoints_;
281+
};
282+
283+
} // namespace ui
284+
285+
#endif // UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_TEXTRANGEPROVIDER_WIN_H_
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--- third_party/accessibility/ax/platform/ax_platform_node_textrangeprovider_win.h 2022-12-28 14:12:58.174051107 -0800
2+
+++ -2022-12-28 14:18:30.707489284 -0800
3+
@@ -34,7 +34,7 @@
4+
AXNodePosition::AXPositionInstance start,
5+
AXNodePosition::AXPositionInstance end);
6+
7+
- // Creates an instance of the class for unit tests, where AXPlatformNodes
8+
+ // Creates an instance of the class for unit tests, where AXPlatformNodes
9+
// cannot be queried automatically from endpoints.
10+
static ITextRangeProvider* CreateTextRangeProviderForTesting(
11+
AXPlatformNodeWin* owner,

0 commit comments

Comments
 (0)