Commit 09dc80fd authored by David Tseng's avatar David Tseng Committed by Commit Bot

Stop TtsUtterance if its associated WebContents is destroyed

In
https://chromium-review.googlesource.com/c/chromium/src/+/2261098

TtsUtteranceImpl started taking an optional WebContents in its
constructor.

When the WebContents is destroyed, the utterance is cleared and stopped
from triggering tts.

This change exposes this behavior up to c/p/b and cleans up
AccessibilityManager which approximated this behavior by calling
TtsController::Stop().

Some notable examples that benefit from this:
- extensions that trigger tts utterances, when destroyed, stop those
  utterances
- the tts settings webui, if closed, will stop tts utterances in
  progress

Change-Id: I7169b7ea3093e24e093c6c478040d943f34353f5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2541002
Commit-Queue: David Tseng <dtseng@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#828529}
parent 183f11ba
...@@ -3,7 +3,6 @@ specific_include_rules = { ...@@ -3,7 +3,6 @@ specific_include_rules = {
"+ash/root_window_controller.h", "+ash/root_window_controller.h",
"+ash/shell.h", "+ash/shell.h",
"+ash/sticky_keys/sticky_keys_controller.h", "+ash/sticky_keys/sticky_keys_controller.h",
"+content/public/browser/tts_controller.h",
], ],
"magnification_manager\.cc": [ "magnification_manager\.cc": [
"+ash/magnifier/magnification_controller.h", "+ash/magnifier/magnification_controller.h",
......
...@@ -71,7 +71,6 @@ ...@@ -71,7 +71,6 @@
#include "content/public/browser/notification_details.h" #include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h" #include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h" #include "content/public/browser/notification_source.h"
#include "content/public/browser/tts_controller.h"
#include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui.h"
#include "content/public/common/content_switches.h" #include "content/public/common/content_switches.h"
#include "extensions/browser/api/virtual_keyboard_private/virtual_keyboard_delegate.h" #include "extensions/browser/api/virtual_keyboard_private/virtual_keyboard_delegate.h"
...@@ -1399,9 +1398,6 @@ void AccessibilityManager::PostUnloadChromeVox() { ...@@ -1399,9 +1398,6 @@ void AccessibilityManager::PostUnloadChromeVox() {
// In case the user darkened the screen, undarken it now. // In case the user darkened the screen, undarken it now.
SetDarkenScreen(false); SetDarkenScreen(false);
// Stop speech.
content::TtsController::GetInstance()->Stop();
audio_focus_manager_->SetEnforcementMode( audio_focus_manager_->SetEnforcementMode(
media_session::mojom::EnforcementMode::kDefault); media_session::mojom::EnforcementMode::kDefault);
} }
...@@ -1434,9 +1430,6 @@ void AccessibilityManager::PostUnloadSelectToSpeak() { ...@@ -1434,9 +1430,6 @@ void AccessibilityManager::PostUnloadSelectToSpeak() {
// Clear the accessibility focus ring and highlight. // Clear the accessibility focus ring and highlight.
RemoveFocusRings(extension_misc::kSelectToSpeakExtensionId); RemoveFocusRings(extension_misc::kSelectToSpeakExtensionId);
HideHighlights(); HideHighlights();
// Stop speech.
content::TtsController::GetInstance()->Stop();
} }
void AccessibilityManager::PostLoadSwitchAccess() { void AccessibilityManager::PostLoadSwitchAccess() {
......
...@@ -35,7 +35,8 @@ int GetMetricsBucketIndex(const Profile* profile) { ...@@ -35,7 +35,8 @@ int GetMetricsBucketIndex(const Profile* profile) {
return 0; return 0;
ProfileAttributesEntry* entry; ProfileAttributesEntry* entry;
if (!g_browser_process->profile_manager() if (!g_browser_process->profile_manager() ||
!g_browser_process->profile_manager()
->GetProfileAttributesStorage() ->GetProfileAttributesStorage()
.GetProfileAttributesWithPath(profile->GetPath(), &entry)) { .GetProfileAttributesWithPath(profile->GetPath(), &entry)) {
// This can happen if the profile is deleted. // This can happen if the profile is deleted.
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
#include "content/public/browser/tts_controller.h" #include "content/public/browser/tts_controller.h"
#include "extensions/browser/event_router.h" #include "extensions/browser/event_router.h"
#include "extensions/browser/extension_function_registry.h" #include "extensions/browser/extension_function_registry.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/process_manager.h"
#include "third_party/blink/public/mojom/speech/speech_synthesis.mojom.h" #include "third_party/blink/public/mojom/speech/speech_synthesis.mojom.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
...@@ -270,8 +272,16 @@ ExtensionFunction::ResponseAction TtsSpeakFunction::Run() { ...@@ -270,8 +272,16 @@ ExtensionFunction::ResponseAction TtsSpeakFunction::Run() {
// the behavior more predictable and easier to write unit tests for too. // the behavior more predictable and easier to write unit tests for too.
Respond(NoArguments()); Respond(NoArguments());
std::unique_ptr<content::TtsUtterance> utterance = std::unique_ptr<content::TtsUtterance> utterance;
content::TtsUtterance::Create(browser_context()); if (extension()) {
extensions::ExtensionHost* host =
extensions::ProcessManager::Get(browser_context())
->GetBackgroundHostForExtension(extension()->id());
utterance = content::TtsUtterance::Create(host->host_contents());
} else {
utterance = content::TtsUtterance::Create(browser_context());
}
utterance->SetText(text); utterance->SetText(text);
utterance->SetVoiceName(voice_name); utterance->SetVoiceName(voice_name);
utterance->SetSrcId(src_id); utterance->SetSrcId(src_id);
......
...@@ -312,6 +312,10 @@ IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakOptionalArgs) { ...@@ -312,6 +312,10 @@ IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakOptionalArgs) {
.WillOnce(Return(true)); .WillOnce(Return(true));
EXPECT_CALL(mock_platform_impl_, DoSpeak(_, "Echo", _, _, _)) EXPECT_CALL(mock_platform_impl_, DoSpeak(_, "Echo", _, _, _))
.WillOnce(Return()); .WillOnce(Return());
// Called when the extension unloads.
EXPECT_CALL(mock_platform_impl_, StopSpeaking()).WillOnce(Return(false));
ASSERT_TRUE(RunExtensionTest("tts/optional_args")) << message_; ASSERT_TRUE(RunExtensionTest("tts/optional_args")) << message_;
} }
......
...@@ -40,7 +40,7 @@ TEST(TtsControllerDelegateImplTest, TestTtsControllerUtteranceDefaults) { ...@@ -40,7 +40,7 @@ TEST(TtsControllerDelegateImplTest, TestTtsControllerUtteranceDefaults) {
double volume = blink::mojom::kSpeechSynthesisDoublePrefNotSet; double volume = blink::mojom::kSpeechSynthesisDoublePrefNotSet;
std::unique_ptr<content::TtsUtterance> utterance1 = std::unique_ptr<content::TtsUtterance> utterance1 =
content::TtsUtterance::Create(nullptr); content::TtsUtterance::Create();
tts_controller_delegate->UpdateUtteranceDefaultsFromPrefs( tts_controller_delegate->UpdateUtteranceDefaultsFromPrefs(
utterance1.get(), &rate, &pitch, &volume); utterance1.get(), &rate, &pitch, &volume);
// Updated to global defaults. // Updated to global defaults.
...@@ -59,7 +59,7 @@ TEST(TtsControllerDelegateImplTest, TestTtsControllerUtteranceDefaults) { ...@@ -59,7 +59,7 @@ TEST(TtsControllerDelegateImplTest, TestTtsControllerUtteranceDefaults) {
tts_controller_delegate->pref_service_ = &pref_service_; tts_controller_delegate->pref_service_ = &pref_service_;
std::unique_ptr<content::TtsUtterance> utterance2 = std::unique_ptr<content::TtsUtterance> utterance2 =
content::TtsUtterance::Create(nullptr); content::TtsUtterance::Create();
tts_controller_delegate->UpdateUtteranceDefaultsFromPrefs( tts_controller_delegate->UpdateUtteranceDefaultsFromPrefs(
utterance2.get(), &rate, &pitch, &volume); utterance2.get(), &rate, &pitch, &volume);
// Updated to pref values. // Updated to pref values.
...@@ -73,7 +73,7 @@ TEST(TtsControllerDelegateImplTest, TestTtsControllerUtteranceDefaults) { ...@@ -73,7 +73,7 @@ TEST(TtsControllerDelegateImplTest, TestTtsControllerUtteranceDefaults) {
volume = 1.3f; volume = 1.3f;
std::unique_ptr<content::TtsUtterance> utterance3 = std::unique_ptr<content::TtsUtterance> utterance3 =
content::TtsUtterance::Create(nullptr); content::TtsUtterance::Create();
tts_controller_delegate->UpdateUtteranceDefaultsFromPrefs( tts_controller_delegate->UpdateUtteranceDefaultsFromPrefs(
utterance3.get(), &rate, &pitch, &volume); utterance3.get(), &rate, &pitch, &volume);
// Updated to pref values. // Updated to pref values.
...@@ -85,7 +85,7 @@ TEST(TtsControllerDelegateImplTest, TestTtsControllerUtteranceDefaults) { ...@@ -85,7 +85,7 @@ TEST(TtsControllerDelegateImplTest, TestTtsControllerUtteranceDefaults) {
TEST(TtsControllerDelegateImplTest, GetPreferredVoiceIdsForUtterance) { TEST(TtsControllerDelegateImplTest, GetPreferredVoiceIdsForUtterance) {
MockTtsControllerDelegate delegate; MockTtsControllerDelegate delegate;
std::unique_ptr<content::TtsUtterance> utterance = std::unique_ptr<content::TtsUtterance> utterance =
content::TtsUtterance::Create(nullptr); content::TtsUtterance::Create();
auto ids = delegate.GetPreferredVoiceIdsForUtterance(utterance.get()); auto ids = delegate.GetPreferredVoiceIdsForUtterance(utterance.get());
EXPECT_EQ(nullptr, ids.get()); EXPECT_EQ(nullptr, ids.get());
......
...@@ -141,7 +141,7 @@ void TtsHandler::HandlePreviewTtsVoice(const base::ListValue* args) { ...@@ -141,7 +141,7 @@ void TtsHandler::HandlePreviewTtsVoice(const base::ListValue* args) {
json->GetString("extension", &extension_id); json->GetString("extension", &extension_id);
std::unique_ptr<content::TtsUtterance> utterance = std::unique_ptr<content::TtsUtterance> utterance =
content::TtsUtterance::Create((Profile::FromWebUI(web_ui()))); content::TtsUtterance::Create(web_ui()->GetWebContents());
utterance->SetText(text); utterance->SetText(text);
utterance->SetVoiceName(name); utterance->SetVoiceName(name);
utterance->SetEngineId(extension_id); utterance->SetEngineId(extension_id);
......
...@@ -231,12 +231,12 @@ class TtsControllerTest : public testing::Test { ...@@ -231,12 +231,12 @@ class TtsControllerTest : public testing::Test {
}; };
TEST_F(TtsControllerTest, TestTtsControllerShutdown) { TEST_F(TtsControllerTest, TestTtsControllerShutdown) {
std::unique_ptr<TtsUtterance> utterance1 = TtsUtterance::Create(nullptr); std::unique_ptr<TtsUtterance> utterance1 = TtsUtterance::Create();
utterance1->SetCanEnqueue(true); utterance1->SetCanEnqueue(true);
utterance1->SetSrcId(1); utterance1->SetSrcId(1);
controller()->SpeakOrEnqueue(std::move(utterance1)); controller()->SpeakOrEnqueue(std::move(utterance1));
std::unique_ptr<TtsUtterance> utterance2 = TtsUtterance::Create(nullptr); std::unique_ptr<TtsUtterance> utterance2 = TtsUtterance::Create();
utterance2->SetCanEnqueue(true); utterance2->SetCanEnqueue(true);
utterance2->SetSrcId(2); utterance2->SetSrcId(2);
controller()->SpeakOrEnqueue(std::move(utterance2)); controller()->SpeakOrEnqueue(std::move(utterance2));
...@@ -286,8 +286,7 @@ TEST_F(TtsControllerTest, TestBrowserContextRemoved) { ...@@ -286,8 +286,7 @@ TEST_F(TtsControllerTest, TestBrowserContextRemoved) {
} }
#else #else
TEST_F(TtsControllerTest, TestTtsControllerUtteranceDefaults) { TEST_F(TtsControllerTest, TestTtsControllerUtteranceDefaults) {
std::unique_ptr<TtsUtterance> utterance1 = std::unique_ptr<TtsUtterance> utterance1 = content::TtsUtterance::Create();
content::TtsUtterance::Create(nullptr);
// Initialized to default (unset constant) values. // Initialized to default (unset constant) values.
EXPECT_EQ(blink::mojom::kSpeechSynthesisDoublePrefNotSet, EXPECT_EQ(blink::mojom::kSpeechSynthesisDoublePrefNotSet,
utterance1->GetContinuousParameters().rate); utterance1->GetContinuousParameters().rate);
...@@ -312,7 +311,7 @@ TEST_F(TtsControllerTest, TestGetMatchingVoice) { ...@@ -312,7 +311,7 @@ TEST_F(TtsControllerTest, TestGetMatchingVoice) {
{ {
// Calling GetMatchingVoice with no voices returns -1. // Calling GetMatchingVoice with no voices returns -1.
std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create(nullptr)); std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create());
std::vector<VoiceData> voices; std::vector<VoiceData> voices;
EXPECT_EQ(-1, controller()->GetMatchingVoice(utterance.get(), voices)); EXPECT_EQ(-1, controller()->GetMatchingVoice(utterance.get(), voices));
} }
...@@ -320,7 +319,7 @@ TEST_F(TtsControllerTest, TestGetMatchingVoice) { ...@@ -320,7 +319,7 @@ TEST_F(TtsControllerTest, TestGetMatchingVoice) {
{ {
// Calling GetMatchingVoice with any voices returns the first one // Calling GetMatchingVoice with any voices returns the first one
// even if there are no criteria that match. // even if there are no criteria that match.
std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create(nullptr)); std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create());
std::vector<VoiceData> voices(2); std::vector<VoiceData> voices(2);
EXPECT_EQ(0, controller()->GetMatchingVoice(utterance.get(), voices)); EXPECT_EQ(0, controller()->GetMatchingVoice(utterance.get(), voices));
} }
...@@ -328,7 +327,7 @@ TEST_F(TtsControllerTest, TestGetMatchingVoice) { ...@@ -328,7 +327,7 @@ TEST_F(TtsControllerTest, TestGetMatchingVoice) {
{ {
// If nothing else matches, the English voice is returned. // If nothing else matches, the English voice is returned.
// (In tests the language will always be English.) // (In tests the language will always be English.)
std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create(nullptr)); std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create());
std::vector<VoiceData> voices; std::vector<VoiceData> voices;
VoiceData fr_voice; VoiceData fr_voice;
fr_voice.lang = "fr"; fr_voice.lang = "fr";
...@@ -379,7 +378,7 @@ TEST_F(TtsControllerTest, TestGetMatchingVoice) { ...@@ -379,7 +378,7 @@ TEST_F(TtsControllerTest, TestGetMatchingVoice) {
voice8.native = true; voice8.native = true;
voices.push_back(voice8); voices.push_back(voice8);
std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create(nullptr)); std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create());
EXPECT_EQ(0, controller()->GetMatchingVoice(utterance.get(), voices)); EXPECT_EQ(0, controller()->GetMatchingVoice(utterance.get(), voices));
std::set<TtsEventType> types; std::set<TtsEventType> types;
...@@ -443,7 +442,7 @@ TEST_F(TtsControllerTest, TestGetMatchingVoice) { ...@@ -443,7 +442,7 @@ TEST_F(TtsControllerTest, TestGetMatchingVoice) {
voice1.name = "voice1"; voice1.name = "voice1";
voice1.lang = "en-US"; voice1.lang = "en-US";
voices.push_back(voice1); voices.push_back(voice1);
std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create(nullptr)); std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create());
// voice1 is matched against the exact default system language. // voice1 is matched against the exact default system language.
TestContentBrowserClient::GetInstance()->set_application_locale("en-US"); TestContentBrowserClient::GetInstance()->set_application_locale("en-US");
......
...@@ -31,7 +31,7 @@ class TtsSsmlBrowserTest : public ContentBrowserTest { ...@@ -31,7 +31,7 @@ class TtsSsmlBrowserTest : public ContentBrowserTest {
std::unique_ptr<MockTtsControllerImpl> controller = std::unique_ptr<MockTtsControllerImpl> controller =
std::make_unique<MockTtsControllerImpl>(); std::make_unique<MockTtsControllerImpl>();
std::unique_ptr<TtsUtterance> utterance = TtsUtterance::Create(nullptr); std::unique_ptr<TtsUtterance> utterance = TtsUtterance::Create();
utterance->SetText(input); utterance->SetText(input);
base::RunLoop run_loop; base::RunLoop run_loop;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "content/browser/speech/tts_utterance_impl.h" #include "content/browser/speech/tts_utterance_impl.h"
#include "base/values.h" #include "base/values.h"
#include "content/public/browser/web_contents.h"
#include "third_party/blink/public/mojom/speech/speech_synthesis.mojom.h" #include "third_party/blink/public/mojom/speech/speech_synthesis.mojom.h"
namespace content { namespace content {
...@@ -36,11 +37,25 @@ UtteranceContinuousParameters::UtteranceContinuousParameters() ...@@ -36,11 +37,25 @@ UtteranceContinuousParameters::UtteranceContinuousParameters()
// static // static
int TtsUtteranceImpl::next_utterance_id_ = 0; int TtsUtteranceImpl::next_utterance_id_ = 0;
// static
std::unique_ptr<TtsUtterance> TtsUtterance::Create(WebContents* web_contents) {
DCHECK(web_contents);
return std::make_unique<TtsUtteranceImpl>(web_contents->GetBrowserContext(),
web_contents);
}
// static
std::unique_ptr<TtsUtterance> TtsUtterance::Create( std::unique_ptr<TtsUtterance> TtsUtterance::Create(
BrowserContext* browser_context) { BrowserContext* browser_context) {
DCHECK(browser_context);
return std::make_unique<TtsUtteranceImpl>(browser_context, nullptr); return std::make_unique<TtsUtteranceImpl>(browser_context, nullptr);
} }
// static
std::unique_ptr<TtsUtterance> TtsUtterance::Create() {
return std::make_unique<TtsUtteranceImpl>(nullptr, nullptr);
}
TtsUtteranceImpl::TtsUtteranceImpl(BrowserContext* browser_context, TtsUtteranceImpl::TtsUtteranceImpl(BrowserContext* browser_context,
WebContents* web_contents) WebContents* web_contents)
: WebContentsObserver(web_contents), : WebContentsObserver(web_contents),
......
...@@ -18,6 +18,7 @@ class Value; ...@@ -18,6 +18,7 @@ class Value;
namespace content { namespace content {
class BrowserContext; class BrowserContext;
class TtsUtterance; class TtsUtterance;
class WebContents;
// Events sent back from the TTS engine indicating the progress. // Events sent back from the TTS engine indicating the progress.
enum TtsEventType { enum TtsEventType {
...@@ -59,10 +60,15 @@ class CONTENT_EXPORT UtteranceEventDelegate { ...@@ -59,10 +60,15 @@ class CONTENT_EXPORT UtteranceEventDelegate {
// One speech utterance. // One speech utterance.
class CONTENT_EXPORT TtsUtterance { class CONTENT_EXPORT TtsUtterance {
public: public:
// Construct an utterance given a profile and a completion task to call // Construct an utterance given a WebContents, BrowserContext, or no backing
// when the utterance is done speaking. Before speaking this utterance, // context. Prefer the more specific WebContents variant if possible. The
// its other parameters like text, rate, pitch, etc. should all be set. // utterance's speaking lifetime is tied to their lifetime.
// Before speaking this utterance, its other parameters like text, rate,
// pitch, etc. should all be set.
static std::unique_ptr<TtsUtterance> Create(WebContents* web_contents);
static std::unique_ptr<TtsUtterance> Create(BrowserContext* browser_context); static std::unique_ptr<TtsUtterance> Create(BrowserContext* browser_context);
static std::unique_ptr<TtsUtterance> Create();
virtual ~TtsUtterance() = default; virtual ~TtsUtterance() = default;
// Sends an event to the delegate. If the event type is TTS_EVENT_END // Sends an event to the delegate. If the event type is TTS_EVENT_END
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment