| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/accessibility/browser_accessibility_android.h" |
| |
| #include <memory> |
| |
| #include "base/test/scoped_feature_list.h" |
| #include "build/build_config.h" |
| #include "content/browser/accessibility/ax_style_data.h" |
| #include "content/browser/accessibility/browser_accessibility_manager_android.h" |
| #include "content/browser/accessibility/web_contents_accessibility_android.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/test/test_content_client.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/accessibility/ax_enums.mojom-data-view.h" |
| #include "ui/accessibility/platform/browser_accessibility_manager.h" |
| #include "ui/accessibility/platform/test_ax_node_id_delegate.h" |
| #include "ui/accessibility/platform/test_ax_platform_tree_manager_delegate.h" |
| #include "ui/strings/grit/auto_image_annotation_strings.h" |
| |
| namespace content { |
| |
| namespace { |
| BrowserAccessibilityManagerAndroid* ToBrowserAccessibilityManagerAndroid( |
| ui::BrowserAccessibilityManager* manager) { |
| return static_cast<BrowserAccessibilityManagerAndroid*>(manager); |
| } |
| } // namespace |
| |
| using RetargetEventType = ui::AXTreeManager::RetargetEventType; |
| using RangePairs = AXStyleData::RangePairs; |
| |
| using ::testing::Eq; |
| using ::testing::Pair; |
| using ::testing::UnorderedElementsAre; |
| |
| class MockContentClient : public TestContentClient { |
| public: |
| std::u16string GetLocalizedString(int message_id) override { |
| switch (message_id) { |
| case IDS_AX_UNLABELED_IMAGE_ROLE_DESCRIPTION: |
| return u"Unlabeled image"; |
| case IDS_AX_IMAGE_ELIGIBLE_FOR_ANNOTATION_ANDROID: |
| return u"This image isn't labeled. Double tap on the more options " |
| u"button at the top of the browser to get image descriptions."; |
| case IDS_AX_IMAGE_ANNOTATION_PENDING: |
| return u"Getting description..."; |
| case IDS_AX_IMAGE_ANNOTATION_ADULT: |
| return u"Appears to contain adult content. No description available."; |
| case IDS_AX_IMAGE_ANNOTATION_NO_DESCRIPTION: |
| return u"No description available."; |
| default: |
| return std::u16string(); |
| } |
| } |
| }; |
| |
| class MockWebContentsAccessibilityAndroid |
| : public WebContentsAccessibilityAndroid { |
| public: |
| MockWebContentsAccessibilityAndroid() {} |
| }; |
| |
| class BrowserAccessibilityAndroidTest : public ::testing::Test { |
| public: |
| BrowserAccessibilityAndroidTest(); |
| |
| BrowserAccessibilityAndroidTest(const BrowserAccessibilityAndroidTest&) = |
| delete; |
| BrowserAccessibilityAndroidTest& operator=( |
| const BrowserAccessibilityAndroidTest&) = delete; |
| |
| ~BrowserAccessibilityAndroidTest() override; |
| |
| protected: |
| static const ui::AXNodeID ROOT_ID = 100; |
| |
| std::unique_ptr<ui::TestAXPlatformTreeManagerDelegate> |
| test_browser_accessibility_delegate_; |
| ui::TestAXNodeIdDelegate node_id_delegate_; |
| MockWebContentsAccessibilityAndroid mock_web_contents_accessibility_android_; |
| |
| private: |
| void SetUp() override; |
| MockContentClient client_; |
| |
| // This is needed to prevent a DCHECK failure when OnAccessibilityApiUsage |
| // is called in BrowserAccessibility::GetRole. |
| content::BrowserTaskEnvironment task_environment_; |
| }; |
| |
| BrowserAccessibilityAndroidTest::BrowserAccessibilityAndroidTest() = default; |
| |
| BrowserAccessibilityAndroidTest::~BrowserAccessibilityAndroidTest() = default; |
| |
| void BrowserAccessibilityAndroidTest::SetUp() { |
| test_browser_accessibility_delegate_ = |
| std::make_unique<ui::TestAXPlatformTreeManagerDelegate>(); |
| test_browser_accessibility_delegate_->SetWebContentsAccessibility( |
| &mock_web_contents_accessibility_android_); |
| SetContentClient(&client_); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TestRetargetTextOnly) { |
| ui::AXNodeData text1; |
| text1.id = 111; |
| text1.role = ax::mojom::Role::kStaticText; |
| text1.SetName("Hello, world"); |
| |
| ui::AXNodeData para1; |
| para1.id = 11; |
| para1.role = ax::mojom::Role::kParagraph; |
| para1.child_ids = {text1.id}; |
| |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.child_ids = {para1.id}; |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| MakeAXTreeUpdateForTesting(root, para1, text1), node_id_delegate_, |
| test_browser_accessibility_delegate_.get())); |
| |
| ui::BrowserAccessibility* root_obj = manager->GetBrowserAccessibilityRoot(); |
| EXPECT_FALSE(root_obj->IsLeaf()); |
| EXPECT_TRUE(root_obj->CanFireEvents()); |
| ui::BrowserAccessibility* para_obj = root_obj->PlatformGetChild(0); |
| EXPECT_TRUE(para_obj->IsLeaf()); |
| EXPECT_TRUE(para_obj->CanFireEvents()); |
| ui::BrowserAccessibility* text_obj = manager->GetFromID(111); |
| EXPECT_TRUE(text_obj->IsLeaf()); |
| EXPECT_FALSE(text_obj->CanFireEvents()); |
| ui::BrowserAccessibility* updated = |
| manager->RetargetBrowserAccessibilityForEvents( |
| text_obj, RetargetEventType::RetargetEventTypeBlinkHover); |
| // |updated| should be the paragraph. |
| EXPECT_EQ(11, updated->GetId()); |
| EXPECT_TRUE(updated->CanFireEvents()); |
| manager.reset(); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TestRetargetHeading) { |
| ui::AXNodeData text1; |
| text1.id = 111; |
| text1.role = ax::mojom::Role::kStaticText; |
| |
| ui::AXNodeData heading1; |
| heading1.id = 11; |
| heading1.role = ax::mojom::Role::kHeading; |
| heading1.SetName("heading"); |
| heading1.child_ids = {text1.id}; |
| |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.child_ids = {heading1.id}; |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| MakeAXTreeUpdateForTesting(root, heading1, text1), node_id_delegate_, |
| test_browser_accessibility_delegate_.get())); |
| |
| ui::BrowserAccessibility* root_obj = manager->GetBrowserAccessibilityRoot(); |
| EXPECT_FALSE(root_obj->IsLeaf()); |
| EXPECT_TRUE(root_obj->CanFireEvents()); |
| ui::BrowserAccessibility* heading_obj = root_obj->PlatformGetChild(0); |
| EXPECT_TRUE(heading_obj->IsLeaf()); |
| EXPECT_TRUE(heading_obj->CanFireEvents()); |
| ui::BrowserAccessibility* text_obj = manager->GetFromID(111); |
| EXPECT_TRUE(text_obj->IsLeaf()); |
| EXPECT_FALSE(text_obj->CanFireEvents()); |
| ui::BrowserAccessibility* updated = |
| manager->RetargetBrowserAccessibilityForEvents( |
| text_obj, RetargetEventType::RetargetEventTypeBlinkHover); |
| // |updated| should be the heading. |
| EXPECT_EQ(11, updated->GetId()); |
| EXPECT_TRUE(updated->CanFireEvents()); |
| manager.reset(); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TestRetargetFocusable) { |
| ui::AXNodeData text1; |
| text1.id = 111; |
| text1.role = ax::mojom::Role::kStaticText; |
| |
| ui::AXNodeData para1; |
| para1.id = 11; |
| para1.role = ax::mojom::Role::kParagraph; |
| para1.AddState(ax::mojom::State::kFocusable); |
| para1.SetName("focusable"); |
| para1.child_ids = {text1.id}; |
| |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.child_ids = {para1.id}; |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| MakeAXTreeUpdateForTesting(root, para1, text1), node_id_delegate_, |
| test_browser_accessibility_delegate_.get())); |
| |
| ui::BrowserAccessibility* root_obj = manager->GetBrowserAccessibilityRoot(); |
| EXPECT_FALSE(root_obj->IsLeaf()); |
| EXPECT_TRUE(root_obj->CanFireEvents()); |
| ui::BrowserAccessibility* para_obj = root_obj->PlatformGetChild(0); |
| EXPECT_FALSE(para_obj->IsLeaf()); |
| EXPECT_TRUE(para_obj->CanFireEvents()); |
| ui::BrowserAccessibility* text_obj = manager->GetFromID(111); |
| EXPECT_TRUE(text_obj->IsLeaf()); |
| EXPECT_TRUE(text_obj->CanFireEvents()); |
| ui::BrowserAccessibility* updated = |
| manager->RetargetBrowserAccessibilityForEvents( |
| text_obj, RetargetEventType::RetargetEventTypeBlinkHover); |
| // |updated| should be the paragraph. |
| EXPECT_EQ(11, updated->GetId()); |
| EXPECT_TRUE(updated->CanFireEvents()); |
| manager.reset(); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TestRetargetInputControl) { |
| // Build the tree that has a form with input time. |
| // +rootWebArea |
| // ++genericContainer |
| // +++form |
| // ++++labelText |
| // +++++staticText |
| // ++++inputTime |
| // +++++genericContainer |
| // ++++++staticText |
| // ++++button |
| // +++++staticText |
| ui::AXNodeData label_text; |
| label_text.id = 11111; |
| label_text.role = ax::mojom::Role::kStaticText; |
| label_text.SetName("label"); |
| |
| ui::AXNodeData label; |
| label.id = 1111; |
| label.role = ax::mojom::Role::kLabelText; |
| label.child_ids = {label_text.id}; |
| |
| ui::AXNodeData input_text; |
| input_text.id = 111211; |
| input_text.role = ax::mojom::Role::kStaticText; |
| input_text.SetName("input_text"); |
| |
| ui::AXNodeData input_container; |
| input_container.id = 11121; |
| input_container.role = ax::mojom::Role::kGenericContainer; |
| input_container.child_ids = {input_text.id}; |
| |
| ui::AXNodeData input_time; |
| input_time.id = 1112; |
| input_time.role = ax::mojom::Role::kInputTime; |
| input_time.AddState(ax::mojom::State::kFocusable); |
| input_time.child_ids = {input_container.id}; |
| |
| ui::AXNodeData button_text; |
| button_text.id = 11131; |
| button_text.role = ax::mojom::Role::kStaticText; |
| button_text.AddState(ax::mojom::State::kFocusable); |
| button_text.SetName("button"); |
| |
| ui::AXNodeData button; |
| button.id = 1113; |
| button.role = ax::mojom::Role::kButton; |
| button.child_ids = {button_text.id}; |
| |
| ui::AXNodeData form; |
| form.id = 111; |
| form.role = ax::mojom::Role::kForm; |
| form.child_ids = {label.id, input_time.id, button.id}; |
| |
| ui::AXNodeData container; |
| container.id = 11; |
| container.role = ax::mojom::Role::kGenericContainer; |
| container.child_ids = {form.id}; |
| |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.child_ids = {container.id}; |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| MakeAXTreeUpdateForTesting(root, container, form, label, label_text, |
| input_time, input_container, input_text, |
| button, button_text), |
| node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| ui::BrowserAccessibility* root_obj = manager->GetBrowserAccessibilityRoot(); |
| EXPECT_FALSE(root_obj->IsLeaf()); |
| EXPECT_TRUE(root_obj->CanFireEvents()); |
| ui::BrowserAccessibility* label_obj = manager->GetFromID(label.id); |
| EXPECT_TRUE(label_obj->IsLeaf()); |
| EXPECT_TRUE(label_obj->CanFireEvents()); |
| ui::BrowserAccessibility* label_text_obj = manager->GetFromID(label_text.id); |
| EXPECT_TRUE(label_text_obj->IsLeaf()); |
| EXPECT_FALSE(label_text_obj->CanFireEvents()); |
| ui::BrowserAccessibility* updated = |
| manager->RetargetBrowserAccessibilityForEvents( |
| label_text_obj, RetargetEventType::RetargetEventTypeBlinkHover); |
| EXPECT_EQ(label.id, updated->GetId()); |
| EXPECT_TRUE(updated->CanFireEvents()); |
| |
| ui::BrowserAccessibility* input_time_obj = manager->GetFromID(input_time.id); |
| EXPECT_TRUE(input_time_obj->IsLeaf()); |
| EXPECT_TRUE(input_time_obj->CanFireEvents()); |
| ui::BrowserAccessibility* input_time_container_obj = |
| manager->GetFromID(input_container.id); |
| EXPECT_TRUE(input_time_container_obj->IsLeaf()); |
| EXPECT_FALSE(input_time_container_obj->CanFireEvents()); |
| updated = manager->RetargetBrowserAccessibilityForEvents( |
| input_time_container_obj, RetargetEventType::RetargetEventTypeBlinkHover); |
| EXPECT_EQ(input_time.id, updated->GetId()); |
| EXPECT_TRUE(updated->CanFireEvents()); |
| ui::BrowserAccessibility* input_text_obj = manager->GetFromID(input_text.id); |
| EXPECT_TRUE(input_text_obj->IsLeaf()); |
| EXPECT_FALSE(input_text_obj->CanFireEvents()); |
| updated = manager->RetargetBrowserAccessibilityForEvents( |
| input_text_obj, RetargetEventType::RetargetEventTypeBlinkHover); |
| EXPECT_EQ(input_time.id, updated->GetId()); |
| EXPECT_TRUE(updated->CanFireEvents()); |
| |
| ui::BrowserAccessibility* button_obj = manager->GetFromID(button.id); |
| EXPECT_TRUE(button_obj->IsLeaf()); |
| EXPECT_TRUE(button_obj->CanFireEvents()); |
| ui::BrowserAccessibility* button_text_obj = |
| manager->GetFromID(button_text.id); |
| EXPECT_TRUE(button_text_obj->IsLeaf()); |
| EXPECT_FALSE(button_text_obj->CanFireEvents()); |
| updated = manager->RetargetBrowserAccessibilityForEvents( |
| button_text_obj, RetargetEventType::RetargetEventTypeBlinkHover); |
| EXPECT_EQ(button.id, updated->GetId()); |
| EXPECT_TRUE(updated->CanFireEvents()); |
| manager.reset(); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TestGetTextContent) { |
| ui::AXNodeData text1; |
| text1.id = 111; |
| text1.role = ax::mojom::Role::kStaticText; |
| text1.SetName("1Foo"); |
| |
| ui::AXNodeData text2; |
| text2.id = 112; |
| text2.role = ax::mojom::Role::kStaticText; |
| text2.SetName("2Bar"); |
| |
| ui::AXNodeData text3; |
| text3.id = 113; |
| text3.role = ax::mojom::Role::kStaticText; |
| text3.SetName("3Baz"); |
| |
| ui::AXNodeData container_para; |
| container_para.id = 11; |
| container_para.role = ax::mojom::Role::kGenericContainer; |
| container_para.child_ids = {text1.id, text2.id, text3.id}; |
| |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.child_ids = {container_para.id}; |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| MakeAXTreeUpdateForTesting(root, container_para, text1, text2, text3), |
| node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| ui::BrowserAccessibility* container_obj = manager->GetFromID(11); |
| // Default caller gets full text. |
| EXPECT_EQ(u"1Foo2Bar3Baz", container_obj->GetTextContentUTF16()); |
| |
| BrowserAccessibilityAndroid* node = |
| static_cast<BrowserAccessibilityAndroid*>(container_obj); |
| // No predicate returns all text. |
| EXPECT_EQ(u"1Foo2Bar3Baz", node->GetSubstringTextContentUTF16(std::nullopt)); |
| // Non-empty predicate terminates after one text node. |
| EXPECT_EQ(u"1Foo", node->GetSubstringTextContentUTF16(1)); |
| // Length of 5 not satisfied by one node. |
| EXPECT_EQ(u"1Foo2Bar", node->GetSubstringTextContentUTF16(5)); |
| // Length of 10 not satisfied by two nodes. |
| EXPECT_EQ(u"1Foo2Bar3Baz", node->GetSubstringTextContentUTF16(10)); |
| manager.reset(); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, |
| TestImageRoleDescription_UnlabeledImage) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = 1; |
| tree.nodes.resize(6); |
| tree.nodes[0].id = 1; |
| tree.nodes[0].child_ids = {2, 3, 4, 5, 6}; |
| |
| // Images with these annotation statuses should report "Unlabeled image" |
| tree.nodes[1].id = 2; |
| tree.nodes[1].role = ax::mojom::Role::kImage; |
| tree.nodes[1].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kEligibleForAnnotation); |
| |
| tree.nodes[2].id = 3; |
| tree.nodes[2].role = ax::mojom::Role::kImage; |
| tree.nodes[2].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kAnnotationPending); |
| |
| tree.nodes[3].id = 4; |
| tree.nodes[3].role = ax::mojom::Role::kImage; |
| tree.nodes[3].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kAnnotationEmpty); |
| |
| tree.nodes[4].id = 5; |
| tree.nodes[4].role = ax::mojom::Role::kImage; |
| tree.nodes[4].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kAnnotationAdult); |
| |
| tree.nodes[5].id = 6; |
| tree.nodes[5].role = ax::mojom::Role::kImage; |
| tree.nodes[5].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kAnnotationProcessFailed); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| BrowserAccessibilityManagerAndroid* android_manager = |
| ToBrowserAccessibilityManagerAndroid(manager.get()); |
| android_manager->set_allow_image_descriptions_for_testing(true); |
| |
| for (int child_index = 0; |
| child_index < static_cast<int>(tree.nodes[0].child_ids.size()); |
| ++child_index) { |
| BrowserAccessibilityAndroid* child = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild( |
| child_index)); |
| |
| EXPECT_EQ(u"Unlabeled image", child->GetRoleDescription()); |
| } |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TestImageRoleDescription_Empty) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = 1; |
| tree.nodes.resize(6); |
| tree.nodes[0].id = 1; |
| tree.nodes[0].child_ids = {2, 3, 4, 5, 6}; |
| |
| // Images with these annotation statuses should report nothing. |
| tree.nodes[1].id = 2; |
| tree.nodes[1].role = ax::mojom::Role::kImage; |
| tree.nodes[1].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kAnnotationSucceeded); |
| |
| tree.nodes[2].id = 3; |
| tree.nodes[2].role = ax::mojom::Role::kImage; |
| tree.nodes[2].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kNone); |
| |
| tree.nodes[3].id = 4; |
| tree.nodes[3].role = ax::mojom::Role::kImage; |
| tree.nodes[3].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kWillNotAnnotateDueToScheme); |
| |
| tree.nodes[4].id = 5; |
| tree.nodes[4].role = ax::mojom::Role::kImage; |
| tree.nodes[4].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kIneligibleForAnnotation); |
| |
| tree.nodes[5].id = 6; |
| tree.nodes[5].role = ax::mojom::Role::kImage; |
| tree.nodes[5].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kSilentlyEligibleForAnnotation); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| BrowserAccessibilityManagerAndroid* android_manager = |
| ToBrowserAccessibilityManagerAndroid(manager.get()); |
| android_manager->set_allow_image_descriptions_for_testing(true); |
| |
| for (int child_index = 0; |
| child_index < static_cast<int>(tree.nodes[0].child_ids.size()); |
| ++child_index) { |
| BrowserAccessibilityAndroid* child = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild( |
| child_index)); |
| |
| EXPECT_EQ(std::u16string(), child->GetRoleDescription()); |
| } |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TestImageInnerText_Eligible) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = 1; |
| tree.nodes.resize(3); |
| tree.nodes[0].id = 1; |
| tree.nodes[0].child_ids = {2, 3}; |
| |
| tree.nodes[1].id = 2; |
| tree.nodes[1].role = ax::mojom::Role::kImage; |
| tree.nodes[1].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kEligibleForAnnotation); |
| tree.nodes[1].AddIntAttribute( |
| ax::mojom::IntAttribute::kTextDirection, |
| static_cast<int32_t>(ax::mojom::WritingDirection::kLtr)); |
| |
| tree.nodes[2].id = 3; |
| tree.nodes[2].role = ax::mojom::Role::kImage; |
| tree.nodes[2].SetName("image_name"); |
| tree.nodes[2].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kEligibleForAnnotation); |
| tree.nodes[2].AddIntAttribute( |
| ax::mojom::IntAttribute::kTextDirection, |
| static_cast<int32_t>(ax::mojom::WritingDirection::kRtl)); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| BrowserAccessibilityManagerAndroid* android_manager = |
| ToBrowserAccessibilityManagerAndroid(manager.get()); |
| android_manager->set_allow_image_descriptions_for_testing(true); |
| |
| BrowserAccessibilityAndroid* image_ltr = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild(0)); |
| |
| EXPECT_EQ( |
| u"This image isn't labeled. Double tap on the more options " |
| u"button at the top of the browser to get image descriptions.", |
| image_ltr->GetTextContentUTF16()); |
| |
| BrowserAccessibilityAndroid* image_rtl = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild(1)); |
| |
| EXPECT_EQ( |
| u"image_name, This image isn't labeled. Double tap on the more options " |
| u"button at the top of the browser to get image descriptions.", |
| image_rtl->GetTextContentUTF16()); |
| EXPECT_EQ(std::u16string(), image_rtl->GetSupplementalDescription()); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, |
| TestImageInnerText_PendingAdultOrEmpty) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = 1; |
| tree.nodes.resize(5); |
| tree.nodes[0].id = 1; |
| tree.nodes[0].child_ids = {2, 3, 4, 5}; |
| |
| tree.nodes[1].id = 2; |
| tree.nodes[1].role = ax::mojom::Role::kImage; |
| tree.nodes[1].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kAnnotationPending); |
| |
| tree.nodes[2].id = 3; |
| tree.nodes[2].role = ax::mojom::Role::kImage; |
| tree.nodes[2].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kAnnotationEmpty); |
| |
| tree.nodes[3].id = 4; |
| tree.nodes[3].role = ax::mojom::Role::kImage; |
| tree.nodes[3].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kAnnotationAdult); |
| |
| tree.nodes[4].id = 5; |
| tree.nodes[4].role = ax::mojom::Role::kImage; |
| tree.nodes[4].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kAnnotationProcessFailed); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| BrowserAccessibilityManagerAndroid* android_manager = |
| ToBrowserAccessibilityManagerAndroid(manager.get()); |
| android_manager->set_allow_image_descriptions_for_testing(true); |
| |
| BrowserAccessibilityAndroid* image_pending = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild(0)); |
| |
| BrowserAccessibilityAndroid* image_empty = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild(1)); |
| |
| BrowserAccessibilityAndroid* image_adult = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild(2)); |
| |
| BrowserAccessibilityAndroid* image_failed = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild(3)); |
| |
| EXPECT_EQ(u"Getting description...", image_pending->GetTextContentUTF16()); |
| EXPECT_EQ(u"No description available.", image_empty->GetTextContentUTF16()); |
| EXPECT_EQ(u"Appears to contain adult content. No description available.", |
| image_adult->GetTextContentUTF16()); |
| EXPECT_EQ(u"No description available.", image_failed->GetTextContentUTF16()); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TestImageInnerText_Ineligible) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = 1; |
| tree.nodes.resize(5); |
| tree.nodes[0].id = 1; |
| tree.nodes[0].child_ids = {2, 3, 4, 5}; |
| |
| tree.nodes[1].id = 2; |
| tree.nodes[1].role = ax::mojom::Role::kImage; |
| tree.nodes[1].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kNone); |
| |
| tree.nodes[2].id = 3; |
| tree.nodes[2].role = ax::mojom::Role::kImage; |
| tree.nodes[2].SetName("image_name"); |
| tree.nodes[2].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kWillNotAnnotateDueToScheme); |
| |
| tree.nodes[3].id = 4; |
| tree.nodes[3].role = ax::mojom::Role::kImage; |
| tree.nodes[3].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kIneligibleForAnnotation); |
| |
| tree.nodes[4].id = 5; |
| tree.nodes[4].role = ax::mojom::Role::kImage; |
| tree.nodes[4].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kSilentlyEligibleForAnnotation); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| BrowserAccessibilityManagerAndroid* android_manager = |
| ToBrowserAccessibilityManagerAndroid(manager.get()); |
| android_manager->set_allow_image_descriptions_for_testing(true); |
| |
| BrowserAccessibilityAndroid* image_none = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild(0)); |
| |
| BrowserAccessibilityAndroid* image_scheme = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild(1)); |
| |
| BrowserAccessibilityAndroid* image_ineligible = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild(2)); |
| |
| BrowserAccessibilityAndroid* image_silent = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild(3)); |
| |
| EXPECT_EQ(std::u16string(), image_none->GetTextContentUTF16()); |
| EXPECT_EQ(u"image_name", image_scheme->GetTextContentUTF16()); |
| EXPECT_EQ(std::u16string(), image_scheme->GetSupplementalDescription()); |
| EXPECT_EQ(std::u16string(), image_ineligible->GetTextContentUTF16()); |
| EXPECT_EQ(std::u16string(), image_silent->GetTextContentUTF16()); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, |
| TestImageInnerText_AnnotationSucceeded) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = 1; |
| tree.nodes.resize(3); |
| tree.nodes[0].id = 1; |
| tree.nodes[0].child_ids = {2, 3}; |
| |
| tree.nodes[1].id = 2; |
| tree.nodes[1].role = ax::mojom::Role::kImage; |
| tree.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kImageAnnotation, |
| "test_annotation"); |
| tree.nodes[1].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kAnnotationSucceeded); |
| |
| tree.nodes[2].id = 3; |
| tree.nodes[2].role = ax::mojom::Role::kImage; |
| tree.nodes[2].SetName("image_name"); |
| tree.nodes[2].AddStringAttribute(ax::mojom::StringAttribute::kImageAnnotation, |
| "test_annotation"); |
| tree.nodes[2].SetImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus::kAnnotationSucceeded); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| BrowserAccessibilityManagerAndroid* android_manager = |
| ToBrowserAccessibilityManagerAndroid(manager.get()); |
| android_manager->set_allow_image_descriptions_for_testing(true); |
| |
| BrowserAccessibilityAndroid* image_succeeded = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild(0)); |
| |
| BrowserAccessibilityAndroid* image_succeeded_with_name = |
| static_cast<BrowserAccessibilityAndroid*>( |
| manager->GetBrowserAccessibilityRoot()->PlatformGetChild(1)); |
| |
| EXPECT_EQ(u"test_annotation", image_succeeded->GetTextContentUTF16()); |
| EXPECT_EQ(u"image_name, test_annotation", |
| image_succeeded_with_name->GetTextContentUTF16()); |
| EXPECT_EQ(std::u16string(), |
| image_succeeded_with_name->GetSupplementalDescription()); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TextStyling_Suggestions) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101}; |
| last_node->role = ax::mojom::Role::kTextField; |
| last_node->SetValue(u"Some very wrrrongly spelled words"); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->child_ids = {201, 202, 203}; |
| last_node->role = ax::mojom::Role::kGenericContainer; |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 201; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("Some "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 202; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("very wrrrongly spelled"); |
| last_node->AddIntListAttribute( |
| ax::mojom::IntListAttribute::kMarkerTypes, |
| {static_cast<int>(ax::mojom::MarkerType::kSuggestion)}); |
| last_node->AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerStarts, |
| {5}); |
| last_node->AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerEnds, |
| {14}); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 203; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" words"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"Some very wrrrongly spelled words")); |
| ASSERT_TRUE(style_data.suggestions); |
| EXPECT_THAT(*style_data.suggestions, |
| UnorderedElementsAre(Pair(u"", RangePairs{{10, 19}}))); |
| } |
| |
| // TODO: aluh - Enable once link nodes are merged into text content. |
| TEST_F(BrowserAccessibilityAndroidTest, DISABLED_TextStyling_Links) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101, 102, 103}; |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("A "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 102; |
| last_node->child_ids = {201}; |
| last_node->role = ax::mojom::Role::kLink; |
| last_node->AddStringAttribute(ax::mojom::StringAttribute::kUrl, |
| "https://www.example.com/"); |
| last_node->SetName("simple"); |
| last_node->SetNameFrom(ax::mojom::NameFrom::kContents); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 201; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("simple"); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 103; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" link"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"A simple link")); |
| ASSERT_TRUE(style_data.links); |
| EXPECT_THAT(*style_data.links, |
| UnorderedElementsAre( |
| Pair(u"https://www.example.com/", RangePairs{{2, 8}}))); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TextStyling_NestedStyle) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101, 102, 103}; |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("Some "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 102; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("bold"); |
| last_node->AddTextStyle(ax::mojom::TextStyle::kBold); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 103; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" text"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"Some bold text")); |
| ASSERT_TRUE(style_data.text_styles); |
| EXPECT_THAT(*style_data.text_styles, |
| UnorderedElementsAre( |
| Pair(ax::mojom::TextStyle::kBold, RangePairs{{5, 9}}))); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TextStyling_MixedStyles) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101, 102, 103, 104, 105}; |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("Some "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 102; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("bold "); |
| last_node->AddTextStyle(ax::mojom::TextStyle::kBold); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 103; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("and"); |
| last_node->AddTextStyle(ax::mojom::TextStyle::kBold); |
| last_node->AddTextStyle(ax::mojom::TextStyle::kItalic); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 104; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" italic"); |
| last_node->AddTextStyle(ax::mojom::TextStyle::kItalic); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 105; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" text"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"Some bold and italic text")); |
| ASSERT_TRUE(style_data.text_styles); |
| EXPECT_THAT( |
| *style_data.text_styles, |
| UnorderedElementsAre( |
| Pair(ax::mojom::TextStyle::kBold, RangePairs{{5, 10}, {10, 13}}), |
| Pair(ax::mojom::TextStyle::kItalic, RangePairs{{10, 13}, {13, 20}}))); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TextStyling_TextSizes) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101, 102, 103, 104, 105}; |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("Some "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 102; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("big"); |
| last_node->AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 24.0f); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 103; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" and"); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 104; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" invisible"); |
| last_node->AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 0.0f); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 105; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" text"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"Some big and invisible text")); |
| ASSERT_TRUE(style_data.text_sizes); |
| EXPECT_THAT(*style_data.text_sizes, |
| UnorderedElementsAre(Pair(24.0f, RangePairs{{5, 8}}), |
| Pair(0.0f, RangePairs{{12, 22}}))); |
| } |
| |
| // TODO: aluh - Enable once super/subscript nodes are merged into text content. |
| TEST_F(BrowserAccessibilityAndroidTest, DISABLED_TextStyling_TextPositions) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101, 102, 104}; |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("Some "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 102; |
| last_node->role = ax::mojom::Role::kSuperscript; |
| last_node->SetTextPosition(ax::mojom::TextPosition::kSuperscript); |
| last_node->child_ids = {103}; |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 103; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("superscript"); |
| last_node->SetTextPosition(ax::mojom::TextPosition::kSuperscript); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 104; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" text"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"Some superscript text")); |
| ASSERT_TRUE(style_data.text_positions); |
| EXPECT_THAT(*style_data.text_positions, |
| UnorderedElementsAre(Pair(ax::mojom::TextPosition::kSuperscript, |
| RangePairs{{5, 16}}))); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TextStyling_ForegroundColors) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101, 102, 103}; |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("Some "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 102; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("red"); |
| last_node->AddIntAttribute(ax::mojom::IntAttribute::kColor, 0xFFFF0000); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 103; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" text"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"Some red text")); |
| ASSERT_TRUE(style_data.foreground_colors); |
| EXPECT_THAT( |
| *style_data.foreground_colors, |
| UnorderedElementsAre(Pair(0x00000000, RangePairs{{0, 5}, {8, 13}}), |
| Pair(0xFFFF0000, RangePairs{{5, 8}}))); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TextStyling_BackgroundColors) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101, 102, 103}; |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("Some "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 102; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("highlighted"); |
| last_node->AddIntAttribute(ax::mojom::IntAttribute::kBackgroundColor, |
| 0xFF00FF00); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 103; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" text"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"Some highlighted text")); |
| ASSERT_TRUE(style_data.background_colors); |
| EXPECT_THAT( |
| *style_data.background_colors, |
| UnorderedElementsAre(Pair(0x00000000, RangePairs{{0, 5}, {16, 21}}), |
| Pair(0xFF00FF00, RangePairs{{5, 16}}))); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TextStyling_BlendedColors) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101, 102, 103}; |
| last_node->role = ax::mojom::Role::kGenericContainer; |
| last_node->AddIntAttribute(ax::mojom::IntAttribute::kColor, 0xFFFF0000); |
| last_node->AddIntAttribute(ax::mojom::IntAttribute::kBackgroundColor, |
| 0xFFFFFF00); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("Some "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 102; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("blended color"); |
| last_node->AddIntAttribute(ax::mojom::IntAttribute::kColor, 0x55007788); |
| last_node->AddIntAttribute(ax::mojom::IntAttribute::kBackgroundColor, |
| 0x8800FFFF); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 103; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" text"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"Some blended color text")); |
| ASSERT_TRUE(style_data.foreground_colors); |
| EXPECT_THAT( |
| *style_data.foreground_colors, |
| UnorderedElementsAre(Pair(0xFFFF0000, RangePairs{{0, 5}, {18, 23}}), |
| Pair(0xFFAA282D, RangePairs{{5, 18}}))); |
| ASSERT_TRUE(style_data.background_colors); |
| EXPECT_THAT( |
| *style_data.background_colors, |
| UnorderedElementsAre(Pair(0xFFFFFF00, RangePairs{{0, 5}, {18, 23}}), |
| Pair(0xFF77FF88, RangePairs{{5, 18}}))); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TextStyling_FontFamilies) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101, 102, 103}; |
| last_node->AddStringAttribute(ax::mojom::StringAttribute::kFontFamily, |
| "serif"); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("Some "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 102; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("sans serif"); |
| last_node->AddStringAttribute(ax::mojom::StringAttribute::kFontFamily, |
| "sans-serif"); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 103; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" text"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"Some sans serif text")); |
| ASSERT_TRUE(style_data.font_families); |
| EXPECT_THAT(*style_data.font_families, |
| UnorderedElementsAre(Pair("serif", RangePairs{{0, 5}, {15, 20}}), |
| Pair("sans-serif", RangePairs{{5, 15}}))); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TextStyling_Locales) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101, 102, 103}; |
| last_node->AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "en-US"); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("Some "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 102; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("繁體中文"); |
| last_node->AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "zh-TW"); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 103; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" text"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"Some 繁體中文 text")); |
| ASSERT_TRUE(style_data.locales); |
| EXPECT_THAT(*style_data.locales, |
| UnorderedElementsAre(Pair("en-US", RangePairs{{0, 5}, {9, 14}}), |
| Pair("zh-TW", RangePairs{{5, 9}}))); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TextStyling_ManyAttributes) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101, 102, 103}; |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("Some "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 102; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("fancy"); |
| last_node->AddTextStyle(ax::mojom::TextStyle::kBold); |
| last_node->AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 32.0f); |
| last_node->AddIntAttribute(ax::mojom::IntAttribute::kColor, 0xFFFF0000); |
| last_node->AddIntAttribute(ax::mojom::IntAttribute::kBackgroundColor, |
| 0xFF0000FF); |
| last_node->AddStringAttribute(ax::mojom::StringAttribute::kFontFamily, |
| "serif"); |
| last_node->AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "ja-JP"); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 103; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" text"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"Some fancy text")); |
| ASSERT_TRUE(style_data.text_styles); |
| EXPECT_THAT(*style_data.text_styles, |
| UnorderedElementsAre( |
| Pair(ax::mojom::TextStyle::kBold, RangePairs{{5, 10}}))); |
| ASSERT_TRUE(style_data.text_sizes); |
| EXPECT_THAT(*style_data.text_sizes, |
| UnorderedElementsAre(Pair(32.0f, RangePairs{{5, 10}}))); |
| ASSERT_TRUE(style_data.foreground_colors); |
| EXPECT_THAT( |
| *style_data.foreground_colors, |
| UnorderedElementsAre(Pair(0x00000000, RangePairs{{0, 5}, {10, 15}}), |
| Pair(0xFFFF0000, RangePairs{{5, 10}}))); |
| ASSERT_TRUE(style_data.background_colors); |
| EXPECT_THAT( |
| *style_data.background_colors, |
| UnorderedElementsAre(Pair(0x00000000, RangePairs{{0, 5}, {10, 15}}), |
| Pair(0xFF0000FF, RangePairs{{5, 10}}))); |
| ASSERT_TRUE(style_data.font_families); |
| EXPECT_THAT(*style_data.font_families, |
| UnorderedElementsAre(Pair("serif", RangePairs{{5, 10}}))); |
| ASSERT_TRUE(style_data.locales); |
| EXPECT_THAT(*style_data.locales, |
| UnorderedElementsAre(Pair("ja-JP", RangePairs{{5, 10}}))); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TextStyling_IgnoreInvalidValues) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101, 102, 103}; |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("Some "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 102; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("normal"); |
| last_node->AddIntAttribute(ax::mojom::IntAttribute::kTextStyle, 0); |
| last_node->AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, -1.0); |
| last_node->AddStringAttribute(ax::mojom::StringAttribute::kFontFamily, ""); |
| last_node->AddStringAttribute(ax::mojom::StringAttribute::kLanguage, ""); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 103; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" text"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"Some normal text")); |
| EXPECT_FALSE(style_data.text_styles); |
| EXPECT_FALSE(style_data.text_sizes); |
| EXPECT_FALSE(style_data.font_families); |
| EXPECT_FALSE(style_data.locales); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TextStyling_EmptyStyledText) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = ROOT_ID; |
| |
| ui::AXNodeData* last_node = &tree.nodes.emplace_back(); |
| last_node->id = ROOT_ID; |
| last_node->child_ids = {101, 102, 103}; |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 101; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName("Some "); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 102; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(""); |
| last_node->AddTextStyle(ax::mojom::TextStyle::kBold); |
| |
| last_node = &tree.nodes.emplace_back(); |
| last_node->id = 103; |
| last_node->role = ax::mojom::Role::kStaticText; |
| last_node->SetName(" text"); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| AXStyleData style_data; |
| BrowserAccessibilityAndroid* container = |
| static_cast<BrowserAccessibilityAndroid*>(manager->GetFromID(ROOT_ID)); |
| EXPECT_THAT( |
| container->GetSubstringTextContentUTF16(std::nullopt, &style_data), |
| Eq(u"Some text")); |
| ASSERT_TRUE(style_data.text_styles); |
| EXPECT_THAT(*style_data.text_styles, |
| UnorderedElementsAre( |
| Pair(ax::mojom::TextStyle::kBold, RangePairs{{5, 5}}))); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TestJavaNodeCache_AttributeChange) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = 1; |
| tree.nodes.resize(2); |
| tree.nodes[0].id = 1; |
| tree.nodes[0].role = ax::mojom::Role::kRootWebArea; |
| tree.nodes[0].child_ids = {2}; |
| |
| tree.nodes[1].id = 2; |
| tree.nodes[1].role = ax::mojom::Role::kButton; |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| BrowserAccessibilityManagerAndroid* android_manager = |
| ToBrowserAccessibilityManagerAndroid(manager.get()); |
| const auto& actual = android_manager->nodes_already_cleared_for_test(); |
| EXPECT_EQ(2, actual.size()); |
| EXPECT_TRUE(actual.contains(1)); |
| EXPECT_TRUE(actual.contains(2)); |
| |
| ui::AXUpdatesAndEvents updates_and_events; |
| updates_and_events.updates.resize(1); |
| updates_and_events.updates[0].nodes.resize(1); |
| updates_and_events.updates[0].nodes[0].id = 2; |
| updates_and_events.updates[0].nodes[0].AddStringAttribute( |
| ax::mojom::StringAttribute::kName, "hello"); |
| |
| manager->OnAccessibilityEvents(updates_and_events); |
| |
| EXPECT_EQ(2, actual.size()); |
| EXPECT_TRUE(actual.contains(1)); |
| EXPECT_TRUE(actual.contains(2)); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TestJavaNodeCache_NodeDeleted) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = 1; |
| tree.nodes.resize(2); |
| tree.nodes[0].id = 1; |
| tree.nodes[0].role = ax::mojom::Role::kRootWebArea; |
| tree.nodes[0].child_ids = {2}; |
| |
| tree.nodes[1].id = 2; |
| tree.nodes[1].role = ax::mojom::Role::kButton; |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| BrowserAccessibilityManagerAndroid* android_manager = |
| ToBrowserAccessibilityManagerAndroid(manager.get()); |
| const auto& actual = android_manager->nodes_already_cleared_for_test(); |
| EXPECT_EQ(2, actual.size()); |
| EXPECT_TRUE(actual.contains(1)); |
| EXPECT_TRUE(actual.contains(2)); |
| |
| ui::AXUpdatesAndEvents updates_and_events; |
| updates_and_events.updates.resize(1); |
| updates_and_events.updates[0].nodes.resize(1); |
| updates_and_events.updates[0].nodes[0].id = 1; |
| updates_and_events.updates[0].nodes[0].role = ax::mojom::Role::kRootWebArea; |
| |
| manager->OnAccessibilityEvents(updates_and_events); |
| |
| EXPECT_EQ(2, actual.size()); |
| EXPECT_TRUE(actual.contains(1)); |
| EXPECT_TRUE(actual.contains(2)); |
| } |
| |
| TEST_F(BrowserAccessibilityAndroidTest, TestJavaNodeCache_NodeUnignored) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = 1; |
| tree.nodes.resize(3); |
| tree.nodes[0].id = 1; |
| tree.nodes[0].role = ax::mojom::Role::kRootWebArea; |
| tree.nodes[0].child_ids = {2}; |
| |
| tree.nodes[1].id = 2; |
| tree.nodes[1].role = ax::mojom::Role::kButton; |
| tree.nodes[1].AddState(ax::mojom::State::kIgnored); |
| tree.nodes[1].child_ids = {3}; |
| |
| tree.nodes[2].id = 3; |
| tree.nodes[2].role = ax::mojom::Role::kStaticText; |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| BrowserAccessibilityManagerAndroid::Create( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| BrowserAccessibilityManagerAndroid* android_manager = |
| ToBrowserAccessibilityManagerAndroid(manager.get()); |
| const auto& actual = android_manager->nodes_already_cleared_for_test(); |
| EXPECT_EQ(3, actual.size()); |
| EXPECT_TRUE(actual.contains(1)); |
| EXPECT_TRUE(actual.contains(2)); |
| EXPECT_TRUE(actual.contains(3)); |
| |
| ui::AXUpdatesAndEvents updates_and_events; |
| updates_and_events.updates.resize(1); |
| updates_and_events.updates[0].nodes.resize(1); |
| updates_and_events.updates[0].nodes[0].id = 2; |
| updates_and_events.updates[0].nodes[0].role = ax::mojom::Role::kButton; |
| |
| manager->OnAccessibilityEvents(updates_and_events); |
| |
| EXPECT_EQ(3, actual.size()); |
| // From an AXEventGenerator::Event::CHILDREN_CHANGED. |
| EXPECT_TRUE(actual.contains(1)); |
| // From an AXTreeObserver::Change; the only actual tree update. |
| EXPECT_TRUE(actual.contains(2)); |
| // From an AXEventGenerator::Event::PARENT_CHANGED. |
| EXPECT_TRUE(actual.contains(3)); |
| } |
| |
| } // namespace content |