// Copyright 2019 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/platform/ax_platform_node_textrangeprovider_win.h" #include "base/win/scoped_bstr.h" #include "base/win/scoped_safearray.h" #include "base/win/scoped_variant.h" #include "content/browser/accessibility/accessibility_content_browsertest.h" #include "content/browser/accessibility/browser_accessibility.h" #include "content/browser/accessibility/browser_accessibility_com_win.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/test/accessibility_notification_waiter.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/hit_test_region_observer.h" #include "content/shell/browser/shell.h" #include "content/test/content_browser_test_utils_internal.h" #include "net/dns/mock_host_resolver.h" using Microsoft::WRL::ComPtr; namespace content { #define EXPECT_UIA_DOUBLE_SAFEARRAY_EQ(safearray, expected_property_values) \ { \ EXPECT_EQ(sizeof(V_R8(LPVARIANT(NULL))), \ ::SafeArrayGetElemsize(safearray)); \ ASSERT_EQ(1u, SafeArrayGetDim(safearray)); \ LONG array_lower_bound; \ ASSERT_HRESULT_SUCCEEDED( \ SafeArrayGetLBound(safearray, 1, &array_lower_bound)); \ LONG array_upper_bound; \ ASSERT_HRESULT_SUCCEEDED( \ SafeArrayGetUBound(safearray, 1, &array_upper_bound)); \ double* array_data; \ ASSERT_HRESULT_SUCCEEDED(::SafeArrayAccessData( \ safearray, reinterpret_cast(&array_data))); \ size_t count = array_upper_bound - array_lower_bound + 1; \ ASSERT_EQ(expected_property_values.size(), count); \ for (size_t i = 0; i < count; ++i) { \ EXPECT_EQ(array_data[i], expected_property_values[i]); \ } \ ASSERT_HRESULT_SUCCEEDED(::SafeArrayUnaccessData(safearray)); \ } #define EXPECT_UIA_TEXTRANGE_EQ(provider, expected_content) \ { \ base::win::ScopedBstr provider_content; \ ASSERT_HRESULT_SUCCEEDED( \ provider->GetText(-1, provider_content.Receive())); \ EXPECT_STREQ(expected_content, provider_content.Get()); \ } #define EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(text_range_provider, endpoint, unit, \ count, expected_text, expected_count) \ { \ int result_count; \ EXPECT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit( \ endpoint, unit, count, &result_count)); \ EXPECT_EQ(expected_count, result_count); \ EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, expected_text); \ } #define EXPECT_UIA_MOVE(text_range_provider, unit, count, expected_text, \ expected_count) \ { \ int result_count; \ EXPECT_HRESULT_SUCCEEDED( \ text_range_provider->Move(unit, count, &result_count)); \ EXPECT_EQ(expected_count, result_count); \ EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, expected_text); \ } class AXPlatformNodeTextRangeProviderWinBrowserTest : public AccessibilityContentBrowserTest { protected: const base::string16 kEmbeddedCharacterAsString = { ui::AXPlatformNodeBase::kEmbeddedCharacter}; void SetUpOnMainThread() override { host_resolver()->AddRule("*", "127.0.0.1"); SetupCrossSiteRedirector(embedded_test_server()); ASSERT_TRUE(embedded_test_server()->Start()); } RenderWidgetHostImpl* GetWidgetHost() { return RenderWidgetHostImpl::From( shell()->web_contents()->GetRenderViewHost()->GetWidget()); } void SynchronizeThreads() { MainThreadFrameObserver observer(GetWidgetHost()); observer.Wait(); } BrowserAccessibilityManager* GetManager() const { WebContentsImpl* web_contents = static_cast(shell()->web_contents()); return web_contents->GetRootBrowserAccessibilityManager(); } void GetTextRangeProviderFromTextNode( const BrowserAccessibility& target_node, ITextRangeProvider** text_range_provider) { BrowserAccessibilityComWin* target_node_com = ToBrowserAccessibilityWin(&target_node)->GetCOM(); ASSERT_NE(nullptr, target_node_com); ComPtr text_provider; ASSERT_HRESULT_SUCCEEDED( target_node_com->GetPatternProvider(UIA_TextPatternId, &text_provider)); ASSERT_NE(nullptr, text_provider.Get()); ASSERT_HRESULT_SUCCEEDED( text_provider->get_DocumentRange(text_range_provider)); } void GetDocumentRangeForMarkup(const std::string& html_markup, ITextRangeProvider** text_range_provider) { LoadInitialAccessibilityTreeFromHtml(html_markup); GetTextRangeProviderFromTextNode(*GetManager()->GetRoot(), text_range_provider); } // Run through ITextRangeProvider::ScrollIntoView top tests. It's assumed that // the browser has already loaded an HTML document's accessibility tree. // Assert the text range generated for an accessibility node is scrolled to be // flush with the top of the viewport. // expected_start_role: the expected accessibility role of the text range // start node under test // fstart: the function to retrieve the accessibility text // range start node under test from the root // accessibility node // expected_end_role: the expected accessibility role of the text range // end node under test // fend: the function to retrieve the accessibility text // range end node under test from the root // accessibility node // align_to_top: true to test top viewport alignment, otherwise test // bottom viewport alignment void ScrollIntoViewBrowserTestTemplate( const ax::mojom::Role expected_start_role, BrowserAccessibility* (BrowserAccessibility::*fstart)() const, const ax::mojom::Role expected_end_role, BrowserAccessibility* (BrowserAccessibility::*fend)() const, const bool align_to_top) { BrowserAccessibility* root_browser_accessibility = GetRootAndAssertNonNull(); BrowserAccessibility* browser_accessibility_start = (root_browser_accessibility->*fstart)(); ASSERT_NE(nullptr, browser_accessibility_start); ASSERT_EQ(expected_start_role, browser_accessibility_start->GetRole()); BrowserAccessibility* browser_accessibility_end = (root_browser_accessibility->*fend)(); ASSERT_NE(nullptr, browser_accessibility_end); ASSERT_EQ(expected_end_role, browser_accessibility_end->GetRole()); AssertScrollIntoView(root_browser_accessibility, browser_accessibility_start, browser_accessibility_end, align_to_top); } // Run through ITextRangeProvider::ScrollIntoView top tests. It's assumed that // the browser has already loaded an HTML document's accessibility tree. // Assert the text range generated for an accessibility node is scrolled to be // flush with the top of the viewport. // expected_start_role: the expected accessibility role of the text range // start node under test // fstart: the function to retrieve the accessibility text // range start node under test from the root // accessibility node // fstart_arg: an index argument for fstart // expected_end_role: the expected accessibility role of the text range // end node under test // fend: the function to retrieve the accessibility text // range end node under test from the root // accessibility node // fend_arg: an index argument for fend // align_to_top: true to test top viewport alignment, otherwise test // bottom viewport alignment void ScrollIntoViewBrowserTestTemplate( const ax::mojom::Role expected_start_role, BrowserAccessibility* (BrowserAccessibility::*fstart)(uint32_t) const, const uint32_t fstart_arg, const ax::mojom::Role expected_end_role, BrowserAccessibility* (BrowserAccessibility::*fend)(uint32_t) const, const uint32_t fend_arg, const bool align_to_top) { BrowserAccessibility* root_browser_accessibility = GetRootAndAssertNonNull(); BrowserAccessibility* browser_accessibility_start = (root_browser_accessibility->*fstart)(fstart_arg); ASSERT_NE(nullptr, browser_accessibility_start); ASSERT_EQ(expected_start_role, browser_accessibility_start->GetRole()); BrowserAccessibility* browser_accessibility_end = (root_browser_accessibility->*fend)(fend_arg); ASSERT_NE(nullptr, browser_accessibility_end); ASSERT_EQ(expected_end_role, browser_accessibility_end->GetRole()); AssertScrollIntoView(root_browser_accessibility, browser_accessibility_start, browser_accessibility_end, align_to_top); } void ScrollIntoViewFromIframeBrowserTestTemplate( const ax::mojom::Role expected_start_role, BrowserAccessibility* (BrowserAccessibility::*fstart)() const, const ax::mojom::Role expected_end_role, BrowserAccessibility* (BrowserAccessibility::*fend)() const, const bool align_to_top) { BrowserAccessibility* root_browser_accessibility = GetRootAndAssertNonNull(); BrowserAccessibility* leaf_iframe_browser_accessibility = root_browser_accessibility->InternalDeepestLastChild(); ASSERT_NE(nullptr, leaf_iframe_browser_accessibility); ASSERT_EQ(ax::mojom::Role::kIframe, leaf_iframe_browser_accessibility->GetRole()); AXTreeID iframe_tree_id = AXTreeID::FromString( leaf_iframe_browser_accessibility->GetStringAttribute( ax::mojom::StringAttribute::kChildTreeId)); BrowserAccessibilityManager* iframe_browser_accessibility_manager = BrowserAccessibilityManager::FromID(iframe_tree_id); ASSERT_NE(nullptr, iframe_browser_accessibility_manager); BrowserAccessibility* root_iframe_browser_accessibility = iframe_browser_accessibility_manager->GetRoot(); ASSERT_NE(nullptr, root_iframe_browser_accessibility); ASSERT_EQ(ax::mojom::Role::kRootWebArea, root_iframe_browser_accessibility->GetRole()); BrowserAccessibility* browser_accessibility_start = (root_iframe_browser_accessibility->*fstart)(); ASSERT_NE(nullptr, browser_accessibility_start); ASSERT_EQ(expected_start_role, browser_accessibility_start->GetRole()); BrowserAccessibility* browser_accessibility_end = (root_iframe_browser_accessibility->*fend)(); ASSERT_NE(nullptr, browser_accessibility_end); ASSERT_EQ(expected_end_role, browser_accessibility_end->GetRole()); AssertScrollIntoView(root_iframe_browser_accessibility, browser_accessibility_start, browser_accessibility_end, align_to_top); } void AssertScrollIntoView(BrowserAccessibility* root_browser_accessibility, BrowserAccessibility* browser_accessibility_start, BrowserAccessibility* browser_accessibility_end, const bool align_to_top) { ui::AXNodePosition::AXPositionInstance start = browser_accessibility_start->CreateTextPositionAt(0); ui::AXNodePosition::AXPositionInstance end = browser_accessibility_end->CreateTextPositionAt(0) ->CreatePositionAtEndOfAnchor(); BrowserAccessibilityComWin* start_browser_accessibility_com_win = ToBrowserAccessibilityWin(browser_accessibility_start)->GetCOM(); ASSERT_NE(nullptr, start_browser_accessibility_com_win); ComPtr text_range_provider = ui::AXPlatformNodeTextRangeProviderWin::CreateTextRangeProvider( start_browser_accessibility_com_win, std::move(start), std::move(end)); ASSERT_NE(nullptr, text_range_provider); gfx::Rect previous_range_bounds = align_to_top ? browser_accessibility_start->GetBoundsRect( ui::AXCoordinateSystem::kFrame, ui::AXClippingBehavior::kUnclipped) : browser_accessibility_end->GetBoundsRect( ui::AXCoordinateSystem::kFrame, ui::AXClippingBehavior::kUnclipped); AccessibilityNotificationWaiter location_changed_waiter( GetWebContentsAndAssertNonNull(), ui::kAXModeComplete, ax::mojom::Event::kLocationChanged); ASSERT_HRESULT_SUCCEEDED(text_range_provider->ScrollIntoView(align_to_top)); location_changed_waiter.WaitForNotification(); gfx::Rect root_page_bounds = root_browser_accessibility->GetBoundsRect( ui::AXCoordinateSystem::kFrame, ui::AXClippingBehavior::kUnclipped); if (align_to_top) { gfx::Rect range_bounds = browser_accessibility_start->GetBoundsRect( ui::AXCoordinateSystem::kFrame, ui::AXClippingBehavior::kUnclipped); ASSERT_NE(previous_range_bounds.y(), range_bounds.y()); ASSERT_NEAR(root_page_bounds.y(), range_bounds.y(), 1); } else { gfx::Rect range_bounds = browser_accessibility_end->GetBoundsRect( ui::AXCoordinateSystem::kFrame, ui::AXClippingBehavior::kUnclipped); gfx::Size viewport_size = gfx::Size(root_page_bounds.width(), root_page_bounds.height()); ASSERT_NE(previous_range_bounds.y(), range_bounds.y()); ASSERT_NEAR(root_page_bounds.y() + viewport_size.height(), range_bounds.y() + range_bounds.height(), 1); } } void ScrollIntoViewTopBrowserTestTemplate( const ax::mojom::Role expected_role, BrowserAccessibility* (BrowserAccessibility::*f)() const) { ScrollIntoViewBrowserTestTemplate(expected_role, f, expected_role, f, true); } void ScrollIntoViewTopBrowserTestTemplate( const ax::mojom::Role expected_role_start, BrowserAccessibility* (BrowserAccessibility::*fstart)() const, const ax::mojom::Role expected_role_end, BrowserAccessibility* (BrowserAccessibility::*fend)() const) { ScrollIntoViewBrowserTestTemplate(expected_role_start, fstart, expected_role_end, fend, true); } void ScrollIntoViewTopBrowserTestTemplate( const ax::mojom::Role expected_role_start, BrowserAccessibility* (BrowserAccessibility::*fstart)(uint32_t) const, const uint32_t fstart_arg, const ax::mojom::Role expected_role_end, BrowserAccessibility* (BrowserAccessibility::*fend)(uint32_t) const, const uint32_t fend_arg) { ScrollIntoViewBrowserTestTemplate(expected_role_start, fstart, fstart_arg, expected_role_end, fend, fend_arg, true); } void ScrollIntoViewBottomBrowserTestTemplate( const ax::mojom::Role expected_role, BrowserAccessibility* (BrowserAccessibility::*f)() const) { ScrollIntoViewBrowserTestTemplate(expected_role, f, expected_role, f, false); } void ScrollIntoViewBottomBrowserTestTemplate( const ax::mojom::Role expected_role_start, BrowserAccessibility* (BrowserAccessibility::*fstart)() const, const ax::mojom::Role expected_role_end, BrowserAccessibility* (BrowserAccessibility::*fend)() const) { ScrollIntoViewBrowserTestTemplate(expected_role_start, fstart, expected_role_end, fend, false); } void ScrollIntoViewBottomBrowserTestTemplate( const ax::mojom::Role expected_role_start, BrowserAccessibility* (BrowserAccessibility::*fstart)(uint32_t) const, const uint32_t fstart_arg, const ax::mojom::Role expected_role_end, BrowserAccessibility* (BrowserAccessibility::*fend)(uint32_t) const, const uint32_t fend_arg) { ScrollIntoViewBrowserTestTemplate(expected_role_start, fstart, fstart_arg, expected_role_end, fend, fend_arg, false); } void AssertMoveByUnitForMarkup( const TextUnit& unit, const std::string& html_markup, const std::vector& expected_text) { ComPtr text_range; GetDocumentRangeForMarkup(html_markup, &text_range); ASSERT_NE(nullptr, text_range.Get()); text_range->ExpandToEnclosingUnit(unit); size_t index = 0; int count_moved = 1; while (count_moved == 1 && index < expected_text.size()) { EXPECT_UIA_TEXTRANGE_EQ(text_range, expected_text[index++]); ASSERT_HRESULT_SUCCEEDED(text_range->Move(unit, 1, &count_moved)); } EXPECT_EQ(expected_text.size(), index); EXPECT_EQ(0, count_moved); count_moved = -1; index = expected_text.size(); while (count_moved == -1 && index > 0) { EXPECT_UIA_TEXTRANGE_EQ(text_range, expected_text[--index]); ASSERT_HRESULT_SUCCEEDED(text_range->Move(unit, -1, &count_moved)); } EXPECT_EQ(0, count_moved); EXPECT_EQ(0u, index); } }; IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, GetAttributeValue) { LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
Text1 Text2
)HTML")); ComPtr mix_attribute_value; EXPECT_HRESULT_SUCCEEDED( UiaGetReservedMixedAttributeValue(&mix_attribute_value)); auto* node = FindNode(ax::mojom::Role::kStaticText, "Text1"); ASSERT_NE(nullptr, node); EXPECT_TRUE(node->PlatformIsLeaf()); EXPECT_EQ(0u, node->PlatformChildCount()); ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"Text1"); base::win::ScopedVariant value; EXPECT_HRESULT_SUCCEEDED(text_range_provider->GetAttributeValue( UIA_FontSizeAttributeId, value.Receive())); EXPECT_EQ(value.type(), VT_R8); EXPECT_EQ(V_R8(value.ptr()), 12.0); EXPECT_HRESULT_SUCCEEDED( text_range_provider->ExpandToEnclosingUnit(TextUnit_Paragraph)); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"Text1 Text2"); EXPECT_HRESULT_SUCCEEDED(text_range_provider->GetAttributeValue( UIA_FontSizeAttributeId, value.Receive())); EXPECT_EQ(value.type(), VT_UNKNOWN); EXPECT_EQ(V_UNKNOWN(value.ptr()), mix_attribute_value.Get()) << "expected 'mixed attribute value' interface pointer"; } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, GetAttributeValueIsReadonlyEmptyTextInputs) { LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML( )HTML")); auto* input_text_node = FindNode(ax::mojom::Role::kTextField, "input_text"); ASSERT_NE(nullptr, input_text_node); EXPECT_TRUE(input_text_node->PlatformIsLeaf()); EXPECT_EQ(0u, input_text_node->PlatformChildCount()); ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*input_text_node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, kEmbeddedCharacterAsString.c_str()); base::win::ScopedVariant value; EXPECT_HRESULT_SUCCEEDED(text_range_provider->GetAttributeValue( UIA_IsReadOnlyAttributeId, value.Receive())); EXPECT_EQ(value.type(), VT_BOOL); EXPECT_EQ(V_BOOL(value.ptr()), VARIANT_FALSE); text_range_provider.Reset(); value.Reset(); auto* input_search_node = FindNode(ax::mojom::Role::kSearchBox, "input_search"); ASSERT_NE(nullptr, input_search_node); EXPECT_TRUE(input_search_node->PlatformIsLeaf()); EXPECT_EQ(0u, input_search_node->PlatformChildCount()); GetTextRangeProviderFromTextNode(*input_search_node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, kEmbeddedCharacterAsString.c_str()); EXPECT_HRESULT_SUCCEEDED(text_range_provider->GetAttributeValue( UIA_IsReadOnlyAttributeId, value.Receive())); EXPECT_EQ(value.type(), VT_BOOL); EXPECT_EQ(V_BOOL(value.ptr()), VARIANT_FALSE); text_range_provider.Reset(); value.Reset(); } // With a rich text field, the read-only attribute should be determined based on // the editable root node's editable state. IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, GetAttributeValueIsReadonlyRichTextField) { LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(

