-
Notifications
You must be signed in to change notification settings - Fork 6k
Implement ITextProvider and ITextRangeProvider for UIA #38284
Conversation
@@ -176,7 +204,7 @@ class AccessibilityBridge | |||
std::unordered_map<AccessibilityNodeId, | |||
std::shared_ptr<FlutterPlatformNodeDelegate>> | |||
id_wrapper_map_; | |||
ui::AXTree tree_; | |||
std::unique_ptr<ui::AXTree> tree_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed this to a pointer so we can return a pointer in the implementation for AX Tree Manager.
@@ -524,7 +533,7 @@ void AccessibilityBridge::SetTreeData(const SemanticsNode& node, | |||
// 1. this text field has a valid selection | |||
// 2. this text field doesn't have a valid selection but had selection stored | |||
// in the tree. | |||
if (flags & FlutterSemanticsFlag::kFlutterSemanticsFlagIsTextField) { | |||
if (flags & FlutterSemanticsFlag::kFlutterSemanticsFlagIsTextField && flags & FlutterSemanticsFlag::kFlutterSemanticsFlagIsFocused) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We want to only update these values for the focused node so that the text selection data points to the focused node, not to whichever text node happens to be latest in the update.
case ui::AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED: { | ||
ui::AXNode::AXID focus_id = GetAXTreeData().sel_focus_object_id; | ||
auto focus_delegate = GetFlutterPlatformNodeDelegateFromID(focus_id).lock(); | ||
DispatchWinAccessibilityEvent( | ||
std::static_pointer_cast<FlutterPlatformNodeDelegateWindows>(focus_delegate), ax::mojom::Event::kDocumentSelectionChanged); | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document selection change event should be fired on the node containing the changed selection, not the root.
@@ -1078,7 +1079,7 @@ TEST_F(AXPositionTest, GetMaxTextOffsetAndGetTextWithGeneratedContent) { | |||
root_1.role = ax::mojom::Role::kRootWebArea; | |||
root_1.child_ids = {text_field_2.id}; | |||
|
|||
text_field_2.role = ax::mojom::Role::kTextField; | |||
text_field_2.role = ax::mojom::Role::kGroup; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chromium uses kTextField for parent nodes containing further text-containing children. We use it to represent an editable text field; we use kGroup for general grouping nodes.
// https://github.com/flutter/flutter/issues/109804 | ||
case UIA_TextEditPatternId: | ||
case UIA_TextPatternId: | ||
if (IsText() || IsTextField() || data.role == ax::mojom::Role::kRootWebArea) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not use kRootWebArea
, but it is expected by unittests.
@@ -0,0 +1,354 @@ | |||
// Copyright 2013 The Flutter Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style license that can be | |||
// found in the LICENSE file. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is Chromium code, right? Should these have Chromium license headers?
// TODO(schectman): figure out when we actually want this attribute set or | ||
// not. | ||
node_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, | ||
true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to double-check, should this be kept?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I probably will need to set up a new issue for this if there isn't one yet. I don't think we currently have a good way to keep track of this attribute that is used by the AX code, so this is a placeholder as I think commonly used elements usually behave as their own line wrt a11y.
@@ -1316,7 +1331,8 @@ class AXPosition { | |||
child_position->affinity_ = ax::mojom::TextAffinity::kUpstream; | |||
break; | |||
} | |||
child_position = text_position->CreateChildPositionAt(i); | |||
AXPositionInstance child = text_position->CreateChildPositionAt(i); | |||
child_position = std::move(child); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed?
if (!common_anchor || !start_anchor || !end_anchor) | ||
return UIA_E_ELEMENTNOTAVAILABLE; | ||
|
||
SAFEARRAY* safe_array = SafeArrayCreateVector(VT_UNKNOWN, 0, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this different from the Chromium implementation? Is this something we should add in the future? If so, should we leave a TODO here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add a TODO. This would require us to implement a method to get all children nodes that are between two particular nodes on the tree in traversal order, which I am not aware of currently having.
Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change). If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review. |
ab4d15a
to
03a7b21
Compare
max_text_offset_in_parent = dummy_position->MaxTextOffset(); | ||
if (parent_offset > max_text_offset_in_parent) { | ||
parent_offset = max_text_offset_in_parent; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a comment as to why this is necessary?
auto current_line_start = start->Clone(); | ||
while (!current_line_start->IsNullPosition() && *current_line_start < *end) { | ||
auto current_line_end = current_line_start->CreateNextLineEndPosition( | ||
AXBoundaryBehavior::CrossBoundary); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you explain why AXBoundaryDetection::kDontCheckInitialPosition
was removed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method signature for our version of CreateNextLineEndPosition
takes a parameter of a different type than Chrome's current version of the same. We take a AXBoundaryBehavior
, while Chromium takes a struct consisting of AXBoundaryBehavior, AXBoundaryDetection
for (auto data : nodes) { | ||
if (!data.node->IsText() && !data.node->data().IsTextField()) { | ||
continue; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary? If looks like the original test passed without this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is because we treat the kRootWebArea
role differently than in Chrome.
Copy ITextProvider methods into AXPlatformNodeWin Apply selection change event to focus node instead of root The rumor come out: Why does is reads violate in map find No more memory errors, expansion still wobbly
625178b
to
63cbbd9
Compare
I will attempt to split this PR into several smaller PRs with more organized commits. |
In order for text providing nodes to properly interact with screen reader commands via UIA, we need to implement text and textedit pattern providers. As an example, this allows screen readers to narrate the character after the carat when the user moves the carat in an edit field with the arrow keys.
Unit tests for
AXPlatformNodeText(Range)ProviderWin
are modeled after those found in the Chromium source. Details for some required modification to account for differences between the two codebases, and a handful of the newly introduced test cases are currently disabled, which is tracked in flutter/flutter#117012.Functionality for searching for text in a text range does not yet respect the
ignore_case
option: flutter/flutter#117013.Part of flutter/flutter#116219
Pre-launch Checklist
///
).