7#include <UIAutomation.h>
17#include "flutter/fml/platform/win/wstring_conversion.h"
18#include "third_party/icu/source/i18n/unicode/usearch.h"
20#define UIA_VALIDATE_TEXTRANGEPROVIDER_CALL() \
21 if (!GetOwner() || !GetOwner()->GetDelegate() || !start() || \
22 !start()->GetAnchor() || !end() || !end()->GetAnchor()) \
23 return UIA_E_ELEMENTNOTAVAILABLE; \
24 SetStart(start()->AsValidPosition()); \
25 SetEnd(end()->AsValidPosition());
26#define UIA_VALIDATE_TEXTRANGEPROVIDER_CALL_1_IN(in) \
27 if (!GetOwner() || !GetOwner()->GetDelegate() || !start() || \
28 !start()->GetAnchor() || !end() || !end()->GetAnchor()) \
29 return UIA_E_ELEMENTNOTAVAILABLE; \
32 SetStart(start()->AsValidPosition()); \
33 SetEnd(end()->AsValidPosition());
34#define UIA_VALIDATE_TEXTRANGEPROVIDER_CALL_1_OUT(out) \
35 if (!GetOwner() || !GetOwner()->GetDelegate() || !start() || \
36 !start()->GetAnchor() || !end() || !end()->GetAnchor()) \
37 return UIA_E_ELEMENTNOTAVAILABLE; \
41 SetStart(start()->AsValidPosition()); \
42 SetEnd(end()->AsValidPosition());
43#define UIA_VALIDATE_TEXTRANGEPROVIDER_CALL_1_IN_1_OUT(in, out) \
44 if (!GetOwner() || !GetOwner()->GetDelegate() || !start() || \
45 !start()->GetAnchor() || !end() || !end()->GetAnchor()) \
46 return UIA_E_ELEMENTNOTAVAILABLE; \
50 SetStart(start()->AsValidPosition()); \
51 SetEnd(end()->AsValidPosition());
54#define UIA_VALIDATE_BOUNDS(bounds) \
55 if (bounds.OffsetFromOrigin().IsZero() && bounds.IsEmpty()) \
56 return UIA_E_NOTSUPPORTED;
63 AXPlatformNodeTextRangeProviderWin*
host)
77 clipping_behavior, offscreen_result);
91 AXPlatformNodeTextRangeProviderWin* host_;
94AXPlatformNodeTextRangeProviderWin::AXPlatformNodeTextRangeProviderWin() {}
96AXPlatformNodeTextRangeProviderWin::~AXPlatformNodeTextRangeProviderWin() {}
98ITextRangeProvider* AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider(
99 AXPositionInstance start,
100 AXPositionInstance end) {
101 CComObject<AXPlatformNodeTextRangeProviderWin>* text_range_provider =
nullptr;
102 if (
SUCCEEDED(CComObject<AXPlatformNodeTextRangeProviderWin>::CreateInstance(
103 &text_range_provider))) {
105 text_range_provider->SetStart(std::move(start));
106 text_range_provider->SetEnd(std::move(end));
107 text_range_provider->AddRef();
108 return text_range_provider;
115AXPlatformNodeTextRangeProviderWin::CreateTextRangeProviderForTesting(
116 AXPlatformNodeWin* owner,
117 AXPositionInstance start,
118 AXPositionInstance end) {
119 Microsoft::WRL::ComPtr<ITextRangeProvider> text_range_provider =
120 CreateTextRangeProvider(
start->Clone(),
end->Clone());
121 Microsoft::WRL::ComPtr<AXPlatformNodeTextRangeProviderWin>
122 text_range_provider_win;
123 if (
SUCCEEDED(text_range_provider->QueryInterface(
124 IID_PPV_ARGS(&text_range_provider_win)))) {
125 text_range_provider_win->SetOwnerForTesting(owner);
126 return text_range_provider_win.Get();
135HRESULT AXPlatformNodeTextRangeProviderWin::Clone(ITextRangeProvider** clone) {
138 *clone = CreateTextRangeProvider(
start()->Clone(),
end()->Clone());
146 Microsoft::WRL::ComPtr<AXPlatformNodeTextRangeProviderWin> other_provider;
147 if (other->QueryInterface(IID_PPV_ARGS(&other_provider)) != S_OK)
148 return UIA_E_INVALIDOPERATION;
150 if (*
start() == *(other_provider->start()) &&
151 *
end() == *(other_provider->end())) {
157HRESULT AXPlatformNodeTextRangeProviderWin::CompareEndpoints(
158 TextPatternRangeEndpoint this_endpoint,
159 ITextRangeProvider* other,
160 TextPatternRangeEndpoint other_endpoint,
164 Microsoft::WRL::ComPtr<AXPlatformNodeTextRangeProviderWin> other_provider;
165 if (other->QueryInterface(IID_PPV_ARGS(&other_provider)) != S_OK)
166 return UIA_E_INVALIDOPERATION;
168 const AXPositionInstance& this_provider_endpoint =
169 (this_endpoint == TextPatternRangeEndpoint_Start) ?
start() :
end();
170 const AXPositionInstance& other_provider_endpoint =
171 (other_endpoint == TextPatternRangeEndpoint_Start)
172 ? other_provider->start()
173 : other_provider->end();
175 std::optional<int> comparison =
176 this_provider_endpoint->CompareTo(*other_provider_endpoint);
178 return UIA_E_INVALIDOPERATION;
180 if (comparison.value() < 0)
182 else if (comparison.value() > 0)
189HRESULT AXPlatformNodeTextRangeProviderWin::ExpandToEnclosingUnit(
191 return ExpandToEnclosingUnitImpl(unit);
194HRESULT AXPlatformNodeTextRangeProviderWin::ExpandToEnclosingUnitImpl(
198 AXPositionInstance normalized_start =
start()->Clone();
199 AXPositionInstance normalized_end =
end()->Clone();
200 NormalizeTextRange(normalized_start, normalized_end);
201 SetStart(std::move(normalized_start));
202 SetEnd(std::move(normalized_end));
209 case TextUnit_Character: {
212 AXPositionInstance end_backup =
end()->Clone();
213 SetEnd(
start()->CreateNextCharacterPosition(
216 if (
end()->IsNullPosition()) {
219 AXPositionInstance start_backup =
start()->Clone();
220 SetStart(
start()->CreatePreviousCharacterPosition(
223 if (
start()->IsNullPosition()) {
225 SetStart(std::move(start_backup));
226 SetEnd(std::move(end_backup));
229 SetEnd(
start()->CreateNextCharacterPosition(
234 AXPositionInstance normalized_start =
start()->Clone();
235 AXPositionInstance normalized_end =
end()->Clone();
236 NormalizeTextRange(normalized_start, normalized_end);
237 SetStart(std::move(normalized_start));
238 SetEnd(std::move(normalized_end));
241 case TextUnit_Format:
242 SetStart(
start()->CreatePreviousFormatStartPosition(
244 SetEnd(
start()->CreateNextFormatEndPosition(
247 case TextUnit_Word: {
248 AXPositionInstance start_backup =
start()->Clone();
249 SetStart(
start()->CreatePreviousWordStartPosition(
256 SetEnd(
start()->CreateNextWordStartPosition(
266 SetStart(
start()->CreateBoundaryStartPosition(
269 &AtEndOfLinePredicate));
272 SetEnd(
start()->CreateBoundaryEndPosition(
275 &AtEndOfLinePredicate));
277 case TextUnit_Paragraph:
278 SetStart(
start()->CreatePreviousParagraphStartPosition(
280 SetEnd(
start()->CreateNextParagraphStartPosition(
283 case TextUnit_Page: {
286 const AXNode* common_anchor =
start()->LowestCommonAnchor(*
end());
287 if (common_anchor->tree()->HasPaginationSupport()) {
288 SetStart(
start()->CreatePreviousPageStartPosition(
290 SetEnd(
start()->CreateNextPageEndPosition(
296 case TextUnit_Document:
298 start()->CreatePositionAtStartOfDocument()->AsLeafTextPosition());
299 SetEnd(
start()->CreatePositionAtEndOfDocument());
302 return UIA_E_NOTSUPPORTED;
309HRESULT AXPlatformNodeTextRangeProviderWin::FindAttribute(
310 TEXTATTRIBUTEID text_attribute_id,
313 ITextRangeProvider**
result) {
345 AXPositionInstance normalized_start =
start()->Clone();
346 AXPositionInstance normalized_end =
end()->Clone();
347 NormalizeTextRange(normalized_start, normalized_end);
350 AXPositionInstance matched_range_start =
nullptr;
351 AXPositionInstance matched_range_end =
nullptr;
353 std::vector<AXNodeRange> anchors;
354 AXNodeRange range(normalized_start->Clone(), normalized_end->Clone());
355 for (AXNodeRange leaf_text_range : range)
356 anchors.emplace_back(std::move(leaf_text_range));
358 auto expand_match = [&matched_range_start, &matched_range_end, is_backward](
359 auto& current_start,
auto& current_end) {
363 if (matched_range_start !=
nullptr && matched_range_end !=
nullptr) {
367 matched_range_start = current_start->Clone();
369 matched_range_end = current_end->Clone();
373 matched_range_start = current_start->Clone();
374 matched_range_end = current_end->Clone();
380 ? FindAttributeRange(text_attribute_id, attribute_val,
381 anchors.crbegin(), anchors.crend(), expand_match)
382 : FindAttributeRange(text_attribute_id, attribute_val,
383 anchors.cbegin(), anchors.cend(), expand_match);
387 if (matched_range_start !=
nullptr && matched_range_end !=
nullptr)
388 *
result = CreateTextRangeProvider(std::move(matched_range_start),
389 std::move(matched_range_end));
393template <
typename AnchorIterator,
typename ExpandMatchLambda>
394HRESULT AXPlatformNodeTextRangeProviderWin::FindAttributeRange(
395 const TEXTATTRIBUTEID text_attribute_id,
397 const AnchorIterator first,
398 const AnchorIterator last,
399 ExpandMatchLambda expand_match) {
400 AXPlatformNodeWin* current_platform_node;
401 bool is_match_found =
false;
403 for (
auto it = first; it != last; ++it) {
404 const auto& current_start = it->anchor();
405 const auto& current_end = it->focus();
407 BASE_DCHECK(current_start->GetAnchor() == current_end->GetAnchor());
409 AXPlatformNodeDelegate* delegate = GetDelegate(current_start);
412 current_platform_node =
static_cast<AXPlatformNodeWin*
>(
413 delegate->GetFromNodeID(current_start->GetAnchor()->id()));
416 if (
FAILED(current_platform_node->GetTextAttributeValue(
417 text_attribute_id, current_start->text_offset(),
418 current_end->text_offset(), ¤t_attribute_value))) {
422 if (!current_attribute_value.
Compare(attribute_val)) {
426 is_match_found =
true;
427 expand_match(current_start, current_end);
428 }
else if (is_match_found) {
439 const std::u16string_view find_in,
444 backwards ? find_in.rfind(search_string) : find_in.find(search_string);
445 if (index == std::u16string::npos) {
449 *find_length = search_string.size();
454 std::u16string_view find_in,
459 UErrorCode status = U_ZERO_ERROR;
460 UCollator* col = ucol_open(uloc_getDefault(), &status);
461 UStringSearch* search = usearch_openFromCollator(
462 search_string.data(), search_string.size(), find_in.data(),
463 find_in.size(), col,
nullptr, &status);
464 if (!U_SUCCESS(status)) {
466 usearch_close(search);
471 UCollator* collator = usearch_getCollator(search);
472 ucol_setStrength(collator, ignore_case ? UCOL_PRIMARY : UCOL_TERTIARY);
473 usearch_reset(search);
474 status = U_ZERO_ERROR;
475 usearch_setText(search, find_in.data(), find_in.size(), &status);
476 if (!U_SUCCESS(status)) {
478 usearch_close(search);
483 int32_t index = backwards ? usearch_last(search, &status)
484 : usearch_first(search, &status);
486 if (U_SUCCESS(status) && index != USEARCH_DONE) {
488 *find_start =
static_cast<size_t>(index);
489 *find_length =
static_cast<size_t>(usearch_getMatchedLength(search));
491 usearch_close(search);
495HRESULT AXPlatformNodeTextRangeProviderWin::FindText(
499 ITextRangeProvider**
result) {
521 ScopedAXEmbeddedObjectBehaviorSetter ax_embedded_object_behavior(
525 if (search_string.length() <= 0)
528 size_t appended_newlines_count = 0;
529 std::u16string text_range = GetString(-1, &appended_newlines_count);
532 if (
StringSearch(search_string, text_range, &find_start, &find_length,
533 ignore_case, backwards) &&
534 find_length > appended_newlines_count) {
541 const AXNode* common_anchor =
start()->LowestCommonAnchor(*
end());
542 AXPositionInstance start_ancestor_position =
543 start()->CreateAncestorPosition(common_anchor,
545 BASE_DCHECK(!start_ancestor_position->IsNullPosition());
546 AXPositionInstance end_ancestor_position =
end()->CreateAncestorPosition(
548 BASE_DCHECK(!end_ancestor_position->IsNullPosition());
549 const AXNode* anchor = start_ancestor_position->GetAnchor();
551 const int start_offset =
552 start_ancestor_position->text_offset() + find_start;
553 const int end_offset = start_offset + find_length - appended_newlines_count;
554 const int max_end_offset = end_ancestor_position->text_offset();
555 BASE_DCHECK(start_offset <= end_offset && end_offset <= max_end_offset);
557 AXPositionInstance
start =
559 anchor->tree()->GetAXTreeID(), anchor->id(), start_offset,
561 ->AsLeafTextPosition();
562 AXPositionInstance
end =
564 anchor->tree()->GetAXTreeID(), anchor->id(), end_offset,
566 ->AsLeafTextPosition();
573HRESULT AXPlatformNodeTextRangeProviderWin::GetAttributeValue(
574 TEXTATTRIBUTEID attribute_id,
594 if (attribute_id == UIA_IsReadOnlyAttributeId &&
595 start()->anchor_id() !=
end()->anchor_id() &&
596 start()->AtEndOfParagraph() &&
end()->AtStartOfParagraph() &&
597 *
start()->CreateNextCharacterPosition(
599 AXPlatformNodeWin* common_anchor = GetLowestAccessibleCommonPlatformNode();
602 HRESULT hr = common_anchor->GetTextAttributeValue(
603 attribute_id, std::nullopt, std::nullopt, &attribute_value);
614 AXPositionInstance normalized_start =
start()->Clone();
615 AXPositionInstance normalized_end =
end()->Clone();
616 NormalizeTextRange(normalized_start, normalized_end);
619 const auto end_leaf_text_position = normalized_end->AsLeafTextPosition();
620 auto end = end_leaf_text_position->CreateNextAnchorPosition();
623 for (
auto it = normalized_start->AsLeafTextPosition();
624 it->anchor_id() !=
end->anchor_id() || it->tree_id() !=
end->tree_id();
625 it = it->CreateNextAnchorPosition()) {
630 if (it->IsNullPosition())
633 AXPlatformNodeDelegate* delegate = GetDelegate(it.get());
636 AXPlatformNodeWin* platform_node =
static_cast<AXPlatformNodeWin*
>(
637 delegate->GetFromNodeID(it->anchor_id()));
642 platform_node =
static_cast<AXPlatformNodeWin*
>(
644 platform_node->GetDelegate()->GetLowestPlatformAncestor()));
648 const bool at_end_leaf_text_anchor =
649 it->anchor_id() == end_leaf_text_position->anchor_id() &&
650 it->tree_id() == end_leaf_text_position->tree_id();
651 const std::optional<int> start_offset =
652 it->IsTextPosition() ? std::make_optional(it->text_offset())
654 const std::optional<int> end_offset =
655 at_end_leaf_text_anchor
656 ? std::make_optional(end_leaf_text_position->text_offset())
658 HRESULT hr = platform_node->GetTextAttributeValue(
659 attribute_id, start_offset, end_offset, ¤t_value);
663 if (attribute_value.
Type() == VT_EMPTY) {
664 attribute_value = std::move(current_value);
665 }
else if (attribute_value != current_value) {
666 V_VT(
value) = VT_UNKNOWN;
667 return ::UiaGetReservedMixedAttributeValue(&V_UNKNOWN(
value));
671 if (ShouldReleaseTextAttributeAsSafearray(attribute_id, attribute_value))
678HRESULT AXPlatformNodeTextRangeProviderWin::GetBoundingRectangles(
679 SAFEARRAY** screen_physical_pixel_rectangles) {
682 *screen_physical_pixel_rectangles =
nullptr;
683 AXNodeRange range(
start()->Clone(),
end()->Clone());
684 AXRangePhysicalPixelRectDelegate rect_delegate(
this);
685 std::vector<gfx::Rect> rects = range.GetRects(&rect_delegate);
688 SAFEARRAY* safe_array = SafeArrayCreateVector(
689 VT_R8 , 0 , rects.size() * 4);
692 return E_OUTOFMEMORY;
694 if (rects.size() > 0) {
695 double* double_array =
nullptr;
696 HRESULT hr = SafeArrayAccessData(safe_array,
697 reinterpret_cast<void**
>(&double_array));
700 for (
size_t rect_index = 0; rect_index < rects.size(); rect_index++) {
702 double_array[rect_index * 4] =
rect.x();
703 double_array[rect_index * 4 + 1] =
rect.y();
704 double_array[rect_index * 4 + 2] =
rect.width();
705 double_array[rect_index * 4 + 3] =
rect.height();
707 hr = SafeArrayUnaccessData(safe_array);
712 SafeArrayDestroy(safe_array);
717 *screen_physical_pixel_rectangles = safe_array;
721HRESULT AXPlatformNodeTextRangeProviderWin::GetEnclosingElement(
722 IRawElementProviderSimple** element) {
725 AXPlatformNodeWin* enclosing_node = GetLowestAccessibleCommonPlatformNode();
727 return UIA_E_ELEMENTNOTAVAILABLE;
729 enclosing_node->GetNativeViewAccessible()->QueryInterface(
730 IID_PPV_ARGS(element));
736HRESULT AXPlatformNodeTextRangeProviderWin::GetText(
int max_count, BSTR*
text) {
745 if (!full_text.empty()) {
746 size_t length = full_text.length();
748 if (max_count != -1 && max_count <
static_cast<int>(
length))
749 *
text = SysAllocStringLen(full_text.c_str(), max_count);
751 *
text = SysAllocStringLen(full_text.c_str(),
length);
753 *
text = SysAllocString(
L"");
758HRESULT AXPlatformNodeTextRangeProviderWin::Move(TextUnit unit,
768 auto start_backup =
start()->Clone();
769 auto end_backup =
end()->Clone();
770 bool is_degenerate_range = (*
start() == *
end());
774 int start_units_moved = 0;
775 HRESULT hr = MoveEndpointByUnitImpl(TextPatternRangeEndpoint_Start, unit,
776 count, &start_units_moved);
778 bool succeeded_move =
SUCCEEDED(hr) && start_units_moved != 0;
779 if (succeeded_move) {
780 SetEnd(
start()->Clone());
781 if (!is_degenerate_range) {
782 bool forwards =
count > 0;
783 if (forwards &&
start()->AtEndOfDocument()) {
787 int current_start_units_moved = 0;
788 hr = MoveEndpointByUnitImpl(TextPatternRangeEndpoint_Start, unit, -1,
789 ¤t_start_units_moved);
790 start_units_moved -= 1;
791 succeeded_move =
SUCCEEDED(hr) && current_start_units_moved == -1 &&
792 start_units_moved > 0;
797 int end_units_moved = 0;
798 hr = MoveEndpointByUnitImpl(TextPatternRangeEndpoint_End, unit, 1,
800 succeeded_move =
SUCCEEDED(hr) && end_units_moved == 1;
806 if (
start()->anchor_id() !=
end()->anchor_id() &&
807 (unit == TextUnit_Character || unit == TextUnit_Word)) {
808 ExpandToEnclosingUnitImpl(unit);
813 if (!succeeded_move) {
814 SetStart(std::move(start_backup));
815 SetEnd(std::move(end_backup));
816 start_units_moved = 0;
821 *units_moved = start_units_moved;
825HRESULT AXPlatformNodeTextRangeProviderWin::MoveEndpointByUnit(
826 TextPatternRangeEndpoint endpoint,
830 return MoveEndpointByUnitImpl(endpoint, unit,
count, units_moved);
833HRESULT AXPlatformNodeTextRangeProviderWin::MoveEndpointByUnitImpl(
834 TextPatternRangeEndpoint endpoint,
846 bool is_start_endpoint = endpoint == TextPatternRangeEndpoint_Start;
847 AXPositionInstance position_to_move =
848 is_start_endpoint ?
start()->Clone() :
end()->Clone();
850 AXPositionInstance new_position;
854 case TextUnit_Character:
856 MoveEndpointByCharacter(position_to_move,
count, units_moved);
859 new_position = MoveEndpointByWord(position_to_move,
count, units_moved);
862 new_position = MoveEndpointByLine(position_to_move, is_start_endpoint,
865 case TextUnit_Paragraph:
866 new_position = MoveEndpointByParagraph(
867 position_to_move, is_start_endpoint,
count, units_moved);
870 new_position = MoveEndpointByPage(position_to_move, is_start_endpoint,
873 case TextUnit_Document:
875 MoveEndpointByDocument(position_to_move,
count, units_moved);
878 return UIA_E_NOTSUPPORTED;
880 if (is_start_endpoint)
881 SetStart(std::move(new_position));
883 SetEnd(std::move(new_position));
887 std::optional<int> endpoint_comparison =
891 if (endpoint_comparison.value_or(0) > 0) {
892 if (is_start_endpoint)
893 SetEnd(
start()->Clone());
895 SetStart(
end()->Clone());
900HRESULT AXPlatformNodeTextRangeProviderWin::MoveEndpointByRange(
901 TextPatternRangeEndpoint this_endpoint,
902 ITextRangeProvider* other,
903 TextPatternRangeEndpoint other_endpoint) {
906 Microsoft::WRL::ComPtr<AXPlatformNodeTextRangeProviderWin> other_provider;
907 if (other->QueryInterface(IID_PPV_ARGS(&other_provider)) != S_OK)
908 return UIA_E_INVALIDOPERATION;
910 const AXPositionInstance& other_provider_endpoint =
911 (other_endpoint == TextPatternRangeEndpoint_Start)
912 ? other_provider->start()
913 : other_provider->end();
915 if (this_endpoint == TextPatternRangeEndpoint_Start) {
916 SetStart(other_provider_endpoint->Clone());
918 SetEnd(
start()->Clone());
920 SetEnd(other_provider_endpoint->Clone());
922 SetStart(
end()->Clone());
927HRESULT AXPlatformNodeTextRangeProviderWin::Select() {
930 AXPositionInstance selection_start =
start()->Clone();
931 AXPositionInstance selection_end =
end()->Clone();
936 if (selection_start->tree_id() != selection_end->tree_id()) {
939 selection_start = selection_end->CreatePositionAtStartOfAXTree();
944 BASE_DCHECK(selection_start->tree_id() == selection_end->tree_id());
950 if (selection_start->GetAnchor()->IsInListMarker() ||
951 selection_end->GetAnchor()->IsInListMarker()) {
955 AXPlatformNodeDelegate* delegate =
956 GetDelegate(selection_start->tree_id(), selection_start->anchor_id());
959 AXNodeRange new_selection_range(std::move(selection_start),
960 std::move(selection_end));
961 RemoveFocusFromPreviousSelectionIfNeeded(new_selection_range);
963 AXActionData action_data;
964 action_data.anchor_node_id = new_selection_range.anchor()->anchor_id();
965 action_data.anchor_offset = new_selection_range.anchor()->text_offset();
966 action_data.focus_node_id = new_selection_range.focus()->anchor_id();
967 action_data.focus_offset = new_selection_range.focus()->text_offset();
970 delegate->AccessibilityPerformAction(action_data);
974HRESULT AXPlatformNodeTextRangeProviderWin::AddToSelection() {
976 return UIA_E_INVALIDOPERATION;
980AXPlatformNodeTextRangeProviderWin::RemoveFromSelection() {
982 return UIA_E_INVALIDOPERATION;
985HRESULT AXPlatformNodeTextRangeProviderWin::ScrollIntoView(
BOOL align_to_top) {
988 const AXPositionInstance start_common_ancestor =
989 start()->LowestCommonAncestor(*
end());
990 const AXPositionInstance end_common_ancestor =
991 end()->LowestCommonAncestor(*
start());
992 if (start_common_ancestor->IsNullPosition() ||
993 end_common_ancestor->IsNullPosition()) {
997 const AXNode* common_ancestor_anchor = start_common_ancestor->GetAnchor();
998 BASE_DCHECK(common_ancestor_anchor == end_common_ancestor->GetAnchor());
1000 const AXTreeID common_ancestor_tree_id = start_common_ancestor->tree_id();
1001 const AXPlatformNodeDelegate* root_delegate =
1002 GetRootDelegate(common_ancestor_tree_id);
1004 const gfx::Rect root_frame_bounds = root_delegate->GetBoundsRect(
1008 const AXPlatformNode* common_ancestor_platform_node =
1009 GetOwner()->GetDelegate()->GetFromTreeIDAndNodeID(
1010 common_ancestor_tree_id, common_ancestor_anchor->id());
1012 AXPlatformNodeDelegate* common_ancestor_delegate =
1013 common_ancestor_platform_node->GetDelegate();
1015 const gfx::Rect text_range_container_frame_bounds =
1022 target_point =
gfx::Point(root_frame_bounds.
x(), root_frame_bounds.
y());
1026 root_frame_bounds.
y() + root_frame_bounds.
height());
1029 if ((align_to_top &&
start()->GetAnchor()->
IsText()) ||
1030 (!align_to_top &&
end()->GetAnchor()->
IsText())) {
1031 const gfx::Rect text_range_frame_bounds =
1032 common_ancestor_delegate->GetInnerTextRangeBoundsRect(
1033 start_common_ancestor->text_offset(),
1039 target_point.
Offset(0, -(text_range_container_frame_bounds.
height() -
1040 text_range_frame_bounds.
height()));
1042 target_point.
Offset(0, -text_range_frame_bounds.
height());
1046 target_point.
Offset(0, -text_range_container_frame_bounds.
height());
1049 const gfx::Rect root_screen_bounds = root_delegate->GetBoundsRect(
1054 AXActionData action_data;
1056 action_data.target_node_id = common_ancestor_anchor->id();
1057 action_data.target_point = target_point;
1058 if (!common_ancestor_delegate->AccessibilityPerformAction(action_data))
1066HRESULT AXPlatformNodeTextRangeProviderWin::GetChildren(SAFEARRAY** children) {
1068 std::vector<gfx::NativeViewAccessible> descendants;
1070 AXPlatformNodeWin* start_anchor =
1071 GetPlatformNodeFromAXNode(
start()->GetAnchor());
1072 AXPlatformNodeWin* end_anchor = GetPlatformNodeFromAXNode(
end()->GetAnchor());
1073 AXPlatformNodeWin* common_anchor = GetLowestAccessibleCommonPlatformNode();
1074 if (!common_anchor || !start_anchor || !end_anchor)
1075 return UIA_E_ELEMENTNOTAVAILABLE;
1077 SAFEARRAY* safe_array = SafeArrayCreateVector(VT_UNKNOWN, 0, 0);
1082 *children = safe_array;
1087bool AXPlatformNodeTextRangeProviderWin::AtStartOfLinePredicate(
1088 const AXPositionInstance& position) {
1089 return !position->IsIgnored() && position->AtStartOfAnchor() &&
1090 (position->AtStartOfLine() || position->AtStartOfInlineBlock());
1094bool AXPlatformNodeTextRangeProviderWin::AtEndOfLinePredicate(
1095 const AXPositionInstance& position) {
1096 return !position->IsIgnored() && position->AtEndOfAnchor() &&
1097 (position->AtEndOfLine() || position->AtStartOfInlineBlock());
1101AXPlatformNodeTextRangeProviderWin::AXPositionInstance
1102AXPlatformNodeTextRangeProviderWin::GetNextTextBoundaryPosition(
1103 const AXPositionInstance& position,
1109 switch (boundary_type) {
1111 return position->CreateBoundaryStartPosition(
options, boundary_direction,
1112 &AtStartOfLinePredicate,
1113 &AtEndOfLinePredicate);
1115 return position->CreateBoundaryEndPosition(
options, boundary_direction,
1116 &AtStartOfLinePredicate,
1117 &AtEndOfLinePredicate);
1119 return position->CreatePositionAtTextBoundary(
1120 boundary_type, boundary_direction,
options);
1124std::u16string AXPlatformNodeTextRangeProviderWin::GetString(
1126 size_t* appended_newlines_count) {
1127 AXNodeRange range(
start()->Clone(),
end()->Clone());
1129 false, appended_newlines_count);
1132AXPlatformNodeWin* AXPlatformNodeTextRangeProviderWin::GetOwner()
const {
1135 if (owner_for_test_.Get())
1136 return owner_for_test_.Get();
1138 const AXPositionInstance& position =
1141 if (position->IsNullPosition())
1144 const AXNode* anchor = position->GetAnchor();
1146 const AXTreeManager* tree_manager =
1149 const AXPlatformTreeManager* platform_tree_manager =
1150 static_cast<const AXPlatformTreeManager*
>(tree_manager);
1151 return static_cast<AXPlatformNodeWin*
>(
1152 platform_tree_manager->GetPlatformNodeFromTree(*anchor));
1155AXPlatformNodeDelegate* AXPlatformNodeTextRangeProviderWin::GetDelegate(
1156 const AXPositionInstanceType* position)
const {
1157 return GetDelegate(position->tree_id(), position->anchor_id());
1160AXPlatformNodeDelegate* AXPlatformNodeTextRangeProviderWin::GetDelegate(
1161 const AXTreeID tree_id,
1163 AXPlatformNode* platform_node =
1164 GetOwner()->GetDelegate()->GetFromTreeIDAndNodeID(tree_id, node_id);
1168 return platform_node->GetDelegate();
1171AXPlatformNodeTextRangeProviderWin::AXPositionInstance
1172AXPlatformNodeTextRangeProviderWin::MoveEndpointByCharacter(
1173 const AXPositionInstance& endpoint,
1176 return MoveEndpointByUnitHelper(std::move(endpoint),
1181AXPlatformNodeTextRangeProviderWin::AXPositionInstance
1182AXPlatformNodeTextRangeProviderWin::MoveEndpointByWord(
1183 const AXPositionInstance& endpoint,
1186 return MoveEndpointByUnitHelper(std::move(endpoint),
1191AXPlatformNodeTextRangeProviderWin::AXPositionInstance
1192AXPlatformNodeTextRangeProviderWin::MoveEndpointByLine(
1193 const AXPositionInstance& endpoint,
1194 bool is_start_endpoint,
1197 return MoveEndpointByUnitHelper(std::move(endpoint),
1201 count, units_moved);
1204AXPlatformNodeTextRangeProviderWin::AXPositionInstance
1205AXPlatformNodeTextRangeProviderWin::MoveEndpointByParagraph(
1206 const AXPositionInstance& endpoint,
1207 const bool is_start_endpoint,
1210 return MoveEndpointByUnitHelper(std::move(endpoint),
1214 count, units_moved);
1217AXPlatformNodeTextRangeProviderWin::AXPositionInstance
1218AXPlatformNodeTextRangeProviderWin::MoveEndpointByPage(
1219 const AXPositionInstance& endpoint,
1220 const bool is_start_endpoint,
1229 AXPositionInstance common_ancestor =
start()->LowestCommonAncestor(*
end());
1230 if (!common_ancestor->GetAnchor()->tree()->HasPaginationSupport())
1231 return MoveEndpointByDocument(std::move(endpoint),
count, units_moved);
1233 return MoveEndpointByUnitHelper(std::move(endpoint),
1237 count, units_moved);
1240AXPlatformNodeTextRangeProviderWin::AXPositionInstance
1241AXPlatformNodeTextRangeProviderWin::MoveEndpointByDocument(
1242 const AXPositionInstance& endpoint,
1248 *units_moved = !endpoint->AtStartOfDocument() ? -1 : 0;
1250 return endpoint->CreatePositionAtStartOfDocument();
1252 *units_moved = !endpoint->AtEndOfDocument() ? 1 : 0;
1253 return endpoint->CreatePositionAtEndOfDocument();
1256AXPlatformNodeTextRangeProviderWin::AXPositionInstance
1257AXPlatformNodeTextRangeProviderWin::MoveEndpointByUnitHelper(
1258 const AXPositionInstance& endpoint,
1267 const AXNode* initial_endpoint = endpoint->GetAnchor();
1272 AXPositionInstance current_endpoint = endpoint->AsLeafTextPosition();
1273 AXPositionInstance next_endpoint = GetNextTextBoundaryPosition(
1274 current_endpoint, boundary_type,
1278 bool is_ignored_for_text_navigation =
false;
1286 !(next_endpoint->GetAnchor() == current_endpoint->GetAnchor() &&
1287 *next_endpoint == *current_endpoint)) {
1288 is_ignored_for_text_navigation =
false;
1289 current_endpoint = std::move(next_endpoint);
1291 next_endpoint = GetNextTextBoundaryPosition(
1292 current_endpoint, boundary_type,
1302 is_ignored_for_text_navigation =
1304 current_endpoint->GetAnchor()->data().role !=
1306 if (!is_ignored_for_text_navigation)
1310 *units_moved = (
count > 0) ? iteration : -iteration;
1312 if (is_ignored_for_text_navigation &&
1313 initial_endpoint != current_endpoint->GetAnchor()) {
1317 *units_moved += (
count > 0) ? 1 : -1;
1320 return current_endpoint;
1323void AXPlatformNodeTextRangeProviderWin::NormalizeTextRange(
1324 AXPositionInstance& start,
1325 AXPositionInstance& end) {
1326 if (!
start->IsValid() || !
end->IsValid())
1331 NormalizeAsUnignoredTextRange(start, end);
1334 AXPositionInstance normalized_start =
1336 :
start->AsLeafTextPositionBeforeCharacter();
1344 AXPositionInstance normalized_end =
1346 :
end->AsLeafTextPositionAfterCharacter();
1348 if (!normalized_start->IsNullPosition() &&
1349 !normalized_end->IsNullPosition()) {
1350 start = std::move(normalized_start);
1351 end = std::move(normalized_end);
1358void AXPlatformNodeTextRangeProviderWin::NormalizeAsUnignoredPosition(
1359 AXPositionInstance& position) {
1360 if (position->IsNullPosition() || !position->IsValid())
1363 if (position->IsIgnored()) {
1364 AXPositionInstance normalized_position = position->AsUnignoredPosition(
1366 if (normalized_position->IsNullPosition()) {
1367 normalized_position = position->AsUnignoredPosition(
1371 if (!normalized_position->IsNullPosition())
1372 position = std::move(normalized_position);
1378void AXPlatformNodeTextRangeProviderWin::NormalizeAsUnignoredTextRange(
1379 AXPositionInstance& start,
1380 AXPositionInstance& end) {
1381 if (!
start->IsValid() || !
end->IsValid())
1384 if (!
start->IsIgnored() && !
end->IsIgnored())
1386 NormalizeAsUnignoredPosition(start);
1387 NormalizeAsUnignoredPosition(end);
1391AXPlatformNodeDelegate* AXPlatformNodeTextRangeProviderWin::GetRootDelegate(
1393 const AXTreeManager* ax_tree_manager =
1396 AXNode* root_node = ax_tree_manager->GetRootAsAXNode();
1397 const AXPlatformNode* root_platform_node =
1398 GetOwner()->GetDelegate()->GetFromTreeIDAndNodeID(tree_id,
1401 return root_platform_node->GetDelegate();
1404void AXPlatformNodeTextRangeProviderWin::SetStart(
1405 AXPositionInstance new_start) {
1406 endpoints_.SetStart(std::move(new_start));
1409void AXPlatformNodeTextRangeProviderWin::SetEnd(AXPositionInstance new_end) {
1410 endpoints_.SetEnd(std::move(new_end));
1413void AXPlatformNodeTextRangeProviderWin::SetOwnerForTesting(
1414 AXPlatformNodeWin* owner) {
1415 owner_for_test_ = owner;
1418AXNode* AXPlatformNodeTextRangeProviderWin::GetSelectionCommonAnchor() {
1419 AXPlatformNodeDelegate* delegate = GetOwner()->GetDelegate();
1420 AXTree::Selection unignored_selection = delegate->GetUnignoredSelection();
1421 AXPlatformNode* anchor_object =
1422 delegate->GetFromNodeID(unignored_selection.anchor_object_id);
1423 AXPlatformNode* focus_object =
1424 delegate->GetFromNodeID(unignored_selection.focus_object_id);
1426 if (!anchor_object || !focus_object)
1430 anchor_object->GetDelegate()->CreateTextPositionAt(
1431 unignored_selection.anchor_offset);
1433 focus_object->GetDelegate()->CreateTextPositionAt(
1434 unignored_selection.focus_offset);
1436 return start->LowestCommonAnchor(*end);
1449void AXPlatformNodeTextRangeProviderWin::
1450 RemoveFocusFromPreviousSelectionIfNeeded(
const AXNodeRange& new_selection) {
1451 const AXNode* old_selection_node = GetSelectionCommonAnchor();
1452 const AXNode* new_selection_node =
1453 new_selection.anchor()->LowestCommonAnchor(*new_selection.focus());
1455 if (!old_selection_node)
1458 if (!new_selection_node ||
1461 AXPlatformNodeDelegate* root_delegate =
1462 GetRootDelegate(old_selection_node->tree()->GetAXTreeID());
1465 AXActionData focus_action;
1467 root_delegate->AccessibilityPerformAction(focus_action);
1472AXPlatformNodeTextRangeProviderWin::GetPlatformNodeFromAXNode(
1473 const AXNode* node)
const {
1478 AXPlatformNodeWin* platform_node =
1480 GetDelegate(node->tree()->GetAXTreeID(), node->id())
1481 ->GetNativeViewAccessible()));
1484 return platform_node;
1488AXPlatformNodeTextRangeProviderWin::GetLowestAccessibleCommonPlatformNode()
1490 AXNode* common_anchor =
start()->LowestCommonAnchor(*
end());
1494 return GetPlatformNodeFromAXNode(common_anchor)->GetLowestAccessibleElement();
1498bool AXPlatformNodeTextRangeProviderWin::TextAttributeIsArrayType(
1499 TEXTATTRIBUTEID attribute_id) {
1501 return attribute_id == UIA_AnnotationObjectsAttributeId ||
1502 attribute_id == UIA_AnnotationTypesAttributeId ||
1503 attribute_id == UIA_TabsAttributeId;
1507bool AXPlatformNodeTextRangeProviderWin::TextAttributeIsUiaReservedValue(
1510 if (vector.
Type() != VT_UNKNOWN)
1515 Microsoft::WRL::ComPtr<IUnknown> mixed_attribute_value;
1516 HRESULT hr = ::UiaGetReservedMixedAttributeValue(&mixed_attribute_value);
1518 mixed_attribute_value_variant.
Set(mixed_attribute_value.Get());
1523 Microsoft::WRL::ComPtr<IUnknown> not_supported_value;
1524 HRESULT hr = ::UiaGetReservedNotSupportedValue(¬_supported_value);
1526 not_supported_value_variant.
Set(not_supported_value.Get());
1529 return !vector.
Compare(mixed_attribute_value_variant) ||
1530 !vector.
Compare(not_supported_value_variant);
1534bool AXPlatformNodeTextRangeProviderWin::ShouldReleaseTextAttributeAsSafearray(
1535 TEXTATTRIBUTEID attribute_id,
1539 return TextAttributeIsArrayType(attribute_id) &&
1540 !TextAttributeIsUiaReservedValue(attribute_value);
1543AXPlatformNodeTextRangeProviderWin::TextRangeEndpoints::TextRangeEndpoints() {
1548AXPlatformNodeTextRangeProviderWin::TextRangeEndpoints::~TextRangeEndpoints() {
1553void AXPlatformNodeTextRangeProviderWin::TextRangeEndpoints::SetStart(
1554 AXPositionInstance new_start) {
1555 bool did_tree_change = start_->tree_id() != new_start->tree_id();
1560 start_->tree_id() != end_->tree_id()) {
1561 RemoveObserver(start_->tree_id());
1564 start_ = std::move(new_start);
1566 if (did_tree_change && !start_->IsNullPosition() &&
1567 start_->tree_id() != end_->tree_id()) {
1568 AddObserver(start_->tree_id());
1572void AXPlatformNodeTextRangeProviderWin::TextRangeEndpoints::SetEnd(
1573 AXPositionInstance new_end) {
1574 bool did_tree_change = end_->tree_id() != new_end->tree_id();
1579 end_->tree_id() != start_->tree_id()) {
1580 RemoveObserver(end_->tree_id());
1583 end_ = std::move(new_end);
1585 if (did_tree_change && !end_->IsNullPosition() &&
1586 start_->tree_id() != end_->tree_id()) {
1587 AddObserver(end_->tree_id());
1591void AXPlatformNodeTextRangeProviderWin::TextRangeEndpoints::AddObserver(
1592 const AXTreeID tree_id) {
1593 AXTreeManager* ax_tree_manager =
1596 ax_tree_manager->GetTree()->AddObserver(
this);
1599void AXPlatformNodeTextRangeProviderWin::TextRangeEndpoints::RemoveObserver(
1600 const AXTreeID tree_id) {
1601 AXTreeManager* ax_tree_manager =
1603 if (ax_tree_manager)
1609void AXPlatformNodeTextRangeProviderWin::TextRangeEndpoints::
1610 OnSubtreeWillBeDeleted(AXTree* tree, AXNode* node) {
1618 BASE_DCHECK(tree->GetAXTreeID() == node->tree()->GetAXTreeID());
1620 AdjustEndpointForSubtreeDeletion(tree, node,
true );
1621 AdjustEndpointForSubtreeDeletion(tree, node,
false );
1624void AXPlatformNodeTextRangeProviderWin::TextRangeEndpoints::
1625 AdjustEndpointForSubtreeDeletion(AXTree* tree,
1626 const AXNode*
const node,
1627 bool is_start_endpoint) {
1628 AXPositionInstance endpoint =
1629 is_start_endpoint ? start_->Clone() : end_->Clone();
1630 if (tree->GetAXTreeID() != endpoint->tree_id())
1636 AXNode* endpoint_anchor = endpoint->GetAnchor();
1637 if (!node->parent() || !endpoint_anchor) {
1639 : SetEnd(AXNodePosition::CreateNullPosition());
1643 DeletionOfInterest deletion_of_interest = {tree->GetAXTreeID(), node->id()};
1649 endpoint_anchor == node->parent()) {
1650 if (is_start_endpoint)
1651 validation_necessary_for_start_ = deletion_of_interest;
1653 validation_necessary_for_end_ = deletion_of_interest;
1661 if (!endpoint_anchor->IsDescendantOfCrossingTreeBoundary(node))
1664 AXPositionInstance new_endpoint = endpoint->CreateAncestorPosition(
1669 new_endpoint = new_endpoint->CreateParentPosition();
1670 AXPositionInstance other_endpoint =
1671 is_start_endpoint ? end_->Clone() : start_->Clone();
1675 NormalizeAsUnignoredPosition(new_endpoint);
1676 NormalizeAsUnignoredPosition(other_endpoint);
1684 endpoint_anchor = new_endpoint->GetAnchor();
1685 if (!endpoint_anchor ||
1686 endpoint_anchor->IsDescendantOfCrossingTreeBoundary(node))
1695 if (is_start_endpoint) {
1696 if (*other_endpoint < *new_endpoint)
1697 SetEnd(new_endpoint->Clone());
1699 SetStart(std::move(new_endpoint));
1700 validation_necessary_for_start_ = deletion_of_interest;
1702 if (*new_endpoint < *other_endpoint)
1703 SetStart(new_endpoint->Clone());
1705 SetEnd(std::move(new_endpoint));
1706 validation_necessary_for_end_ = deletion_of_interest;
1712void AXPlatformNodeTextRangeProviderWin::TextRangeEndpoints::OnNodeDeleted(
1717 if (validation_necessary_for_start_.has_value() &&
1718 validation_necessary_for_start_->tree_id == tree->GetAXTreeID() &&
1719 validation_necessary_for_start_->node_id == node_id) {
1720 if (!start_->IsNullPosition() && start_->GetAnchor()->data().id != 0)
1721 SetStart(start_->AsValidPosition());
1725 validation_necessary_for_start_ = std::nullopt;
1728 if (validation_necessary_for_end_.has_value() &&
1729 validation_necessary_for_end_->tree_id == tree->GetAXTreeID() &&
1730 validation_necessary_for_end_->node_id == node_id) {
1731 if (!end_->IsNullPosition() && end_->GetAnchor()->data().id != 0)
1732 SetEnd(end_->AsValidPosition());
1736 validation_necessary_for_end_ = std::nullopt;
static bool is_degenerate(const SkPath &path)
void Set(const wchar_t *str)
VARIANT ReleaseAsSafearrayVariant()
int Compare(const VARIANT &other, bool ignore_case=false) const
VARIANT ReleaseAsScalarVariant()
void Offset(int delta_x, int delta_y)
constexpr int height() const
Vector2d OffsetFromOrigin() const
std::unique_ptr< AXPosition< AXNodePosition, AXNode > > AXPositionInstance
static AXPositionInstance CreateNullPosition()
static AXPositionInstance CreateTextPosition(AXTreeID tree_id, AXNode::AXID anchor_id, int text_offset, ax::mojom::TextAffinity affinity)
AXRangePhysicalPixelRectDelegate(AXPlatformNodeTextRangeProviderWin *host)
gfx::Rect GetBoundsRect(AXTreeID tree_id, AXNode::AXID node_id, AXOffscreenResult *offscreen_result) override
gfx::Rect GetInnerTextRangeBoundsRect(AXTreeID tree_id, AXNode::AXID node_id, int start_offset, int end_offset, ui::AXClippingBehavior clipping_behavior, AXOffscreenResult *offscreen_result) override
AXTreeManager * GetManager(AXTreeID tree_id)
static AXTreeManagerMap & GetInstance()
virtual AXTree * GetTree() const =0
void RemoveObserver(AXTreeObserver *observer)
sk_sp< SkBlender > blender SkRect rect
def Compare(symbols1, symbols2)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service host
std::u16string WideStringToUtf16(const std::wstring_view str)
std::wstring Utf16ToWideString(const std::u16string_view str)
const myers::Point & get(const myers::Segment &)
SIN Vec< N, float > abs(const Vec< N, float > &x)
static bool StringSearchBasic(const std::u16string_view search_string, const std::u16string_view find_in, size_t *find_start, size_t *find_length, bool backwards)
@ StopIfAlreadyAtBoundary
@ StopAtLastAnchorBoundary
bool StringSearch(std::u16string_view search_string, std::u16string_view find_in, size_t *find_start, size_t *find_length, bool ignore_case, bool backwards)
bool IsText(ax::mojom::Role role)
#define BASE_DCHECK(condition)