3.14

)HTML")); auto* text_field_node = FindNode(ax::mojom::Role::kTextField, "text_field"); ASSERT_NE(nullptr, text_field_node); ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*text_field_node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); base::win::ScopedVariant value; EXPECT_HRESULT_SUCCEEDED(text_range_provider->GetAttributeValue( UIA_IsReadOnlyAttributeId, value.Receive())); EXPECT_EQ(value.type(), VT_BOOL); EXPECT_EQ(V_BOOL(value.ptr()), VARIANT_FALSE); text_range_provider.Reset(); value.Reset(); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, DoNotNormalizeRangeWithVisibleCaretOrSelection) { LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
Text1
)HTML")); // In order for the test harness to effectively simulate typing in a text // input, first change the value of the text input and then focus it. Only // editing the value won't show the cursor and only focusing will put the // cursor at the beginning of the text input, so both steps are necessary. auto* input_text_node = FindNode(ax::mojom::Role::kTextField, "input_text"); ASSERT_NE(nullptr, input_text_node); EXPECT_TRUE(input_text_node->PlatformIsLeaf()); EXPECT_EQ(0u, input_text_node->PlatformChildCount()); AccessibilityNotificationWaiter edit_waiter(shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kValueChanged); ui::AXActionData edit_data; edit_data.target_node_id = input_text_node->GetId(); edit_data.action = ax::mojom::Action::kSetValue; edit_data.value = "test"; input_text_node->AccessibilityPerformAction(edit_data); edit_waiter.WaitForNotification(); AccessibilityNotificationWaiter focus_waiter( shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kFocus); ui::AXActionData focus_data; focus_data.target_node_id = input_text_node->GetId(); focus_data.action = ax::mojom::Action::kFocus; input_text_node->AccessibilityPerformAction(focus_data); focus_waiter.WaitForNotification(); ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*input_text_node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"test"); // Move the first position so that both endpoints are at the end of the text // input. This is where calls to NormalizeTextRange can be problematic. EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Character, /*count*/ 4, /*expected_text*/ L"", /*expected_count*/ 4); // Clone the original text range so we can keep track if NormalizeEndpoints // causes a change in position. ComPtr text_range_provider_clone; text_range_provider->Clone(&text_range_provider_clone); // Since both ranges are identical, the result of CompareEndpoints should be // 0. int result = 0; EXPECT_HRESULT_SUCCEEDED(text_range_provider->CompareEndpoints( TextPatternRangeEndpoint_End, text_range_provider_clone.Get(), TextPatternRangeEndpoint_Start, &result)); ASSERT_EQ(0, result); // Calling GetAttributeValue will call NormalizeTextRange, which shouldn't // change the result of CompareEndpoints below. base::win::ScopedVariant value; EXPECT_HRESULT_SUCCEEDED(text_range_provider->GetAttributeValue( UIA_IsReadOnlyAttributeId, value.Receive())); EXPECT_EQ(value.type(), VT_BOOL); EXPECT_EQ(V_BOOL(value.ptr()), VARIANT_FALSE); value.Reset(); EXPECT_HRESULT_SUCCEEDED(text_range_provider_clone->GetAttributeValue( UIA_IsReadOnlyAttributeId, value.Receive())); EXPECT_EQ(value.type(), VT_BOOL); EXPECT_EQ(V_BOOL(value.ptr()), VARIANT_FALSE); value.Reset(); EXPECT_HRESULT_SUCCEEDED(text_range_provider->CompareEndpoints( TextPatternRangeEndpoint_End, text_range_provider_clone.Get(), TextPatternRangeEndpoint_Start, &result)); ASSERT_EQ(0, result); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, GetBoundingRectangles) { LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(

