diff options
Diffstat (limited to 'chromium/chrome/browser/accessibility')
11 files changed, 782 insertions, 148 deletions
diff --git a/chromium/chrome/browser/accessibility/accessibility_extension_api.cc b/chromium/chrome/browser/accessibility/accessibility_extension_api.cc index 528d180500f..6782b42860e 100644 --- a/chromium/chrome/browser/accessibility/accessibility_extension_api.cc +++ b/chromium/chrome/browser/accessibility/accessibility_extension_api.cc @@ -58,11 +58,6 @@ namespace { const char kErrorNotSupported[] = "This API is not supported on this platform."; -#if defined(OS_CHROMEOS) -constexpr int kBackButtonWidth = 45; -constexpr int kBackButtonHeight = 45; -#endif - } // namespace ExtensionFunction::ResponseAction @@ -420,57 +415,6 @@ AccessibilityPrivateToggleDictationFunction::Run() { } ExtensionFunction::ResponseAction -AccessibilityPrivateSetSwitchAccessMenuStateFunction::Run() { - // TODO(anastasi): Remove this function once menu refactor is complete. - std::unique_ptr<accessibility_private::SetSwitchAccessMenuState::Params> - params = accessibility_private::SetSwitchAccessMenuState::Params::Create( - *args_); - EXTENSION_FUNCTION_VALIDATE(params); - - chromeos::AccessibilityManager* manager = - chromeos::AccessibilityManager::Get(); - - if (!params->show) { - manager->HideSwitchAccessMenu(); - return RespondNow(NoArguments()); - } - - accessibility_private::ScreenRect elem = std::move(params->element_bounds); - gfx::Rect element_bounds(elem.left, elem.top, elem.width, elem.height); - int item_count = params->item_count; - - // If we have an item count of 0, the panel is showing only the back button. - if (item_count == 0) { - manager->ShowSwitchAccessMenu(element_bounds, kBackButtonWidth, - kBackButtonHeight, - true /* back_button_only */); - return RespondNow(NoArguments()); - } - - int padding = 40; - int item_width = 88; - - int item_height; - if (::switches::IsExperimentalAccessibilitySwitchAccessTextEnabled()) { - item_height = 85; - } else { - item_height = 60; - } - // TODO(anastasi): This should be a preference that the user can change. - int max_cols = 3; - - // The number of rows is the number of items divided by the max columns, - // rounded down. - int rows = 1 + (item_count - 1) / max_cols; - int cols = rows == 1 ? item_count : max_cols; - int width = padding + (item_width * cols); - int height = padding + (item_height * rows); - - manager->ShowSwitchAccessMenu(element_bounds, width, height); - return RespondNow(NoArguments()); -} - -ExtensionFunction::ResponseAction AccessibilityPrivateForwardKeyEventsToSwitchAccessFunction::Run() { std::unique_ptr<accessibility_private::ForwardKeyEventsToSwitchAccess::Params> params = diff --git a/chromium/chrome/browser/accessibility/accessibility_extension_api.h b/chromium/chrome/browser/accessibility/accessibility_extension_api.h index 3601324682a..1ea0559c8df 100644 --- a/chromium/chrome/browser/accessibility/accessibility_extension_api.h +++ b/chromium/chrome/browser/accessibility/accessibility_extension_api.h @@ -109,8 +109,8 @@ class AccessibilityPrivateOnSelectToSpeakStateChangedFunction ACCESSIBILITY_PRIVATE_ONSELECTTOSPEAKSTATECHANGED) }; -// API function that is called when the Autoclick extension finds scrollable -// bounds. +// API function that is called when the Accessibility Common extension finds +// scrollable bounds. class AccessibilityPrivateOnScrollableBoundsForPointFoundFunction : public ExtensionFunction { ~AccessibilityPrivateOnScrollableBoundsForPointFoundFunction() override {} @@ -129,15 +129,6 @@ class AccessibilityPrivateToggleDictationFunction : public ExtensionFunction { ACCESSIBILITY_PRIVATE_TOGGLEDICTATION) }; -// API function that is called to show or hide the Switch Access menu. -class AccessibilityPrivateSetSwitchAccessMenuStateFunction - : public ExtensionFunction { - ~AccessibilityPrivateSetSwitchAccessMenuStateFunction() override {} - ResponseAction Run() override; - DECLARE_EXTENSION_FUNCTION("accessibilityPrivate.setSwitchAccessMenuState", - ACCESSIBILITY_PRIVATE_SETSWITCHACCESSMENUSTATE) -}; - // API function that requests that key events be forwarded to the Switch // Access extension. class AccessibilityPrivateForwardKeyEventsToSwitchAccessFunction diff --git a/chromium/chrome/browser/accessibility/accessibility_permission_context.cc b/chromium/chrome/browser/accessibility/accessibility_permission_context.cc index ce2689e3c5b..38fa011f4de 100644 --- a/chromium/chrome/browser/accessibility/accessibility_permission_context.cc +++ b/chromium/chrome/browser/accessibility/accessibility_permission_context.cc @@ -7,10 +7,9 @@ AccessibilityPermissionContext::AccessibilityPermissionContext( content::BrowserContext* browser_context) - : PermissionContextBase( - browser_context, - ContentSettingsType::ACCESSIBILITY_EVENTS, - blink::mojom::FeaturePolicyFeature::kAccessibilityEvents) {} + : PermissionContextBase(browser_context, + ContentSettingsType::ACCESSIBILITY_EVENTS, + blink::mojom::FeaturePolicyFeature::kNotFound) {} AccessibilityPermissionContext::~AccessibilityPermissionContext() = default; diff --git a/chromium/chrome/browser/accessibility/accessibility_ui.cc b/chromium/chrome/browser/accessibility/accessibility_ui.cc index c77a24e895d..012e421c6e7 100644 --- a/chromium/chrome/browser/accessibility/accessibility_ui.cc +++ b/chromium/chrome/browser/accessibility/accessibility_ui.cc @@ -13,6 +13,7 @@ #include "base/bind_helpers.h" #include "base/command_line.h" #include "base/json/json_writer.h" +#include "base/optional.h" #include "base/strings/pattern.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" @@ -80,6 +81,7 @@ static const char kInternal[] = "internal"; static const char kLabelImages[] = "labelImages"; static const char kNative[] = "native"; static const char kPage[] = "page"; +static const char kPDF[] = "pdf"; static const char kScreenReader[] = "screenreader"; static const char kShowOrRefreshTree[] = "showOrRefreshTree"; static const char kText[] = "text"; @@ -177,6 +179,7 @@ void HandleAccessibilityRequestCallback( bool text = mode.has_mode(ui::AXMode::kInlineTextBoxes); bool screenreader = mode.has_mode(ui::AXMode::kScreenReader); bool html = mode.has_mode(ui::AXMode::kHTML); + bool pdf = mode.has_mode(ui::AXMode::kPDF); // The "native" and "web" flags are disabled if // --disable-renderer-accessibility is set. @@ -203,6 +206,9 @@ void HandleAccessibilityRequestCallback( ? (label_images ? kOn : kOff) : kDisabled); + // The "pdf" flag is independent of the others. + data.SetString(kPDF, pdf ? kOn : kOff); + bool show_internal = pref->GetBoolean(prefs::kShowInternalAccessibilityTree); data.SetString(kInternal, show_internal ? kOn : kOff); @@ -367,7 +373,14 @@ void AccessibilityUIObserver::AccessibilityEventReceived( AccessibilityUIMessageHandler::AccessibilityUIMessageHandler() = default; -AccessibilityUIMessageHandler::~AccessibilityUIMessageHandler() = default; +AccessibilityUIMessageHandler::~AccessibilityUIMessageHandler() { + if (!observer_) + return; + content::WebContents* web_contents = observer_->web_contents(); + if (!web_contents) + return; + StopRecording(web_contents); +} void AccessibilityUIMessageHandler::RegisterMessages() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -641,6 +654,12 @@ void AccessibilityUIMessageHandler::Callback(const std::string& str) { event_logs_.push_back(str); } +void AccessibilityUIMessageHandler::StopRecording( + content::WebContents* web_contents) { + web_contents->RecordAccessibilityEvents(false, base::nullopt); + observer_.reset(nullptr); +} + void AccessibilityUIMessageHandler::RequestAccessibilityEvents( const base::ListValue* args) { const base::DictionaryValue* data; @@ -648,7 +667,7 @@ void AccessibilityUIMessageHandler::RequestAccessibilityEvents( int process_id = *data->FindIntPath(kProcessIdField); int routing_id = *data->FindIntPath(kRoutingIdField); - bool start = *data->FindBoolPath(kStartField); + bool start_recording = *data->FindBoolPath(kStartField); AllowJavascript(); @@ -661,22 +680,17 @@ void AccessibilityUIMessageHandler::RequestAccessibilityEvents( std::unique_ptr<base::DictionaryValue> result(BuildTargetDescriptor(rvh)); content::WebContents* web_contents = content::WebContents::FromRenderViewHost(rvh); - if (start) { + if (start_recording) { if (observer_) { return; } web_contents->RecordAccessibilityEvents( - base::BindRepeating(&AccessibilityUIMessageHandler::Callback, - base::Unretained(this)), - true); + true, base::BindRepeating(&AccessibilityUIMessageHandler::Callback, + base::Unretained(this))); observer_ = std::make_unique<AccessibilityUIObserver>(web_contents, &event_logs_); } else { - web_contents->RecordAccessibilityEvents( - base::BindRepeating(&AccessibilityUIMessageHandler::Callback, - base::Unretained(this)), - false); - observer_.release(); + StopRecording(web_contents); std::string event_logs_str; for (std::string log : event_logs_) { diff --git a/chromium/chrome/browser/accessibility/accessibility_ui.h b/chromium/chrome/browser/accessibility/accessibility_ui.h index e7ead6ef86f..906b24117cd 100644 --- a/chromium/chrome/browser/accessibility/accessibility_ui.h +++ b/chromium/chrome/browser/accessibility/accessibility_ui.h @@ -21,6 +21,7 @@ class ListValue; namespace content { struct AXEventNotificationDetails; +class WebContents; } // namespace content namespace user_prefs { @@ -68,6 +69,7 @@ class AccessibilityUIMessageHandler : public content::WebUIMessageHandler { void RequestNativeUITree(const base::ListValue* args); void RequestAccessibilityEvents(const base::ListValue* args); void Callback(const std::string&); + void StopRecording(content::WebContents* web_contents); DISALLOW_COPY_AND_ASSIGN(AccessibilityUIMessageHandler); }; diff --git a/chromium/chrome/browser/accessibility/caption_controller.cc b/chromium/chrome/browser/accessibility/caption_controller.cc index b18fa01155d..b84de71e2af 100644 --- a/chromium/chrome/browser/accessibility/caption_controller.cc +++ b/chromium/chrome/browser/accessibility/caption_controller.cc @@ -53,15 +53,6 @@ void CaptionController::RegisterProfilePrefs( registry->RegisterFilePathPref(prefs::kSODAPath, base::FilePath()); } -// static -void CaptionController::InitOffTheRecordPrefs(Profile* off_the_record_profile) { - DCHECK(off_the_record_profile->IsOffTheRecord()); - off_the_record_profile->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled, - false); - off_the_record_profile->GetPrefs()->SetFilePath(prefs::kSODAPath, - base::FilePath()); -} - void CaptionController::Init() { // Hidden behind a feature flag. if (!base::FeatureList::IsEnabled(media::kLiveCaption)) @@ -107,7 +98,8 @@ void CaptionController::UpdateSpeechRecognitionServiceEnabled() { base::BindOnce(&component_updater::SODAComponentInstallerPolicy:: UpdateSODAComponentOnDemand)); } else { - // TODO(evliu): Unregister SODA component. + // Do nothing. The SODA component will be uninstalled and removed from the + // device on the next start up. } } @@ -147,32 +139,42 @@ void CaptionController::UpdateAccessibilityCaptionHistograms() { } void CaptionController::OnBrowserAdded(Browser* browser) { - if (browser->profile() != profile_) + if (browser->profile() != profile_ && + browser->profile()->GetOriginalProfile() != profile_) { return; + } + DCHECK(!caption_bubble_controllers_.count(browser)); caption_bubble_controllers_[browser] = CaptionBubbleController::Create(browser); caption_bubble_controllers_[browser]->UpdateCaptionStyle(caption_style_); } void CaptionController::OnBrowserRemoved(Browser* browser) { - if (browser->profile() != profile_) + if (browser->profile() != profile_ && + browser->profile()->GetOriginalProfile() != profile_) { return; + } DCHECK(caption_bubble_controllers_.count(browser)); caption_bubble_controllers_.erase(browser); } -void CaptionController::DispatchTranscription( +bool CaptionController::DispatchTranscription( content::WebContents* web_contents, const chrome::mojom::TranscriptionResultPtr& transcription_result) { Browser* browser = chrome::FindBrowserWithWebContents(web_contents); - if (!browser) - return; - if (!caption_bubble_controllers_.count(browser)) - return; - caption_bubble_controllers_[browser]->OnTranscription(transcription_result, - web_contents); + if (!browser || !caption_bubble_controllers_.count(browser)) + return false; + return caption_bubble_controllers_[browser]->OnTranscription( + transcription_result, web_contents); +} + +CaptionBubbleController* +CaptionController::GetCaptionBubbleControllerForBrowser(Browser* browser) { + if (!browser || !caption_bubble_controllers_.count(browser)) + return nullptr; + return caption_bubble_controllers_[browser].get(); } void CaptionController::UpdateCaptionStyle() { diff --git a/chromium/chrome/browser/accessibility/caption_controller.h b/chromium/chrome/browser/accessibility/caption_controller.h index 78e3ee77cdf..274b46ab8bf 100644 --- a/chromium/chrome/browser/accessibility/caption_controller.h +++ b/chromium/chrome/browser/accessibility/caption_controller.h @@ -62,19 +62,21 @@ class CaptionController : public BrowserListObserver, public KeyedService { static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); - // Off the record profiles will default to having the feature disabled. - static void InitOffTheRecordPrefs(Profile* off_the_record_profile); - void Init(); // Routes a transcription to the CaptionBubbleController that belongs to the - // appropriate browser. - void DispatchTranscription( + // appropriate browser. Returns whether the transcription result was routed + // successfully. Transcriptions will halt if this returns false. + bool DispatchTranscription( content::WebContents* web_contents, const chrome::mojom::TranscriptionResultPtr& transcription_result); + CaptionBubbleController* GetCaptionBubbleControllerForBrowser( + Browser* browser); + private: friend class CaptionControllerFactory; + friend class CaptionControllerTest; // BrowserListObserver: void OnBrowserAdded(Browser* browser) override; diff --git a/chromium/chrome/browser/accessibility/caption_controller_browsertest.cc b/chromium/chrome/browser/accessibility/caption_controller_browsertest.cc new file mode 100644 index 00000000000..5b46895bc04 --- /dev/null +++ b/chromium/chrome/browser/accessibility/caption_controller_browsertest.cc @@ -0,0 +1,579 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/accessibility/caption_controller.h" + +#include "base/files/file_path.h" +#include "build/build_config.h" +#include "chrome/browser/accessibility/caption_controller_factory.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/lifetime/application_lifetime.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/profiles/profile_window.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/caption_bubble_controller.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/sync_preferences/pref_service_syncable.h" +#include "content/public/test/browser_test.h" +#include "media/base/media_switches.h" + +namespace captions { + +// Blocks until a new profile is created. +void UnblockOnProfileCreation(base::RunLoop* run_loop, + Profile* profile, + Profile::CreateStatus status) { + if (status == Profile::CREATE_STATUS_INITIALIZED) + run_loop->Quit(); +} + +Profile* CreateProfile() { + ProfileManager* profile_manager = g_browser_process->profile_manager(); + base::FilePath profile_path = + profile_manager->GenerateNextProfileDirectoryPath(); + base::RunLoop run_loop; + profile_manager->CreateProfileAsync( + profile_path, base::Bind(&UnblockOnProfileCreation, &run_loop), + base::string16(), std::string()); + run_loop.Run(); + return profile_manager->GetProfileByPath(profile_path); +} + +class CaptionControllerTest : public InProcessBrowserTest { + public: + CaptionControllerTest() = default; + ~CaptionControllerTest() override = default; + CaptionControllerTest(const CaptionControllerTest&) = delete; + CaptionControllerTest& operator=(const CaptionControllerTest&) = delete; + + // InProcessBrowserTest overrides: + void SetUp() override { + scoped_feature_list_.InitAndEnableFeature(media::kLiveCaption); + InProcessBrowserTest::SetUp(); + } + + void SetLiveCaptionEnabled(bool enabled) { + browser()->profile()->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled, + enabled); + } + + CaptionController* GetController() { + return GetControllerForBrowser(browser()); + } + + CaptionController* GetControllerForBrowser(Browser* browser) { + return GetControllerForProfile(browser->profile()); + } + + CaptionController* GetControllerForProfile(Profile* profile) { + return CaptionControllerFactory::GetForProfile(profile); + } + + CaptionBubbleController* GetBubbleController() { + return GetBubbleControllerForBrowser(browser()); + } + + CaptionBubbleController* GetBubbleControllerForBrowser(Browser* browser) { + return GetControllerForBrowser(browser) + ->GetCaptionBubbleControllerForBrowser(browser); + } + + bool DispatchTranscription(std::string text) { + return DispatchTranscriptionToBrowser(text, browser()); + } + + bool DispatchTranscriptionToBrowser(std::string text, Browser* browser) { + return DispatchTranscriptionToBrowserForProfile(text, browser, + browser->profile()); + } + + bool DispatchTranscriptionToBrowserForProfile(std::string text, + Browser* browser, + Profile* profile) { + return GetControllerForProfile(profile)->DispatchTranscription( + browser->tab_strip_model()->GetActiveWebContents(), + chrome::mojom::TranscriptionResult::New(text, true /* is_final */)); + } + + int NumBubbleControllers() { + return NumBubbleControllersForProfile(browser()->profile()); + } + + int NumBubbleControllersForProfile(Profile* profile) { + return GetControllerForProfile(profile)->caption_bubble_controllers_.size(); + } + + bool IsWidgetVisible() { return IsWidgetVisibleOnBrowser(browser()); } + + bool IsWidgetVisibleOnBrowser(Browser* browser) { + return GetBubbleControllerForBrowser(browser)->IsWidgetVisibleForTesting(); + } + + std::string GetBubbleLabelText() { + return GetBubbleLabelTextOnBrowser(browser()); + } + + std::string GetBubbleLabelTextOnBrowser(Browser* browser) { + return GetBubbleControllerForBrowser(browser) + ->GetBubbleLabelTextForTesting(); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, ProfilePrefsAreRegistered) { + EXPECT_FALSE( + browser()->profile()->GetPrefs()->GetBoolean(prefs::kLiveCaptionEnabled)); + EXPECT_EQ(base::FilePath(), + browser()->profile()->GetPrefs()->GetFilePath(prefs::kSODAPath)); +} + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, + ProfilePrefsAreRegistered_Incognito) { + // Set live caption enabled on the regular profile. + SetLiveCaptionEnabled(true); + EXPECT_TRUE( + browser()->profile()->GetPrefs()->GetBoolean(prefs::kLiveCaptionEnabled)); + EXPECT_EQ(base::FilePath(), + browser()->profile()->GetPrefs()->GetFilePath(prefs::kSODAPath)); + + // Ensure that live caption is also enabled in the incognito profile. + Profile* incognito_profile = browser()->profile()->GetPrimaryOTRProfile(); + EXPECT_TRUE( + incognito_profile->GetPrefs()->GetBoolean(prefs::kLiveCaptionEnabled)); + EXPECT_EQ(base::FilePath(), + browser()->profile()->GetPrefs()->GetFilePath(prefs::kSODAPath)); +} + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, LiveCaptionEnabledChanged) { + EXPECT_EQ(nullptr, GetBubbleController()); + EXPECT_EQ(0, NumBubbleControllers()); + + SetLiveCaptionEnabled(true); + EXPECT_NE(nullptr, GetBubbleController()); + EXPECT_EQ(1, NumBubbleControllers()); + + SetLiveCaptionEnabled(false); + EXPECT_EQ(nullptr, GetBubbleController()); + EXPECT_EQ(0, NumBubbleControllers()); +} + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, + LiveCaptionEnabledChanged_BubbleVisible) { + SetLiveCaptionEnabled(true); + // Make the bubble visible by dispatching a transcription. + DispatchTranscription( + "In Switzerland it is illegal to own just one guinea pig."); +// The CaptionBubbleController is currently only implemented in Views. +#if defined(TOOLKIT_VIEWS) + EXPECT_TRUE(IsWidgetVisible()); +#else + EXPECT_FALSE(IsWidgetVisible()); +#endif + + SetLiveCaptionEnabled(false); + EXPECT_EQ(nullptr, GetBubbleController()); + EXPECT_EQ(0, NumBubbleControllers()); +} + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, OnBrowserAdded) { + EXPECT_EQ(0, NumBubbleControllers()); + + // Add a new browser and then enable live caption. Test that a caption bubble + // controller is created. + CreateBrowser(browser()->profile()); + SetLiveCaptionEnabled(true); + EXPECT_EQ(2, NumBubbleControllers()); + + // Add a new browser and test that a caption bubble controller is created. + CreateBrowser(browser()->profile()); + EXPECT_EQ(3, NumBubbleControllers()); + + // Disable live caption. Add a new browser and test that a caption bubble + // controller is not created. + SetLiveCaptionEnabled(false); + CreateBrowser(browser()->profile()); + EXPECT_EQ(0, NumBubbleControllers()); +} + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, OnBrowserAdded_Incognito) { + EXPECT_EQ(0, NumBubbleControllers()); + + // Add a new incognito browser and then enable live caption. Test that a + // caption bubble controller is created in each browser (incognito and + // regular). + CreateIncognitoBrowser(); + SetLiveCaptionEnabled(true); + EXPECT_EQ(2, NumBubbleControllers()); + + // Add a new incognito browser and test that a caption bubble controller is + // created. + CreateIncognitoBrowser(); + EXPECT_EQ(3, NumBubbleControllers()); + + // Disable live caption. Add a new incognito browser and test that a caption + // bubble controller is not created. + SetLiveCaptionEnabled(false); + CreateIncognitoBrowser(); + EXPECT_EQ(0, NumBubbleControllers()); +} + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, OnBrowserRemoved) { + CaptionController* controller = GetController(); + Browser* browser1 = browser(); + // Add 3 browsers. + Browser* browser2 = CreateBrowser(browser()->profile()); + Browser* browser3 = CreateBrowser(browser()->profile()); + Browser* browser4 = CreateBrowser(browser()->profile()); + + SetLiveCaptionEnabled(true); + EXPECT_EQ(4, NumBubbleControllers()); + + // Close browser4 and test that the caption bubble controller was destroyed. + browser4->window()->Close(); + ui_test_utils::WaitForBrowserToClose(); + EXPECT_EQ(nullptr, + controller->GetCaptionBubbleControllerForBrowser(browser4)); + EXPECT_EQ(3, NumBubbleControllers()); + + // Make the bubble on browser3 visible by dispatching a transcription. + DispatchTranscriptionToBrowser( + "If you lift a kangaroo's tail off the ground it can't hop.", browser3); +// The CaptionBubbleController is currently only implemented in Views. +#if defined(TOOLKIT_VIEWS) + EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser3)); +#else + EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser3)); +#endif + browser3->window()->Close(); + ui_test_utils::WaitForBrowserToClose(); + EXPECT_EQ(nullptr, + controller->GetCaptionBubbleControllerForBrowser(browser3)); + EXPECT_EQ(2, NumBubbleControllers()); + + // Make the bubble on browser2 visible by dispatching a transcription. + DispatchTranscriptionToBrowser( + "A lion's roar can be heard from 5 miles away.", browser2); +// The CaptionBubbleController is currently only implemented in Views. +#if defined(TOOLKIT_VIEWS) + EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser2)); +#else + EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser2)); +#endif + + // Close all browsers and verify that the caption bubbles are destroyed on + // the two remaining browsers. + chrome::CloseAllBrowsers(); + ui_test_utils::WaitForBrowserToClose(); + ui_test_utils::WaitForBrowserToClose(); + EXPECT_EQ(nullptr, + controller->GetCaptionBubbleControllerForBrowser(browser2)); + EXPECT_EQ(nullptr, + controller->GetCaptionBubbleControllerForBrowser(browser1)); +} + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, OnBrowserRemoved_Incognito) { + CaptionController* controller = GetController(); + Browser* incognito_browser1 = CreateIncognitoBrowser(); + Browser* incognito_browser2 = CreateIncognitoBrowser(); + + SetLiveCaptionEnabled(true); + // There is 1 regular browser and 2 incognito browsers. + EXPECT_EQ(3, NumBubbleControllers()); + + // Close incognito_browser2 and test that the caption bubble controller was + // destroyed. + incognito_browser2->window()->Close(); + ui_test_utils::WaitForBrowserToClose(); + EXPECT_EQ(nullptr, controller->GetCaptionBubbleControllerForBrowser( + incognito_browser2)); + EXPECT_EQ(2, NumBubbleControllers()); + + // Make the bubble on incognito_browser1 visible by dispatching a + // transcription. + DispatchTranscriptionToBrowser( + "If you lift a kangaroo's tail off the ground it can't hop.", + incognito_browser1); +// The CaptionBubbleController is currently only implemented in Views. +#if defined(TOOLKIT_VIEWS) + EXPECT_TRUE(IsWidgetVisibleOnBrowser(incognito_browser1)); +#else + EXPECT_FALSE(IsWidgetVisibleOnBrowser(incognito_browser1)); +#endif + incognito_browser1->window()->Close(); + ui_test_utils::WaitForBrowserToClose(); + EXPECT_EQ(nullptr, controller->GetCaptionBubbleControllerForBrowser( + incognito_browser1)); + EXPECT_EQ(1, NumBubbleControllers()); +} + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, DispatchTranscription) { + bool success = DispatchTranscription("A baby spider is called a spiderling."); + EXPECT_FALSE(success); + EXPECT_EQ(0, NumBubbleControllers()); + + SetLiveCaptionEnabled(true); + success = DispatchTranscription( + "A baby octopus is about the size of a flea when it is born."); + EXPECT_TRUE(success); +// The CaptionBubbleController is currently only implemented in Views. +#if defined(TOOLKIT_VIEWS) + EXPECT_TRUE(IsWidgetVisible()); + EXPECT_EQ("A baby octopus is about the size of a flea when it is born.", + GetBubbleLabelText()); +#else + EXPECT_FALSE(IsWidgetVisible()); +#endif + + SetLiveCaptionEnabled(false); + success = DispatchTranscription( + "Approximately 10-20% of power outages in the US are caused by " + "squirrels."); + EXPECT_FALSE(success); + EXPECT_EQ(0, NumBubbleControllers()); +} + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, + DispatchTranscription_MultipleBrowsers) { + Browser* browser1 = browser(); + Browser* browser2 = CreateBrowser(browser()->profile()); + Browser* incognito_browser = CreateIncognitoBrowser(); + SetLiveCaptionEnabled(true); + + // Dispatch transcription routes the transcription to the right browser. + bool success = DispatchTranscriptionToBrowser( + "Honeybees can recognize human faces.", browser1); + EXPECT_TRUE(success); +// The CaptionBubbleController is currently only implemented in Views. +#if defined(TOOLKIT_VIEWS) + EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1)); + EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser2)); + EXPECT_FALSE(IsWidgetVisibleOnBrowser(incognito_browser)); + EXPECT_EQ("Honeybees can recognize human faces.", + GetBubbleLabelTextOnBrowser(browser1)); + EXPECT_EQ("", GetBubbleLabelTextOnBrowser(browser2)); + EXPECT_EQ("", GetBubbleLabelTextOnBrowser(incognito_browser)); +#else + EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser1)); +#endif + + success = DispatchTranscriptionToBrowser( + "A blue whale's heart is the size of a small car.", browser2); + EXPECT_TRUE(success); +// The CaptionBubbleController is currently only implemented in Views. +#if defined(TOOLKIT_VIEWS) + EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1)); + EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser2)); + EXPECT_FALSE(IsWidgetVisibleOnBrowser(incognito_browser)); + EXPECT_EQ("A blue whale's heart is the size of a small car.", + GetBubbleLabelTextOnBrowser(browser2)); + EXPECT_EQ("Honeybees can recognize human faces.", + GetBubbleLabelTextOnBrowser(browser1)); + EXPECT_EQ("", GetBubbleLabelTextOnBrowser(incognito_browser)); +#else + EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser2)); +#endif + + success = DispatchTranscriptionToBrowser( + "Squirrels forget where they hide about half of their nuts.", + incognito_browser); + EXPECT_TRUE(success); +// The CaptionBubbleController is currently only implemented in Views. +#if defined(TOOLKIT_VIEWS) + EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1)); + EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser2)); + EXPECT_TRUE(IsWidgetVisibleOnBrowser(incognito_browser)); + EXPECT_EQ("A blue whale's heart is the size of a small car.", + GetBubbleLabelTextOnBrowser(browser2)); + EXPECT_EQ("Honeybees can recognize human faces.", + GetBubbleLabelTextOnBrowser(browser1)); + EXPECT_EQ("Squirrels forget where they hide about half of their nuts.", + GetBubbleLabelTextOnBrowser(incognito_browser)); +#else + EXPECT_FALSE(IsWidgetVisibleOnBrowser(incognito_browser)); +#endif +} + +#if !defined(OS_CHROMEOS) // No multi-profile on ChromeOS. + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, + LiveCaptionEnabledChanged_MultipleProfiles) { + Profile* profile1 = browser()->profile(); + Profile* profile2 = CreateProfile(); + CreateBrowser(profile2); + + // The profiles start with no caption bubble controllers. + EXPECT_EQ(0, NumBubbleControllersForProfile(profile1)); + EXPECT_EQ(0, NumBubbleControllersForProfile(profile2)); + + // Enable live caption on profile1. + SetLiveCaptionEnabled(true); + EXPECT_EQ(1, NumBubbleControllersForProfile(profile1)); + EXPECT_EQ(0, NumBubbleControllersForProfile(profile2)); + + // Enable live caption on profile2. + profile2->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled, true); + EXPECT_EQ(1, NumBubbleControllersForProfile(profile1)); + EXPECT_EQ(1, NumBubbleControllersForProfile(profile2)); + + // Disable live caption on profile1. + SetLiveCaptionEnabled(false); + EXPECT_EQ(0, NumBubbleControllersForProfile(profile1)); + EXPECT_EQ(1, NumBubbleControllersForProfile(profile2)); + + // Disable live caption on profile2. + profile2->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled, false); + EXPECT_EQ(0, NumBubbleControllersForProfile(profile1)); + EXPECT_EQ(0, NumBubbleControllersForProfile(profile2)); +} + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, OnBrowserAdded_MultipleProfiles) { + Profile* profile1 = browser()->profile(); + Profile* profile2 = CreateProfile(); + + // Enable live caption on both profiles. + SetLiveCaptionEnabled(true); + profile2->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled, true); + + // Add a new browser to profile1. Test that there are caption bubble + // controllers on all of the existing browsers. + CreateBrowser(profile1); + EXPECT_EQ(2, NumBubbleControllersForProfile(profile1)); + EXPECT_EQ(0, NumBubbleControllersForProfile(profile2)); + + // Add a new browser to profile 2. Test that a caption bubble controller is + // created in profile2 and not in profile1. + Browser* profile2_browser2 = CreateBrowser(profile2); + EXPECT_NE( + nullptr, + GetControllerForProfile(profile2)->GetCaptionBubbleControllerForBrowser( + profile2_browser2)); + EXPECT_EQ( + nullptr, + GetControllerForProfile(profile1)->GetCaptionBubbleControllerForBrowser( + profile2_browser2)); + EXPECT_EQ(2, NumBubbleControllersForProfile(profile1)); + EXPECT_EQ(1, NumBubbleControllersForProfile(profile2)); + + // Disable live caption on profile1. Add a new browser to both profiles, and + // test that a caption bubble controller is only created on profile2. + SetLiveCaptionEnabled(false); + EXPECT_EQ(0, NumBubbleControllersForProfile(profile1)); + EXPECT_EQ(1, NumBubbleControllersForProfile(profile2)); + CreateBrowser(profile1); + CreateBrowser(profile2); + EXPECT_EQ(0, NumBubbleControllersForProfile(profile1)); + EXPECT_EQ(2, NumBubbleControllersForProfile(profile2)); +} + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, + OnBrowserRemoved_MultipleProfiles) { + Profile* profile1 = browser()->profile(); + Profile* profile2 = CreateProfile(); + Browser* browser1 = browser(); + Browser* browser2 = CreateBrowser(profile2); + CaptionController* controller1 = GetControllerForProfile(profile1); + CaptionController* controller2 = GetControllerForProfile(profile2); + + // Enable live caption on both profiles. + SetLiveCaptionEnabled(true); + profile2->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled, true); + EXPECT_EQ(1, NumBubbleControllersForProfile(profile1)); + EXPECT_EQ(1, NumBubbleControllersForProfile(profile2)); + + // Close browser2 and test that the caption bubble controller was destroyed. + browser2->window()->Close(); + ui_test_utils::WaitForBrowserToClose(); + EXPECT_EQ(nullptr, + controller2->GetCaptionBubbleControllerForBrowser(browser2)); + EXPECT_EQ(1, NumBubbleControllersForProfile(profile1)); + EXPECT_EQ(0, NumBubbleControllersForProfile(profile2)); + + // Make the bubble on incognito_browser1 visible by dispatching a + // transcription. + DispatchTranscriptionToBrowser( + "If you lift a kangaroo's tail off the ground it can't hop.", browser1); +// The CaptionBubbleController is currently only implemented in Views. +#if defined(TOOLKIT_VIEWS) + EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1)); +#else + EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser1)); +#endif + browser1->window()->Close(); + ui_test_utils::WaitForBrowserToClose(); + EXPECT_EQ(nullptr, + controller1->GetCaptionBubbleControllerForBrowser(browser1)); + EXPECT_EQ(0, NumBubbleControllersForProfile(profile1)); + EXPECT_EQ(0, NumBubbleControllersForProfile(profile2)); +} + +IN_PROC_BROWSER_TEST_F(CaptionControllerTest, + DispatchTranscription_MultipleProfiles) { + Profile* profile1 = browser()->profile(); + Profile* profile2 = CreateProfile(); + Browser* browser1 = browser(); + Browser* browser2 = CreateBrowser(profile2); + + // Enable live caption on both profiles. + SetLiveCaptionEnabled(true); + profile2->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled, true); + + // Dispatch transcription routes the transcription to the right browser on the + // right profile. + bool success = DispatchTranscriptionToBrowserForProfile( + "Only female mosquitos bite.", browser1, profile1); + EXPECT_TRUE(success); +// The CaptionBubbleController is currently only implemented in Views. +#if defined(TOOLKIT_VIEWS) + EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1)); + EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser2)); + EXPECT_EQ("Only female mosquitos bite.", + GetBubbleLabelTextOnBrowser(browser1)); + EXPECT_EQ("", GetBubbleLabelTextOnBrowser(browser2)); +#else + EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser1)); +#endif + + success = DispatchTranscriptionToBrowserForProfile( + "Mosquitos were around at the time of the dinosaurs.", browser2, + profile2); + EXPECT_TRUE(success); +// The CaptionBubbleController is currently only implemented in Views. +#if defined(TOOLKIT_VIEWS) + EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1)); + EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser2)); + EXPECT_EQ("Only female mosquitos bite.", + GetBubbleLabelTextOnBrowser(browser1)); + EXPECT_EQ("Mosquitos were around at the time of the dinosaurs.", + GetBubbleLabelTextOnBrowser(browser2)); +#else + EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser1)); +#endif + + // Dispatch transcription returns false for browsers on different profiles. + success = DispatchTranscriptionToBrowserForProfile( + "There are over 3000 species of mosquitos.", browser1, profile2); + EXPECT_FALSE(success); +// The CaptionBubbleController is currently only implemented in Views. +#if defined(TOOLKIT_VIEWS) + EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1)); + EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser2)); + EXPECT_EQ("Only female mosquitos bite.", + GetBubbleLabelTextOnBrowser(browser1)); + EXPECT_EQ("Mosquitos were around at the time of the dinosaurs.", + GetBubbleLabelTextOnBrowser(browser2)); +#else + EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser1)); + EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser2)); +#endif +} + +#endif // !defined (OS_CHROMEOS) + +} // namespace captions diff --git a/chromium/chrome/browser/accessibility/caption_host_impl.cc b/chromium/chrome/browser/accessibility/caption_host_impl.cc index f16d93d566a..deaa6ec6554 100644 --- a/chromium/chrome/browser/accessibility/caption_host_impl.cc +++ b/chromium/chrome/browser/accessibility/caption_host_impl.cc @@ -40,21 +40,28 @@ CaptionHostImpl::CaptionHostImpl(content::RenderFrameHost* frame_host) CaptionHostImpl::~CaptionHostImpl() = default; void CaptionHostImpl::OnTranscription( - chrome::mojom::TranscriptionResultPtr transcription_result) { - if (!frame_host_) + chrome::mojom::TranscriptionResultPtr transcription_result, + OnTranscriptionCallback reply) { + if (!frame_host_) { + std::move(reply).Run(false); return; + } content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(frame_host_); if (!web_contents) { frame_host_ = nullptr; + std::move(reply).Run(false); return; } Profile* profile = Profile::FromBrowserContext(web_contents->GetBrowserContext()); - if (!profile) + if (!profile) { + std::move(reply).Run(false); return; - CaptionControllerFactory::GetForProfile(profile)->DispatchTranscription( - web_contents, transcription_result); + } + std::move(reply).Run( + CaptionControllerFactory::GetForProfile(profile)->DispatchTranscription( + web_contents, transcription_result)); } void CaptionHostImpl::RenderFrameDeleted(content::RenderFrameHost* frame_host) { diff --git a/chromium/chrome/browser/accessibility/caption_host_impl.h b/chromium/chrome/browser/accessibility/caption_host_impl.h index 0445179620f..3e3615e6c2d 100644 --- a/chromium/chrome/browser/accessibility/caption_host_impl.h +++ b/chromium/chrome/browser/accessibility/caption_host_impl.h @@ -38,7 +38,8 @@ class CaptionHostImpl : public chrome::mojom::CaptionHost, // chrome::mojom::CaptionHost: void OnTranscription( - chrome::mojom::TranscriptionResultPtr transcription_result) override; + chrome::mojom::TranscriptionResultPtr transcription_result, + OnTranscriptionCallback reply) override; // content::WebContentsObserver: void RenderFrameDeleted(content::RenderFrameHost* frame_host) override; diff --git a/chromium/chrome/browser/accessibility/image_annotation_browsertest.cc b/chromium/chrome/browser/accessibility/image_annotation_browsertest.cc index e979f31f2e4..70fbc9c17e3 100644 --- a/chromium/chrome/browser/accessibility/image_annotation_browsertest.cc +++ b/chromium/chrome/browser/accessibility/image_annotation_browsertest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <map> + #include "base/bind_helpers.h" #include "base/check.h" #include "base/feature_list.h" @@ -34,13 +36,14 @@ #include "services/image_annotation/public/mojom/image_annotation.mojom.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/accessibility/accessibility_features.h" #include "ui/accessibility/ax_enum_util.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_tree.h" #include "url/gurl.h" constexpr base::FilePath::CharType kDocRoot[] = - FILE_PATH_LITERAL("chrome/test/data/accessibility"); + FILE_PATH_LITERAL("chrome/test/data"); namespace { @@ -49,8 +52,13 @@ void DescribeNodesWithAnnotations(const ui::AXNode& node, std::string annotation = node.GetStringAttribute(ax::mojom::StringAttribute::kImageAnnotation); if (!annotation.empty()) { - descriptions->push_back(ui::ToString(node.data().role) + std::string(" ") + - annotation); + std::string role_str = ui::ToString(node.data().role); + std::string name = + node.GetStringAttribute(ax::mojom::StringAttribute::kName); + if (!name.empty() && node.data().role != ax::mojom::Role::kRootWebArea) + descriptions->push_back(role_str + " " + name + " " + annotation); + else + descriptions->push_back(role_str + " " + annotation); } for (const auto* child : node.children()) DescribeNodesWithAnnotations(*child, descriptions); @@ -58,10 +66,11 @@ void DescribeNodesWithAnnotations(const ui::AXNode& node, std::vector<std::string> DescribeNodesWithAnnotations( const ui::AXTreeUpdate& tree_update) { - ui::AXTree tree(tree_update); std::vector<std::string> descriptions; - DCHECK(tree.root()); - DescribeNodesWithAnnotations(*tree.root(), &descriptions); + if (tree_update.root_id) { + ui::AXTree tree(tree_update); + DescribeNodesWithAnnotations(*tree.root(), &descriptions); + } return descriptions; } @@ -87,6 +96,11 @@ class FakeAnnotator : public image_annotation::mojom::Annotator { return_label_results_ = label; } + static void AddCustomLabelResultMapping(const std::string& filename, + const std::string& label) { + custom_label_result_mapping_[filename] = label; + } + static void SetReturnErrorCode( image_annotation::mojom::AnnotateImageError error_code) { return_error_code_ = error_code; @@ -114,19 +128,25 @@ class FakeAnnotator : public image_annotation::mojom::Annotator { return; } - // Use the filename to create an annotation string. - // Adds some trailing whitespace and punctuation to check that clean-up - // happens correctly when combining annotation strings. + // Use the filename to create annotation strings. Check a map from filename + // to desired label, otherwise just construct a string based on the + // filename. Adds some trailing whitespace and punctuation to check that + // clean-up happens correctly when combining annotation strings. std::string image_filename = GURL(image_id).ExtractFileName(); + std::string label_text; + if (base::Contains(custom_label_result_mapping_, image_filename)) { + label_text = custom_label_result_mapping_[image_filename]; + } else { + label_text = image_filename + " '" + description_language_tag + "' Label"; + } + std::string ocr_text = image_filename + " Annotation . "; + image_annotation::mojom::AnnotationPtr ocr_annotation = image_annotation::mojom::Annotation::New( - image_annotation::mojom::AnnotationType::kOcr, 1.0, - image_filename + " Annotation . "); - + image_annotation::mojom::AnnotationType::kOcr, 1.0, ocr_text); image_annotation::mojom::AnnotationPtr label_annotation = image_annotation::mojom::Annotation::New( - image_annotation::mojom::AnnotationType::kLabel, 1.0, - image_filename + " '" + description_language_tag + "' Label"); + image_annotation::mojom::AnnotationType::kLabel, 1.0, label_text); // Return enabled results as an annotation. std::vector<image_annotation::mojom::AnnotationPtr> annotations; @@ -145,6 +165,7 @@ class FakeAnnotator : public image_annotation::mojom::Annotator { mojo::ReceiverSet<image_annotation::mojom::Annotator> receivers_; static bool return_ocr_results_; static bool return_label_results_; + static std::map<std::string, std::string> custom_label_result_mapping_; static base::Optional<image_annotation::mojom::AnnotateImageError> return_error_code_; @@ -156,6 +177,8 @@ bool FakeAnnotator::return_ocr_results_ = false; // static bool FakeAnnotator::return_label_results_ = false; // static +std::map<std::string, std::string> FakeAnnotator::custom_label_result_mapping_; +// static base::Optional<image_annotation::mojom::AnnotateImageError> FakeAnnotator::return_error_code_; @@ -197,8 +220,10 @@ class ImageAnnotationBrowserTest : public InProcessBrowserTest { protected: void SetUp() override { - scoped_feature_list_.InitAndEnableFeature( - features::kExperimentalAccessibilityLabels); + scoped_feature_list_.InitWithFeatures( + std::vector<base::Feature>({features::kExperimentalAccessibilityLabels, + features::kAugmentExistingImageLabels}), + std::vector<base::Feature>({})); InProcessBrowserTest::SetUp(); } @@ -252,8 +277,8 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, AnnotateImageInAccessibilityTree) { FakeAnnotator::SetReturnOcrResults(true); FakeAnnotator::SetReturnLabelResults(true); - ui_test_utils::NavigateToURL(browser(), - https_server_.GetURL("/image_annotation.html")); + ui_test_utils::NavigateToURL( + browser(), https_server_.GetURL("/accessibility/image_annotation.html")); content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); @@ -266,7 +291,8 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, ImagesInLinks) { FakeAnnotator::SetReturnOcrResults(true); ui_test_utils::NavigateToURL( - browser(), https_server_.GetURL("/image_annotation_link.html")); + browser(), + https_server_.GetURL("/accessibility/image_annotation_link.html")); // Block until the accessibility tree has at least 8 annotations. If // that never happens, the test will time out. @@ -296,10 +322,71 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, ImagesInLinks) { "image Appears to say: green.png Annotation")); } +IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, AugmentImageNames) { + FakeAnnotator::SetReturnLabelResults(true); + FakeAnnotator::AddCustomLabelResultMapping("frog.jpg", "Tadpole"); + FakeAnnotator::AddCustomLabelResultMapping("train.png", "Locomotive"); + FakeAnnotator::AddCustomLabelResultMapping("cloud.png", "Cumulonimbus"); + FakeAnnotator::AddCustomLabelResultMapping("goat.jpg", "Billy goat"); + FakeAnnotator::AddCustomLabelResultMapping("dog.jpg", "Puppy"); + + ui_test_utils::NavigateToURL( + browser(), + https_server_.GetURL("/accessibility/image_annotation_augment.html")); + + // Block until the accessibility tree has at least 5 annotations. If + // that never happens, the test will time out. + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + while (5 > DescribeNodesWithAnnotations( + content::GetAccessibilityTreeSnapshot(web_contents)) + .size()) { + content::WaitForAccessibilityTreeToChange(web_contents); + } + + ui::AXTreeUpdate ax_tree_update = + content::GetAccessibilityTreeSnapshot(web_contents); + EXPECT_THAT(DescribeNodesWithAnnotations(ax_tree_update), + testing::ElementsAre( + "image the Appears to be: Tadpole", + "image photo background Appears to be: Locomotive", + "image 12345678.jpg Appears to be: Cumulonimbus", + "image Sunday, Feb 6, 1966 Appears to be: Billy goat", + "image fotografia bianca e nero Appears to be: Puppy")); +} + +IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, AugmentImageNamesInLinks) { + FakeAnnotator::SetReturnLabelResults(true); + FakeAnnotator::AddCustomLabelResultMapping("frog.jpg", "Tadpole"); + FakeAnnotator::AddCustomLabelResultMapping("train.png", "Locomotive"); + + ui_test_utils::NavigateToURL( + browser(), https_server_.GetURL( + "/accessibility/image_annotation_augment_links.html")); + + // Block until the accessibility tree has at least 3 annotations. If + // that never happens, the test will time out. + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + ui::AXTreeUpdate ax_tree_update = + content::GetAccessibilityTreeSnapshot(web_contents); + while (3 > DescribeNodesWithAnnotations(ax_tree_update).size()) { + content::WaitForAccessibilityTreeToChange(web_contents); + ax_tree_update = content::GetAccessibilityTreeSnapshot(web_contents); + } + + EXPECT_THAT( + DescribeNodesWithAnnotations(ax_tree_update), + testing::ElementsAre("link photo background Appears to be: Locomotive", + "image photo background Appears to be: Locomotive", + "image the Appears to be: Tadpole")); +} + IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, ImageDoc) { FakeAnnotator::SetReturnOcrResults(true); ui_test_utils::NavigateToURL( - browser(), https_server_.GetURL("/image_annotation_doc.html")); + browser(), + https_server_.GetURL("/accessibility/image_annotation_doc.html")); // Block until the accessibility tree has at least 2 annotations. If // that never happens, the test will time out. @@ -323,7 +410,8 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, ImageDoc) { IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, ImageUrl) { FakeAnnotator::SetReturnOcrResults(true); - ui_test_utils::NavigateToURL(browser(), https_server_.GetURL("/red.png")); + ui_test_utils::NavigateToURL(browser(), + https_server_.GetURL("/accessibility/red.png")); // Block until the accessibility tree has at least 2 annotations. If // that never happens, the test will time out. @@ -351,7 +439,8 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, NoAnnotationsAvailable) { FakeAnnotator::SetReturnLabelResults(false); ui_test_utils::NavigateToURL( - browser(), https_server_.GetURL("/image_annotation_doc.html")); + browser(), + https_server_.GetURL("/accessibility/image_annotation_doc.html")); // Block until the annotation status for the root is empty. If that // never occurs then the test will time out. @@ -359,8 +448,9 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, NoAnnotationsAvailable) { browser()->tab_strip_model()->GetActiveWebContents(); ui::AXTreeUpdate snapshot = content::GetAccessibilityTreeSnapshot(web_contents); - while (snapshot.nodes[0].GetImageAnnotationStatus() != - ax::mojom::ImageAnnotationStatus::kAnnotationEmpty) { + while (snapshot.nodes.empty() || + snapshot.nodes[0].GetImageAnnotationStatus() != + ax::mojom::ImageAnnotationStatus::kAnnotationEmpty) { content::WaitForAccessibilityTreeToChange(web_contents); snapshot = content::GetAccessibilityTreeSnapshot(web_contents); } @@ -372,7 +462,8 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, AnnotationError) { image_annotation::mojom::AnnotateImageError::kFailure); ui_test_utils::NavigateToURL( - browser(), https_server_.GetURL("/image_annotation_doc.html")); + browser(), + https_server_.GetURL("/accessibility/image_annotation_doc.html")); // Block until the annotation status for the root contains an error code. If // that never occurs then the test will time out. @@ -380,8 +471,9 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, AnnotationError) { browser()->tab_strip_model()->GetActiveWebContents(); ui::AXTreeUpdate snapshot = content::GetAccessibilityTreeSnapshot(web_contents); - while (snapshot.nodes[0].GetImageAnnotationStatus() != - ax::mojom::ImageAnnotationStatus::kAnnotationProcessFailed) { + while (snapshot.nodes.empty() || + snapshot.nodes[0].GetImageAnnotationStatus() != + ax::mojom::ImageAnnotationStatus::kAnnotationProcessFailed) { content::WaitForAccessibilityTreeToChange(web_contents); snapshot = content::GetAccessibilityTreeSnapshot(web_contents); } @@ -390,8 +482,8 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, AnnotationError) { IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, ImageWithSrcSet) { FakeAnnotator::SetReturnOcrResults(true); FakeAnnotator::SetReturnLabelResults(true); - ui_test_utils::NavigateToURL(browser(), - https_server_.GetURL("/image_srcset.html")); + ui_test_utils::NavigateToURL( + browser(), https_server_.GetURL("/accessibility/image_srcset.html")); content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); @@ -406,8 +498,8 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, FakeAnnotator::SetReturnOcrResults(true); FakeAnnotator::SetReturnLabelResults(true); - ui_test_utils::NavigateToURL(browser(), - https_server_.GetURL("/image_annotation.html")); + ui_test_utils::NavigateToURL( + browser(), https_server_.GetURL("/accessibility/image_annotation.html")); content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); content::WaitForAccessibilityTreeToContainNodeWithName( @@ -415,8 +507,8 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, "Appears to say: red.png Annotation. Appears to be: red.png 'en' Label"); SetAcceptLanguages("fr,en"); - ui_test_utils::NavigateToURL(browser(), - https_server_.GetURL("/image_annotation.html")); + ui_test_utils::NavigateToURL( + browser(), https_server_.GetURL("/accessibility/image_annotation.html")); web_contents = browser()->tab_strip_model()->GetActiveWebContents(); content::WaitForAccessibilityTreeToContainNodeWithName( web_contents, @@ -466,8 +558,8 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, FakeAnnotator::SetReturnLabelResults(false); // The following test page should have at least two images on it. - ui_test_utils::NavigateToURL(browser(), - https_server_.GetURL("/image_annotation.html")); + ui_test_utils::NavigateToURL( + browser(), https_server_.GetURL("/accessibility/image_annotation.html")); content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); @@ -504,7 +596,8 @@ IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, // The following test page should have at least two images on it. ui_test_utils::NavigateToURL( - browser(), https_server_.GetURL("/image_annotation_link.html")); + browser(), + https_server_.GetURL("/accessibility/image_annotation_link.html")); content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); |