blob: cbf181202a2ef3bef444702ef0c9e01eb3399bb1 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "build/build_config.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.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/public/test/scoped_accessibility_mode_override.h"
#include "content/shell/browser/shell.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_mode.h"
#include "ui/accessibility/platform/browser_accessibility.h"
namespace content {
const char kMinimalPageDataURL[] =
"data:text/html,<html><head></head><body>Hello, world</body></html>";
class AccessibilityModeTest : public ContentBrowserTest {
protected:
WebContentsImpl* web_contents() {
return static_cast<WebContentsImpl*>(shell()->web_contents());
}
protected:
const ui::BrowserAccessibility* FindNode(ax::mojom::Role role,
const std::string& name) {
const ui::BrowserAccessibility* root =
GetManager()->GetBrowserAccessibilityRoot();
CHECK(root);
return FindNodeInSubtree(*root, role, name);
}
ui::BrowserAccessibilityManager* GetManager() {
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
return web_contents->GetRootBrowserAccessibilityManager();
}
private:
const ui::BrowserAccessibility* FindNodeInSubtree(
const ui::BrowserAccessibility& node,
ax::mojom::Role role,
const std::string& name) {
if (node.GetRole() == role &&
node.GetStringAttribute(ax::mojom::StringAttribute::kName) == name)
return &node;
for (unsigned int i = 0; i < node.PlatformChildCount(); ++i) {
const ui::BrowserAccessibility* result =
FindNodeInSubtree(*node.PlatformGetChild(i), role, name);
if (result)
return result;
}
return nullptr;
}
};
IN_PROC_BROWSER_TEST_F(AccessibilityModeTest, AccessibilityModeOff) {
EXPECT_TRUE(NavigateToURL(shell(), GURL(kMinimalPageDataURL)));
auto accessibility_mode = web_contents()->GetAccessibilityMode();
bool hasNativeAPIs = accessibility_mode.has_mode(ui::AXMode::kNativeAPIs);
// Strip off kNativeAPIs, which may be set in some situations.
accessibility_mode.set_mode(ui::AXMode::kNativeAPIs, false);
EXPECT_TRUE(accessibility_mode.is_mode_off());
if (!hasNativeAPIs) {
EXPECT_EQ(nullptr, GetManager());
}
}
IN_PROC_BROWSER_TEST_F(AccessibilityModeTest, AccessibilityModeComplete) {
EXPECT_TRUE(NavigateToURL(shell(), GURL(kMinimalPageDataURL)));
auto accessibility_mode = web_contents()->GetAccessibilityMode();
// Strip off kNativeAPIs, which may be set in some situations.
accessibility_mode.set_mode(ui::AXMode::kNativeAPIs, false);
ASSERT_TRUE(accessibility_mode.is_mode_off());
AccessibilityNotificationWaiter waiter(shell()->web_contents());
ScopedAccessibilityModeOverride scoped_accessibility_mode(
web_contents(), ui::kAXModeComplete);
EXPECT_EQ(web_contents()->GetAccessibilityMode(), ui::kAXModeComplete);
ASSERT_TRUE(waiter.WaitForNotification());
EXPECT_NE(nullptr, GetManager());
}
// Tests that adding kAXModeComplete via ui::AXPlatformNode gives the flags
// to an active WebContents.
IN_PROC_BROWSER_TEST_F(AccessibilityModeTest,
AccessibilityModeCompleteViaNode) {
EXPECT_TRUE(NavigateToURL(shell(), GURL(kMinimalPageDataURL)));
auto accessibility_mode = web_contents()->GetAccessibilityMode();
// Strip off kNativeAPIs, which may be set in some situations.
accessibility_mode.set_mode(ui::AXMode::kNativeAPIs, false);
ASSERT_TRUE(accessibility_mode.is_mode_off());
AccessibilityNotificationWaiter waiter(shell()->web_contents());
ui::AXPlatformNode::NotifyAddAXModeFlags(ui::kAXModeComplete);
ASSERT_TRUE(waiter.WaitForNotification());
EXPECT_EQ(web_contents()->GetAccessibilityMode(), ui::kAXModeComplete);
EXPECT_NE(nullptr, GetManager());
}
// Tests that adding kAXModeComplete via BrowserAccessibilityState gives the
// flags to an active WebContents.
IN_PROC_BROWSER_TEST_F(AccessibilityModeTest,
AccessibilityModeCompleteViaContent) {
EXPECT_TRUE(NavigateToURL(shell(), GURL(kMinimalPageDataURL)));
auto accessibility_mode = web_contents()->GetAccessibilityMode();
// Strip off kNativeAPIs, which may be set in some situations.
accessibility_mode.set_mode(ui::AXMode::kNativeAPIs, false);
ASSERT_TRUE(accessibility_mode.is_mode_off());
AccessibilityNotificationWaiter waiter(shell()->web_contents());
BrowserAccessibilityState::GetInstance()->AddAccessibilityModeFlags(
ui::kAXModeComplete);
ASSERT_TRUE(waiter.WaitForNotification());
EXPECT_EQ(web_contents()->GetAccessibilityMode(), ui::kAXModeComplete);
EXPECT_NE(nullptr, GetManager());
}
IN_PROC_BROWSER_TEST_F(AccessibilityModeTest,
AccessibilityModeWebContentsOnly) {
EXPECT_TRUE(NavigateToURL(shell(), GURL(kMinimalPageDataURL)));
auto accessibility_mode = web_contents()->GetAccessibilityMode();
// Strip off kNativeAPIs, which may be set in some situations.
accessibility_mode.set_mode(ui::AXMode::kNativeAPIs, false);
ASSERT_TRUE(accessibility_mode.is_mode_off());
AccessibilityNotificationWaiter waiter(shell()->web_contents());
ScopedAccessibilityModeOverride scoped_accessibility_mode(
web_contents(), ui::kAXModeWebContentsOnly);
accessibility_mode = web_contents()->GetAccessibilityMode();
bool hasNativeAPIs = accessibility_mode.has_mode(ui::AXMode::kNativeAPIs);
// Strip off kNativeAPIs, which may be set in some situations.
accessibility_mode.set_mode(ui::AXMode::kNativeAPIs, false);
EXPECT_EQ(accessibility_mode, ui::kAXModeWebContentsOnly);
ASSERT_TRUE(waiter.WaitForNotification());
// No BrowserAccessibilityManager if kNativeAPIs isn't set.
if (!hasNativeAPIs) {
EXPECT_EQ(nullptr, GetManager());
}
}
IN_PROC_BROWSER_TEST_F(AccessibilityModeTest, AddingModes) {
EXPECT_TRUE(NavigateToURL(shell(), GURL(kMinimalPageDataURL)));
AccessibilityNotificationWaiter waiter(shell()->web_contents());
ScopedAccessibilityModeOverride scoped_accessibility_mode(
web_contents(), ui::kAXModeWebContentsOnly);
auto accessibility_mode = web_contents()->GetAccessibilityMode();
// Strip off kNativeAPIs, which may be set in some situations.
accessibility_mode.set_mode(ui::AXMode::kNativeAPIs, false);
EXPECT_TRUE(accessibility_mode == ui::kAXModeWebContentsOnly);
ASSERT_TRUE(waiter.WaitForNotification());
EXPECT_EQ(nullptr, GetManager());
AccessibilityNotificationWaiter waiter2(shell()->web_contents());
ScopedAccessibilityModeOverride scoped_accessibility_mode2(
web_contents(), ui::kAXModeComplete);
EXPECT_EQ(web_contents()->GetAccessibilityMode(), ui::kAXModeComplete);
ASSERT_TRUE(waiter2.WaitForNotification());
EXPECT_NE(nullptr, GetManager());
}
IN_PROC_BROWSER_TEST_F(AccessibilityModeTest,
FullAccessibilityHasInlineTextBoxes) {
// TODO(dmazzoni): On Android we use an ifdef to disable inline text boxes,
// we should do it with accessibility flags instead. http://crbug.com/672205
#if !BUILDFLAG(IS_ANDROID)
EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
AccessibilityNotificationWaiter waiter(shell()->web_contents(),
ui::kAXModeComplete,
ax::mojom::Event::kLoadComplete);
GURL url("data:text/html,<p>Para</p>");
EXPECT_TRUE(NavigateToURL(shell(), url));
ASSERT_TRUE(waiter.WaitForNotification());
const ui::BrowserAccessibility* text =
FindNode(ax::mojom::Role::kStaticText, "Para");
ASSERT_NE(nullptr, text);
ASSERT_EQ(1U, text->InternalChildCount());
ui::BrowserAccessibility* inline_text = text->InternalGetChild(0);
ASSERT_NE(nullptr, inline_text);
EXPECT_EQ(ax::mojom::Role::kInlineTextBox, inline_text->GetRole());
#endif // !BUILDFLAG(IS_ANDROID)
}
IN_PROC_BROWSER_TEST_F(AccessibilityModeTest,
MinimalAccessibilityModeHasNoInlineTextBoxes) {
// TODO(dmazzoni): On Android we use an ifdef to disable inline text boxes,
// we should do it with accessibility flags instead. http://crbug.com/672205
#if !BUILDFLAG(IS_ANDROID)
EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
AccessibilityNotificationWaiter waiter(
shell()->web_contents(),
ui::AXMode::kNativeAPIs | ui::AXMode::kWebContents,
ax::mojom::Event::kLoadComplete);
GURL url("data:text/html,<p>Para</p>");
EXPECT_TRUE(NavigateToURL(shell(), url));
ASSERT_TRUE(waiter.WaitForNotification());
const ui::BrowserAccessibility* text =
FindNode(ax::mojom::Role::kStaticText, "Para");
ASSERT_NE(nullptr, text);
EXPECT_EQ(0U, text->InternalChildCount());
#endif // !BUILDFLAG(IS_ANDROID)
}
IN_PROC_BROWSER_TEST_F(AccessibilityModeTest, AddScreenReaderModeFlag) {
EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
AccessibilityNotificationWaiter waiter(
shell()->web_contents(),
ui::AXMode::kNativeAPIs | ui::AXMode::kWebContents,
ax::mojom::Event::kLoadComplete);
GURL url("data:text/html,<input aria-label=Foo placeholder=Bar>");
EXPECT_TRUE(NavigateToURL(shell(), url));
ASSERT_TRUE(waiter.WaitForNotification());
const ui::BrowserAccessibility* textbox =
FindNode(ax::mojom::Role::kTextField, "Foo");
ASSERT_NE(nullptr, textbox);
EXPECT_FALSE(
textbox->HasStringAttribute(ax::mojom::StringAttribute::kPlaceholder));
int original_id = textbox->GetId();
AccessibilityNotificationWaiter waiter2(shell()->web_contents(), ui::AXMode(),
ax::mojom::Event::kLoadComplete);
ScopedAccessibilityModeOverride ax_mode_override(ui::AXMode::kScreenReader);
ASSERT_TRUE(waiter2.WaitForNotification());
const ui::BrowserAccessibility* textbox2 =
FindNode(ax::mojom::Role::kTextField, "Foo");
ASSERT_NE(nullptr, textbox2);
EXPECT_TRUE(
textbox2->HasStringAttribute(ax::mojom::StringAttribute::kPlaceholder));
EXPECT_EQ(original_id, textbox2->GetId());
}
IN_PROC_BROWSER_TEST_F(AccessibilityModeTest,
ReEnablingAccessibilityDoesNotTimeout) {
EXPECT_TRUE(NavigateToURL(shell(), GURL(kMinimalPageDataURL)));
auto accessibility_mode = web_contents()->GetAccessibilityMode();
// Strip off kNativeAPIs, which may be set in some situations.
accessibility_mode.set_mode(ui::AXMode::kNativeAPIs, false);
ASSERT_TRUE(accessibility_mode.is_mode_off());
AccessibilityNotificationWaiter waiter(shell()->web_contents());
ScopedAccessibilityModeOverride scoped_accessibility_mode(
web_contents(), ui::kAXModeWebContentsOnly);
accessibility_mode = web_contents()->GetAccessibilityMode();
bool hasNativeAPIs = accessibility_mode.has_mode(ui::AXMode::kNativeAPIs);
// Strip off kNativeAPIs, which may be set in some situations.
accessibility_mode.set_mode(ui::AXMode::kNativeAPIs, false);
EXPECT_TRUE(accessibility_mode == ui::kAXModeWebContentsOnly);
ASSERT_TRUE(waiter.WaitForNotification());
EXPECT_EQ(nullptr, GetManager());
AccessibilityNotificationWaiter waiter2(shell()->web_contents());
ScopedAccessibilityModeOverride scoped_accessibility_mode2(
web_contents(), ui::kAXModeComplete);
EXPECT_TRUE(web_contents()->GetAccessibilityMode() == ui::kAXModeComplete);
ASSERT_TRUE(waiter2.WaitForNotification());
if (!hasNativeAPIs) {
EXPECT_NE(nullptr, GetManager());
}
}
// Test platform node ids on OS's that have platform nodes.
#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(AccessibilityModeTest, ReEnablingDoesNotAlterUniqueIds) {
ASSERT_TRUE(NavigateToURL(shell(), GURL(R"HTML(
data:text/html,<!DOCTYPE html>
<html>
<body>
<button>Button 1</button>
<iframe srcdoc="
<!DOCTYPE html>
<html>
<body>
<button>Button 2</button>
</body>
</html>
"></iframe>
<button>Button 3</button>
</body>
</html>)HTML")));
auto accessibility_mode = web_contents()->GetAccessibilityMode();
// Strip off kNativeAPIs, which may be set in some situations.
accessibility_mode.set_mode(ui::AXMode::kNativeAPIs, false);
// Accessibility should now be off.
ASSERT_TRUE(accessibility_mode.is_mode_off());
EXPECT_EQ(nullptr, GetManager());
// Turn accessibility on.
AccessibilityNotificationWaiter waiter(shell()->web_contents());
BrowserAccessibilityState::GetInstance()->AddAccessibilityModeFlags(
ui::kAXModeComplete);
ASSERT_TRUE(waiter.WaitForNotification());
WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(),
"Button 2");
// Save unique ids.
accessibility_mode = web_contents()->GetAccessibilityMode();
ASSERT_TRUE(accessibility_mode.has_mode(ui::AXMode::kNativeAPIs));
ASSERT_TRUE(accessibility_mode.has_mode(ui::AXMode::kWebContents));
EXPECT_NE(nullptr, GetManager());
const ui::BrowserAccessibility* button_1 =
FindNode(ax::mojom::Role::kButton, "Button 1");
ASSERT_NE(nullptr, button_1);
const ui::BrowserAccessibility* button_2 =
FindNode(ax::mojom::Role::kButton, "Button 2");
ASSERT_NE(nullptr, button_2);
int32_t unique_id_1 = button_1->GetAXPlatformNode()->GetUniqueId();
int32_t unique_id_2 = button_2->GetAXPlatformNode()->GetUniqueId();
// Turn accessibility off again.
BrowserAccessibilityState::GetInstance()->ResetAccessibilityMode();
accessibility_mode = web_contents()->GetAccessibilityMode();
ASSERT_TRUE(accessibility_mode.is_mode_off());
EXPECT_EQ(nullptr, GetManager());
// Turn accessibility on again.
AccessibilityNotificationWaiter waiter_3(shell()->web_contents());
BrowserAccessibilityState::GetInstance()->AddAccessibilityModeFlags(
ui::kAXModeBasic);
ASSERT_TRUE(waiter_3.WaitForNotification());
WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(),
"Button 2");
// Compare unique id for newly created a11y nodes with previous unique ids.
accessibility_mode = web_contents()->GetAccessibilityMode();
ASSERT_TRUE(accessibility_mode.has_mode(ui::AXMode::kNativeAPIs));
ASSERT_TRUE(accessibility_mode.has_mode(ui::AXMode::kWebContents));
EXPECT_NE(nullptr, GetManager());
const ui::BrowserAccessibility* button_1_refresh =
FindNode(ax::mojom::Role::kButton, "Button 1");
ASSERT_NE(nullptr, button_1_refresh);
// button_1 is now a dangling pointer for the old button.
// The pointers are not the same, proving that button_1_refresh is new.
ASSERT_NE(button_1, button_1_refresh);
const ui::BrowserAccessibility* button_2_refresh =
FindNode(ax::mojom::Role::kButton, "Button 2");
ASSERT_NE(nullptr, button_2_refresh);
// button_2 is now a dangling pointer for the old button.
// The pointers are not the same, proving that button_2_refresh is new.
ASSERT_NE(button_2, button_2_refresh);
EXPECT_EQ(unique_id_1, button_1_refresh->GetAXPlatformNode()->GetUniqueId());
EXPECT_EQ(unique_id_2, button_2_refresh->GetAXPlatformNode()->GetUniqueId());
}
#endif // #if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
} // namespace content