AsdfAsdfAsdf

)HTML")); auto* node = FindNode(ax::mojom::Role::kStaticText, "AsdfAsdfAsdf"); ASSERT_NE(nullptr, node); EXPECT_TRUE(node->PlatformIsLeaf()); EXPECT_EQ(0u, node->PlatformChildCount()); ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"AsdfAsdfAsdf"); base::win::ScopedSafearray rectangles; EXPECT_HRESULT_SUCCEEDED( text_range_provider->GetBoundingRectangles(rectangles.Receive())); // |view_offset| is necessary to account for differences in the shell // between platforms (e.g. title bar height) because the results of // |GetBoundingRectangles| are in screen coordinates. gfx::Vector2d view_offset = node->manager()->GetViewBoundsInScreenCoordinates().OffsetFromOrigin(); std::vector expected_values = { 8 + view_offset.x(), 16 + view_offset.y(), 49, 17, 8 + view_offset.x(), 34 + view_offset.y(), 44, 17}; EXPECT_UIA_DOUBLE_SAFEARRAY_EQ(rectangles.Get(), expected_values); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewTopStaticText) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/text.html"); ScrollIntoViewTopBrowserTestTemplate( ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestFirstChild); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewBottomStaticText) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/text.html"); ScrollIntoViewBottomBrowserTestTemplate( ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestFirstChild); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewTopEmbeddedText) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/embedded-text.html"); ScrollIntoViewTopBrowserTestTemplate( ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestLastChild); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewBottomEmbeddedText) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/embedded-text.html"); ScrollIntoViewBottomBrowserTestTemplate( ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestLastChild); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewTopEmbeddedTextCrossNode) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/embedded-text.html"); ScrollIntoViewTopBrowserTestTemplate( ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestFirstChild, ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestLastChild); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewBottomEmbeddedTextCrossNode) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/embedded-text.html"); ScrollIntoViewBottomBrowserTestTemplate( ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestFirstChild, ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestLastChild); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewTopTable) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/table.html"); ScrollIntoViewTopBrowserTestTemplate( ax::mojom::Role::kTable, &BrowserAccessibility::PlatformGetChild, 0, ax::mojom::Role::kTable, &BrowserAccessibility::PlatformGetChild, 0); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewBottomTable) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/table.html"); ScrollIntoViewBottomBrowserTestTemplate( ax::mojom::Role::kTable, &BrowserAccessibility::PlatformGetChild, 0, ax::mojom::Role::kTable, &BrowserAccessibility::PlatformGetChild, 0); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewTopTableText) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/table.html"); ScrollIntoViewTopBrowserTestTemplate( ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestLastChild); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewBottomTableText) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/table.html"); ScrollIntoViewBottomBrowserTestTemplate( ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestLastChild); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewTopLinkText) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/link.html"); ScrollIntoViewTopBrowserTestTemplate( ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestLastChild); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewBottomLinkText) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/link.html"); ScrollIntoViewBottomBrowserTestTemplate( ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestLastChild); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewTopLinkContainer) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/link.html"); ScrollIntoViewTopBrowserTestTemplate(ax::mojom::Role::kGenericContainer, &BrowserAccessibility::PlatformGetChild, 0, ax::mojom::Role::kGenericContainer, &BrowserAccessibility::PlatformGetChild, 0); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewBottomLinkContainer) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/link.html"); ScrollIntoViewBottomBrowserTestTemplate( ax::mojom::Role::kGenericContainer, &BrowserAccessibility::PlatformGetChild, 0, ax::mojom::Role::kGenericContainer, &BrowserAccessibility::PlatformGetChild, 0); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewTopTextFromIFrame) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/iframe-text.html"); WaitForAccessibilityTreeToContainNodeWithName( shell()->web_contents(), "Game theory is \"the study of Mathematical model mathematical models of " "conflict and cooperation between intelligent rational decision-makers." "\""); ScrollIntoViewFromIframeBrowserTestTemplate( ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestFirstChild, ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestLastChild, true); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ScrollIntoViewBottomTextFromIFrame) { LoadInitialAccessibilityTreeFromHtmlFilePath( "/accessibility/scrolling/iframe-text.html"); WaitForAccessibilityTreeToContainNodeWithName( shell()->web_contents(), "Game theory is \"the study of Mathematical model mathematical models of " "conflict and cooperation between intelligent rational decision-makers." "\""); ScrollIntoViewFromIframeBrowserTestTemplate( ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestFirstChild, ax::mojom::Role::kStaticText, &BrowserAccessibility::PlatformDeepestLastChild, false); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, MoveEndpointByUnitFormat) { LoadInitialAccessibilityTreeFromHtml( R"HTML(
plain 1
plain 2
plain heading
italic 1
italic 2

heading

heading

bold 1
bold 2
)HTML"); auto* node = FindNode(ax::mojom::Role::kStaticText, "plain 1"); ASSERT_NE(nullptr, node); EXPECT_TRUE(node->PlatformIsLeaf()); EXPECT_EQ(0u, node->PlatformChildCount()); ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"plain 1"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 1, /*expected_text*/ L"plain 1\nplain 2", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 1, /*expected_text*/ L"plain 1\nplain 2\nplain heading", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 1, /*expected_text*/ L"plain 1\nplain 2\nplain heading\nitalic 1\nitalic 2", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ -1, /*expected_text*/ L"plain 1\nplain 2\nplain heading\n", /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 1, /*expected_text*/ L"plain 1\nplain 2\nplain heading\nitalic 1\nitalic 2", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 1, /*expected_text*/ L"plain 1\nplain 2\nplain heading\nitalic 1\nitalic 2\nheading", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 1, /*expected_text*/ L"plain 1\nplain 2\nplain heading\nitalic 1\nitalic 2\nheading\nheading", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 5, /*expected_text*/ L"plain 1\nplain 2\nplain heading\nitalic 1\nitalic 2" L"\nheading\nheading\nbold 1\nbold 2", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ -8, /*expected_text*/ L"", /*expected_count*/ -6); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 1, /*expected_text*/ L"plain 1\nplain 2", /*expected_count*/ 1); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, MoveEndpointByUnitFormatAllFormats) { LoadInitialAccessibilityTreeFromHtml( R"HTML(
plain 1
plain 2
background-color 1
background-color 2
color 1
color 2
overline 1
overline 2
line-through 1
line-through 2
sup 1
sup 2
bold 1
bold 2
font-family 1
font-family 2
)HTML"); auto* node = FindNode(ax::mojom::Role::kStaticText, "plain 1"); ASSERT_NE(nullptr, node); EXPECT_TRUE(node->PlatformIsLeaf()); EXPECT_EQ(0u, node->PlatformChildCount()); ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"plain 1"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 1, /*expected_text*/ L"plain 1\nplain 2", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 1, /*expected_text*/ L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ -1, /*expected_text*/ L"plain 1\nplain 2\n", /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 2, /*expected_text*/ L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor " L"1\ncolor 2", /*expected_count*/ 2); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ -1, /*expected_text*/ L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\n", /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 2, /*expected_text*/ L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor " L"1\ncolor 2\noverline 1\noverline 2", /*expected_count*/ 2); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ -1, /*expected_text*/ L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor " L"1\ncolor 2\n", /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 2, /*expected_text*/ L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor " L"1\ncolor 2\noverline 1\noverline 2\nline-through 1\nline-through 2", /*expected_count*/ 2); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ -1, /*expected_text*/ L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor " L"1\ncolor 2\noverline 1\noverline 2\n", /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 2, /*expected_text*/ L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor " L"1\ncolor 2\noverline 1\noverline 2\nline-through 1\nline-through " L"2\nsup 1\nsup 2", /*expected_count*/ 2); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ -1, /*expected_text*/ L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor " L"1\ncolor 2\noverline 1\noverline 2\nline-through 1\nline-through 2\n", /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 2, /*expected_text*/ L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor " L"1\ncolor 2\noverline 1\noverline 2\nline-through 1\nline-through " L"2\nsup 1\nsup 2\nbold 1\nbold 2", /*expected_count*/ 2); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ -1, /*expected_text*/ L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor " L"1\ncolor 2\noverline 1\noverline 2\nline-through 1\nline-through " L"2\nsup 1\nsup 2\n", /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ 2, /*expected_text*/ L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor " L"1\ncolor 2\noverline 1\noverline 2\nline-through 1\nline-through " L"2\nsup 1\nsup 2\nbold 1\nbold 2\nfont-family 1\nfont-family 2", /*expected_count*/ 2); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Format, /*count*/ -1, /*expected_text*/ L"plain 1\nplain 2\nbackground-color 1\nbackground-color 2\ncolor " L"1\ncolor 2\noverline 1\noverline 2\nline-through 1\nline-through " L"2\nsup 1\nsup 2\nbold 1\nbold 2\n", /*expected_count*/ -1); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, MoveEndpointByUnitParagraph) { LoadInitialAccessibilityTreeFromHtml( R"HTML(
start
text with :before and :after content, then a bold element with a block before content then a italic element with a block after content
end
)HTML"); BrowserAccessibility* start_node = FindNode(ax::mojom::Role::kStaticText, "start"); ASSERT_NE(nullptr, start_node); BrowserAccessibility* end_node = FindNode(ax::mojom::Role::kStaticText, "end"); ASSERT_NE(nullptr, end_node); std::vector paragraphs = { L"start", L"text with [:before] and [:after]content, then a", L"bold element with a [block]before content then a italic", L"element with a [block] after content", L"end", }; // FORWARD NAVIGATION ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*start_node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"start"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[0].c_str(), /*expected_count*/ 0); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -2, /*expected_text*/ L"", /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[0].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[0] + L"\n" + paragraphs[1]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[1].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[1] + L"\n" + paragraphs[2]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[2].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[2] + L"\n" + paragraphs[3]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[3].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[3] + L"\n" + paragraphs[4]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[4].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[4].c_str(), /*expected_count*/ 0); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 2, /*expected_text*/ L"", /*expected_count*/ 1); // REVERSE NAVIGATION GetTextRangeProviderFromTextNode(*end_node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"end"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[4].c_str(), /*expected_count*/ 0); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 2, /*expected_text*/ L"", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[4].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[3] + L"\n" + paragraphs[4]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[3].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[2] + L"\n" + paragraphs[3]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[2].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[1] + L"\n" + paragraphs[2]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[1].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[0] + L"\n" + paragraphs[1]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[0].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[0].c_str(), /*expected_count*/ 0); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -2, /*expected_text*/ L"", /*expected_count*/ -1); } IN_PROC_BROWSER_TEST_F( AXPlatformNodeTextRangeProviderWinBrowserTest, MoveEndpointByUnitParagraphCollapseTrailingLineBreakingObjects) { LoadInitialAccessibilityTreeFromHtml( R"HTML(
start
some text



more text
end
)HTML"); BrowserAccessibility* start_node = FindNode(ax::mojom::Role::kStaticText, "start"); ASSERT_NE(nullptr, start_node); BrowserAccessibility* end_node = FindNode(ax::mojom::Role::kStaticText, "end"); ASSERT_NE(nullptr, end_node); std::vector paragraphs = { L"start", L"some text\n\n\n", L"more text", L"end", }; // FORWARD NAVIGATION ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*start_node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"start"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[0] + L"\n" + paragraphs[1]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[1].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[1] + paragraphs[2]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[2].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[2] + L"\n" + paragraphs[3]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[3].c_str(), /*expected_count*/ 1); // REVERSE NAVIGATION GetTextRangeProviderFromTextNode(*end_node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"end"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[2] + L"\n" + paragraphs[3]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[2].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[1] + paragraphs[2]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[1].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[0] + L"\n" + paragraphs[1]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[0].c_str(), /*expected_count*/ -1); } IN_PROC_BROWSER_TEST_F( AXPlatformNodeTextRangeProviderWinBrowserTest, MoveEndpointByUnitParagraphCollapseConsecutiveParentChildLineBreakingObjects) { LoadInitialAccessibilityTreeFromHtml( R"HTML(
start
text with :before and :after content, then a
bold element
)HTML"); BrowserAccessibility* start_node = FindNode(ax::mojom::Role::kStaticText, "start"); ASSERT_NE(nullptr, start_node); std::vector paragraphs = { L"start", L"text with [:before] and [:after]content, then a", L"bold element", }; // Forward navigation. ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*start_node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"start"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[0] + L"\n" + paragraphs[1]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[0] + L"\n" + paragraphs[1] + L"\n" + paragraphs[2]).c_str(), /*expected_count*/ 1); // Reverse navigation. EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[0] + L"\n" + paragraphs[1]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[0].c_str(), /*expected_count*/ -1); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, MoveEndpointByUnitParagraphPreservedWhiteSpace) { LoadInitialAccessibilityTreeFromHtml( R"HTML(
start
First Paragraph Second Paragraph
Third Paragraph Fourth Paragraph
Fifth Paragraph Sixth Paragraph
Seventh Paragraph Eighth Paragraph
end
)HTML"); BrowserAccessibility* start_node = FindNode(ax::mojom::Role::kStaticText, "start"); ASSERT_NE(nullptr, start_node); BrowserAccessibility* end_node = FindNode(ax::mojom::Role::kStaticText, "end"); ASSERT_NE(nullptr, end_node); ComPtr text_range_provider; std::vector paragraphs = { L"start\n", L" First Paragraph\n", L" Second Paragraph\n \n", L"Third Paragraph\n", L"Fourth Paragraph\n\n ", L"Fifth Paragraph\n ", L"Sixth Paragraph\n \n ", L"Seventh Paragraph\n ", L"Eighth Paragraph\n ", L"end", }; // FORWARD NAVIGATION GetTextRangeProviderFromTextNode(*start_node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"start"); // The first paragraph extends beyond the end of the "start" node, because // the preserved whitespace node begins with a line break, so // move once to capture that. EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[0].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[0] + paragraphs[1]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[1].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[1] + paragraphs[2]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[2].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[2] + paragraphs[3]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[3].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[3] + paragraphs[4]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[4].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[4] + paragraphs[5]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[5].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[5] + paragraphs[6]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[6].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[6] + paragraphs[7]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[7].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[7] + paragraphs[8]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[8].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[8] + paragraphs[9]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[9].c_str(), /*expected_count*/ 1); // REVERSE NAVIGATION GetTextRangeProviderFromTextNode(*end_node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"end"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[8] + paragraphs[9]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[8].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[7] + paragraphs[8]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[7].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[6] + paragraphs[7]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[6].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[5] + paragraphs[6]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[5].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[4] + paragraphs[5]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[4].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[3] + paragraphs[4]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[3].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[2] + paragraphs[3]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[2].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[1] + paragraphs[2]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[1].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[0] + paragraphs[1]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[0].c_str(), /*expected_count*/ -1); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, MoveByUnitParagraphWithAriaHiddenNodes) { const std::string html_markup = R"HTML(
start
1. Paragraph with hidden inline in between
2. Paragraph parts wrapped by span with hidden inline in between
3. Paragraph before hidden block4. Paragraph after hidden block
5. Paragraph with leading and trailing hidden span
6. Paragraph with leading and trailing hidden block
end
)HTML"; const std::vector paragraphs = { L"start", L"1. Paragraph with hidden inline in between", L"2. Paragraph parts wrapped by span with hidden inline in between", L"3. Paragraph before hidden block", L"4. Paragraph after hidden block", L"5. Paragraph with leading and trailing hidden span", L"6. Paragraph with leading and trailing hidden block", L"end", }; AssertMoveByUnitForMarkup(TextUnit_Paragraph, html_markup, paragraphs); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, MoveEndpointByUnitParagraphWithEmbeddedObject) { LoadInitialAccessibilityTreeFromHtml( R"HTML( start end )HTML"); BrowserAccessibility* start_node = FindNode(ax::mojom::Role::kStaticText, "start"); ASSERT_NE(nullptr, start_node); BrowserAccessibility* end_node = FindNode(ax::mojom::Role::kStaticText, "end"); ASSERT_NE(nullptr, end_node); std::vector paragraphs = { L"start", kEmbeddedCharacterAsString, L"end", }; // FORWARD NAVIGATION ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*start_node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"start"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[0].c_str(), /*expected_count*/ 0); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -2, /*expected_text*/ L"", /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[0].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[0] + paragraphs[1]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[1].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ (paragraphs[1] + paragraphs[2]).c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[2].c_str(), /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[2].c_str(), /*expected_count*/ 0); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 2, /*expected_text*/ L"", /*expected_count*/ 1); // REVERSE NAVIGATION GetTextRangeProviderFromTextNode(*end_node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"end"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ 1, /*expected_text*/ paragraphs[2].c_str(), /*expected_count*/ 0); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 2, /*expected_text*/ L"", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[2].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[1] + paragraphs[2]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[1].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ (paragraphs[0] + paragraphs[1]).c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[0].c_str(), /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ -1, /*expected_text*/ paragraphs[0].c_str(), /*expected_count*/ 0); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Paragraph, /*count*/ -2, /*expected_text*/ L"", /*expected_count*/ -1); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, IFrameTraversal) { LoadInitialAccessibilityTreeFromUrl(embedded_test_server()->GetURL( "/accessibility/html/iframe-cross-process.html")); WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(), "Text in iframe"); auto* node = FindNode(ax::mojom::Role::kStaticText, "After frame"); ASSERT_NE(nullptr, node); EXPECT_TRUE(node->PlatformIsLeaf()); EXPECT_EQ(0u, node->PlatformChildCount()); ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"After frame"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Word, /*count*/ -1, /*expected_text*/ L"iframe\nAfter frame", /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Word, /*count*/ -2, /*expected_text*/ L"Text in iframe\nAfter frame", /*expected_count*/ -2); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Word, /*count*/ -3, /*expected_text*/ L"Text in ", /*expected_count*/ -3); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Word, /*count*/ 2, /*expected_text*/ L"Text in iframe\nAfter ", /*expected_count*/ 2); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Line, /*count*/ 1, /*expected_text*/ L"Text in iframe\nAfter frame", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Document, /*count*/ 1, /*expected_text*/ L"", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Character, /*count*/ -17, /*expected_text*/ L"iframe\nAfter frame", /*expected_count*/ -17); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Line, /*count*/ -1, /*expected_text*/ L"iframe", /*expected_count*/ -1); text_range_provider->ExpandToEnclosingUnit(TextUnit_Line); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"Text in iframe"); text_range_provider->ExpandToEnclosingUnit(TextUnit_Document); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"Before frame\nText in iframe\nAfter frame"); EXPECT_UIA_MOVE(text_range_provider, TextUnit_Word, /*count*/ 2, /*expected_text*/ L"Text ", /*expected_count*/ 2); EXPECT_UIA_MOVE(text_range_provider, TextUnit_Word, /*count*/ -1, /*expected_text*/ L"frame", /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Character, /*count*/ 1, /*expected_text*/ L"frame\nT", /*expected_count*/ 1); EXPECT_UIA_MOVE(text_range_provider, TextUnit_Character, /*count*/ 6, /*expected_text*/ L"e", /*expected_count*/ 6); EXPECT_UIA_MOVE(text_range_provider, TextUnit_Character, /*count*/ 19, /*expected_text*/ L"f", /*expected_count*/ 19); EXPECT_UIA_MOVE(text_range_provider, TextUnit_Character, /*count*/ -7, /*expected_text*/ L"e", /*expected_count*/ -7); EXPECT_UIA_MOVE(text_range_provider, TextUnit_Line, /*count*/ 1, /*expected_text*/ L"After frame", /*expected_count*/ 1); text_range_provider->ExpandToEnclosingUnit(TextUnit_Document); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"Before frame\nText in iframe\nAfter frame"); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, OutOfProcessIFrameTraversal) { GURL main_url(embedded_test_server()->GetURL( "a.com", "/accessibility/html/iframe-cross-process.html")); LoadInitialAccessibilityTreeFromUrl(main_url); WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(), "Text in iframe"); FrameTreeNode* root = static_cast(shell()->web_contents()) ->GetFrameTree() ->root(); ASSERT_EQ(1U, root->child_count()); // Navigate oopif to URL. FrameTreeNode* iframe_node = root->child_at(0); GURL iframe_url(embedded_test_server()->GetURL( "b.com", "/accessibility/html/frame/static_text.html")); WebContentsImpl* iframe_web_contents = WebContentsImpl::FromFrameTreeNode(iframe_node); DCHECK(iframe_web_contents); { AccessibilityNotificationWaiter waiter(iframe_web_contents, ui::kAXModeComplete, ax::mojom::Event::kLoadComplete); NavigateFrameToURL(iframe_node, iframe_url); waiter.WaitForNotification(); } SynchronizeThreads(); WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(), "Text in iframe"); WaitForHitTestData(iframe_node->current_frame_host()); FrameTreeVisualizer visualizer; ASSERT_EQ( " Site A ------------ proxies for B\n" " +--Site B ------- proxies for A\n" "Where A = http://a.com/\n" " B = http://b.com/", visualizer.DepictFrameTree(root)); auto* node = FindNode(ax::mojom::Role::kStaticText, "After frame"); ASSERT_NE(nullptr, node); EXPECT_TRUE(node->PlatformIsLeaf()); EXPECT_EQ(0u, node->PlatformChildCount()); ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"After frame"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Word, /*count*/ -1, /*expected_text*/ L"iframe\nAfter frame", /*expected_count*/ -1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Word, /*count*/ -2, /*expected_text*/ L"Text in iframe\nAfter frame", /*expected_count*/ -2); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Word, /*count*/ -3, /*expected_text*/ L"Text in ", /*expected_count*/ -3); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Word, /*count*/ 2, /*expected_text*/ L"Text in iframe\nAfter ", /*expected_count*/ 2); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Line, /*count*/ 1, /*expected_text*/ L"Text in iframe\nAfter frame", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Document, /*count*/ 1, /*expected_text*/ L"", /*expected_count*/ 1); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Character, /*count*/ -17, /*expected_text*/ L"iframe\nAfter frame", /*expected_count*/ -17); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Line, /*count*/ -1, /*expected_text*/ L"iframe", /*expected_count*/ -1); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, ExpandToEnclosingFormat) { LoadInitialAccessibilityTreeFromHtml( R"HTML(
plain
text
italic
text
)HTML"); auto* node = FindNode(ax::mojom::Role::kStaticText, "plain"); ASSERT_NE(nullptr, node); EXPECT_TRUE(node->PlatformIsLeaf()); EXPECT_EQ(0u, node->PlatformChildCount()); ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*node, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"plain"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Character, /*count*/ 3, /*expected_text*/ L"in", /*expected_count*/ 3); ASSERT_HRESULT_SUCCEEDED( text_range_provider->ExpandToEnclosingUnit(TextUnit_Format)); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"plain\ntext"); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Character, /*count*/ 3, /*expected_text*/ L"plain\ntext\nita", /*expected_count*/ 3); EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Character, /*count*/ 10, /*expected_text*/ L"ta", /*expected_count*/ 10); ASSERT_HRESULT_SUCCEEDED( text_range_provider->ExpandToEnclosingUnit(TextUnit_Format)); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"italic\ntext"); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, EntireMarkupSuccessiveMoveByCharacter) { AssertMoveByUnitForMarkup( TextUnit_Character, "Test ing.", {L"T", L"e", L"s", L"t", L" ", L"i", L"n", L"g", L"."}); // The text consists of an e acute, and two emoticons. const std::string html = R"HTML( )HTML"; AssertMoveByUnitForMarkup( TextUnit_Character, html, {L"e\x0301", L"\xD83D\xDC69\x200D\x2764\xFE0F\x200D\xD83D\xDC69", L"\xD83D\xDC36"}); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, EntireMarkupSuccessiveMoveByWord) { AssertMoveByUnitForMarkup(TextUnit_Word, "this is a test.", {L"this ", L"is ", L"a ", L"test."}); AssertMoveByUnitForMarkup(TextUnit_Word, " this is a test. ", {L"this ", L"is ", L"a ", L"test."}); AssertMoveByUnitForMarkup( TextUnit_Word, "It said: to be continued...", {L"It ", L"said: ", L"to ", L"be ", L"continued..."}); AssertMoveByUnitForMarkup(TextUnit_Word, "a link with multiple words and text after.", {L"a ", L"link ", L"with ", L"multiple ", L"words", L"and ", L"text ", L"after."}); AssertMoveByUnitForMarkup(TextUnit_Word, "a and text after.", {L"a ", L"and ", L"text ", L"after."}); AssertMoveByUnitForMarkup( TextUnit_Word, "
  1. item one
  2. item two
