blob: b3c84d3f2e94e9c941d3ff84ea67dbc45346e30d [file]
// Copyright 2019 The Chromium Authors
// 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_win.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/win/scoped_variant.h"
#include "content/browser/accessibility/accessibility_content_browsertest.h"
#include "content/browser/renderer_host/render_widget_host_view_aura.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/test/accessibility_notification_waiter.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/platform/browser_accessibility.h"
#include "ui/accessibility/platform/browser_accessibility_com_win.h"
#include "ui/accessibility/platform/uia_registrar_win.h"
using base::win::ScopedVariant;
using Microsoft::WRL::ComPtr;
namespace content {
#define EXPECT_UIA_INT_EQ(node, property_id, expected) \
{ \
base::win::ScopedVariant expectedVariant(expected); \
ASSERT_EQ(VT_I4, expectedVariant.type()); \
base::win::ScopedVariant actual; \
ASSERT_HRESULT_SUCCEEDED( \
node->GetPropertyValue(property_id, actual.Receive())); \
EXPECT_EQ(expectedVariant.ptr()->intVal, actual.ptr()->intVal); \
}
#define EXPECT_UIA_BSTR_EQ(node, property_id, expected) \
{ \
ScopedVariant expectedVariant(expected); \
ASSERT_EQ(VT_BSTR, expectedVariant.type()); \
ASSERT_NE(nullptr, expectedVariant.ptr()->bstrVal); \
ScopedVariant actual; \
ASSERT_HRESULT_SUCCEEDED( \
node->GetPropertyValue(property_id, actual.Receive())); \
ASSERT_EQ(VT_BSTR, actual.type()); \
ASSERT_NE(nullptr, actual.ptr()->bstrVal); \
EXPECT_STREQ(expectedVariant.ptr()->bstrVal, actual.ptr()->bstrVal); \
}
class AXPlatformNodeWinBrowserTest : public AccessibilityContentBrowserTest {
protected:
template <typename T>
ComPtr<T> QueryInterfaceFromNode(
ui::BrowserAccessibility* browser_accessibility) {
ComPtr<T> result;
EXPECT_HRESULT_SUCCEEDED(
browser_accessibility->GetNativeViewAccessible()->QueryInterface(
__uuidof(T), &result));
return result;
}
ComPtr<IAccessible> IAccessibleFromNode(
ui::BrowserAccessibility* browser_accessibility) {
return QueryInterfaceFromNode<IAccessible>(browser_accessibility);
}
ComPtr<IAccessible2> ToIAccessible2(ComPtr<IAccessible> accessible) {
CHECK(accessible);
ComPtr<IServiceProvider> service_provider;
accessible.As(&service_provider);
ComPtr<IAccessible2> result;
CHECK(SUCCEEDED(service_provider->QueryService(IID_IAccessible2,
IID_PPV_ARGS(&result))));
return result;
}
ui::BrowserAccessibility* FindNodeAfter(ui::BrowserAccessibility* begin,
const std::string& name) {
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
ui::BrowserAccessibilityManager* manager =
web_contents->GetRootBrowserAccessibilityManager();
ui::BrowserAccessibility* node = begin;
while (node && (node->GetName() != name)) {
node = manager->NextInTreeOrder(node);
}
return node;
}
void UIAGetPropertyValueFlowsFromBrowserTestTemplate(
const ui::BrowserAccessibility* target_browser_accessibility,
const std::vector<std::string>& expected_names) {
ASSERT_NE(nullptr, target_browser_accessibility);
auto* target_browser_accessibility_com_win =
ToBrowserAccessibilityWin(target_browser_accessibility)->GetCOM();
ASSERT_NE(nullptr, target_browser_accessibility_com_win);
base::win::ScopedVariant flows_from_variant;
target_browser_accessibility_com_win->GetPropertyValue(
UIA_FlowsFromPropertyId, flows_from_variant.Receive());
ASSERT_EQ(VT_ARRAY | VT_UNKNOWN, flows_from_variant.type());
ASSERT_EQ(1u, SafeArrayGetDim(V_ARRAY(flows_from_variant.ptr())));
LONG lower_bound, upper_bound, size;
ASSERT_HRESULT_SUCCEEDED(
SafeArrayGetLBound(V_ARRAY(flows_from_variant.ptr()), 1, &lower_bound));
ASSERT_HRESULT_SUCCEEDED(
SafeArrayGetUBound(V_ARRAY(flows_from_variant.ptr()), 1, &upper_bound));
size = upper_bound - lower_bound + 1;
ASSERT_EQ(static_cast<LONG>(expected_names.size()), size);
std::vector<std::string> names;
for (LONG i = 0; i < size; ++i) {
ComPtr<IUnknown> unknown_element;
ASSERT_HRESULT_SUCCEEDED(
SafeArrayGetElement(V_ARRAY(flows_from_variant.ptr()), &i,
static_cast<void**>(&unknown_element)));
ASSERT_NE(nullptr, unknown_element);
ComPtr<IRawElementProviderSimple> raw_element_provider_simple = nullptr;
ASSERT_HRESULT_SUCCEEDED(
unknown_element.As(&raw_element_provider_simple));
ASSERT_NE(nullptr, raw_element_provider_simple);
base::win::ScopedVariant name;
ASSERT_HRESULT_SUCCEEDED(raw_element_provider_simple->GetPropertyValue(
UIA_NamePropertyId, name.Receive()));
ASSERT_EQ(VT_BSTR, name.type());
names.push_back(base::WideToUTF8(
std::wstring(V_BSTR(name.ptr()), SysStringLen(V_BSTR(name.ptr())))));
}
ASSERT_THAT(names, ::testing::UnorderedElementsAreArray(expected_names));
}
void UIAIWindowProviderGetIsModalBrowserTestTemplate(
ax::mojom::Role expected_role,
ui::BrowserAccessibility* (ui::BrowserAccessibility::*f)(size_t) const,
size_t index_arg,
bool expected_is_modal,
bool expected_is_window_provider_available) {
ui::BrowserAccessibility* root_browser_accessibility =
GetRootAndAssertNonNull();
ui::BrowserAccessibilityComWin* root_browser_accessibility_com_win =
ToBrowserAccessibilityWin(root_browser_accessibility)->GetCOM();
ASSERT_NE(nullptr, root_browser_accessibility_com_win);
ui::BrowserAccessibility* browser_accessibility =
(root_browser_accessibility->*f)(index_arg);
ASSERT_NE(nullptr, browser_accessibility);
ASSERT_EQ(expected_role, browser_accessibility->GetRole());
ui::BrowserAccessibilityComWin* browser_accessibility_com_win =
ToBrowserAccessibilityWin(browser_accessibility)->GetCOM();
ASSERT_NE(nullptr, browser_accessibility_com_win);
ComPtr<IWindowProvider> window_provider = nullptr;
ASSERT_HRESULT_SUCCEEDED(browser_accessibility_com_win->GetPatternProvider(
UIA_WindowPatternId, &window_provider));
if (expected_is_window_provider_available) {
ASSERT_NE(nullptr, window_provider.Get());
BOOL is_modal = FALSE;
ASSERT_HRESULT_SUCCEEDED(window_provider->get_IsModal(&is_modal));
ASSERT_EQ(expected_is_modal, is_modal);
} else {
ASSERT_EQ(nullptr, window_provider.Get());
}
}
};
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinBrowserTest,
IA2ScrollToPointIframeText) {
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."
"\"");
ui::BrowserAccessibility* browser_accessibility =
GetRootAndAssertNonNull()->PlatformDeepestLastChild();
ASSERT_NE(nullptr, browser_accessibility);
ASSERT_EQ(ax::mojom::Role::kStaticText, browser_accessibility->GetRole());
ui::BrowserAccessibility* iframe_browser_accessibility =
browser_accessibility->manager()->GetBrowserAccessibilityRoot();
ASSERT_NE(nullptr, iframe_browser_accessibility);
ASSERT_EQ(ax::mojom::Role::kRootWebArea,
iframe_browser_accessibility->GetRole());
gfx::Rect iframe_screen_bounds = iframe_browser_accessibility->GetBoundsRect(
ui::AXCoordinateSystem::kScreenDIPs, ui::AXClippingBehavior::kUnclipped);
AccessibilityNotificationWaiter location_changed_waiter(
shell()->web_contents(), ax::mojom::Event::kLocationChanged);
ComPtr<IAccessible2> root_iaccessible2 =
ToIAccessible2(IAccessibleFromNode(browser_accessibility));
ASSERT_EQ(S_OK, root_iaccessible2->scrollToPoint(
IA2_COORDTYPE_SCREEN_RELATIVE, iframe_screen_bounds.x(),
iframe_screen_bounds.y()));
ASSERT_TRUE(location_changed_waiter.WaitForNotification());
gfx::Rect bounds = browser_accessibility->GetBoundsRect(
ui::AXCoordinateSystem::kScreenDIPs, ui::AXClippingBehavior::kUnclipped);
ASSERT_EQ(iframe_screen_bounds.y(), bounds.y());
}
class AXPlatformNodeWinUIABrowserTest : public AXPlatformNodeWinBrowserTest {};
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAGetPropertyValueFlowsFromNone) {
LoadInitialAccessibilityTreeFromHtmlFilePath(
"/accessibility/aria/aria-label.html");
base::win::ScopedVariant flows_from_variant;
ComPtr<IRawElementProviderSimple> node_provider =
QueryInterfaceFromNode<IRawElementProviderSimple>(
FindNode(ax::mojom::Role::kCheckBox, "aria label"));
node_provider->GetPropertyValue(UIA_FlowsFromPropertyId,
flows_from_variant.Receive());
ASSERT_EQ(VT_ARRAY | VT_UNKNOWN, flows_from_variant.type());
ASSERT_EQ(nullptr, V_ARRAY(flows_from_variant.ptr()));
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAGetPropertyValueWebContentsHistogram) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<p>Hello World</p>
</html>
)HTML"));
base::HistogramTester histogram_tester;
base::win::ScopedVariant property_value;
ComPtr<IRawElementProviderSimple> node_provider =
QueryInterfaceFromNode<IRawElementProviderSimple>(
FindNode(ax::mojom::Role::kStaticText, "Hello World"));
histogram_tester.ExpectTotalCount(
"Accessibility.Performance.WinAPIs2.WebContents.UMA_API_GET_PROPERTY_"
"VALUE",
0);
node_provider->GetPropertyValue(UIA_NamePropertyId, property_value.Receive());
histogram_tester.ExpectTotalCount(
"Accessibility.Performance.WinAPIs2.WebContents.UMA_API_GET_PROPERTY_"
"VALUE",
1);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAGetPropertyValueFlowsFromSingle) {
LoadInitialAccessibilityTreeFromHtmlFilePath(
"/accessibility/aria/aria-flowto.html");
UIAGetPropertyValueFlowsFromBrowserTestTemplate(
FindNode(ax::mojom::Role::kFooter, "next"), {"current"});
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAGetPropertyValueFlowsFromMultiple) {
LoadInitialAccessibilityTreeFromHtmlFilePath(
"/accessibility/aria/aria-flowto-multiple.html");
UIAGetPropertyValueFlowsFromBrowserTestTemplate(
FindNode(ax::mojom::Role::kGenericContainer, "b3"), {"a3", "c3"});
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest, UIANamePropertyValue) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<ol>
<li>list item 1</li>
<li></li>
<li>before <div><span>problem</span></div>, after</li>
<li>before <a href="https://blah.com">problem</a>, after</li>
<li aria-label="from author">from content</li>
</ol>
</html>
)HTML"));
ui::BrowserAccessibility* list_node =
GetRootAndAssertNonNull()->PlatformGetChild(0);
ui::BrowserAccessibility* item_node = list_node->PlatformGetChild(0);
ASSERT_NE(nullptr, item_node);
EXPECT_UIA_BSTR_EQ(ToBrowserAccessibilityWin(item_node)->GetCOM(),
UIA_NamePropertyId, L"list item 1");
// Empty string as name should correspond to empty <li>.
item_node = list_node->PlatformGetChild(1);
ASSERT_NE(nullptr, item_node);
EXPECT_UIA_BSTR_EQ(ToBrowserAccessibilityWin(item_node)->GetCOM(),
UIA_NamePropertyId, L"");
// <li> with complex structure and text.
item_node = list_node->PlatformGetChild(2);
ASSERT_NE(nullptr, item_node);
EXPECT_UIA_BSTR_EQ(ToBrowserAccessibilityWin(item_node)->GetCOM(),
UIA_NamePropertyId, L"beforeproblem, after");
// <li> with a link inside
item_node = list_node->PlatformGetChild(3);
ASSERT_NE(nullptr, item_node);
EXPECT_UIA_BSTR_EQ(ToBrowserAccessibilityWin(item_node)->GetCOM(),
UIA_NamePropertyId, L"before problem, after");
// <li> with name specified by the author rather than by the contents.
item_node = list_node->PlatformGetChild(4);
ASSERT_NE(nullptr, item_node);
EXPECT_UIA_BSTR_EQ(ToBrowserAccessibilityWin(item_node)->GetCOM(),
UIA_NamePropertyId, L"from author");
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAIWindowProviderGetIsModalOnDialog) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body role="none">
<dialog open>Example Text</dialog>
</body>
</html>
)HTML"));
UIAIWindowProviderGetIsModalBrowserTestTemplate(
ax::mojom::Role::kDialog, &ui::BrowserAccessibility::PlatformGetChild, 0,
false, true);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAIWindowProviderGetIsModalOnDialogAriaModalFalse) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body role="none">
<dialog open aria-modal="false">Example Text</dialog>
</body>
</html>
)HTML"));
UIAIWindowProviderGetIsModalBrowserTestTemplate(
ax::mojom::Role::kDialog, &ui::BrowserAccessibility::PlatformGetChild, 0,
false, true);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAIWindowProviderGetIsModalOnDialogAriaModalTrue) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body role="none">
<dialog open aria-modal="true">Example Text</dialog>
</body>
</html>
)HTML"));
UIAIWindowProviderGetIsModalBrowserTestTemplate(
ax::mojom::Role::kDialog, &ui::BrowserAccessibility::PlatformGetChild, 0,
true, true);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAIWindowProviderGetIsModalOnDiv) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<div>Example Text</div>
</body>
</html>
)HTML"));
UIAIWindowProviderGetIsModalBrowserTestTemplate(
ax::mojom::Role::kGenericContainer,
&ui::BrowserAccessibility::PlatformGetChild, 0, false, false);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAIWindowProviderGetIsModalOnDivAriaModalFalse) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<div aria-modal="false">Example Text</div>
</body>
</html>
)HTML"));
UIAIWindowProviderGetIsModalBrowserTestTemplate(
ax::mojom::Role::kGenericContainer,
&ui::BrowserAccessibility::PlatformGetChild, 0, false, false);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAIWindowProviderGetIsModalOnDivAriaModalTrue) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<div aria-modal="true">Example Text</div>
</body>
</html>
)HTML"));
UIAIWindowProviderGetIsModalBrowserTestTemplate(
ax::mojom::Role::kGenericContainer,
&ui::BrowserAccessibility::PlatformGetChild, 0, false, false);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAIWindowProviderGetIsModalOnDivDialog) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<div role="dialog">Example Text</div>
</body>
</html>
)HTML"));
UIAIWindowProviderGetIsModalBrowserTestTemplate(
ax::mojom::Role::kDialog, &ui::BrowserAccessibility::PlatformGetChild, 0,
false, true);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAIWindowProviderGetIsModalOnDivDialogAriaModalFalse) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<div role="dialog" aria-modal="false">Example Text</div>
</body>
</html>
)HTML"));
UIAIWindowProviderGetIsModalBrowserTestTemplate(
ax::mojom::Role::kDialog, &ui::BrowserAccessibility::PlatformGetChild, 0,
false, true);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAIWindowProviderGetIsModalOnDivDialogAriaModalTrue) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<div role="dialog" aria-modal="true">Example Text</div>
</body>
</html>
)HTML"));
UIAIWindowProviderGetIsModalBrowserTestTemplate(
ax::mojom::Role::kDialog, &ui::BrowserAccessibility::PlatformGetChild, 0,
true, true);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAIWindowProviderGetIsModalOnDivAlertDialog) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<div role="alertdialog">Example Text</div>
</body>
</html>
)HTML"));
UIAIWindowProviderGetIsModalBrowserTestTemplate(
ax::mojom::Role::kAlertDialog,
&ui::BrowserAccessibility::PlatformGetChild, 0, false, true);
}
IN_PROC_BROWSER_TEST_F(
AXPlatformNodeWinUIABrowserTest,
UIAIWindowProviderGetIsModalOnDivAlertDialogAriaModalFalse) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<div role="alertdialog" aria-modal="false">Example Text</div>
</body>
</html>
)HTML"));
UIAIWindowProviderGetIsModalBrowserTestTemplate(
ax::mojom::Role::kAlertDialog,
&ui::BrowserAccessibility::PlatformGetChild, 0, false, true);
}
IN_PROC_BROWSER_TEST_F(
AXPlatformNodeWinUIABrowserTest,
UIAIWindowProviderGetIsModalOnDivAlertDialogAriaModalTrue) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<div role="alertdialog" aria-modal="true">Example Text</div>
</body>
</html>
)HTML"));
UIAIWindowProviderGetIsModalBrowserTestTemplate(
ax::mojom::Role::kAlertDialog,
&ui::BrowserAccessibility::PlatformGetChild, 0, true, true);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAGetPropertyValueAutomationId) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<div id="id"></div>
</body>
</html>
)HTML"));
ui::BrowserAccessibility* root_browser_accessibility =
GetRootAndAssertNonNull();
ui::BrowserAccessibilityComWin* root_browser_accessibility_com_win =
ToBrowserAccessibilityWin(root_browser_accessibility)->GetCOM();
ASSERT_NE(nullptr, root_browser_accessibility_com_win);
ui::BrowserAccessibility* browser_accessibility =
root_browser_accessibility->PlatformDeepestLastChild();
ASSERT_NE(nullptr, browser_accessibility);
ASSERT_EQ(ax::mojom::Role::kGenericContainer,
browser_accessibility->GetRole());
ui::BrowserAccessibilityComWin* browser_accessibility_com_win =
ToBrowserAccessibilityWin(browser_accessibility)->GetCOM();
ASSERT_NE(nullptr, browser_accessibility_com_win);
base::win::ScopedVariant expected_scoped_variant;
expected_scoped_variant.Set(SysAllocString(L"id"));
base::win::ScopedVariant scoped_variant;
EXPECT_HRESULT_SUCCEEDED(browser_accessibility_com_win->GetPropertyValue(
UIA_AutomationIdPropertyId, scoped_variant.Receive()));
EXPECT_EQ(0, expected_scoped_variant.Compare(scoped_variant));
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAGetPropertyValueNonEmptyAutomationIdOnRootWebArea) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<button></button>
</body>
</html>
)HTML"));
ui::BrowserAccessibility* root_browser_accessibility =
GetRootAndAssertNonNull();
ASSERT_NE(nullptr, root_browser_accessibility);
ASSERT_EQ(ax::mojom::Role::kRootWebArea,
root_browser_accessibility->GetRole());
ui::BrowserAccessibilityComWin* root_browser_accessibility_com_win =
ToBrowserAccessibilityWin(root_browser_accessibility)->GetCOM();
ASSERT_NE(nullptr, root_browser_accessibility_com_win);
// kRootWebArea nodes should not be empty. Some UIA clients appear to rely on
// whether it's empty or not. See https://crbug.com/40065516#comment32.
base::win::ScopedVariant expected_scoped_variant;
expected_scoped_variant.Set(SysAllocString(L"RootWebArea"));
base::win::ScopedVariant scoped_variant;
EXPECT_HRESULT_SUCCEEDED(root_browser_accessibility_com_win->GetPropertyValue(
UIA_AutomationIdPropertyId, scoped_variant.Receive()));
EXPECT_EQ(0, expected_scoped_variant.Compare(scoped_variant));
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest, UIAScrollIntoView) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<div id="id" style="height: 20px; overflow: scroll;">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
</ul>
</div>
</body>
</html>
)HTML"));
ui::BrowserAccessibility* root_browser_accessibility =
GetRootAndAssertNonNull();
ui::BrowserAccessibilityComWin* root_browser_accessibility_com_win =
ToBrowserAccessibilityWin(root_browser_accessibility)->GetCOM();
ASSERT_NE(nullptr, root_browser_accessibility_com_win);
ui::BrowserAccessibility* browser_accessibility =
root_browser_accessibility->PlatformDeepestLastChild();
ASSERT_NE(nullptr, browser_accessibility);
ASSERT_EQ(ax::mojom::Role::kStaticText, browser_accessibility->GetRole());
ui::BrowserAccessibilityComWin* browser_accessibility_com_win =
ToBrowserAccessibilityWin(browser_accessibility)->GetCOM();
ASSERT_NE(nullptr, browser_accessibility_com_win);
ui::AXPlatformNodeWin* platform_node = static_cast<ui::AXPlatformNodeWin*>(
ui::AXPlatformNode::FromNativeViewAccessible(
browser_accessibility->GetNativeViewAccessible()));
ASSERT_NE(nullptr, platform_node);
AccessibilityNotificationWaiter waiter(shell()->web_contents(),
ax::mojom::Event::kLocationChanged);
EXPECT_HRESULT_SUCCEEDED(platform_node->ScrollIntoView());
ASSERT_TRUE(waiter.WaitForNotification());
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinUIABrowserTest,
UIAGetPropertyValueCulture) {
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<div lang='en-us'>en-us</div>
<div lang='en-gb'>en-gb</div>
<div lang='ru-ru'>ru-ru</div>
<div lang='fake'>fake</div>
<div>no lang</div>
<div lang=''>empty lang</div>
</body>
</html>
)HTML"));
ui::BrowserAccessibility* root_node = GetRootAndAssertNonNull();
ui::BrowserAccessibility* body_node = root_node->PlatformGetFirstChild();
ASSERT_NE(nullptr, body_node);
ui::BrowserAccessibility* node = FindNodeAfter(body_node, "en-us");
ASSERT_NE(nullptr, node);
ui::BrowserAccessibilityComWin* en_us_node_com_win =
ToBrowserAccessibilityWin(node)->GetCOM();
ASSERT_NE(nullptr, en_us_node_com_win);
constexpr int en_us_lcid = 1033;
EXPECT_UIA_INT_EQ(en_us_node_com_win, UIA_CulturePropertyId, en_us_lcid);
node = FindNodeAfter(node, "en-gb");
ASSERT_NE(nullptr, node);
ui::BrowserAccessibilityComWin* en_gb_node_com_win =
ToBrowserAccessibilityWin(node)->GetCOM();
ASSERT_NE(nullptr, en_gb_node_com_win);
constexpr int en_gb_lcid = 2057;
EXPECT_UIA_INT_EQ(en_gb_node_com_win, UIA_CulturePropertyId, en_gb_lcid);
node = FindNodeAfter(node, "ru-ru");
ASSERT_NE(nullptr, node);
ui::BrowserAccessibilityComWin* ru_ru_node_com_win =
ToBrowserAccessibilityWin(node)->GetCOM();
ASSERT_NE(nullptr, ru_ru_node_com_win);
constexpr int ru_ru_lcid = 1049;
EXPECT_UIA_INT_EQ(ru_ru_node_com_win, UIA_CulturePropertyId, ru_ru_lcid);
// Setting to an invalid language should return a failed HRESULT.
node = FindNodeAfter(node, "fake");
ASSERT_NE(nullptr, node);
ui::BrowserAccessibilityComWin* fake_lang_node_com_win =
ToBrowserAccessibilityWin(node)->GetCOM();
ASSERT_NE(nullptr, fake_lang_node_com_win);
base::win::ScopedVariant actual_value;
EXPECT_HRESULT_FAILED(fake_lang_node_com_win->GetPropertyValue(
UIA_CulturePropertyId, actual_value.Receive()));
// No lang should default to the page's default language (en-us).
node = FindNodeAfter(node, "no lang");
ASSERT_NE(nullptr, node);
ui::BrowserAccessibilityComWin* no_lang_node_com_win =
ToBrowserAccessibilityWin(node)->GetCOM();
ASSERT_NE(nullptr, no_lang_node_com_win);
EXPECT_UIA_INT_EQ(no_lang_node_com_win, UIA_CulturePropertyId, en_us_lcid);
// Empty lang should default to the page's default language (en-us).
node = FindNodeAfter(node, "empty lang");
ASSERT_NE(nullptr, node);
ui::BrowserAccessibilityComWin* empty_lang_node_com_win =
ToBrowserAccessibilityWin(node)->GetCOM();
ASSERT_NE(nullptr, empty_lang_node_com_win);
EXPECT_UIA_INT_EQ(empty_lang_node_com_win, UIA_CulturePropertyId, en_us_lcid);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinBrowserTest,
HitTestOnAncestorOfWebRoot) {
EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
// Load the page.
AccessibilityNotificationWaiter waiter(shell()->web_contents(),
ax::mojom::Event::kLoadComplete);
const char url_str[] =
"data:text/html,"
"<!doctype html>"
"<html><head><title>Accessibility Test</title></head>"
"<body>"
"<button>This is a button</button>"
"</body></html>";
GURL url(url_str);
EXPECT_TRUE(NavigateToURL(shell(), url));
ASSERT_TRUE(waiter.WaitForNotification());
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
ui::BrowserAccessibilityManager* manager =
web_contents->GetRootBrowserAccessibilityManager();
// Find a node to hit test. Note that this is a really simple page,
// so synchronous hit testing will work fine.
ui::BrowserAccessibility* node = manager->GetBrowserAccessibilityRoot();
while (node && node->GetRole() != ax::mojom::Role::kButton) {
node = manager->NextInTreeOrder(node);
}
DCHECK(node);
// Get the screen bounds of the hit target and find the point in the middle.
gfx::Rect bounds = node->GetClippedScreenBoundsRect();
gfx::Point point = bounds.CenterPoint();
// Get the root AXPlatformNodeWin.
ui::AXPlatformNodeWin* root_platform_node =
static_cast<ui::AXPlatformNodeWin*>(
ui::AXPlatformNode::FromNativeViewAccessible(
manager->GetBrowserAccessibilityRoot()
->GetNativeViewAccessible()));
// First test that calling accHitTest on the root node returns the button.
{
base::win::ScopedVariant hit_child_variant;
ASSERT_EQ(S_OK, root_platform_node->accHitTest(
point.x(), point.y(), hit_child_variant.Receive()));
ASSERT_EQ(VT_DISPATCH, hit_child_variant.type());
ASSERT_NE(nullptr, hit_child_variant.ptr());
ComPtr<IAccessible> accessible;
ASSERT_HRESULT_SUCCEEDED(V_DISPATCH(hit_child_variant.ptr())
->QueryInterface(IID_PPV_ARGS(&accessible)));
ui::AXPlatformNode* hit_child =
ui::AXPlatformNode::FromNativeViewAccessible(accessible.Get());
ASSERT_NE(nullptr, hit_child);
EXPECT_EQ(node->GetId(), hit_child->GetDelegate()->GetData().id);
}
// Now test it again, but this time caliing accHitTest on the parent
// IAccessible of the web root node.
{
RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
shell()->web_contents()->GetRenderWidgetHostView());
IAccessible* ancestor = rwhva->GetParentNativeViewAccessible();
base::win::ScopedVariant hit_child_variant;
ASSERT_EQ(S_OK, ancestor->accHitTest(point.x(), point.y(),
hit_child_variant.Receive()));
ASSERT_EQ(VT_DISPATCH, hit_child_variant.type());
ASSERT_NE(nullptr, hit_child_variant.ptr());
ComPtr<IAccessible> accessible;
ASSERT_HRESULT_SUCCEEDED(V_DISPATCH(hit_child_variant.ptr())
->QueryInterface(IID_PPV_ARGS(&accessible)));
ui::AXPlatformNode* hit_child =
ui::AXPlatformNode::FromNativeViewAccessible(accessible.Get());
ASSERT_NE(nullptr, hit_child);
EXPECT_EQ(node->GetId(), hit_child->GetDelegate()->GetData().id);
}
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinBrowserTest, IFrameTraversal) {
LoadInitialAccessibilityTreeFromHtmlFilePath(
"/accessibility/html/iframe-traversal.html");
WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(),
"Text in iframe");
ui::BrowserAccessibility* root_node = GetRootAndAssertNonNull();
ui::BrowserAccessibility* before_iframe_node =
FindNodeAfter(root_node, "Before iframe");
ASSERT_NE(nullptr, before_iframe_node);
ASSERT_EQ(ax::mojom::Role::kStaticText, before_iframe_node->GetRole());
ASSERT_EQ(0U, before_iframe_node->PlatformChildCount());
ASSERT_EQ(1U, before_iframe_node->InternalChildCount());
before_iframe_node = before_iframe_node->InternalGetFirstChild();
ASSERT_NE(nullptr, before_iframe_node);
ASSERT_EQ(ax::mojom::Role::kInlineTextBox, before_iframe_node->GetRole());
ui::BrowserAccessibility* inside_iframe_node =
FindNodeAfter(before_iframe_node, "Text in iframe");
ASSERT_NE(nullptr, inside_iframe_node);
ASSERT_EQ(ax::mojom::Role::kStaticText, inside_iframe_node->GetRole());
ASSERT_EQ(0U, inside_iframe_node->PlatformChildCount());
ASSERT_EQ(1U, inside_iframe_node->InternalChildCount());
inside_iframe_node = inside_iframe_node->InternalGetFirstChild();
ASSERT_NE(nullptr, inside_iframe_node);
ASSERT_EQ(ax::mojom::Role::kInlineTextBox, inside_iframe_node->GetRole());
ui::BrowserAccessibility* after_iframe_node =
FindNodeAfter(inside_iframe_node, "After iframe");
ASSERT_NE(nullptr, after_iframe_node);
ASSERT_EQ(ax::mojom::Role::kStaticText, after_iframe_node->GetRole());
ASSERT_EQ(0U, after_iframe_node->PlatformChildCount());
ASSERT_EQ(1U, after_iframe_node->InternalChildCount());
after_iframe_node = after_iframe_node->InternalGetFirstChild();
ASSERT_NE(nullptr, after_iframe_node);
ASSERT_EQ(ax::mojom::Role::kInlineTextBox, after_iframe_node->GetRole());
EXPECT_LT(*before_iframe_node->CreateTextPositionAt(0),
*inside_iframe_node->CreateTextPositionAt(0));
// The following positions should not be equivalent because they are on two
// separate lines in the accessibility tree's text representation, i.e. the
// first has an upstream affinity while the second has a downstream affinity.
// Note that an iframe boundary is also a line boundary.
EXPECT_LT(*before_iframe_node->CreateTextPositionAt(13),
*inside_iframe_node->CreateTextPositionAt(0));
EXPECT_LT(*inside_iframe_node->CreateTextPositionAt(0),
*after_iframe_node->CreateTextPositionAt(0));
// The following positions should not be equivalent because they are on two
// separate lines in the accessibility tree's text representation, i.e. the
// first has an upstream affinity while the second has a downstream affinity.
// Note that an iframe boundary is also a line boundary.
EXPECT_LT(*inside_iframe_node->CreateTextPositionAt(14),
*after_iframe_node->CreateTextPositionAt(0));
// Traverse the leaves of the AXTree forwards.
ui::BrowserAccessibility::AXPosition tree_position =
root_node->CreateTextPositionAt(0)->CreateNextLeafTreePosition();
EXPECT_TRUE(tree_position->IsTreePosition());
EXPECT_EQ(before_iframe_node->node(), tree_position->GetAnchor());
tree_position = tree_position->CreateNextLeafTreePosition();
EXPECT_TRUE(tree_position->IsTreePosition());
EXPECT_EQ(inside_iframe_node->node(), tree_position->GetAnchor());
tree_position = tree_position->CreateNextLeafTreePosition();
EXPECT_TRUE(tree_position->IsTreePosition());
EXPECT_EQ(after_iframe_node->node(), tree_position->GetAnchor());
tree_position = tree_position->CreateNextLeafTreePosition();
EXPECT_TRUE(tree_position->IsNullPosition());
// Traverse the leaves of the AXTree backwards.
tree_position = after_iframe_node->CreateTextPositionAt(0)
->CreatePositionAtEndOfAnchor()
->AsLeafTreePosition();
EXPECT_TRUE(tree_position->IsTreePosition());
EXPECT_EQ(after_iframe_node->node(), tree_position->GetAnchor());
tree_position = tree_position->CreatePreviousLeafTreePosition();
EXPECT_TRUE(tree_position->IsTreePosition());
EXPECT_EQ(inside_iframe_node->node(), tree_position->GetAnchor());
tree_position = tree_position->CreatePreviousLeafTreePosition();
EXPECT_TRUE(tree_position->IsTreePosition());
EXPECT_EQ(before_iframe_node->node(), tree_position->GetAnchor());
tree_position = tree_position->CreatePreviousLeafTreePosition();
EXPECT_TRUE(tree_position->IsNullPosition());
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinBrowserTest,
UIAIsWebContentRootProperty) {
LoadInitialAccessibilityTreeFromHtmlFilePath(
"/accessibility/html/iframe-traversal.html");
WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(),
"Text in iframe");
ui::BrowserAccessibility* root_node = GetRootAndAssertNonNull();
ui::BrowserAccessibility* iframe_text_node =
FindNodeAfter(root_node, "Text in iframe");
ASSERT_NE(nullptr, iframe_text_node);
ui::BrowserAccessibility* iframe_root =
iframe_text_node->manager()->GetBrowserAccessibilityRoot();
ASSERT_NE(nullptr, iframe_root);
PROPERTYID property_id =
ui::UiaRegistrarWin::GetInstance().GetIsWebContentRootPropertyId();
ASSERT_NE(0, property_id);
ScopedVariant root_value;
ASSERT_HRESULT_SUCCEEDED(
ToBrowserAccessibilityWin(root_node)->GetCOM()->GetPropertyValue(
property_id, root_value.Receive()));
ASSERT_EQ(VT_BOOL, root_value.type());
EXPECT_EQ(VARIANT_TRUE, V_BOOL(root_value.ptr()));
ScopedVariant iframe_value;
ASSERT_HRESULT_SUCCEEDED(
ToBrowserAccessibilityWin(iframe_root)
->GetCOM()
->GetPropertyValue(property_id, iframe_value.Receive()));
ASSERT_EQ(VT_BOOL, iframe_value.type());
EXPECT_EQ(VARIANT_FALSE, V_BOOL(iframe_value.ptr()));
}
// Test fixture for MathML UIA property tests.
class AXPlatformNodeWinMathMLBrowserTest : public AXPlatformNodeWinBrowserTest {
public:
AXPlatformNodeWinMathMLBrowserTest() {
scoped_feature_list_.InitAndEnableFeature(::features::kUiaMathMlSupport);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinMathMLBrowserTest,
UIAMathMLFeatureFlagEnabled) {
// Verify the MathML feature flag is enabled and property ID is registered.
EXPECT_TRUE(base::FeatureList::IsEnabled(features::kUiaMathMlSupport));
PROPERTYID mathml_property_id =
ui::UiaRegistrarWin::GetInstance().GetMathMLPropertyId();
EXPECT_NE(0, mathml_property_id);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinMathMLBrowserTest,
UIAMathMLPropertyReturnsMathMLForMathElement) {
// Load a page with MathML content.
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>A</mi><mo>=</mo><mi>Ï€</mi><msup><mi>r</mi><mn>2</mn></msup>
</math>
</body>
</html>
)HTML"));
// Find the math element.
ui::BrowserAccessibility* math_node =
FindNode(ax::mojom::Role::kMathMLMath, "");
ASSERT_NE(nullptr, math_node);
ui::BrowserAccessibilityComWin* math_com_win =
ToBrowserAccessibilityWin(math_node)->GetCOM();
ASSERT_NE(nullptr, math_com_win);
// Get the MathML property value.
PROPERTYID mathml_property_id =
ui::UiaRegistrarWin::GetInstance().GetMathMLPropertyId();
ASSERT_NE(0, mathml_property_id);
ScopedVariant mathml_variant;
EXPECT_HRESULT_SUCCEEDED(math_com_win->GetPropertyValue(
mathml_property_id, mathml_variant.Receive()));
// Verify we got a BSTR (string) result with MathML content.
ASSERT_EQ(VT_BSTR, mathml_variant.type());
ASSERT_NE(nullptr, mathml_variant.ptr()->bstrVal);
std::wstring mathml(mathml_variant.ptr()->bstrVal);
// The MathML content should be wrapped with <math> tags.
// Verify the structure contains all expected elements.
EXPECT_TRUE(mathml.find(L"<math>") != std::wstring::npos);
EXPECT_TRUE(mathml.find(L"<mi>A</mi>") != std::wstring::npos);
EXPECT_TRUE(mathml.find(L"<mo>=</mo>") != std::wstring::npos);
EXPECT_TRUE(mathml.find(L"<msup>") != std::wstring::npos);
EXPECT_TRUE(mathml.find(L"<mi>r</mi>") != std::wstring::npos);
EXPECT_TRUE(mathml.find(L"<mn>2</mn>") != std::wstring::npos);
EXPECT_TRUE(mathml.find(L"</msup>") != std::wstring::npos);
EXPECT_TRUE(mathml.find(L"</math>") != std::wstring::npos);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinMathMLBrowserTest,
UIAMathMLPropertyReturnsEmptyForNonMathElement) {
// Load a page with a button (non-math element).
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<button>Click me</button>
</body>
</html>
)HTML"));
// Find the button element.
ui::BrowserAccessibility* button_node =
FindNode(ax::mojom::Role::kButton, "Click me");
ASSERT_NE(nullptr, button_node);
ui::BrowserAccessibilityComWin* button_com_win =
ToBrowserAccessibilityWin(button_node)->GetCOM();
ASSERT_NE(nullptr, button_com_win);
// Get the MathML property value.
PROPERTYID mathml_property_id =
ui::UiaRegistrarWin::GetInstance().GetMathMLPropertyId();
ASSERT_NE(0, mathml_property_id);
ScopedVariant mathml_variant;
EXPECT_HRESULT_SUCCEEDED(button_com_win->GetPropertyValue(
mathml_property_id, mathml_variant.Receive()));
// We shouldn't get a MathML property for non-root math elements.
ASSERT_EQ(VT_EMPTY, mathml_variant.type());
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinMathMLBrowserTest,
UIAMathElementHasMathematicsAnnotationType) {
// Load a page with MathML content.
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
before
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mfrac><mi>x</mi><mn>2</mn></mfrac>
</math>
after
</body>
</html>
)HTML"));
ui::BrowserAccessibility* root_node =
FindNode(ax::mojom::Role::kRootWebArea, "");
ASSERT_NE(nullptr, root_node);
ui::BrowserAccessibility* math_node =
FindNode(ax::mojom::Role::kMathMLMath, "");
ASSERT_NE(nullptr, math_node);
ui::BrowserAccessibilityComWin* root_com_win =
ToBrowserAccessibilityWin(root_node)->GetCOM();
ASSERT_NE(nullptr, root_com_win);
ui::BrowserAccessibilityComWin* math_com_win =
ToBrowserAccessibilityWin(math_node)->GetCOM();
ASSERT_NE(nullptr, math_com_win);
// Get the wrapping text range for the math element from the document's text
// provider.
ComPtr<ITextProvider> text_provider;
ASSERT_HRESULT_SUCCEEDED(
root_com_win->GetPatternProvider(UIA_TextPatternId, &text_provider));
ASSERT_NE(nullptr, text_provider.Get());
ComPtr<IRawElementProviderSimple> math_provider;
ASSERT_HRESULT_SUCCEEDED(
math_com_win->QueryInterface(IID_PPV_ARGS(&math_provider)));
ASSERT_NE(nullptr, math_provider.Get());
ComPtr<ITextRangeProvider> math_range;
ASSERT_HRESULT_SUCCEEDED(
text_provider->RangeFromChild(math_provider.Get(), &math_range));
ASSERT_NE(nullptr, math_range.Get());
base::win::ScopedVariant annotation_types_variant;
ASSERT_HRESULT_SUCCEEDED(math_range->GetAttributeValue(
UIA_AnnotationTypesAttributeId, annotation_types_variant.Receive()));
ASSERT_EQ(VT_ARRAY | VT_I4, annotation_types_variant.type());
ASSERT_EQ(1u, SafeArrayGetDim(V_ARRAY(annotation_types_variant.ptr())));
LONG lower_bound = 0;
LONG upper_bound = -1;
ASSERT_HRESULT_SUCCEEDED(SafeArrayGetLBound(
V_ARRAY(annotation_types_variant.ptr()), 1, &lower_bound));
ASSERT_HRESULT_SUCCEEDED(SafeArrayGetUBound(
V_ARRAY(annotation_types_variant.ptr()), 1, &upper_bound));
ASSERT_EQ(1, upper_bound - lower_bound + 1);
LONG index = lower_bound;
int annotation_type = 0;
ASSERT_HRESULT_SUCCEEDED(SafeArrayGetElement(
V_ARRAY(annotation_types_variant.ptr()), &index, &annotation_type));
EXPECT_EQ(AnnotationType_Mathematics, annotation_type);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinMathMLBrowserTest,
UIAMathMLSerializesSuperscript) {
// Load a page with MathML content (superscript: x^2).
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<msup><mi>x</mi><mn>2</mn></msup>
</math>
</body>
</html>
)HTML"));
// Find the math element.
ui::BrowserAccessibility* math_node =
FindNode(ax::mojom::Role::kMathMLMath, "");
ASSERT_NE(nullptr, math_node);
ui::BrowserAccessibilityComWin* math_com_win =
ToBrowserAccessibilityWin(math_node)->GetCOM();
ASSERT_NE(nullptr, math_com_win);
PROPERTYID mathml_property_id =
ui::UiaRegistrarWin::GetInstance().GetMathMLPropertyId();
ASSERT_NE(0, mathml_property_id);
ScopedVariant mathml_variant;
EXPECT_HRESULT_SUCCEEDED(math_com_win->GetPropertyValue(
mathml_property_id, mathml_variant.Receive()));
ASSERT_EQ(VT_BSTR, mathml_variant.type());
std::wstring mathml(mathml_variant.ptr()->bstrVal);
// The MathML content should be wrapped with <math> tags and contain the
// complete superscript structure.
EXPECT_TRUE(mathml.find(L"<math>") != std::wstring::npos);
EXPECT_TRUE(mathml.find(L"<msup>") != std::wstring::npos);
EXPECT_TRUE(mathml.find(L"<mi>x</mi>") != std::wstring::npos);
EXPECT_TRUE(mathml.find(L"<mn>2</mn>") != std::wstring::npos);
EXPECT_TRUE(mathml.find(L"</msup>") != std::wstring::npos);
EXPECT_TRUE(mathml.find(L"</math>") != std::wstring::npos);
}
IN_PROC_BROWSER_TEST_F(AXPlatformNodeWinMathMLBrowserTest,
UIAMathMLTreeIsFlattenedForChildren) {
// Verify that the tree is flattened - children of math elements should be
// hidden from UIA navigation.
LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(
<!DOCTYPE html>
<html>
<body>
<div>Before</div>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>A</mi><mo>=</mo><msup><mi>r</mi><mn>2</mn></msup>
</math>
<div>After</div>
</body>
</html>
)HTML"));
// Find the math element.
ui::BrowserAccessibility* math_node =
FindNode(ax::mojom::Role::kMathMLMath, "");
ASSERT_NE(nullptr, math_node);
ui::BrowserAccessibilityComWin* math_com_win =
ToBrowserAccessibilityWin(math_node)->GetCOM();
ASSERT_NE(nullptr, math_com_win);
}
} // namespace content