From 3a6dec0f7936f85143a38f20ce5b35487638a6c6 Mon Sep 17 00:00:00 2001 From: Andy Dill Date: Thu, 17 Jan 2019 22:28:31 -0800 Subject: [PATCH] refactor: move text-to-speech out of chromium_src (#15024) * chore: add tts patch and buildflag, makes tts work again * chore: add tts patch and buildflag, makes tts work again * fix: make things compile * build: add relevant tts files for linux * fix: update patch and patch description, should now compile on mac * build: move chrome specific sources under chromium_src:chrome target * build: enable_extensions again We are depending on them, check `//electron/chromium_src:chrome` target for more info. * fix: update tts.patch to receive notifications about browser context destruction * fix: extend browser process from chrome layer The global state g_browser_process is shared between //chrome and //electron. * spec: add basic speech synthesis test * spec: skip speech tests on ci * build: fix compilation on windows --- atom/browser/atom_browser_client.cc | 8 + atom/common/api/features.cc | 5 + atom/renderer/renderer_client_base.cc | 9 +- buildflags/BUILD.gn | 1 + buildflags/buildflags.gni | 2 + chromium_src/BUILD.gn | 24 + .../chrome/browser/speech/tts_controller.h | 336 ------------- .../browser/speech/tts_controller_impl.cc | 447 ------------------ .../browser/speech/tts_controller_impl.h | 102 ---- .../chrome/browser/speech/tts_linux.cc | 349 -------------- chromium_src/chrome/browser/speech/tts_mac.mm | 344 -------------- .../browser/speech/tts_message_filter.cc | 176 ------- .../browser/speech/tts_message_filter.h | 62 --- .../chrome/browser/speech/tts_platform.cc | 28 -- .../chrome/browser/speech/tts_platform.h | 80 ---- chromium_src/chrome/browser/speech/tts_win.cc | 311 ------------ chromium_src/chrome/common/tts_messages.h | 61 --- .../chrome/common/tts_utterance_request.cc | 20 - .../chrome/common/tts_utterance_request.h | 45 -- .../chrome/renderer/tts_dispatcher.cc | 198 -------- chromium_src/chrome/renderer/tts_dispatcher.h | 73 --- chromium_src/library_loaders/libspeechd.h | 52 -- .../library_loaders/libspeechd_loader.cc | 252 ---------- filenames.gni | 17 - patches/common/chromium/.patches | 1 + patches/common/chromium/tts.patch | 176 +++++++ spec/chromium-spec.js | 37 ++ 27 files changed, 262 insertions(+), 2954 deletions(-) delete mode 100644 chromium_src/chrome/browser/speech/tts_controller.h delete mode 100644 chromium_src/chrome/browser/speech/tts_controller_impl.cc delete mode 100644 chromium_src/chrome/browser/speech/tts_controller_impl.h delete mode 100644 chromium_src/chrome/browser/speech/tts_linux.cc delete mode 100644 chromium_src/chrome/browser/speech/tts_mac.mm delete mode 100644 chromium_src/chrome/browser/speech/tts_message_filter.cc delete mode 100644 chromium_src/chrome/browser/speech/tts_message_filter.h delete mode 100644 chromium_src/chrome/browser/speech/tts_platform.cc delete mode 100644 chromium_src/chrome/browser/speech/tts_platform.h delete mode 100644 chromium_src/chrome/browser/speech/tts_win.cc delete mode 100644 chromium_src/chrome/common/tts_messages.h delete mode 100644 chromium_src/chrome/common/tts_utterance_request.cc delete mode 100644 chromium_src/chrome/common/tts_utterance_request.h delete mode 100644 chromium_src/chrome/renderer/tts_dispatcher.cc delete mode 100644 chromium_src/chrome/renderer/tts_dispatcher.h delete mode 100644 chromium_src/library_loaders/libspeechd.h delete mode 100644 chromium_src/library_loaders/libspeechd_loader.cc create mode 100644 patches/common/chromium/tts.patch diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index 6bf47f5a676f6..c67261d4466e1 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -90,6 +90,10 @@ #include "components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom.h" #endif // BUILDFLAG(ENABLE_PRINTING) +#if BUILDFLAG(ENABLE_TTS) +#include "chrome/browser/speech/tts_message_filter.h" +#endif // BUILDFLAG(ENABLE_TTS) + using content::BrowserThread; namespace atom { @@ -222,6 +226,10 @@ void AtomBrowserClient::RenderProcessWillLaunch( process_id, host->GetBrowserContext())); #endif +#if BUILDFLAG(ENABLE_TTS) + host->AddFilter(new TtsMessageFilter(host->GetBrowserContext())); +#endif + ProcessPreferences prefs; auto* web_preferences = WebContentsPreferences::From(GetWebContentsFromProcessID(process_id)); diff --git a/atom/common/api/features.cc b/atom/common/api/features.cc index 408a90dc2ebea..c4f75a79206dc 100644 --- a/atom/common/api/features.cc +++ b/atom/common/api/features.cc @@ -39,6 +39,10 @@ bool IsPrintingEnabled() { return BUILDFLAG(ENABLE_PRINTING); } +bool IsTtsEnabled() { + return BUILDFLAG(ENABLE_TTS); +} + void Initialize(v8::Local exports, v8::Local unused, v8::Local context, @@ -52,6 +56,7 @@ void Initialize(v8::Local exports, &IsFakeLocationProviderEnabled); dict.SetMethod("isViewApiEnabled", &IsViewApiEnabled); dict.SetMethod("isPrintingEnabled", &IsPrintingEnabled); + dict.SetMethod("isTtsEnabled", &IsTtsEnabled); } } // namespace diff --git a/atom/renderer/renderer_client_base.cc b/atom/renderer/renderer_client_base.cc index f33fe98ffed3b..37e70bc6fba3b 100644 --- a/atom/renderer/renderer_client_base.cc +++ b/atom/renderer/renderer_client_base.cc @@ -19,7 +19,6 @@ #include "base/command_line.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" -#include "chrome/renderer/tts_dispatcher.h" #include "content/public/common/content_constants.h" #include "content/public/common/content_switches.h" #include "content/public/renderer/render_frame.h" @@ -56,6 +55,10 @@ #include "components/printing/renderer/print_render_frame_helper.h" #endif // BUILDFLAG(ENABLE_PRINTING) +#if BUILDFLAG(ENABLE_TTS) +#include "chrome/renderer/tts_dispatcher.h" +#endif // BUILDFLAG(ENABLE_TTS) + namespace atom { namespace { @@ -228,7 +231,11 @@ void RendererClientBase::DidClearWindowObject( std::unique_ptr RendererClientBase::OverrideSpeechSynthesizer( blink::WebSpeechSynthesizerClient* client) { +#if BUILDFLAG(ENABLE_TTS) return std::make_unique(client); +#else + return nullptr; +#endif } bool RendererClientBase::OverrideCreatePlugin( diff --git a/buildflags/BUILD.gn b/buildflags/BUILD.gn index c28b53b6f9e09..a8b4aaf4bd1fd 100644 --- a/buildflags/BUILD.gn +++ b/buildflags/BUILD.gn @@ -15,6 +15,7 @@ buildflag_header("buildflags") { "ENABLE_VIEW_API=$enable_view_api", "ENABLE_PEPPER_FLASH=$enable_pepper_flash", "ENABLE_PDF_VIEWER=$enable_pdf_viewer", + "ENABLE_TTS=$enable_tts", "OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider", ] } diff --git a/buildflags/buildflags.gni b/buildflags/buildflags.gni index 2603b7088be3e..efc4af8e8261e 100644 --- a/buildflags/buildflags.gni +++ b/buildflags/buildflags.gni @@ -14,6 +14,8 @@ declare_args() { enable_pdf_viewer = false + enable_tts = true + # Provide a fake location provider for mocking # the geolocation responses. Disable it if you # need to test with chromium's location provider. diff --git a/chromium_src/BUILD.gn b/chromium_src/BUILD.gn index e451951e9a291..69dddbcfa04ea 100644 --- a/chromium_src/BUILD.gn +++ b/chromium_src/BUILD.gn @@ -99,4 +99,28 @@ source_set("chrome") { ] } } + + if (enable_tts) { + sources += [ + "//chrome/browser/speech/tts_controller.h", + "//chrome/browser/speech/tts_controller_impl.cc", + "//chrome/browser/speech/tts_controller_impl.h", + "//chrome/browser/speech/tts_mac.mm", + "//chrome/browser/speech/tts_message_filter.cc", + "//chrome/browser/speech/tts_message_filter.h", + "//chrome/browser/speech/tts_platform.cc", + "//chrome/browser/speech/tts_platform.h", + "//chrome/browser/speech/tts_win.cc", + "//chrome/common/tts_messages.h", + "//chrome/common/tts_utterance_request.cc", + "//chrome/common/tts_utterance_request.h", + "//chrome/renderer/tts_dispatcher.cc", + "//chrome/renderer/tts_dispatcher.h", + ] + + if (is_linux) { + sources += [ "//chrome/browser/speech/tts_linux.cc" ] + deps += [ "//third_party/speech-dispatcher" ] + } + } } diff --git a/chromium_src/chrome/browser/speech/tts_controller.h b/chromium_src/chrome/browser/speech/tts_controller.h deleted file mode 100644 index ef2635b0e5b51..0000000000000 --- a/chromium_src/chrome/browser/speech/tts_controller.h +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_ -#define CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_ - -#include -#include -#include -#include -#include - -#include "base/memory/singleton.h" -#include "base/memory/weak_ptr.h" -#include "url/gurl.h" - -class Utterance; -class TtsPlatformImpl; - -namespace base { -class Value; -} - -namespace content { -class BrowserContext; -} - -// Events sent back from the TTS engine indicating the progress. -enum TtsEventType { - TTS_EVENT_START, - TTS_EVENT_END, - TTS_EVENT_WORD, - TTS_EVENT_SENTENCE, - TTS_EVENT_MARKER, - TTS_EVENT_INTERRUPTED, - TTS_EVENT_CANCELLED, - TTS_EVENT_ERROR, - TTS_EVENT_PAUSE, - TTS_EVENT_RESUME -}; - -enum TtsGenderType { TTS_GENDER_NONE, TTS_GENDER_MALE, TTS_GENDER_FEMALE }; - -// Returns true if this event type is one that indicates an utterance -// is finished and can be destroyed. -bool IsFinalTtsEventType(TtsEventType event_type); - -// The continuous parameters that apply to a given utterance. -struct UtteranceContinuousParameters { - UtteranceContinuousParameters(); - - double rate; - double pitch; - double volume; -}; - -// Information about one voice. -struct VoiceData { - VoiceData(); - VoiceData(const VoiceData&); - ~VoiceData(); - - std::string name; - std::string lang; - TtsGenderType gender; - std::string extension_id; - std::set events; - - // If true, the synthesis engine is a remote network resource. - // It may be higher latency and may incur bandwidth costs. - bool remote; - - // If true, this is implemented by this platform's subclass of - // TtsPlatformImpl. If false, this is implemented by an extension. - bool native; - std::string native_voice_identifier; -}; - -// Interface that delegates TTS requests to user-installed extensions. -class TtsEngineDelegate { - public: - virtual ~TtsEngineDelegate() {} - - // Return a list of all available voices registered. - virtual void GetVoices(content::BrowserContext* browser_context, - std::vector* out_voices) = 0; - - // Speak the given utterance by sending an event to the given TTS engine. - virtual void Speak(Utterance* utterance, const VoiceData& voice) = 0; - - // Stop speaking the given utterance by sending an event to the target - // associated with this utterance. - virtual void Stop(Utterance* utterance) = 0; - - // Pause in the middle of speaking this utterance. - virtual void Pause(Utterance* utterance) = 0; - - // Resume speaking this utterance. - virtual void Resume(Utterance* utterance) = 0; - - // Load the built-in component extension for ChromeOS. - virtual bool LoadBuiltInTtsExtension( - content::BrowserContext* browser_context) = 0; -}; - -// Class that wants to receive events on utterances. -class UtteranceEventDelegate { - public: - virtual ~UtteranceEventDelegate() {} - virtual void OnTtsEvent(Utterance* utterance, - TtsEventType event_type, - int char_index, - const std::string& error_message) = 0; -}; - -// Class that wants to be notified when the set of -// voices has changed. -class VoicesChangedDelegate { - public: - virtual ~VoicesChangedDelegate() {} - virtual void OnVoicesChanged() = 0; -}; - -// One speech utterance. -class Utterance { - public: - // Construct an utterance given a profile and a completion task to call - // when the utterance is done speaking. Before speaking this utterance, - // its other parameters like text, rate, pitch, etc. should all be set. - explicit Utterance(content::BrowserContext* browser_context); - ~Utterance(); - - // Sends an event to the delegate. If the event type is TTS_EVENT_END - // or TTS_EVENT_ERROR, deletes the utterance. If |char_index| is -1, - // uses the last good value. - void OnTtsEvent(TtsEventType event_type, - int char_index, - const std::string& error_message); - - // Finish an utterance without sending an event to the delegate. - void Finish(); - - // Getters and setters for the text to speak and other speech options. - void set_text(const std::string& text) { text_ = text; } - const std::string& text() const { return text_; } - - void set_options(const base::Value* options); - const base::Value* options() const { return options_.get(); } - - void set_src_extension_id(const std::string& src_extension_id) { - src_extension_id_ = src_extension_id; - } - const std::string& src_extension_id() { return src_extension_id_; } - - void set_src_id(int src_id) { src_id_ = src_id; } - int src_id() { return src_id_; } - - void set_src_url(const GURL& src_url) { src_url_ = src_url; } - const GURL& src_url() { return src_url_; } - - void set_voice_name(const std::string& voice_name) { - voice_name_ = voice_name; - } - const std::string& voice_name() const { return voice_name_; } - - void set_lang(const std::string& lang) { lang_ = lang; } - const std::string& lang() const { return lang_; } - - void set_gender(TtsGenderType gender) { gender_ = gender; } - TtsGenderType gender() const { return gender_; } - - void set_continuous_parameters(const UtteranceContinuousParameters& params) { - continuous_parameters_ = params; - } - const UtteranceContinuousParameters& continuous_parameters() { - return continuous_parameters_; - } - - void set_can_enqueue(bool can_enqueue) { can_enqueue_ = can_enqueue; } - bool can_enqueue() const { return can_enqueue_; } - - void set_required_event_types(const std::set& types) { - required_event_types_ = types; - } - const std::set& required_event_types() const { - return required_event_types_; - } - - void set_desired_event_types(const std::set& types) { - desired_event_types_ = types; - } - const std::set& desired_event_types() const { - return desired_event_types_; - } - - const std::string& extension_id() const { return extension_id_; } - void set_extension_id(const std::string& extension_id) { - extension_id_ = extension_id; - } - - UtteranceEventDelegate* event_delegate() const { - return event_delegate_.get(); - } - void set_event_delegate( - base::WeakPtr event_delegate) { - event_delegate_ = event_delegate; - } - - // Getters and setters for internal state. - content::BrowserContext* browser_context() const { return browser_context_; } - int id() const { return id_; } - bool finished() const { return finished_; } - - private: - // The BrowserContext that initiated this utterance. - content::BrowserContext* browser_context_; - - // The extension ID of the extension providing TTS for this utterance, or - // empty if native TTS is being used. - std::string extension_id_; - - // The unique ID of this utterance, used to associate callback functions - // with utterances. - int id_; - - // The id of the next utterance, so we can associate requests with - // responses. - static int next_utterance_id_; - - // The text to speak. - std::string text_; - - // The full options arg passed to tts.speak, which may include fields - // other than the ones we explicitly parse, below. - std::unique_ptr options_; - - // The extension ID of the extension that called speak() and should - // receive events. - std::string src_extension_id_; - - // The source extension's ID of this utterance, so that it can associate - // events with the appropriate callback. - int src_id_; - - // The URL of the page where the source extension called speak. - GURL src_url_; - - // The delegate to be called when an utterance event is fired. - base::WeakPtr event_delegate_; - - // The parsed options. - std::string voice_name_; - std::string lang_; - TtsGenderType gender_; - UtteranceContinuousParameters continuous_parameters_; - bool can_enqueue_; - std::set required_event_types_; - std::set desired_event_types_; - - // The index of the current char being spoken. - int char_index_; - - // True if this utterance received an event indicating it's done. - bool finished_; -}; - -// Singleton class that manages text-to-speech for the TTS and TTS engine -// extension APIs, maintaining a queue of pending utterances and keeping -// track of all state. -class TtsController { - public: - // Get the single instance of this class. - static TtsController* GetInstance(); - - // Returns true if we're currently speaking an utterance. - virtual bool IsSpeaking() = 0; - - // Speak the given utterance. If the utterance's can_enqueue flag is true - // and another utterance is in progress, adds it to the end of the queue. - // Otherwise, interrupts any current utterance and speaks this one - // immediately. - virtual void SpeakOrEnqueue(Utterance* utterance) = 0; - - // Stop all utterances and flush the queue. Implies leaving pause mode - // as well. - virtual void Stop() = 0; - - // Pause the speech queue. Some engines may support pausing in the middle - // of an utterance. - virtual void Pause() = 0; - - // Resume speaking. - virtual void Resume() = 0; - - // Handle events received from the speech engine. Events are forwarded to - // the callback function, and in addition, completion and error events - // trigger finishing the current utterance and starting the next one, if - // any. - virtual void OnTtsEvent(int utterance_id, - TtsEventType event_type, - int char_index, - const std::string& error_message) = 0; - - // Return a list of all available voices, including the native voice, - // if supported, and all voices registered by extensions. - virtual void GetVoices(content::BrowserContext* browser_context, - std::vector* out_voices) = 0; - - // Called by the extension system or platform implementation when the - // list of voices may have changed and should be re-queried. - virtual void VoicesChanged() = 0; - - // Add a delegate that wants to be notified when the set of voices changes. - virtual void AddVoicesChangedDelegate(VoicesChangedDelegate* delegate) = 0; - - // Remove delegate that wants to be notified when the set of voices changes. - virtual void RemoveVoicesChangedDelegate(VoicesChangedDelegate* delegate) = 0; - - // Set the delegate that processes TTS requests with user-installed - // extensions. - virtual void SetTtsEngineDelegate(TtsEngineDelegate* delegate) = 0; - - // Get the delegate that processes TTS requests with user-installed - // extensions. - virtual TtsEngineDelegate* GetTtsEngineDelegate() = 0; - - // For unit testing. - virtual void SetPlatformImpl(TtsPlatformImpl* platform_impl) = 0; - virtual int QueueSize() = 0; - - protected: - virtual ~TtsController() {} -}; - -#endif // CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_ diff --git a/chromium_src/chrome/browser/speech/tts_controller_impl.cc b/chromium_src/chrome/browser/speech/tts_controller_impl.cc deleted file mode 100644 index 8c3e79e918c85..0000000000000 --- a/chromium_src/chrome/browser/speech/tts_controller_impl.cc +++ /dev/null @@ -1,447 +0,0 @@ -// Copyright 2014 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/speech/tts_controller_impl.h" - -#include -#include - -#include "base/values.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/speech/tts_platform.h" - -namespace { -// A value to be used to indicate that there is no char index available. -const int kInvalidCharIndex = -1; - -// Given a language/region code of the form 'fr-FR', returns just the basic -// language portion, e.g. 'fr'. -std::string TrimLanguageCode(std::string lang) { - if (lang.size() >= 5 && lang[2] == '-') - return lang.substr(0, 2); - else - return lang; -} - -} // namespace - -bool IsFinalTtsEventType(TtsEventType event_type) { - return (event_type == TTS_EVENT_END || event_type == TTS_EVENT_INTERRUPTED || - event_type == TTS_EVENT_CANCELLED || event_type == TTS_EVENT_ERROR); -} - -// -// UtteranceContinuousParameters -// - -UtteranceContinuousParameters::UtteranceContinuousParameters() - : rate(-1), pitch(-1), volume(-1) {} - -// -// VoiceData -// - -VoiceData::VoiceData() - : gender(TTS_GENDER_NONE), remote(false), native(false) {} - -VoiceData::VoiceData(const VoiceData&) = default; - -VoiceData::~VoiceData() = default; - -// -// Utterance -// - -// static -int Utterance::next_utterance_id_ = 0; - -Utterance::Utterance(content::BrowserContext* browser_context) - : browser_context_(browser_context), - id_(next_utterance_id_++), - src_id_(-1), - gender_(TTS_GENDER_NONE), - can_enqueue_(false), - char_index_(0), - finished_(false) { - options_.reset(new base::DictionaryValue()); -} - -Utterance::~Utterance() { - DCHECK(finished_); -} - -void Utterance::OnTtsEvent(TtsEventType event_type, - int char_index, - const std::string& error_message) { - if (char_index >= 0) - char_index_ = char_index; - if (IsFinalTtsEventType(event_type)) - finished_ = true; - - if (event_delegate_) - event_delegate_->OnTtsEvent(this, event_type, char_index, error_message); - if (finished_) - event_delegate_.reset(); -} - -void Utterance::Finish() { - finished_ = true; -} - -void Utterance::set_options(const base::Value* options) { - options_.reset(options->DeepCopy()); -} - -TtsController* TtsController::GetInstance() { - return TtsControllerImpl::GetInstance(); -} - -// -// TtsControllerImpl -// - -// static -TtsControllerImpl* TtsControllerImpl::GetInstance() { - return base::Singleton::get(); -} - -TtsControllerImpl::TtsControllerImpl() - : current_utterance_(NULL), - paused_(false), - platform_impl_(NULL), - tts_engine_delegate_(NULL) {} - -TtsControllerImpl::~TtsControllerImpl() { - if (current_utterance_) { - current_utterance_->Finish(); - delete current_utterance_; - } - - // Clear any queued utterances too. - ClearUtteranceQueue(false); // Don't sent events. -} - -void TtsControllerImpl::SpeakOrEnqueue(Utterance* utterance) { - // If we're paused and we get an utterance that can't be queued, - // flush the queue but stay in the paused state. - if (paused_ && !utterance->can_enqueue()) { - Stop(); - paused_ = true; - delete utterance; - return; - } - - if (paused_ || (IsSpeaking() && utterance->can_enqueue())) { - utterance_queue_.push(utterance); - } else { - Stop(); - SpeakNow(utterance); - } -} - -void TtsControllerImpl::SpeakNow(Utterance* utterance) { - // Ensure we have all built-in voices loaded. This is a no-op if already - // loaded. - bool loaded_built_in = - GetPlatformImpl()->LoadBuiltInTtsExtension(utterance->browser_context()); - - // Get all available voices and try to find a matching voice. - std::vector voices; - GetVoices(utterance->browser_context(), &voices); - int index = GetMatchingVoice(utterance, voices); - - VoiceData voice; - if (index != -1) { - // Select the matching voice. - voice = voices[index]; - } else { - // However, if no match was found on a platform without native tts voices, - // attempt to get a voice based only on the current locale without respect - // to any supplied voice names. - std::vector native_voices; - - if (GetPlatformImpl()->PlatformImplAvailable()) - GetPlatformImpl()->GetVoices(&native_voices); - - if (native_voices.empty() && !voices.empty()) { - // TODO(dtseng): Notify extension caller of an error. - utterance->set_voice_name(""); - // TODO(gaochun): Replace the global variable g_browser_process with - // GetContentClient()->browser() to eliminate the dependency of browser - // once TTS implementation was moved to content. - utterance->set_lang(g_browser_process->GetApplicationLocale()); - index = GetMatchingVoice(utterance, voices); - - // If even that fails, just take the first available voice. - if (index == -1) - index = 0; - voice = voices[index]; - } else { - // Otherwise, simply give native voices a chance to handle this utterance. - voice.native = true; - } - } - - GetPlatformImpl()->WillSpeakUtteranceWithVoice(utterance, voice); - - if (!voice.native) { -#if !defined(OS_ANDROID) - DCHECK(!voice.extension_id.empty()); - current_utterance_ = utterance; - utterance->set_extension_id(voice.extension_id); - if (tts_engine_delegate_) - tts_engine_delegate_->Speak(utterance, voice); - bool sends_end_event = - voice.events.find(TTS_EVENT_END) != voice.events.end(); - if (!sends_end_event) { - utterance->Finish(); - delete utterance; - current_utterance_ = NULL; - SpeakNextUtterance(); - } -#endif - } else { - // It's possible for certain platforms to send start events immediately - // during |speak|. - current_utterance_ = utterance; - GetPlatformImpl()->clear_error(); - bool success = GetPlatformImpl()->Speak(utterance->id(), utterance->text(), - utterance->lang(), voice, - utterance->continuous_parameters()); - if (!success) - current_utterance_ = NULL; - - // If the native voice wasn't able to process this speech, see if - // the browser has built-in TTS that isn't loaded yet. - if (!success && loaded_built_in) { - utterance_queue_.push(utterance); - return; - } - - if (!success) { - utterance->OnTtsEvent(TTS_EVENT_ERROR, kInvalidCharIndex, - GetPlatformImpl()->error()); - delete utterance; - return; - } - } -} - -void TtsControllerImpl::Stop() { - paused_ = false; - if (current_utterance_ && !current_utterance_->extension_id().empty()) { -#if !defined(OS_ANDROID) - if (tts_engine_delegate_) - tts_engine_delegate_->Stop(current_utterance_); -#endif - } else { - GetPlatformImpl()->clear_error(); - GetPlatformImpl()->StopSpeaking(); - } - - if (current_utterance_) - current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex, - std::string()); - FinishCurrentUtterance(); - ClearUtteranceQueue(true); // Send events. -} - -void TtsControllerImpl::Pause() { - paused_ = true; - if (current_utterance_ && !current_utterance_->extension_id().empty()) { -#if !defined(OS_ANDROID) - if (tts_engine_delegate_) - tts_engine_delegate_->Pause(current_utterance_); -#endif - } else if (current_utterance_) { - GetPlatformImpl()->clear_error(); - GetPlatformImpl()->Pause(); - } -} - -void TtsControllerImpl::Resume() { - paused_ = false; - if (current_utterance_ && !current_utterance_->extension_id().empty()) { -#if !defined(OS_ANDROID) - if (tts_engine_delegate_) - tts_engine_delegate_->Resume(current_utterance_); -#endif - } else if (current_utterance_) { - GetPlatformImpl()->clear_error(); - GetPlatformImpl()->Resume(); - } else { - SpeakNextUtterance(); - } -} - -void TtsControllerImpl::OnTtsEvent(int utterance_id, - TtsEventType event_type, - int char_index, - const std::string& error_message) { - // We may sometimes receive completion callbacks "late", after we've - // already finished the utterance (for example because another utterance - // interrupted or we got a call to Stop). This is normal and we can - // safely just ignore these events. - if (!current_utterance_ || utterance_id != current_utterance_->id()) { - return; - } - current_utterance_->OnTtsEvent(event_type, char_index, error_message); - if (current_utterance_->finished()) { - FinishCurrentUtterance(); - SpeakNextUtterance(); - } -} - -void TtsControllerImpl::GetVoices(content::BrowserContext* browser_context, - std::vector* out_voices) { -#if !defined(OS_ANDROID) - if (browser_context && tts_engine_delegate_) - tts_engine_delegate_->GetVoices(browser_context, out_voices); -#endif - - TtsPlatformImpl* platform_impl = GetPlatformImpl(); - if (platform_impl) { - // Ensure we have all built-in voices loaded. This is a no-op if already - // loaded. - platform_impl->LoadBuiltInTtsExtension(browser_context); - if (platform_impl->PlatformImplAvailable()) - platform_impl->GetVoices(out_voices); - } -} - -bool TtsControllerImpl::IsSpeaking() { - return current_utterance_ != NULL || GetPlatformImpl()->IsSpeaking(); -} - -void TtsControllerImpl::FinishCurrentUtterance() { - if (current_utterance_) { - if (!current_utterance_->finished()) - current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex, - std::string()); - delete current_utterance_; - current_utterance_ = NULL; - } -} - -void TtsControllerImpl::SpeakNextUtterance() { - if (paused_) - return; - - // Start speaking the next utterance in the queue. Keep trying in case - // one fails but there are still more in the queue to try. - while (!utterance_queue_.empty() && !current_utterance_) { - Utterance* utterance = utterance_queue_.front(); - utterance_queue_.pop(); - SpeakNow(utterance); - } -} - -void TtsControllerImpl::ClearUtteranceQueue(bool send_events) { - while (!utterance_queue_.empty()) { - Utterance* utterance = utterance_queue_.front(); - utterance_queue_.pop(); - if (send_events) - utterance->OnTtsEvent(TTS_EVENT_CANCELLED, kInvalidCharIndex, - std::string()); - else - utterance->Finish(); - delete utterance; - } -} - -void TtsControllerImpl::SetPlatformImpl(TtsPlatformImpl* platform_impl) { - platform_impl_ = platform_impl; -} - -int TtsControllerImpl::QueueSize() { - return static_cast(utterance_queue_.size()); -} - -TtsPlatformImpl* TtsControllerImpl::GetPlatformImpl() { - if (!platform_impl_) - platform_impl_ = TtsPlatformImpl::GetInstance(); - return platform_impl_; -} - -int TtsControllerImpl::GetMatchingVoice(const Utterance* utterance, - std::vector& voices) { - // Make two passes: the first time, do strict language matching - // ('fr-FR' does not match 'fr-CA'). The second time, do prefix - // language matching ('fr-FR' matches 'fr' and 'fr-CA') - for (int pass = 0; pass < 2; ++pass) { - for (size_t i = 0; i < voices.size(); ++i) { - const VoiceData& voice = voices[i]; - - if (!utterance->extension_id().empty() && - utterance->extension_id() != voice.extension_id) { - continue; - } - - if (!voice.name.empty() && !utterance->voice_name().empty() && - voice.name != utterance->voice_name()) { - continue; - } - if (!voice.lang.empty() && !utterance->lang().empty()) { - std::string voice_lang = voice.lang; - std::string utterance_lang = utterance->lang(); - if (pass == 1) { - voice_lang = TrimLanguageCode(voice_lang); - utterance_lang = TrimLanguageCode(utterance_lang); - } - if (voice_lang != utterance_lang) { - continue; - } - } - if (voice.gender != TTS_GENDER_NONE && - utterance->gender() != TTS_GENDER_NONE && - voice.gender != utterance->gender()) { - continue; - } - - if (utterance->required_event_types().size() > 0) { - bool has_all_required_event_types = true; - for (std::set::const_iterator iter = - utterance->required_event_types().begin(); - iter != utterance->required_event_types().end(); ++iter) { - if (voice.events.find(*iter) == voice.events.end()) { - has_all_required_event_types = false; - break; - } - } - if (!has_all_required_event_types) - continue; - } - - return static_cast(i); - } - } - - return -1; -} - -void TtsControllerImpl::VoicesChanged() { - for (std::set::iterator iter = - voices_changed_delegates_.begin(); - iter != voices_changed_delegates_.end(); ++iter) { - (*iter)->OnVoicesChanged(); - } -} - -void TtsControllerImpl::AddVoicesChangedDelegate( - VoicesChangedDelegate* delegate) { - voices_changed_delegates_.insert(delegate); -} - -void TtsControllerImpl::RemoveVoicesChangedDelegate( - VoicesChangedDelegate* delegate) { - voices_changed_delegates_.erase(delegate); -} - -void TtsControllerImpl::SetTtsEngineDelegate(TtsEngineDelegate* delegate) { - tts_engine_delegate_ = delegate; -} - -TtsEngineDelegate* TtsControllerImpl::GetTtsEngineDelegate() { - return tts_engine_delegate_; -} diff --git a/chromium_src/chrome/browser/speech/tts_controller_impl.h b/chromium_src/chrome/browser/speech/tts_controller_impl.h deleted file mode 100644 index 04461d6ce2514..0000000000000 --- a/chromium_src/chrome/browser/speech/tts_controller_impl.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2014 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. - -#ifndef CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_ -#define CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_ - -#include -#include -#include -#include -#include - -#include "base/memory/singleton.h" -#include "base/memory/weak_ptr.h" -#include "chrome/browser/speech/tts_controller.h" -#include "url/gurl.h" - -namespace content { -class BrowserContext; -} - -// Singleton class that manages text-to-speech for the TTS and TTS engine -// extension APIs, maintaining a queue of pending utterances and keeping -// track of all state. -class TtsControllerImpl : public TtsController { - public: - // Get the single instance of this class. - static TtsControllerImpl* GetInstance(); - - // TtsController methods - bool IsSpeaking() override; - void SpeakOrEnqueue(Utterance* utterance) override; - void Stop() override; - void Pause() override; - void Resume() override; - void OnTtsEvent(int utterance_id, - TtsEventType event_type, - int char_index, - const std::string& error_message) override; - void GetVoices(content::BrowserContext* browser_context, - std::vector* out_voices) override; - void VoicesChanged() override; - void AddVoicesChangedDelegate(VoicesChangedDelegate* delegate) override; - void RemoveVoicesChangedDelegate(VoicesChangedDelegate* delegate) override; - void SetTtsEngineDelegate(TtsEngineDelegate* delegate) override; - TtsEngineDelegate* GetTtsEngineDelegate() override; - void SetPlatformImpl(TtsPlatformImpl* platform_impl) override; - int QueueSize() override; - - protected: - TtsControllerImpl(); - ~TtsControllerImpl() override; - - private: - // Get the platform TTS implementation (or injected mock). - TtsPlatformImpl* GetPlatformImpl(); - - // Start speaking the given utterance. Will either take ownership of - // |utterance| or delete it if there's an error. Returns true on success. - void SpeakNow(Utterance* utterance); - - // Clear the utterance queue. If send_events is true, will send - // TTS_EVENT_CANCELLED events on each one. - void ClearUtteranceQueue(bool send_events); - - // Finalize and delete the current utterance. - void FinishCurrentUtterance(); - - // Start speaking the next utterance in the queue. - void SpeakNextUtterance(); - - // Given an utterance and a vector of voices, return the - // index of the voice that best matches the utterance. - int GetMatchingVoice(const Utterance* utterance, - std::vector& voices); - - friend struct base::DefaultSingletonTraits; - - // The current utterance being spoken. - Utterance* current_utterance_; - - // Whether the queue is paused or not. - bool paused_; - - // A queue of utterances to speak after the current one finishes. - std::queue utterance_queue_; - - // A set of delegates that want to be notified when the voices change. - std::set voices_changed_delegates_; - - // A pointer to the platform implementation of text-to-speech, for - // dependency injection. - TtsPlatformImpl* platform_impl_; - - // The delegate that processes TTS requests with user-installed extensions. - TtsEngineDelegate* tts_engine_delegate_; - - DISALLOW_COPY_AND_ASSIGN(TtsControllerImpl); -}; - -#endif // CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_ diff --git a/chromium_src/chrome/browser/speech/tts_linux.cc b/chromium_src/chrome/browser/speech/tts_linux.cc deleted file mode 100644 index d491bb7e85a00..0000000000000 --- a/chromium_src/chrome/browser/speech/tts_linux.cc +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright (c) 2012 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 - -#include -#include - -#include "base/command_line.h" -#include "base/debug/leak_annotations.h" -#include "base/memory/singleton.h" -#include "base/synchronization/lock.h" -#include "base/task_scheduler/post_task.h" -#include "chrome/browser/speech/tts_platform.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/common/content_switches.h" - -#include "library_loaders/libspeechd.h" - -using content::BrowserThread; - -namespace { - -const char kNotSupportedError[] = - "Native speech synthesis not supported on this platform."; - -struct SPDChromeVoice { - std::string name; - std::string module; -}; - -} // namespace - -class TtsPlatformImplLinux : public TtsPlatformImpl { - public: - bool PlatformImplAvailable() override; - bool Speak(int utterance_id, - const std::string& utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) override; - bool StopSpeaking() override; - void Pause() override; - void Resume() override; - bool IsSpeaking() override; - void GetVoices(std::vector* out_voices) override; - - void OnSpeechEvent(SPDNotificationType type); - - // Get the single instance of this class. - static TtsPlatformImplLinux* GetInstance(); - - private: - TtsPlatformImplLinux(); - ~TtsPlatformImplLinux() override; - - // Initiate the connection with the speech dispatcher. - void Initialize(); - - // Resets the connection with speech dispatcher. - void Reset(); - - static void NotificationCallback(size_t msg_id, - size_t client_id, - SPDNotificationType type); - - static void IndexMarkCallback(size_t msg_id, - size_t client_id, - SPDNotificationType state, - char* index_mark); - - static SPDNotificationType current_notification_; - - base::Lock initialization_lock_; - LibSpeechdLoader libspeechd_loader_; - SPDConnection* conn_; - - // These apply to the current utterance only. - std::string utterance_; - int utterance_id_; - - // Map a string composed of a voicename and module to the voicename. Used to - // uniquely identify a voice across all available modules. - std::unique_ptr> all_native_voices_; - - friend struct base::DefaultSingletonTraits; - - DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplLinux); -}; - -// static -SPDNotificationType TtsPlatformImplLinux::current_notification_ = SPD_EVENT_END; - -TtsPlatformImplLinux::TtsPlatformImplLinux() : utterance_id_(0) { - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - if (!command_line.HasSwitch(switches::kEnableSpeechDispatcher)) - return; - - base::PostTaskWithTraits( - FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND}, - base::Bind(&TtsPlatformImplLinux::Initialize, base::Unretained(this))); -} - -void TtsPlatformImplLinux::Initialize() { - base::AutoLock lock(initialization_lock_); - - if (!libspeechd_loader_.Load("libspeechd.so.2")) - return; - - { - // spd_open has memory leaks which are hard to suppress. - // http://crbug.com/317360 - ANNOTATE_SCOPED_MEMORY_LEAK; - conn_ = libspeechd_loader_.spd_open("chrome", "extension_api", NULL, - SPD_MODE_THREADED); - } - if (!conn_) - return; - - // Register callbacks for all events. - conn_->callback_begin = conn_->callback_end = conn_->callback_cancel = - conn_->callback_pause = conn_->callback_resume = &NotificationCallback; - - conn_->callback_im = &IndexMarkCallback; - - libspeechd_loader_.spd_set_notification_on(conn_, SPD_BEGIN); - libspeechd_loader_.spd_set_notification_on(conn_, SPD_END); - libspeechd_loader_.spd_set_notification_on(conn_, SPD_CANCEL); - libspeechd_loader_.spd_set_notification_on(conn_, SPD_PAUSE); - libspeechd_loader_.spd_set_notification_on(conn_, SPD_RESUME); -} - -TtsPlatformImplLinux::~TtsPlatformImplLinux() { - base::AutoLock lock(initialization_lock_); - if (conn_) { - libspeechd_loader_.spd_close(conn_); - conn_ = NULL; - } -} - -void TtsPlatformImplLinux::Reset() { - base::AutoLock lock(initialization_lock_); - if (conn_) - libspeechd_loader_.spd_close(conn_); - conn_ = libspeechd_loader_.spd_open("chrome", "extension_api", NULL, - SPD_MODE_THREADED); -} - -bool TtsPlatformImplLinux::PlatformImplAvailable() { - if (!initialization_lock_.Try()) - return false; - bool result = libspeechd_loader_.loaded() && (conn_ != NULL); - initialization_lock_.Release(); - return result; -} - -bool TtsPlatformImplLinux::Speak(int utterance_id, - const std::string& utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) { - if (!PlatformImplAvailable()) { - error_ = kNotSupportedError; - return false; - } - - // Speech dispatcher's speech params are around 3x at either limit. - float rate = params.rate > 3 ? 3 : params.rate; - rate = params.rate < 0.334 ? 0.334 : rate; - float pitch = params.pitch > 3 ? 3 : params.pitch; - pitch = params.pitch < 0.334 ? 0.334 : pitch; - - std::map::iterator it = - all_native_voices_->find(voice.name); - if (it != all_native_voices_->end()) { - libspeechd_loader_.spd_set_output_module(conn_, it->second.module.c_str()); - libspeechd_loader_.spd_set_synthesis_voice(conn_, it->second.name.c_str()); - } - - // Map our multiplicative range to Speech Dispatcher's linear range. - // .334 = -100. - // 3 = 100. - libspeechd_loader_.spd_set_voice_rate(conn_, 100 * log10(rate) / log10(3)); - libspeechd_loader_.spd_set_voice_pitch(conn_, 100 * log10(pitch) / log10(3)); - - // Support languages other than the default - if (!lang.empty()) - libspeechd_loader_.spd_set_language(conn_, lang.c_str()); - - utterance_ = utterance; - utterance_id_ = utterance_id; - - if (libspeechd_loader_.spd_say(conn_, SPD_TEXT, utterance.c_str()) == -1) { - Reset(); - return false; - } - return true; -} - -bool TtsPlatformImplLinux::StopSpeaking() { - if (!PlatformImplAvailable()) - return false; - if (libspeechd_loader_.spd_stop(conn_) == -1) { - Reset(); - return false; - } - return true; -} - -void TtsPlatformImplLinux::Pause() { - if (!PlatformImplAvailable()) - return; - libspeechd_loader_.spd_pause(conn_); -} - -void TtsPlatformImplLinux::Resume() { - if (!PlatformImplAvailable()) - return; - libspeechd_loader_.spd_resume(conn_); -} - -bool TtsPlatformImplLinux::IsSpeaking() { - return current_notification_ == SPD_EVENT_BEGIN; -} - -void TtsPlatformImplLinux::GetVoices(std::vector* out_voices) { - if (!all_native_voices_.get()) { - all_native_voices_.reset(new std::map()); - char** modules = libspeechd_loader_.spd_list_modules(conn_); - if (!modules) - return; - for (int i = 0; modules[i]; i++) { - char* module = modules[i]; - libspeechd_loader_.spd_set_output_module(conn_, module); - SPDVoice** native_voices = - libspeechd_loader_.spd_list_synthesis_voices(conn_); - if (!native_voices) { - free(module); - continue; - } - for (int j = 0; native_voices[j]; j++) { - SPDVoice* native_voice = native_voices[j]; - SPDChromeVoice native_data; - native_data.name = native_voice->name; - native_data.module = module; - std::string key; - key.append(native_data.name); - key.append(" "); - key.append(native_data.module); - all_native_voices_->insert( - std::pair(key, native_data)); - free(native_voices[j]); - } - free(modules[i]); - } - } - - for (std::map::iterator it = - all_native_voices_->begin(); - it != all_native_voices_->end(); it++) { - out_voices->push_back(VoiceData()); - VoiceData& voice = out_voices->back(); - voice.native = true; - voice.name = it->first; - voice.events.insert(TTS_EVENT_START); - voice.events.insert(TTS_EVENT_END); - voice.events.insert(TTS_EVENT_CANCELLED); - voice.events.insert(TTS_EVENT_MARKER); - voice.events.insert(TTS_EVENT_PAUSE); - voice.events.insert(TTS_EVENT_RESUME); - } -} - -void TtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) { - TtsController* controller = TtsController::GetInstance(); - switch (type) { - case SPD_EVENT_BEGIN: - controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string()); - break; - case SPD_EVENT_RESUME: - controller->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME, 0, std::string()); - break; - case SPD_EVENT_END: - controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, utterance_.size(), - std::string()); - break; - case SPD_EVENT_PAUSE: - controller->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE, utterance_.size(), - std::string()); - break; - case SPD_EVENT_CANCEL: - controller->OnTtsEvent(utterance_id_, TTS_EVENT_CANCELLED, 0, - std::string()); - break; - case SPD_EVENT_INDEX_MARK: - controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, std::string()); - break; - } -} - -// static -void TtsPlatformImplLinux::NotificationCallback(size_t msg_id, - size_t client_id, - SPDNotificationType type) { - // We run Speech Dispatcher in threaded mode, so these callbacks should always - // be in a separate thread. - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - current_notification_ = type; - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&TtsPlatformImplLinux::OnSpeechEvent, - base::Unretained(TtsPlatformImplLinux::GetInstance()), - type)); - } -} - -// static -void TtsPlatformImplLinux::IndexMarkCallback(size_t msg_id, - size_t client_id, - SPDNotificationType state, - char* index_mark) { - // TODO(dtseng): index_mark appears to specify an index type supplied by a - // client. Need to explore how this is used before hooking it up with existing - // word, sentence events. - // We run Speech Dispatcher in threaded mode, so these callbacks should always - // be in a separate thread. - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - current_notification_ = state; - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&TtsPlatformImplLinux::OnSpeechEvent, - base::Unretained(TtsPlatformImplLinux::GetInstance()), - state)); - } -} - -// static -TtsPlatformImplLinux* TtsPlatformImplLinux::GetInstance() { - return base::Singleton< - TtsPlatformImplLinux, - base::LeakySingletonTraits>::get(); -} - -// static -TtsPlatformImpl* TtsPlatformImpl::GetInstance() { - return TtsPlatformImplLinux::GetInstance(); -} diff --git a/chromium_src/chrome/browser/speech/tts_mac.mm b/chromium_src/chrome/browser/speech/tts_mac.mm deleted file mode 100644 index c1f77483db416..0000000000000 --- a/chromium_src/chrome/browser/speech/tts_mac.mm +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright (c) 2012 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 - -#include "base/mac/scoped_nsobject.h" -#include "base/memory/singleton.h" -#include "base/strings/sys_string_conversions.h" -#include "base/values.h" -#include "chrome/browser/speech/tts_controller.h" -#include "chrome/browser/speech/tts_platform.h" - -#import - -class TtsPlatformImplMac; - -@interface ChromeTtsDelegate : NSObject { - @private - TtsPlatformImplMac* ttsImplMac_; // weak. -} - -- (id)initWithPlatformImplMac:(TtsPlatformImplMac*)ttsImplMac; - -@end - -// Subclass of NSSpeechSynthesizer that takes an utterance -// string on initialization, retains it and only allows it -// to be spoken once. -// -// We construct a new NSSpeechSynthesizer for each utterance, for -// two reasons: -// 1. To associate delegate callbacks with a particular utterance, -// without assuming anything undocumented about the protocol. -// 2. To work around http://openradar.appspot.com/radar?id=2854403, -// where Nuance voices don't retain the utterance string and -// crash when trying to call willSpeakWord. -@interface SingleUseSpeechSynthesizer : NSSpeechSynthesizer { - @private - base::scoped_nsobject utterance_; - bool didSpeak_; -} - -- (id)initWithUtterance:(NSString*)utterance; -- (bool)startSpeakingRetainedUtterance; -- (bool)startSpeakingString:(NSString*)utterance; - -@end - -class TtsPlatformImplMac : public TtsPlatformImpl { - public: - bool PlatformImplAvailable() override { return true; } - - bool Speak(int utterance_id, - const std::string& utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) override; - - bool StopSpeaking() override; - - void Pause() override; - - void Resume() override; - - bool IsSpeaking() override; - - void GetVoices(std::vector* out_voices) override; - - // Called by ChromeTtsDelegate when we get a callback from the - // native speech engine. - void OnSpeechEvent(NSSpeechSynthesizer* sender, - TtsEventType event_type, - int char_index, - const std::string& error_message); - - // Get the single instance of this class. - static TtsPlatformImplMac* GetInstance(); - - private: - TtsPlatformImplMac(); - ~TtsPlatformImplMac() override; - - base::scoped_nsobject speech_synthesizer_; - base::scoped_nsobject delegate_; - int utterance_id_; - std::string utterance_; - int last_char_index_; - bool paused_; - - friend struct base::DefaultSingletonTraits; - - DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplMac); -}; - -// static -TtsPlatformImpl* TtsPlatformImpl::GetInstance() { - return TtsPlatformImplMac::GetInstance(); -} - -bool TtsPlatformImplMac::Speak(int utterance_id, - const std::string& utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) { - // TODO: convert SSML to SAPI xml. http://crbug.com/88072 - utterance_ = utterance; - paused_ = false; - - NSString* utterance_nsstring = - [NSString stringWithUTF8String:utterance_.c_str()]; - - // Deliberately construct a new speech synthesizer every time Speak is - // called, otherwise there's no way to know whether calls to the delegate - // apply to the current utterance or a previous utterance. In - // experimentation, the overhead of constructing and destructing a - // NSSpeechSynthesizer is minimal. - speech_synthesizer_.reset([[SingleUseSpeechSynthesizer alloc] - initWithUtterance:utterance_nsstring]); - [speech_synthesizer_ setDelegate:delegate_]; - - if (!voice.native_voice_identifier.empty()) { - NSString* native_voice_identifier = - [NSString stringWithUTF8String:voice.native_voice_identifier.c_str()]; - [speech_synthesizer_ setVoice:native_voice_identifier]; - } - - utterance_id_ = utterance_id; - - // TODO: support languages other than the default: crbug.com/88059 - - if (params.rate >= 0.0) { - // The TTS api defines rate via words per minute. Let 200 be the default. - [speech_synthesizer_ setObject:[NSNumber numberWithInt:params.rate * 200] - forProperty:NSSpeechRateProperty - error:nil]; - } - - if (params.pitch >= 0.0) { - // The input is a float from 0.0 to 2.0, with 1.0 being the default. - // Get the default pitch for this voice and modulate it by 50% - 150%. - NSError* errorCode; - NSNumber* defaultPitchObj = - [speech_synthesizer_ objectForProperty:NSSpeechPitchBaseProperty - error:&errorCode]; - int defaultPitch = defaultPitchObj ? [defaultPitchObj intValue] : 48; - int newPitch = static_cast(defaultPitch * (0.5 * params.pitch + 0.5)); - [speech_synthesizer_ setObject:[NSNumber numberWithInt:newPitch] - forProperty:NSSpeechPitchBaseProperty - error:nil]; - } - - if (params.volume >= 0.0) { - [speech_synthesizer_ setObject:[NSNumber numberWithFloat:params.volume] - forProperty:NSSpeechVolumeProperty - error:nil]; - } - - bool success = [speech_synthesizer_ startSpeakingRetainedUtterance]; - if (success) { - TtsController* controller = TtsController::GetInstance(); - controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, ""); - } - return success; -} - -bool TtsPlatformImplMac::StopSpeaking() { - if (speech_synthesizer_.get()) { - [speech_synthesizer_ stopSpeaking]; - speech_synthesizer_.reset(nil); - } - paused_ = false; - return true; -} - -void TtsPlatformImplMac::Pause() { - if (speech_synthesizer_.get() && utterance_id_ && !paused_) { - [speech_synthesizer_ pauseSpeakingAtBoundary:NSSpeechImmediateBoundary]; - paused_ = true; - TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE, - last_char_index_, ""); - } -} - -void TtsPlatformImplMac::Resume() { - if (speech_synthesizer_.get() && utterance_id_ && paused_) { - [speech_synthesizer_ continueSpeaking]; - paused_ = false; - TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME, - last_char_index_, ""); - } -} - -bool TtsPlatformImplMac::IsSpeaking() { - if (speech_synthesizer_) - return [speech_synthesizer_ isSpeaking]; - return false; -} - -void TtsPlatformImplMac::GetVoices(std::vector* outVoices) { - NSArray* voices = [NSSpeechSynthesizer availableVoices]; - - // Create a new temporary array of the available voices with - // the default voice first. - NSMutableArray* orderedVoices = - [NSMutableArray arrayWithCapacity:[voices count]]; - NSString* defaultVoice = [NSSpeechSynthesizer defaultVoice]; - if (defaultVoice) { - [orderedVoices addObject:defaultVoice]; - } - for (NSString* voiceIdentifier in voices) { - if (![voiceIdentifier isEqualToString:defaultVoice]) - [orderedVoices addObject:voiceIdentifier]; - } - - for (NSString* voiceIdentifier in orderedVoices) { - outVoices->push_back(VoiceData()); - VoiceData& data = outVoices->back(); - - NSDictionary* attributes = - [NSSpeechSynthesizer attributesForVoice:voiceIdentifier]; - NSString* name = [attributes objectForKey:NSVoiceName]; - NSString* gender = [attributes objectForKey:NSVoiceGender]; - NSString* localeIdentifier = - [attributes objectForKey:NSVoiceLocaleIdentifier]; - - data.native = true; - data.native_voice_identifier = base::SysNSStringToUTF8(voiceIdentifier); - data.name = base::SysNSStringToUTF8(name); - - NSDictionary* localeComponents = - [NSLocale componentsFromLocaleIdentifier:localeIdentifier]; - NSString* language = [localeComponents objectForKey:NSLocaleLanguageCode]; - NSString* country = [localeComponents objectForKey:NSLocaleCountryCode]; - if (language && country) { - data.lang = - [[NSString stringWithFormat:@"%@-%@", language, country] UTF8String]; - } else { - data.lang = base::SysNSStringToUTF8(language); - } - if ([gender isEqualToString:NSVoiceGenderMale]) - data.gender = TTS_GENDER_MALE; - else if ([gender isEqualToString:NSVoiceGenderFemale]) - data.gender = TTS_GENDER_FEMALE; - else - data.gender = TTS_GENDER_NONE; - data.events.insert(TTS_EVENT_START); - data.events.insert(TTS_EVENT_END); - data.events.insert(TTS_EVENT_WORD); - data.events.insert(TTS_EVENT_ERROR); - data.events.insert(TTS_EVENT_CANCELLED); - data.events.insert(TTS_EVENT_INTERRUPTED); - data.events.insert(TTS_EVENT_PAUSE); - data.events.insert(TTS_EVENT_RESUME); - } -} - -void TtsPlatformImplMac::OnSpeechEvent(NSSpeechSynthesizer* sender, - TtsEventType event_type, - int char_index, - const std::string& error_message) { - // Don't send events from an utterance that's already completed. - // This depends on the fact that we construct a new NSSpeechSynthesizer - // each time we call Speak. - if (sender != speech_synthesizer_.get()) - return; - - if (event_type == TTS_EVENT_END) - char_index = utterance_.size(); - TtsController* controller = TtsController::GetInstance(); - controller->OnTtsEvent(utterance_id_, event_type, char_index, error_message); - last_char_index_ = char_index; -} - -TtsPlatformImplMac::TtsPlatformImplMac() { - utterance_id_ = -1; - paused_ = false; - - delegate_.reset([[ChromeTtsDelegate alloc] initWithPlatformImplMac:this]); -} - -TtsPlatformImplMac::~TtsPlatformImplMac() {} - -// static -TtsPlatformImplMac* TtsPlatformImplMac::GetInstance() { - return base::Singleton::get(); -} - -@implementation ChromeTtsDelegate - -- (id)initWithPlatformImplMac:(TtsPlatformImplMac*)ttsImplMac { - if ((self = [super init])) { - ttsImplMac_ = ttsImplMac; - } - return self; -} - -- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender - didFinishSpeaking:(BOOL)finished_speaking { - ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_END, 0, ""); -} - -- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender - willSpeakWord:(NSRange)character_range - ofString:(NSString*)string { - ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_WORD, character_range.location, - ""); -} - -- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender - didEncounterErrorAtIndex:(NSUInteger)character_index - ofString:(NSString*)string - message:(NSString*)message { - std::string message_utf8 = base::SysNSStringToUTF8(message); - ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_ERROR, character_index, - message_utf8); -} - -@end - -@implementation SingleUseSpeechSynthesizer - -- (id)initWithUtterance:(NSString*)utterance { - self = [super init]; - if (self) { - utterance_.reset([utterance retain]); - didSpeak_ = false; - } - return self; -} - -- (bool)startSpeakingRetainedUtterance { - CHECK(!didSpeak_); - CHECK(utterance_); - didSpeak_ = true; - return [super startSpeakingString:utterance_]; -} - -- (bool)startSpeakingString:(NSString*)utterance { - CHECK(false); - return false; -} - -@end diff --git a/chromium_src/chrome/browser/speech/tts_message_filter.cc b/chromium_src/chrome/browser/speech/tts_message_filter.cc deleted file mode 100644 index 4def461843a63..0000000000000 --- a/chromium_src/chrome/browser/speech/tts_message_filter.cc +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) 2013 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/speech/tts_message_filter.h" - -#include "base/bind.h" -#include "base/logging.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/render_process_host.h" - -using content::BrowserThread; - -TtsMessageFilter::TtsMessageFilter(int render_process_id, - content::BrowserContext* browser_context) - : BrowserMessageFilter(TtsMsgStart), - render_process_id_(render_process_id), - browser_context_(browser_context), - weak_ptr_factory_(this) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - TtsController::GetInstance()->AddVoicesChangedDelegate(this); - - // Balanced in OnChannelClosingInUIThread() to keep the ref-count be non-zero - // until all WeakPtr's are invalidated. - AddRef(); -} - -void TtsMessageFilter::OverrideThreadForMessage(const IPC::Message& message, - BrowserThread::ID* thread) { - switch (message.type()) { - case TtsHostMsg_InitializeVoiceList::ID: - case TtsHostMsg_Speak::ID: - case TtsHostMsg_Pause::ID: - case TtsHostMsg_Resume::ID: - case TtsHostMsg_Cancel::ID: - *thread = BrowserThread::UI; - break; - } -} - -bool TtsMessageFilter::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(TtsMessageFilter, message) - IPC_MESSAGE_HANDLER(TtsHostMsg_InitializeVoiceList, OnInitializeVoiceList) - IPC_MESSAGE_HANDLER(TtsHostMsg_Speak, OnSpeak) - IPC_MESSAGE_HANDLER(TtsHostMsg_Pause, OnPause) - IPC_MESSAGE_HANDLER(TtsHostMsg_Resume, OnResume) - IPC_MESSAGE_HANDLER(TtsHostMsg_Cancel, OnCancel) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -void TtsMessageFilter::OnChannelClosing() { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&TtsMessageFilter::OnChannelClosingInUIThread, this)); -} - -void TtsMessageFilter::OnDestruct() const { - BrowserThread::DeleteOnUIThread::Destruct(this); -} - -void TtsMessageFilter::OnInitializeVoiceList() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - TtsController* tts_controller = TtsController::GetInstance(); - std::vector voices; - tts_controller->GetVoices(browser_context_, &voices); - - std::vector out_voices; - out_voices.resize(voices.size()); - for (size_t i = 0; i < voices.size(); ++i) { - TtsVoice& out_voice = out_voices[i]; - out_voice.voice_uri = voices[i].name; - out_voice.name = voices[i].name; - out_voice.lang = voices[i].lang; - out_voice.local_service = !voices[i].remote; - out_voice.is_default = (i == 0); - } - Send(new TtsMsg_SetVoiceList(out_voices)); -} - -void TtsMessageFilter::OnSpeak(const TtsUtteranceRequest& request) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - std::unique_ptr utterance(new Utterance(browser_context_)); - utterance->set_src_id(request.id); - utterance->set_text(request.text); - utterance->set_lang(request.lang); - utterance->set_voice_name(request.voice); - utterance->set_can_enqueue(true); - - UtteranceContinuousParameters params; - params.rate = request.rate; - params.pitch = request.pitch; - params.volume = request.volume; - utterance->set_continuous_parameters(params); - - utterance->set_event_delegate(weak_ptr_factory_.GetWeakPtr()); - - TtsController::GetInstance()->SpeakOrEnqueue(utterance.release()); -} - -void TtsMessageFilter::OnPause() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - TtsController::GetInstance()->Pause(); -} - -void TtsMessageFilter::OnResume() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - TtsController::GetInstance()->Resume(); -} - -void TtsMessageFilter::OnCancel() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - TtsController::GetInstance()->Stop(); -} - -void TtsMessageFilter::OnTtsEvent(Utterance* utterance, - TtsEventType event_type, - int char_index, - const std::string& error_message) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - switch (event_type) { - case TTS_EVENT_START: - Send(new TtsMsg_DidStartSpeaking(utterance->src_id())); - break; - case TTS_EVENT_END: - Send(new TtsMsg_DidFinishSpeaking(utterance->src_id())); - break; - case TTS_EVENT_WORD: - Send(new TtsMsg_WordBoundary(utterance->src_id(), char_index)); - break; - case TTS_EVENT_SENTENCE: - Send(new TtsMsg_SentenceBoundary(utterance->src_id(), char_index)); - break; - case TTS_EVENT_MARKER: - Send(new TtsMsg_MarkerEvent(utterance->src_id(), char_index)); - break; - case TTS_EVENT_INTERRUPTED: - Send(new TtsMsg_WasInterrupted(utterance->src_id())); - break; - case TTS_EVENT_CANCELLED: - Send(new TtsMsg_WasCancelled(utterance->src_id())); - break; - case TTS_EVENT_ERROR: - Send( - new TtsMsg_SpeakingErrorOccurred(utterance->src_id(), error_message)); - break; - case TTS_EVENT_PAUSE: - Send(new TtsMsg_DidPauseSpeaking(utterance->src_id())); - break; - case TTS_EVENT_RESUME: - Send(new TtsMsg_DidResumeSpeaking(utterance->src_id())); - break; - } -} - -void TtsMessageFilter::OnVoicesChanged() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - OnInitializeVoiceList(); -} - -void TtsMessageFilter::OnChannelClosingInUIThread() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - TtsController::GetInstance()->RemoveVoicesChangedDelegate(this); - - weak_ptr_factory_.InvalidateWeakPtrs(); - Release(); // Balanced in TtsMessageFilter(). -} - -TtsMessageFilter::~TtsMessageFilter() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(!weak_ptr_factory_.HasWeakPtrs()); - TtsController::GetInstance()->RemoveVoicesChangedDelegate(this); -} \ No newline at end of file diff --git a/chromium_src/chrome/browser/speech/tts_message_filter.h b/chromium_src/chrome/browser/speech/tts_message_filter.h deleted file mode 100644 index cb089d5c7d4a2..0000000000000 --- a/chromium_src/chrome/browser/speech/tts_message_filter.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2013 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. - -#ifndef CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_ -#define CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_ - -#include "base/memory/weak_ptr.h" -#include "chrome/browser/speech/tts_controller.h" -#include "chrome/common/tts_messages.h" -#include "content/public/browser/browser_message_filter.h" - -namespace content { -class BrowserContext; -} - -class TtsMessageFilter : public content::BrowserMessageFilter, - public UtteranceEventDelegate, - public VoicesChangedDelegate { - public: - explicit TtsMessageFilter(int render_process_id, - content::BrowserContext* browser_context); - - // content::BrowserMessageFilter implementation. - void OverrideThreadForMessage(const IPC::Message& message, - content::BrowserThread::ID* thread) override; - bool OnMessageReceived(const IPC::Message& message) override; - void OnChannelClosing() override; - void OnDestruct() const override; - - // UtteranceEventDelegate implementation. - void OnTtsEvent(Utterance* utterance, - TtsEventType event_type, - int char_index, - const std::string& error_message) override; - - // VoicesChangedDelegate implementation. - void OnVoicesChanged() override; - - private: - friend class content::BrowserThread; - friend class base::DeleteHelper; - - ~TtsMessageFilter() override; - - void OnInitializeVoiceList(); - void OnSpeak(const TtsUtteranceRequest& utterance); - void OnPause(); - void OnResume(); - void OnCancel(); - - void OnChannelClosingInUIThread(); - - int render_process_id_; - content::BrowserContext* browser_context_; - - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(TtsMessageFilter); -}; - -#endif // CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_ diff --git a/chromium_src/chrome/browser/speech/tts_platform.cc b/chromium_src/chrome/browser/speech/tts_platform.cc deleted file mode 100644 index 220e005f1816d..0000000000000 --- a/chromium_src/chrome/browser/speech/tts_platform.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2012 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/speech/tts_platform.h" - -#include - -bool TtsPlatformImpl::LoadBuiltInTtsExtension( - content::BrowserContext* browser_context) { - return false; -} - -std::string TtsPlatformImpl::error() { - return error_; -} - -void TtsPlatformImpl::clear_error() { - error_ = std::string(); -} - -void TtsPlatformImpl::set_error(const std::string& error) { - error_ = error; -} - -void TtsPlatformImpl::WillSpeakUtteranceWithVoice(const Utterance* utterance, - const VoiceData& voice_data) { -} \ No newline at end of file diff --git a/chromium_src/chrome/browser/speech/tts_platform.h b/chromium_src/chrome/browser/speech/tts_platform.h deleted file mode 100644 index 1a39d0ba0295c..0000000000000 --- a/chromium_src/chrome/browser/speech/tts_platform.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_ -#define CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_ - -#include - -#include "chrome/browser/speech/tts_controller.h" - -// Abstract class that defines the native platform TTS interface, -// subclassed by specific implementations on Win, Mac, etc. -class TtsPlatformImpl { - public: - static TtsPlatformImpl* GetInstance(); - - // Returns true if this platform implementation is supported and available. - virtual bool PlatformImplAvailable() = 0; - - // Some platforms may provide a built-in TTS extension. Returns true - // if the extension was not previously loaded and is now loading, and - // false if it's already loaded or if there's no extension to load. - // Will call TtsController::RetrySpeakingQueuedUtterances when - // the extension finishes loading. - virtual bool LoadBuiltInTtsExtension( - content::BrowserContext* browser_context); - - // Speak the given utterance with the given parameters if possible, - // and return true on success. Utterance will always be nonempty. - // If rate, pitch, or volume are -1.0, they will be ignored. - // - // The TtsController will only try to speak one utterance at - // a time. If it wants to interrupt speech, it will always call Stop - // before speaking again. - virtual bool Speak(int utterance_id, - const std::string& utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) = 0; - - // Stop speaking immediately and return true on success. - virtual bool StopSpeaking() = 0; - - // Returns whether any speech is on going. - virtual bool IsSpeaking() = 0; - - // Append information about voices provided by this platform implementation - // to |out_voices|. - virtual void GetVoices(std::vector* out_voices) = 0; - - // Pause the current utterance, if any, until a call to Resume, - // Speak, or StopSpeaking. - virtual void Pause() = 0; - - // Resume speaking the current utterance, if it was paused. - virtual void Resume() = 0; - - // Allows the platform to monitor speech commands and the voices used - // for each one. - virtual void WillSpeakUtteranceWithVoice(const Utterance* utterance, - const VoiceData& voice_data); - - virtual std::string error(); - virtual void clear_error(); - virtual void set_error(const std::string& error); - - protected: - TtsPlatformImpl() {} - - // On some platforms this may be a leaky singleton - do not rely on the - // destructor being called! http://crbug.com/122026 - virtual ~TtsPlatformImpl() {} - - std::string error_; - - DISALLOW_COPY_AND_ASSIGN(TtsPlatformImpl); -}; - -#endif // CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_ \ No newline at end of file diff --git a/chromium_src/chrome/browser/speech/tts_win.cc b/chromium_src/chrome/browser/speech/tts_win.cc deleted file mode 100644 index 18b95eecbc376..0000000000000 --- a/chromium_src/chrome/browser/speech/tts_win.cc +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright (c) 2012 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 -#include -#include -#include -#include - -#include "base/memory/singleton.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "base/values.h" -#include "base/win/scoped_co_mem.h" -#include "chrome/browser/speech/tts_controller.h" -#include "chrome/browser/speech/tts_platform.h" - -namespace { -// ISpObjectToken key and value names. -const wchar_t kAttributesKey[] = L"Attributes"; -const wchar_t kGenderValue[] = L"Gender"; -const wchar_t kLanguageValue[] = L"Language"; -} // anonymous namespace. - -class TtsPlatformImplWin : public TtsPlatformImpl { - public: - bool PlatformImplAvailable() override { return true; } - - bool Speak(int utterance_id, - const std::string& utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) override; - - bool StopSpeaking() override; - - void Pause() override; - - void Resume() override; - - bool IsSpeaking() override; - - void GetVoices(std::vector* out_voices) override; - - // Get the single instance of this class. - static TtsPlatformImplWin* GetInstance(); - - static void __stdcall SpeechEventCallback(WPARAM w_param, LPARAM l_param); - - private: - TtsPlatformImplWin(); - ~TtsPlatformImplWin() override {} - - void OnSpeechEvent(); - void SetVoiceFromName(const std::string& name); - Microsoft::WRL::ComPtr speech_synthesizer_; - - // These apply to the current utterance only. - std::wstring utterance_; - int utterance_id_; - int prefix_len_; - ULONG stream_number_; - int char_position_; - bool paused_; - std::string last_voice_name_; - friend struct base::DefaultSingletonTraits; - - DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplWin); -}; - -// static -TtsPlatformImpl* TtsPlatformImpl::GetInstance() { - return TtsPlatformImplWin::GetInstance(); -} - -bool TtsPlatformImplWin::Speak(int utterance_id, - const std::string& src_utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) { - std::wstring prefix; - std::wstring suffix; - - if (!speech_synthesizer_.Get()) - return false; - SetVoiceFromName(voice.name); - if (params.rate >= 0.0) { - // Map our multiplicative range of 0.1x to 10.0x onto Microsoft's - // linear range of -10 to 10: - // 0.1 -> -10 - // 1.0 -> 0 - // 10.0 -> 10 - speech_synthesizer_->SetRate(static_cast(10 * log10(params.rate))); - } - - if (params.pitch >= 0.0) { - // The TTS api allows a range of -10 to 10 for speech pitch. - // TODO(dtseng): cleanup if we ever use any other properties that - // require xml. - std::wstring pitch_value = - base::IntToString16(static_cast(params.pitch * 10 - 10)); - prefix = L""; - suffix = L""; - } - - if (params.volume >= 0.0) { - // The TTS api allows a range of 0 to 100 for speech volume. - speech_synthesizer_->SetVolume(static_cast(params.volume * 100)); - } - - // TODO(dmazzoni): convert SSML to SAPI xml. http://crbug.com/88072 - - utterance_ = base::UTF8ToWide(src_utterance); - utterance_id_ = utterance_id; - char_position_ = 0; - std::wstring merged_utterance = prefix + utterance_ + suffix; - prefix_len_ = prefix.size(); - - HRESULT result = speech_synthesizer_->Speak(merged_utterance.c_str(), - SPF_ASYNC, &stream_number_); - return (result == S_OK); -} - -bool TtsPlatformImplWin::StopSpeaking() { - if (speech_synthesizer_.Get()) { - // Clear the stream number so that any further events relating to this - // utterance are ignored. - stream_number_ = 0; - - if (IsSpeaking()) { - // Stop speech by speaking the empty string with the purge flag. - speech_synthesizer_->Speak(L"", SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL); - } - if (paused_) { - speech_synthesizer_->Resume(); - paused_ = false; - } - } - return true; -} - -void TtsPlatformImplWin::Pause() { - if (speech_synthesizer_.Get() && utterance_id_ && !paused_) { - speech_synthesizer_->Pause(); - paused_ = true; - TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE, - char_position_, ""); - } -} - -void TtsPlatformImplWin::Resume() { - if (speech_synthesizer_.Get() && utterance_id_ && paused_) { - speech_synthesizer_->Resume(); - paused_ = false; - TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME, - char_position_, ""); - } -} - -bool TtsPlatformImplWin::IsSpeaking() { - if (speech_synthesizer_.Get()) { - SPVOICESTATUS status; - HRESULT result = speech_synthesizer_->GetStatus(&status, NULL); - if (result == S_OK) { - if (status.dwRunningState == 0 || // 0 == waiting to speak - status.dwRunningState == SPRS_IS_SPEAKING) { - return true; - } - } - } - return false; -} - -void TtsPlatformImplWin::GetVoices(std::vector* out_voices) { - Microsoft::WRL::ComPtr voice_tokens; - unsigned long voice_count; - if (S_OK != - SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.GetAddressOf())) - return; - if (S_OK != voice_tokens->GetCount(&voice_count)) - return; - for (unsigned i = 0; i < voice_count; i++) { - VoiceData voice; - Microsoft::WRL::ComPtr voice_token; - if (S_OK != voice_tokens->Next(1, voice_token.GetAddressOf(), NULL)) - return; - base::win::ScopedCoMem description; - if (S_OK != SpGetDescription(voice_token.Get(), &description)) - continue; - voice.name = base::WideToUTF8(description.get()); - Microsoft::WRL::ComPtr attributes; - if (S_OK != voice_token->OpenKey(kAttributesKey, attributes.GetAddressOf())) - continue; - base::win::ScopedCoMem gender; - if (S_OK == attributes->GetStringValue(kGenderValue, &gender)) { - if (0 == _wcsicmp(gender.get(), L"male")) - voice.gender = TTS_GENDER_MALE; - else if (0 == _wcsicmp(gender.get(), L"female")) - voice.gender = TTS_GENDER_FEMALE; - } - base::win::ScopedCoMem language; - if (S_OK == attributes->GetStringValue(kLanguageValue, &language)) { - int lcid_value; - base::HexStringToInt(base::WideToUTF8(language.get()), &lcid_value); - LCID lcid = MAKELCID(lcid_value, SORT_DEFAULT); - WCHAR locale_name[LOCALE_NAME_MAX_LENGTH] = {0}; - LCIDToLocaleName(lcid, locale_name, LOCALE_NAME_MAX_LENGTH, 0); - voice.lang = base::WideToUTF8(locale_name); - } - voice.native = true; - voice.events.insert(TTS_EVENT_START); - voice.events.insert(TTS_EVENT_END); - voice.events.insert(TTS_EVENT_MARKER); - voice.events.insert(TTS_EVENT_WORD); - voice.events.insert(TTS_EVENT_SENTENCE); - voice.events.insert(TTS_EVENT_PAUSE); - voice.events.insert(TTS_EVENT_RESUME); - out_voices->push_back(voice); - } -} - -void TtsPlatformImplWin::OnSpeechEvent() { - TtsController* controller = TtsController::GetInstance(); - SPEVENT event; - while (S_OK == speech_synthesizer_->GetEvents(1, &event, NULL)) { - if (event.ulStreamNum != stream_number_) - continue; - - switch (event.eEventId) { - case SPEI_START_INPUT_STREAM: - controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, - std::string()); - break; - case SPEI_END_INPUT_STREAM: - char_position_ = utterance_.size(); - controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, char_position_, - std::string()); - break; - case SPEI_TTS_BOOKMARK: - controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, char_position_, - std::string()); - break; - case SPEI_WORD_BOUNDARY: - char_position_ = static_cast(event.lParam) - prefix_len_; - controller->OnTtsEvent(utterance_id_, TTS_EVENT_WORD, char_position_, - std::string()); - break; - case SPEI_SENTENCE_BOUNDARY: - char_position_ = static_cast(event.lParam) - prefix_len_; - controller->OnTtsEvent(utterance_id_, TTS_EVENT_SENTENCE, - char_position_, std::string()); - break; - default: - break; - } - } -} -void TtsPlatformImplWin::SetVoiceFromName(const std::string& name) { - if (name.empty() || name == last_voice_name_) - return; - last_voice_name_ = name; - Microsoft::WRL::ComPtr voice_tokens; - unsigned long voice_count; - if (S_OK != - SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.GetAddressOf())) - return; - if (S_OK != voice_tokens->GetCount(&voice_count)) - return; - for (unsigned i = 0; i < voice_count; i++) { - Microsoft::WRL::ComPtr voice_token; - if (S_OK != voice_tokens->Next(1, voice_token.GetAddressOf(), NULL)) - return; - base::win::ScopedCoMem description; - if (S_OK != SpGetDescription(voice_token.Get(), &description)) - continue; - if (name == base::WideToUTF8(description.get())) { - speech_synthesizer_->SetVoice(voice_token.Get()); - break; - } - } -} -TtsPlatformImplWin::TtsPlatformImplWin() - : utterance_id_(0), - prefix_len_(0), - stream_number_(0), - char_position_(0), - paused_(false) { - ::CoCreateInstance(CLSID_SpVoice, nullptr, CLSCTX_ALL, - IID_PPV_ARGS(&speech_synthesizer_)); - if (speech_synthesizer_.Get()) { - ULONGLONG event_mask = - SPFEI(SPEI_START_INPUT_STREAM) | SPFEI(SPEI_TTS_BOOKMARK) | - SPFEI(SPEI_WORD_BOUNDARY) | SPFEI(SPEI_SENTENCE_BOUNDARY) | - SPFEI(SPEI_END_INPUT_STREAM); - speech_synthesizer_->SetInterest(event_mask, event_mask); - speech_synthesizer_->SetNotifyCallbackFunction( - TtsPlatformImplWin::SpeechEventCallback, 0, 0); - } -} - -// static -TtsPlatformImplWin* TtsPlatformImplWin::GetInstance() { - return base::Singleton>::get(); -} - -// static -void TtsPlatformImplWin::SpeechEventCallback(WPARAM w_param, LPARAM l_param) { - GetInstance()->OnSpeechEvent(); -} diff --git a/chromium_src/chrome/common/tts_messages.h b/chromium_src/chrome/common/tts_messages.h deleted file mode 100644 index f4f20172fbf77..0000000000000 --- a/chromium_src/chrome/common/tts_messages.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2013 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. - -// Multiply-included message file, hence no include guard. - -#include - -#include "chrome/common/tts_utterance_request.h" -#include "ipc/ipc_message_macros.h" -#include "ipc/ipc_param_traits.h" - -#define IPC_MESSAGE_START TtsMsgStart - -IPC_STRUCT_TRAITS_BEGIN(TtsUtteranceRequest) - IPC_STRUCT_TRAITS_MEMBER(id) - IPC_STRUCT_TRAITS_MEMBER(text) - IPC_STRUCT_TRAITS_MEMBER(lang) - IPC_STRUCT_TRAITS_MEMBER(voice) - IPC_STRUCT_TRAITS_MEMBER(volume) - IPC_STRUCT_TRAITS_MEMBER(rate) - IPC_STRUCT_TRAITS_MEMBER(pitch) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(TtsVoice) - IPC_STRUCT_TRAITS_MEMBER(voice_uri) - IPC_STRUCT_TRAITS_MEMBER(name) - IPC_STRUCT_TRAITS_MEMBER(lang) - IPC_STRUCT_TRAITS_MEMBER(local_service) - IPC_STRUCT_TRAITS_MEMBER(is_default) -IPC_STRUCT_TRAITS_END() - -// Renderer -> Browser messages. - -IPC_MESSAGE_CONTROL0(TtsHostMsg_InitializeVoiceList) -IPC_MESSAGE_CONTROL1(TtsHostMsg_Speak, TtsUtteranceRequest) -IPC_MESSAGE_CONTROL0(TtsHostMsg_Pause) -IPC_MESSAGE_CONTROL0(TtsHostMsg_Resume) -IPC_MESSAGE_CONTROL0(TtsHostMsg_Cancel) - -// Browser -> Renderer messages. - -IPC_MESSAGE_CONTROL1(TtsMsg_SetVoiceList, std::vector) -IPC_MESSAGE_CONTROL1(TtsMsg_DidStartSpeaking, int /* utterance id */) -IPC_MESSAGE_CONTROL1(TtsMsg_DidFinishSpeaking, int /* utterance id */) -IPC_MESSAGE_CONTROL1(TtsMsg_DidPauseSpeaking, int /* utterance id */) -IPC_MESSAGE_CONTROL1(TtsMsg_DidResumeSpeaking, int /* utterance id */) -IPC_MESSAGE_CONTROL2(TtsMsg_WordBoundary, - int /* utterance id */, - int /* char index */) -IPC_MESSAGE_CONTROL2(TtsMsg_SentenceBoundary, - int /* utterance id */, - int /* char index */) -IPC_MESSAGE_CONTROL2(TtsMsg_MarkerEvent, - int /* utterance id */, - int /* char index */) -IPC_MESSAGE_CONTROL1(TtsMsg_WasInterrupted, int /* utterance id */) -IPC_MESSAGE_CONTROL1(TtsMsg_WasCancelled, int /* utterance id */) -IPC_MESSAGE_CONTROL2(TtsMsg_SpeakingErrorOccurred, - int /* utterance id */, - std::string /* error message */) \ No newline at end of file diff --git a/chromium_src/chrome/common/tts_utterance_request.cc b/chromium_src/chrome/common/tts_utterance_request.cc deleted file mode 100644 index 66b783cfafd15..0000000000000 --- a/chromium_src/chrome/common/tts_utterance_request.cc +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2013 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/common/tts_utterance_request.h" - -TtsUtteranceRequest::TtsUtteranceRequest() - : id(0), volume(1.0), rate(1.0), pitch(1.0) {} - -TtsUtteranceRequest::~TtsUtteranceRequest() {} - -TtsVoice::TtsVoice() : local_service(true), is_default(false) {} - -TtsVoice::TtsVoice(const TtsVoice&) = default; - -TtsVoice::~TtsVoice() {} - -TtsUtteranceResponse::TtsUtteranceResponse() : id(0) {} - -TtsUtteranceResponse::~TtsUtteranceResponse() {} diff --git a/chromium_src/chrome/common/tts_utterance_request.h b/chromium_src/chrome/common/tts_utterance_request.h deleted file mode 100644 index 8523482ba89d1..0000000000000 --- a/chromium_src/chrome/common/tts_utterance_request.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2013 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. - -#ifndef CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_ -#define CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_ - -#include - -#include "base/macros.h" -#include "base/strings/string16.h" - -struct TtsUtteranceRequest { - TtsUtteranceRequest(); - ~TtsUtteranceRequest(); - - int id; - std::string text; - std::string lang; - std::string voice; - float volume; - float rate; - float pitch; -}; - -struct TtsVoice { - TtsVoice(); - TtsVoice(const TtsVoice&); - ~TtsVoice(); - - std::string voice_uri; - std::string name; - std::string lang; - bool local_service; - bool is_default; -}; - -struct TtsUtteranceResponse { - TtsUtteranceResponse(); - ~TtsUtteranceResponse(); - - int id; -}; - -#endif // CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_ diff --git a/chromium_src/chrome/renderer/tts_dispatcher.cc b/chromium_src/chrome/renderer/tts_dispatcher.cc deleted file mode 100644 index caaa1dd3093db..0000000000000 --- a/chromium_src/chrome/renderer/tts_dispatcher.cc +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) 2013 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/renderer/tts_dispatcher.h" - -#include "base/strings/utf_string_conversions.h" -#include "chrome/common/tts_messages.h" -#include "chrome/common/tts_utterance_request.h" -#include "content/public/renderer/render_thread.h" -#include "third_party/blink/public/platform/web_speech_synthesis_utterance.h" -#include "third_party/blink/public/platform/web_speech_synthesis_voice.h" -#include "third_party/blink/public/platform/web_string.h" -#include "third_party/blink/public/platform/web_vector.h" - -using blink::WebSpeechSynthesisUtterance; -using blink::WebSpeechSynthesisVoice; -using blink::WebSpeechSynthesizerClient; -using blink::WebString; -using blink::WebVector; -using content::RenderThread; - -int TtsDispatcher::next_utterance_id_ = 1; - -TtsDispatcher::TtsDispatcher(WebSpeechSynthesizerClient* client) - : synthesizer_client_(client) { - RenderThread::Get()->AddObserver(this); -} - -TtsDispatcher::~TtsDispatcher() { - RenderThread::Get()->RemoveObserver(this); -} - -bool TtsDispatcher::OnControlMessageReceived(const IPC::Message& message) { - IPC_BEGIN_MESSAGE_MAP(TtsDispatcher, message) - IPC_MESSAGE_HANDLER(TtsMsg_SetVoiceList, OnSetVoiceList) - IPC_MESSAGE_HANDLER(TtsMsg_DidStartSpeaking, OnDidStartSpeaking) - IPC_MESSAGE_HANDLER(TtsMsg_DidFinishSpeaking, OnDidFinishSpeaking) - IPC_MESSAGE_HANDLER(TtsMsg_DidPauseSpeaking, OnDidPauseSpeaking) - IPC_MESSAGE_HANDLER(TtsMsg_DidResumeSpeaking, OnDidResumeSpeaking) - IPC_MESSAGE_HANDLER(TtsMsg_WordBoundary, OnWordBoundary) - IPC_MESSAGE_HANDLER(TtsMsg_SentenceBoundary, OnSentenceBoundary) - IPC_MESSAGE_HANDLER(TtsMsg_MarkerEvent, OnMarkerEvent) - IPC_MESSAGE_HANDLER(TtsMsg_WasInterrupted, OnWasInterrupted) - IPC_MESSAGE_HANDLER(TtsMsg_WasCancelled, OnWasCancelled) - IPC_MESSAGE_HANDLER(TtsMsg_SpeakingErrorOccurred, OnSpeakingErrorOccurred) - IPC_END_MESSAGE_MAP() - - // Always return false because there may be multiple TtsDispatchers - // and we want them all to have a chance to handle this message. - return false; -} - -void TtsDispatcher::UpdateVoiceList() { - RenderThread::Get()->Send(new TtsHostMsg_InitializeVoiceList()); -} - -void TtsDispatcher::Speak(const WebSpeechSynthesisUtterance& web_utterance) { - int id = next_utterance_id_++; - - utterance_id_map_[id] = web_utterance; - - TtsUtteranceRequest utterance; - utterance.id = id; - utterance.text = web_utterance.GetText().Utf8(); - utterance.lang = web_utterance.Lang().Utf8(); - utterance.voice = web_utterance.Voice().Utf8(); - utterance.volume = web_utterance.Volume(); - utterance.rate = web_utterance.Rate(); - utterance.pitch = web_utterance.Pitch(); - RenderThread::Get()->Send(new TtsHostMsg_Speak(utterance)); -} - -void TtsDispatcher::Pause() { - RenderThread::Get()->Send(new TtsHostMsg_Pause()); -} - -void TtsDispatcher::Resume() { - RenderThread::Get()->Send(new TtsHostMsg_Resume()); -} - -void TtsDispatcher::Cancel() { - RenderThread::Get()->Send(new TtsHostMsg_Cancel()); -} - -WebSpeechSynthesisUtterance TtsDispatcher::FindUtterance(int utterance_id) { - base::hash_map::const_iterator iter = - utterance_id_map_.find(utterance_id); - if (iter == utterance_id_map_.end()) - return WebSpeechSynthesisUtterance(); - return iter->second; -} - -void TtsDispatcher::OnSetVoiceList(const std::vector& voices) { - WebVector out_voices(voices.size()); - for (size_t i = 0; i < voices.size(); ++i) { - out_voices[i] = WebSpeechSynthesisVoice(); - out_voices[i].SetVoiceURI(WebString::FromUTF8(voices[i].voice_uri)); - out_voices[i].SetName(WebString::FromUTF8(voices[i].name)); - out_voices[i].SetLanguage(WebString::FromUTF8(voices[i].lang)); - out_voices[i].SetIsLocalService(voices[i].local_service); - out_voices[i].SetIsDefault(voices[i].is_default); - } - synthesizer_client_->SetVoiceList(out_voices); -} - -void TtsDispatcher::OnDidStartSpeaking(int utterance_id) { - if (utterance_id_map_.find(utterance_id) == utterance_id_map_.end()) - return; - - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.IsNull()) - return; - - synthesizer_client_->DidStartSpeaking(utterance); -} - -void TtsDispatcher::OnDidFinishSpeaking(int utterance_id) { - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.IsNull()) - return; - - synthesizer_client_->DidFinishSpeaking(utterance); - utterance_id_map_.erase(utterance_id); -} - -void TtsDispatcher::OnDidPauseSpeaking(int utterance_id) { - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.IsNull()) - return; - - synthesizer_client_->DidPauseSpeaking(utterance); -} - -void TtsDispatcher::OnDidResumeSpeaking(int utterance_id) { - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.IsNull()) - return; - - synthesizer_client_->DidResumeSpeaking(utterance); -} - -void TtsDispatcher::OnWordBoundary(int utterance_id, int char_index) { - CHECK(char_index >= 0); - - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.IsNull()) - return; - - synthesizer_client_->WordBoundaryEventOccurred( - utterance, static_cast(char_index)); -} - -void TtsDispatcher::OnSentenceBoundary(int utterance_id, int char_index) { - CHECK(char_index >= 0); - - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.IsNull()) - return; - - synthesizer_client_->SentenceBoundaryEventOccurred( - utterance, static_cast(char_index)); -} - -void TtsDispatcher::OnMarkerEvent(int utterance_id, int char_index) { - // Not supported yet. -} - -void TtsDispatcher::OnWasInterrupted(int utterance_id) { - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.IsNull()) - return; - - // The web speech API doesn't support "interrupted". - synthesizer_client_->DidFinishSpeaking(utterance); - utterance_id_map_.erase(utterance_id); -} - -void TtsDispatcher::OnWasCancelled(int utterance_id) { - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.IsNull()) - return; - - // The web speech API doesn't support "cancelled". - synthesizer_client_->DidFinishSpeaking(utterance); - utterance_id_map_.erase(utterance_id); -} - -void TtsDispatcher::OnSpeakingErrorOccurred(int utterance_id, - const std::string& error_message) { - WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id); - if (utterance.IsNull()) - return; - - // The web speech API doesn't support an error message. - synthesizer_client_->SpeakingErrorOccurred(utterance); - utterance_id_map_.erase(utterance_id); -} diff --git a/chromium_src/chrome/renderer/tts_dispatcher.h b/chromium_src/chrome/renderer/tts_dispatcher.h deleted file mode 100644 index f81d53b2f79da..0000000000000 --- a/chromium_src/chrome/renderer/tts_dispatcher.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2013 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. - -#ifndef CHROME_RENDERER_TTS_DISPATCHER_H_ -#define CHROME_RENDERER_TTS_DISPATCHER_H_ - -#include - -#include "base/containers/hash_tables.h" -#include "content/public/renderer/render_thread_observer.h" -#include "third_party/blink/public/platform/web_speech_synthesizer.h" -#include "third_party/blink/public/platform/web_speech_synthesizer_client.h" - -namespace IPC { -class Message; -} - -struct TtsVoice; - -// TtsDispatcher is a delegate for methods used by Blink for speech synthesis -// APIs. It's the complement of TtsDispatcherHost (owned by RenderViewHost). -// Each TtsDispatcher is owned by the WebSpeechSynthesizerClient in Blink; -// it registers itself to listen to IPC upon construction and unregisters -// itself when deleted. There can be multiple TtsDispatchers alive at once, -// so each one routes IPC messages to its WebSpeechSynthesizerClient only if -// the utterance id (which is globally unique) matches. -class TtsDispatcher : public blink::WebSpeechSynthesizer, - public content::RenderThreadObserver { - public: - explicit TtsDispatcher(blink::WebSpeechSynthesizerClient* client); - ~TtsDispatcher() override; - - private: - // RenderProcessObserver override. - bool OnControlMessageReceived(const IPC::Message& message) override; - - // blink::WebSpeechSynthesizer implementation. - void UpdateVoiceList() override; - void Speak(const blink::WebSpeechSynthesisUtterance& utterance) override; - void Pause() override; - void Resume() override; - void Cancel() override; - - blink::WebSpeechSynthesisUtterance FindUtterance(int utterance_id); - - void OnSetVoiceList(const std::vector& voices); - void OnDidStartSpeaking(int utterance_id); - void OnDidFinishSpeaking(int utterance_id); - void OnDidPauseSpeaking(int utterance_id); - void OnDidResumeSpeaking(int utterance_id); - void OnWordBoundary(int utterance_id, int char_index); - void OnSentenceBoundary(int utterance_id, int char_index); - void OnMarkerEvent(int utterance_id, int char_index); - void OnWasInterrupted(int utterance_id); - void OnWasCancelled(int utterance_id); - void OnSpeakingErrorOccurred(int utterance_id, - const std::string& error_message); - - // The WebKit client class that we use to send events back to the JS world. - // Weak reference, this will be valid as long as this object exists. - blink::WebSpeechSynthesizerClient* synthesizer_client_; - - // Next utterance id, used to map response IPCs to utterance objects. - static int next_utterance_id_; - - // Map from id to utterance objects. - base::hash_map utterance_id_map_; - - DISALLOW_COPY_AND_ASSIGN(TtsDispatcher); -}; - -#endif // CHROME_RENDERER_TTS_DISPATCHER_H_ diff --git a/chromium_src/library_loaders/libspeechd.h b/chromium_src/library_loaders/libspeechd.h deleted file mode 100644 index a5dfe73310858..0000000000000 --- a/chromium_src/library_loaders/libspeechd.h +++ /dev/null @@ -1,52 +0,0 @@ -// This is generated file. Do not modify directly. -// Path to the code generator: -// tools/generate_library_loader/generate_library_loader.py . - -#ifndef LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H -#define LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H - -#include "third_party/speech-dispatcher/libspeechd.h" -#define LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN - -#include - -class LibSpeechdLoader { - public: - LibSpeechdLoader(); - ~LibSpeechdLoader(); - - bool Load(const std::string& library_name) - __attribute__((warn_unused_result)); - - bool loaded() const { return loaded_; } - - decltype(&::spd_open) spd_open; - decltype(&::spd_say) spd_say; - decltype(&::spd_stop) spd_stop; - decltype(&::spd_close) spd_close; - decltype(&::spd_pause) spd_pause; - decltype(&::spd_resume) spd_resume; - decltype(&::spd_set_notification_on) spd_set_notification_on; - decltype(&::spd_set_voice_rate) spd_set_voice_rate; - decltype(&::spd_set_voice_pitch) spd_set_voice_pitch; - decltype(&::spd_list_synthesis_voices) spd_list_synthesis_voices; - decltype(&::spd_set_synthesis_voice) spd_set_synthesis_voice; - decltype(&::spd_list_modules) spd_list_modules; - decltype(&::spd_set_output_module) spd_set_output_module; - decltype(&::spd_set_language) spd_set_language; - - private: - void CleanUp(bool unload); - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - void* library_; -#endif - - bool loaded_; - - // Disallow copy constructor and assignment operator. - LibSpeechdLoader(const LibSpeechdLoader&); - void operator=(const LibSpeechdLoader&); -}; - -#endif // LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H diff --git a/chromium_src/library_loaders/libspeechd_loader.cc b/chromium_src/library_loaders/libspeechd_loader.cc deleted file mode 100644 index 1ab686be7f2f0..0000000000000 --- a/chromium_src/library_loaders/libspeechd_loader.cc +++ /dev/null @@ -1,252 +0,0 @@ -// This is generated file. Do not modify directly. -// Path to the code generator: -// tools/generate_library_loader/generate_library_loader.py . - -#include "library_loaders/libspeechd.h" - -#include - -// Put these sanity checks here so that they fire at most once -// (to avoid cluttering the build output). -#if !defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) && \ - !defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) -#error neither LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN nor LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED defined -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) && \ - defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) -#error both LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN and LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED defined -#endif - -LibSpeechdLoader::LibSpeechdLoader() : loaded_(false) {} - -LibSpeechdLoader::~LibSpeechdLoader() { - CleanUp(loaded_); -} - -bool LibSpeechdLoader::Load(const std::string& library_name) { - if (loaded_) - return false; - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - library_ = dlopen(library_name.c_str(), RTLD_LAZY); - if (!library_) - return false; -#endif - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_open = - reinterpret_castspd_open)>(dlsym(library_, "spd_open")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_open = &::spd_open; -#endif - if (!spd_open) { - CleanUp(true); - return false; - } - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_say = - reinterpret_castspd_say)>(dlsym(library_, "spd_say")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_say = &::spd_say; -#endif - if (!spd_say) { - CleanUp(true); - return false; - } - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_stop = - reinterpret_castspd_stop)>(dlsym(library_, "spd_stop")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_stop = &::spd_stop; -#endif - if (!spd_stop) { - CleanUp(true); - return false; - } - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_close = - reinterpret_castspd_close)>(dlsym(library_, "spd_close")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_close = &::spd_close; -#endif - if (!spd_close) { - CleanUp(true); - return false; - } - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_pause = - reinterpret_castspd_pause)>(dlsym(library_, "spd_pause")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_pause = &::spd_pause; -#endif - if (!spd_pause) { - CleanUp(true); - return false; - } - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_resume = reinterpret_castspd_resume)>( - dlsym(library_, "spd_resume")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_resume = &::spd_resume; -#endif - if (!spd_resume) { - CleanUp(true); - return false; - } - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_set_notification_on = - reinterpret_castspd_set_notification_on)>( - dlsym(library_, "spd_set_notification_on")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_set_notification_on = &::spd_set_notification_on; -#endif - if (!spd_set_notification_on) { - CleanUp(true); - return false; - } - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_set_voice_rate = reinterpret_castspd_set_voice_rate)>( - dlsym(library_, "spd_set_voice_rate")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_set_voice_rate = &::spd_set_voice_rate; -#endif - if (!spd_set_voice_rate) { - CleanUp(true); - return false; - } - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_set_voice_pitch = reinterpret_castspd_set_voice_pitch)>( - dlsym(library_, "spd_set_voice_pitch")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_set_voice_pitch = &::spd_set_voice_pitch; -#endif - if (!spd_set_voice_pitch) { - CleanUp(true); - return false; - } - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_list_synthesis_voices = - reinterpret_castspd_list_synthesis_voices)>( - dlsym(library_, "spd_list_synthesis_voices")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_list_synthesis_voices = &::spd_list_synthesis_voices; -#endif - if (!spd_list_synthesis_voices) { - CleanUp(true); - return false; - } - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_set_synthesis_voice = - reinterpret_castspd_set_synthesis_voice)>( - dlsym(library_, "spd_set_synthesis_voice")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_set_synthesis_voice = &::spd_set_synthesis_voice; -#endif - if (!spd_set_synthesis_voice) { - CleanUp(true); - return false; - } - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_list_modules = reinterpret_castspd_list_modules)>( - dlsym(library_, "spd_list_modules")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_list_modules = &::spd_list_modules; -#endif - if (!spd_list_modules) { - CleanUp(true); - return false; - } - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_set_output_module = - reinterpret_castspd_set_output_module)>( - dlsym(library_, "spd_set_output_module")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_set_output_module = &::spd_set_output_module; -#endif - if (!spd_set_output_module) { - CleanUp(true); - return false; - } - -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - spd_set_language = reinterpret_castspd_set_language)>( - dlsym(library_, "spd_set_language")); -#endif -#if defined( \ - LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) - spd_set_language = &::spd_set_language; -#endif - if (!spd_set_language) { - CleanUp(true); - return false; - } - - loaded_ = true; - return true; -} - -void LibSpeechdLoader::CleanUp(bool unload) { -#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) - if (unload) { - dlclose(library_); - library_ = NULL; - } -#endif - loaded_ = false; - spd_open = NULL; - spd_say = NULL; - spd_stop = NULL; - spd_close = NULL; - spd_pause = NULL; - spd_resume = NULL; - spd_set_notification_on = NULL; - spd_set_voice_rate = NULL; - spd_set_voice_pitch = NULL; - spd_list_synthesis_voices = NULL; - spd_set_synthesis_voice = NULL; - spd_list_modules = NULL; - spd_set_output_module = NULL; - spd_set_language = NULL; -} diff --git a/filenames.gni b/filenames.gni index 8e1d6b9a062eb..9b05bbe2af22a 100644 --- a/filenames.gni +++ b/filenames.gni @@ -598,31 +598,14 @@ filenames = { "chromium_src/chrome/browser/process_singleton_posix.cc", "chromium_src/chrome/browser/process_singleton_win.cc", "chromium_src/chrome/browser/process_singleton.h", - "chromium_src/chrome/browser/speech/tts_controller.h", - "chromium_src/chrome/browser/speech/tts_controller_impl.cc", - "chromium_src/chrome/browser/speech/tts_controller_impl.h", - "chromium_src/chrome/browser/speech/tts_linux.cc", - "chromium_src/chrome/browser/speech/tts_mac.mm", - "chromium_src/chrome/browser/speech/tts_message_filter.cc", - "chromium_src/chrome/browser/speech/tts_message_filter.h", - "chromium_src/chrome/browser/speech/tts_platform.cc", - "chromium_src/chrome/browser/speech/tts_platform.h", - "chromium_src/chrome/browser/speech/tts_win.cc", "chromium_src/chrome/browser/ui/browser_dialogs.h", "chromium_src/chrome/browser/ui/cocoa/color_chooser_mac.mm", "chromium_src/chrome/browser/ui/views/color_chooser_aura.cc", "chromium_src/chrome/browser/ui/views/color_chooser_aura.h", "chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc", "chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h", - "chromium_src/chrome/common/tts_messages.h", - "chromium_src/chrome/common/tts_utterance_request.cc", - "chromium_src/chrome/common/tts_utterance_request.h", - "chromium_src/chrome/renderer/tts_dispatcher.cc", - "chromium_src/chrome/renderer/tts_dispatcher.h", "chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.cc", "chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.h", - "chromium_src/library_loaders/libspeechd_loader.cc", - "chromium_src/library_loaders/libspeechd.h", ] lib_sources_nss = [ diff --git a/patches/common/chromium/.patches b/patches/common/chromium/.patches index 10f7b9b1a3f7c..3e2fea5d4f336 100644 --- a/patches/common/chromium/.patches +++ b/patches/common/chromium/.patches @@ -88,3 +88,4 @@ ensure_cookie_store.patch disable_color_correct_rendering.patch sqlite_upgrade_from_3_24_to_3_26.patch sqlite_update_api_3_26.patch +tts.patch diff --git a/patches/common/chromium/tts.patch b/patches/common/chromium/tts.patch new file mode 100644 index 0000000000000..91d8d3a2da593 --- /dev/null +++ b/patches/common/chromium/tts.patch @@ -0,0 +1,176 @@ +diff --git a/chrome/browser/speech/tts_controller_impl.cc b/chrome/browser/speech/tts_controller_impl.cc +index 2ca56c3a247e..f58e33b3c019 100644 +--- a/chrome/browser/speech/tts_controller_impl.cc ++++ b/chrome/browser/speech/tts_controller_impl.cc +@@ -624,12 +624,14 @@ const PrefService* TtsControllerImpl::GetPrefService( + const Utterance* utterance) { + const PrefService* prefs = nullptr; + // The utterance->browser_context() is null in tests. ++#if 0 + if (utterance->browser_context()) { + const Profile* profile = + Profile::FromBrowserContext(utterance->browser_context()); + if (profile) + prefs = profile->GetPrefs(); + } ++#endif + return prefs; + } + +diff --git a/chrome/browser/speech/tts_mac.mm b/chrome/browser/speech/tts_mac.mm +index 74996d854df3..d3bf665c2963 100644 +--- a/chrome/browser/speech/tts_mac.mm ++++ b/chrome/browser/speech/tts_mac.mm +@@ -11,7 +11,6 @@ + #include "base/values.h" + #include "chrome/browser/speech/tts_controller.h" + #include "chrome/browser/speech/tts_platform.h" +-#include "extensions/browser/extension_function.h" + + #import + +diff --git a/chrome/browser/speech/tts_message_filter.cc b/chrome/browser/speech/tts_message_filter.cc +index 013c7a9c60f9..73a244a726e3 100644 +--- a/chrome/browser/speech/tts_message_filter.cc ++++ b/chrome/browser/speech/tts_message_filter.cc +@@ -9,14 +9,40 @@ + #include "base/bind.h" + #include "base/logging.h" + #include "chrome/browser/chrome_notification_types.h" ++#if 0 + #include "chrome/browser/profiles/profile.h" ++#endif + #include "chrome/common/tts_messages.h" ++#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h" + #include "content/public/browser/browser_context.h" + #include "content/public/browser/notification_service.h" + #include "content/public/browser/render_process_host.h" + + using content::BrowserThread; + ++namespace { ++ ++class TtsMessageFilterShutdownNotifierFactory ++ : public BrowserContextKeyedServiceShutdownNotifierFactory { ++ public: ++ static TtsMessageFilterShutdownNotifierFactory* GetInstance() { ++ return base::Singleton::get(); ++ } ++ ++ private: ++ friend struct base::DefaultSingletonTraits< ++ TtsMessageFilterShutdownNotifierFactory>; ++ ++ TtsMessageFilterShutdownNotifierFactory() ++ : BrowserContextKeyedServiceShutdownNotifierFactory("TtsMessageFilter") {} ++ ++ ~TtsMessageFilterShutdownNotifierFactory() override {} ++ ++ DISALLOW_COPY_AND_ASSIGN(TtsMessageFilterShutdownNotifierFactory); ++}; ++ ++} // namespace ++ + TtsMessageFilter::TtsMessageFilter(content::BrowserContext* browser_context) + : BrowserMessageFilter(TtsMsgStart), + browser_context_(browser_context), +@@ -24,28 +50,27 @@ TtsMessageFilter::TtsMessageFilter(content::BrowserContext* browser_context) + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + TtsController::GetInstance()->AddVoicesChangedDelegate(this); + +- // TODO(dmazzoni): make it so that we can listen for a BrowserContext +- // being destroyed rather than a Profile. http://crbug.com/444668 +- Profile* profile = Profile::FromBrowserContext(browser_context); +- notification_registrar_.Add(this, +- chrome::NOTIFICATION_PROFILE_DESTROYED, +- content::Source(profile)); ++ browser_context_shutdown_notifier_ = ++ TtsMessageFilterShutdownNotifierFactory::GetInstance() ++ ->Get(browser_context) ++ ->Subscribe(base::Bind(&TtsMessageFilter::BrowserContextDestroyed, ++ base::RetainedRef(this))); + + // Balanced in OnChannelClosingInUIThread() to keep the ref-count be non-zero + // until all pointers to this class are invalidated. + AddRef(); + } + +-void TtsMessageFilter::OverrideThreadForMessage( +- const IPC::Message& message, BrowserThread::ID* thread) { ++void TtsMessageFilter::OverrideThreadForMessage(const IPC::Message& message, ++ BrowserThread::ID* thread) { + switch (message.type()) { +- case TtsHostMsg_InitializeVoiceList::ID: +- case TtsHostMsg_Speak::ID: +- case TtsHostMsg_Pause::ID: +- case TtsHostMsg_Resume::ID: +- case TtsHostMsg_Cancel::ID: +- *thread = BrowserThread::UI; +- break; ++ case TtsHostMsg_InitializeVoiceList::ID: ++ case TtsHostMsg_Speak::ID: ++ case TtsHostMsg_Pause::ID: ++ case TtsHostMsg_Resume::ID: ++ case TtsHostMsg_Cancel::ID: ++ *thread = BrowserThread::UI; ++ break; + } + } + +@@ -207,10 +232,8 @@ void TtsMessageFilter::Cleanup() { + TtsController::GetInstance()->RemoveUtteranceEventDelegate(this); + } + +-void TtsMessageFilter::Observe( +- int type, +- const content::NotificationSource& source, +- const content::NotificationDetails& details) { ++void TtsMessageFilter::BrowserContextDestroyed() { ++ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + browser_context_ = nullptr; +- notification_registrar_.RemoveAll(); ++ browser_context_shutdown_notifier_.reset(); + } +diff --git a/chrome/browser/speech/tts_message_filter.h b/chrome/browser/speech/tts_message_filter.h +index cc9e2806b5c3..d21fb42f1aca 100644 +--- a/chrome/browser/speech/tts_message_filter.h ++++ b/chrome/browser/speech/tts_message_filter.h +@@ -9,10 +9,9 @@ + #include "base/memory/weak_ptr.h" + #include "base/synchronization/lock.h" + #include "chrome/browser/speech/tts_controller.h" ++#include "components/keyed_service/core/keyed_service_shutdown_notifier.h" + #include "content/public/browser/browser_message_filter.h" + #include "content/public/browser/browser_thread.h" +-#include "content/public/browser/notification_observer.h" +-#include "content/public/browser/notification_registrar.h" + + namespace content { + class BrowserContext; +@@ -22,7 +21,6 @@ struct TtsUtteranceRequest; + + class TtsMessageFilter + : public content::BrowserMessageFilter, +- public content::NotificationObserver, + public UtteranceEventDelegate, + public VoicesChangedDelegate { + public: +@@ -64,15 +62,13 @@ class TtsMessageFilter + // about to be deleted. + bool Valid(); + +- // content::NotificationObserver implementation. +- void Observe(int type, +- const content::NotificationSource& source, +- const content::NotificationDetails& details) override; ++ void BrowserContextDestroyed(); + ++ std::unique_ptr ++ browser_context_shutdown_notifier_; + content::BrowserContext* browser_context_; + mutable base::Lock mutex_; + mutable bool valid_; +- content::NotificationRegistrar notification_registrar_; + + DISALLOW_COPY_AND_ASSIGN(TtsMessageFilter); + }; diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index 34f3b1374b0b0..d273945185d48 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -1312,4 +1312,41 @@ describe('chromium feature', () => { }) }) }) + + describe('SpeechSynthesis', () => { + before(function () { + if (isCI || !features.isTtsEnabled()) { + this.skip() + } + }) + + it('should emit lifecycle events', async () => { + const sentence = `long sentence which will take at least a few seconds to + utter so that it's possible to pause and resume before the end` + const utter = new SpeechSynthesisUtterance(sentence) + // Create a dummy utterence so that speech synthesis state + // is initialized for later calls. + speechSynthesis.speak(new SpeechSynthesisUtterance()) + speechSynthesis.cancel() + speechSynthesis.speak(utter) + // paused state after speak() + expect(speechSynthesis.paused).to.be.false() + await new Promise((resolve) => { utter.onstart = resolve }) + // paused state after start event + expect(speechSynthesis.paused).to.be.false() + + speechSynthesis.pause() + // paused state changes async, right before the pause event + expect(speechSynthesis.paused).to.be.false() + await new Promise((resolve) => { utter.onpause = resolve }) + expect(speechSynthesis.paused).to.be.true() + + speechSynthesis.resume() + await new Promise((resolve) => { utter.onresume = resolve }) + // paused state after resume event + expect(speechSynthesis.paused).to.be.false() + + await new Promise((resolve) => { utter.onend = resolve }) + }) + }) })