", {L"1. ", L"item ", L"one", L"2. ", L"item ", L"two"}); AssertMoveByUnitForMarkup(TextUnit_Word, "
  • item one
  • item two
", {L"• ", L"item ", L"one", L"• ", L"item ", L"two"}); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, EntireMarkupSuccessiveMoveByFormat) { AssertMoveByUnitForMarkup( TextUnit_Format, "plain text bold text bold and italic text more bold " "and italic text italic text plain text", {L"plain text ", L"bold text ", L"bold and italic text more bold and italic text", L" italic text", L" plain text"}); AssertMoveByUnitForMarkup( TextUnit_Format, "before after", {L"before ", kEmbeddedCharacterAsString.c_str(), L" after"}); AssertMoveByUnitForMarkup(TextUnit_Format, "before link after", {L"before ", L"link", L" after"}); AssertMoveByUnitForMarkup(TextUnit_Format, "before link after", {L"before ", L"link ", L"after"}); AssertMoveByUnitForMarkup(TextUnit_Format, "before link after", {L"before ", L"link ", L"after"}); AssertMoveByUnitForMarkup( TextUnit_Format, "before link after bold", {L"before ", L"link ", L"after ", L"bold"}); AssertMoveByUnitForMarkup( TextUnit_Format, "before link after", {L"before ", L"link ", L"after"}); AssertMoveByUnitForMarkup( TextUnit_Format, "before link 1link 2 after", {L"before ", L"link 1link 2", L" after"}); AssertMoveByUnitForMarkup( TextUnit_Format, "before text after", {L"before ", L"text ", L"after"}); AssertMoveByUnitForMarkup( TextUnit_Format, "before text 1text 2 after", {L"before ", L"text 1text 2", L" after"}); AssertMoveByUnitForMarkup( TextUnit_Format, "before bold textitalic text after", {L"before ", L"bold text", L"italic text", L" after"}); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, EntireMarkupSuccessiveMoveByLine) { AssertMoveByUnitForMarkup(TextUnit_Line, "
one two
", {L"one ", L"two"}); AssertMoveByUnitForMarkup(TextUnit_Line, "line one
line two", {L"line one", L"line two"}); AssertMoveByUnitForMarkup(TextUnit_Line, "
line one
line two
", {L"line one", L"line two"}); AssertMoveByUnitForMarkup( TextUnit_Line, "
a
", {L"a"}); // This tests a weird edge-case; TextUnit_Line breaks at the beginning of an // inline-block, but not at the end. AssertMoveByUnitForMarkup(TextUnit_Line, "a
b
c", {L"a", L"b\nc"}); AssertMoveByUnitForMarkup( TextUnit_Line, "

line one

  • line two
  • line three
", {L"line one", L"• line two", L"• line three"}); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, MoveByCharacterWithEmbeddedObject) { const std::string html_markup = R"HTML(

after

)HTML"; const std::vector characters = { L"S", L"o", L"m", L"e", L" ", L"t", L"e", L"x", L"t", kEmbeddedCharacterAsString.c_str(), L"a", L"f", L"t", L"e", L"r"}; AssertMoveByUnitForMarkup(TextUnit_Character, html_markup, characters); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, MoveByWordWithEmbeddedObject) { const std::string html_markup = R"HTML(

after input

)HTML"; const std::vector words = { L"Some ", L"text ", kEmbeddedCharacterAsString.c_str(), L"after ", L"input", kEmbeddedCharacterAsString.c_str()}; AssertMoveByUnitForMarkup(TextUnit_Word, html_markup, words); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, BoundingRectangleOfWordBeforeListItemMarker) { LoadInitialAccessibilityTreeFromHtml( R"HTML(

Text before list

  • First list item
  • Second list item
)HTML"); BrowserAccessibility* text_before_list = FindNode(ax::mojom::Role::kStaticText, "Text before list"); ASSERT_NE(nullptr, text_before_list); ComPtr text_range_provider; GetTextRangeProviderFromTextNode(*text_before_list, &text_range_provider); ASSERT_NE(nullptr, text_range_provider.Get()); EXPECT_UIA_MOVE(text_range_provider, TextUnit_Character, /*count*/ 12, /*expected_text*/ L"l", /*expected_count*/ 12); text_range_provider->ExpandToEnclosingUnit(TextUnit_Word); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"list"); base::win::ScopedSafearray rectangles; ASSERT_HRESULT_SUCCEEDED( text_range_provider->GetBoundingRectangles(rectangles.Receive())); gfx::Vector2d view_offset = text_before_list->manager() ->GetViewBoundsInScreenCoordinates() .OffsetFromOrigin(); std::vector expected_values = {85 + view_offset.x(), 16 + view_offset.y(), 20, 17}; EXPECT_UIA_DOUBLE_SAFEARRAY_EQ(rectangles.Get(), expected_values); EXPECT_UIA_MOVE(text_range_provider, TextUnit_Character, /*count*/ 19, /*expected_text*/ L"e", /*expected_count*/ 19); text_range_provider->ExpandToEnclosingUnit(TextUnit_Word); EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"item"); ASSERT_HRESULT_SUCCEEDED( text_range_provider->GetBoundingRectangles(rectangles.Receive())); expected_values = {105 + view_offset.x(), 50 + view_offset.y(), 28, 17}; EXPECT_UIA_DOUBLE_SAFEARRAY_EQ(rectangles.Get(), expected_values); } IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, MoveByFormatWithGeneratedContentTableAndSpans) { const std::string html_markup = "" "" "

First Heading

\nParagraph One
" "

Second Heading

\nParagraph Two
"; const std::vector format_units = { L" \nFirst Heading ", L"\nParagraph One", L" \nSecond Heading ", L"\nParagraph Two"}; AssertMoveByUnitForMarkup(TextUnit_Format, html_markup, format_units); } // https://crbug.com/1036397 IN_PROC_BROWSER_TEST_F( AXPlatformNodeTextRangeProviderWinBrowserTest, DISABLED_MoveByFormatWithGeneratedContentTableAndParagraphs) { const std::string html_markup = R"HTML(

First Heading

Paragraph One

Second Heading

Paragraph Two

)HTML"; const std::vector format_units = { L" \nFirst Heading ", L"\nParagraph One", L" \nSecond Heading ", L"\nParagraph Two"}; AssertMoveByUnitForMarkup(TextUnit_Format, html_markup, format_units); } } // namespace content