Reimplement Chrome OS TTS using a component extension.
The component extension will be checked into the Chrome OS codebase.
This change removes the old implementation that talked to the native
Chrome OS TTS engine via dbus and replaces it with code that loads the
component extension on-demand, retrying speech requests once it
finishes loading.
BUG=157592,67713
Review URL: https://chromiumcodereview.appspot.com/11377125
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@169694 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 82db0d5..ca89194 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -239,6 +239,8 @@
<include name="IDR_CRYPTOHOME_JS" file="resources\chromeos\cryptohome.js" type="BINDATA" />
<!-- manifest file of ChromeVox accessibility extension -->
<include name="IDR_CHROMEVOX_MANIFEST" file="resources\chromeos\access_chromevox\manifest.json" type="BINDATA" />
+ <!-- manifest file of built-in speech synthesis extension -->
+ <include name="IDR_SPEECH_SYNTHESIS_MANIFEST" file="resources\chromeos\speech_synthesis\manifest.json" type="BINDATA" />
<include name="IDR_DIAGNOSTICS_MAIN_CSS" file="resources\chromeos\diagnostics\main.css" type="BINDATA" />
<include name="IDR_DIAGNOSTICS_MAIN_HTML" file="resources\chromeos\diagnostics\main.html" flattenhtml="true" type="BINDATA" />
<include name="IDR_DIAGNOSTICS_MAIN_JS" file="resources\chromeos\diagnostics\main.js" type="BINDATA" />
diff --git a/chrome/browser/chromeos/accessibility/accessibility_util.cc b/chrome/browser/chromeos/accessibility/accessibility_util.cc
index 2340e75..722985d 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_util.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_util.cc
@@ -132,8 +132,7 @@
Profile* profile = ProfileManager::GetDefaultProfile();
ExtensionService* extension_service =
profile->GetExtensionService();
- FilePath path = FilePath(extension_misc::kAccessExtensionPath)
- .AppendASCII(extension_misc::kChromeVoxDirectoryName);
+ FilePath path = FilePath(extension_misc::kChromeVoxExtensionPath);
if (enabled) { // Load ChromeVox
std::string extension_id =
extension_service->component_loader()->Add(IDR_CHROMEVOX_MANIFEST,
diff --git a/chrome/browser/chromeos/audio/audio_mixer_alsa.cc b/chrome/browser/chromeos/audio/audio_mixer_alsa.cc
index 464a4a0..6720880 100644
--- a/chrome/browser/chromeos/audio/audio_mixer_alsa.cc
+++ b/chrome/browser/chromeos/audio/audio_mixer_alsa.cc
@@ -18,7 +18,6 @@
#include "base/message_loop.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
-#include "chrome/browser/speech/extension_api/tts_extension_api_chromeos.h"
#include "content/public/browser/browser_thread.h"
typedef long alsa_long_t; // 'long' is required for ALSA API calls.
@@ -325,12 +324,6 @@
volume_db_ = PercentToDb(initial_volume_percent_);
}
- // The speech synthesis service shouldn't be initialized until after
- // we get to this point, so we call this function to tell it that it's
- // safe to do TTS now. NotificationService would be cleaner,
- // but it's not available at this point.
- EnableChromeOsTts();
-
ApplyState();
return true;
}
diff --git a/chrome/browser/chromeos/audio/audio_mixer_cras.cc b/chrome/browser/chromeos/audio/audio_mixer_cras.cc
index 17519ca..89de431 100644
--- a/chrome/browser/chromeos/audio/audio_mixer_cras.cc
+++ b/chrome/browser/chromeos/audio/audio_mixer_cras.cc
@@ -14,7 +14,6 @@
#include "base/message_loop.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
-#include "chrome/browser/speech/extension_api/tts_extension_api_chromeos.h"
#include "content/public/browser/browser_thread.h"
using content::BrowserThread;
@@ -156,12 +155,6 @@
}
client_connected_ = true;
- // The speech synthesis service shouldn't be initialized until after
- // we get to this point, so we call this function to tell it that it's
- // safe to do TTS now. NotificationService would be cleaner,
- // but it's not available at this point.
- EnableChromeOsTts();
-
ApplyState();
}
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index c0bbcf7d..7c24d36 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -381,10 +381,9 @@
#endif
#if defined(OS_CHROMEOS)
- // Register access extensions only if accessibility is enabled.
+ // Load ChromeVox extension now if spoken feedback is enabled.
if (local_state_->GetBoolean(prefs::kSpokenFeedbackEnabled)) {
- FilePath path = FilePath(extension_misc::kAccessExtensionPath)
- .AppendASCII(extension_misc::kChromeVoxDirectoryName);
+ FilePath path = FilePath(extension_misc::kChromeVoxExtensionPath);
Add(IDR_CHROMEVOX_MANIFEST, path);
}
#endif
diff --git a/chrome/browser/resources/chromeos/speech_synthesis/manifest.json b/chrome/browser/resources/chromeos/speech_synthesis/manifest.json
new file mode 100644
index 0000000..6222c68f
--- /dev/null
+++ b/chrome/browser/resources/chromeos/speech_synthesis/manifest.json
@@ -0,0 +1,57 @@
+{
+ "name": "Chrome OS built-in text-to-speech extension",
+ "version": "0.0.1",
+ "description": "Text-to-speech (TTS) voice extension using Native Client technology.",
+ "manifest_version": 2,
+ "background": {
+ "scripts": [
+ "voice_data_hmm_en-US.js",
+ "tts_main.js"
+ ]
+ },
+ "permissions": [
+ "ttsEngine",
+ "unlimitedStorage"
+ ],
+ "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlKEJseIIbKFyX0BCWNYOWlPEUt1IxBvIoW1PI7DTmipbwyVr3s2EprewYdtr9hCO5Yzs5w/ai1Xnhet5PLAsMje6ZP0Kvq0tlVfaYF8oQHBPF+ifx31RBT7Cn+ZVKLq1fxrwzY063GVhW+CAr06Ar8YRFXtFoC4FHlUNDIoSb4wIDAQAB",
+ "tts_engine": {
+ "voices": [
+ {
+ "voice_name": "Chrome OS US English Voice",
+ "lang": "en-US",
+ "gender": "female",
+ "event_types": [ "start", "word", "end", "error" ]
+ },
+ {
+ "voice_name": "Chrome OS UK English Voice",
+ "lang": "en-GB",
+ "gender": "female",
+ "event_types": [ "start", "word", "end", "error" ]
+ },
+ {
+ "voice_name": "Chrome OS French Voice",
+ "lang": "fr-FR",
+ "gender": "female",
+ "event_types": [ "start", "word", "end", "error" ]
+ },
+ {
+ "voice_name": "Chrome OS Spanish Voice",
+ "lang": "es-ES",
+ "gender": "female",
+ "event_types": [ "start", "word", "end", "error" ]
+ },
+ {
+ "voice_name": "Chrome OS German Voice",
+ "lang": "de-DE",
+ "gender": "female",
+ "event_types": [ "start", "word", "end", "error" ]
+ },
+ {
+ "voice_name": "Chrome OS Italian Voice",
+ "lang": "it-IT",
+ "gender": "female",
+ "event_types": [ "start", "word", "end", "error" ]
+ }
+ ]
+ }
+}
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
index 38d8e34..333646b 100644
--- a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
+++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
@@ -20,10 +20,10 @@
namespace constants = tts_extension_api_constants;
-namespace events {
+namespace tts_engine_events {
const char kOnSpeak[] = "ttsEngine.onSpeak";
const char kOnStop[] = "ttsEngine.onStop";
-}; // namespace events
+}; // namespace tts_engine_events
namespace {
// Given a language/region code of the form 'fr-FR', returns just the basic
@@ -49,9 +49,9 @@
const Extension* extension = *iter;
if (!event_router->ExtensionHasEventListener(
- extension->id(), events::kOnSpeak) ||
+ extension->id(), tts_engine_events::kOnSpeak) ||
!event_router->ExtensionHasEventListener(
- extension->id(), events::kOnStop)) {
+ extension->id(), tts_engine_events::kOnStop)) {
continue;
}
@@ -124,9 +124,9 @@
const Extension* extension = *iter;
if (!event_router->ExtensionHasEventListener(
- extension->id(), events::kOnSpeak) ||
+ extension->id(), tts_engine_events::kOnSpeak) ||
!event_router->ExtensionHasEventListener(
- extension->id(), events::kOnStop)) {
+ extension->id(), tts_engine_events::kOnStop)) {
continue;
}
@@ -221,7 +221,7 @@
extensions::ExtensionSystem::Get(utterance->profile())->event_router()->
DispatchEventToExtension(
extension->id(),
- events::kOnSpeak,
+ tts_engine_events::kOnSpeak,
args.Pass(),
utterance->profile(),
GURL());
@@ -232,7 +232,7 @@
extensions::ExtensionSystem::Get(utterance->profile())->event_router()->
DispatchEventToExtension(
utterance->extension_id(),
- events::kOnStop,
+ tts_engine_events::kOnStop,
args.Pass(),
utterance->profile(),
GURL());
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api.h b/chrome/browser/speech/extension_api/tts_engine_extension_api.h
index 6826264..c2b5c77 100644
--- a/chrome/browser/speech/extension_api/tts_engine_extension_api.h
+++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.h
@@ -18,6 +18,11 @@
class Extension;
}
+namespace tts_engine_events {
+extern const char kOnSpeak[];
+extern const char kOnStop[];
+}
+
// Return a list of all available voices registered by extensions.
void GetExtensionVoices(Profile* profile, base::ListValue* result_voices);
diff --git a/chrome/browser/speech/extension_api/tts_extension_api_chromeos.cc b/chrome/browser/speech/extension_api/tts_extension_api_chromeos.cc
index 64bb4e1..3b475897 100644
--- a/chrome/browser/speech/extension_api/tts_extension_api_chromeos.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_api_chromeos.cc
@@ -2,78 +2,52 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/speech/extension_api/tts_extension_api_chromeos.h"
-
-#include <list>
-
-#include "base/bind.h"
-#include "base/memory/singleton.h"
-#include "base/memory/weak_ptr.h"
-#include "base/message_loop.h"
-#include "base/stl_util.h"
-#include "base/string_number_conversions.h"
-#include "chrome/browser/speech/extension_api/tts_extension_api_controller.h"
#include "chrome/browser/speech/extension_api/tts_extension_api_platform.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/speech_synthesizer_client.h"
+#include "chrome/browser/speech/extension_api/tts_extension_loader_chromeos.h"
-using base::DoubleToString;
-
-namespace {
-const int kSpeechCheckDelayIntervalMs = 100;
-};
-
-// Very simple queue of utterances that can't be spoken because audio
-// isn't initialized yet.
-struct QueuedUtterance {
- int utterance_id;
- std::string utterance;
- std::string lang;
- UtteranceContinuousParameters params;
-};
-
+// Chrome OS doesn't have native TTS, instead it includes a built-in
+// component extension that provides speech synthesis. This class includes
+// an implementation of LoadBuiltInTtsExtension and dummy implementations of
+// everything else.
class ExtensionTtsPlatformImplChromeOs
: public ExtensionTtsPlatformImpl {
public:
- virtual bool PlatformImplAvailable() {
- return true;
+ // ExtensionTtsPlatformImpl overrides:
+ virtual bool PlatformImplAvailable() OVERRIDE {
+ return false;
+ }
+
+ virtual bool LoadBuiltInTtsExtension(Profile* profile) OVERRIDE {
+ return TtsExtensionLoaderChromeOs::GetInstance(profile)->LoadTtsExtension();
}
virtual bool Speak(
int utterance_id,
const std::string& utterance,
const std::string& lang,
- const UtteranceContinuousParameters& params);
+ const UtteranceContinuousParameters& params) OVERRIDE {
+ return false;
+ }
- virtual bool StopSpeaking();
+ virtual bool StopSpeaking() OVERRIDE {
+ return false;
+ }
- virtual bool IsSpeaking();
+ virtual bool IsSpeaking() OVERRIDE {
+ return false;
+ }
- virtual bool SendsEvent(TtsEventType event_type);
+ virtual bool SendsEvent(TtsEventType event_type) OVERRIDE {
+ return false;
+ }
// Get the single instance of this class.
static ExtensionTtsPlatformImplChromeOs* GetInstance();
- // TTS won't begin until this is called.
- void Enable();
-
private:
- ExtensionTtsPlatformImplChromeOs();
+ ExtensionTtsPlatformImplChromeOs() {}
virtual ~ExtensionTtsPlatformImplChromeOs() {}
- void PollUntilSpeechFinishes(int utterance_id);
- void ContinuePollingIfSpeechIsNotFinished(int utterance_id, bool result);
-
- void AppendSpeakOption(std::string key,
- std::string value,
- std::string* options);
-
- int utterance_id_;
- int utterance_length_;
- std::list<QueuedUtterance*> queued_utterances_;
- bool enabled_;
- base::WeakPtrFactory<ExtensionTtsPlatformImplChromeOs> weak_ptr_factory_;
-
friend struct DefaultSingletonTraits<ExtensionTtsPlatformImplChromeOs>;
DISALLOW_COPY_AND_ASSIGN(ExtensionTtsPlatformImplChromeOs);
@@ -84,163 +58,8 @@
return ExtensionTtsPlatformImplChromeOs::GetInstance();
}
-ExtensionTtsPlatformImplChromeOs::ExtensionTtsPlatformImplChromeOs()
- : enabled_(false),
- ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
-}
-
-bool ExtensionTtsPlatformImplChromeOs::Speak(
- int utterance_id,
- const std::string& utterance,
- const std::string& lang,
- const UtteranceContinuousParameters& params) {
- if (!enabled_) {
- QueuedUtterance *queued = new QueuedUtterance();
- queued->utterance_id = utterance_id;
- queued->utterance = utterance;
- queued->lang = lang;
- queued->params = params;
- queued_utterances_.push_back(queued);
- return true;
- }
-
- utterance_id_ = utterance_id;
- utterance_length_ = utterance.size();
-
- std::string options;
-
- if (!lang.empty()) {
- AppendSpeakOption(
- chromeos::SpeechSynthesizerClient::kSpeechPropertyLocale,
- lang,
- &options);
- }
-
- if (params.rate >= 0.0) {
- AppendSpeakOption(
- chromeos::SpeechSynthesizerClient::kSpeechPropertyRate,
- DoubleToString(params.rate),
- &options);
- }
-
- if (params.pitch >= 0.0) {
- // The TTS service allows a range of 0 to 2 for speech pitch.
- AppendSpeakOption(
- chromeos::SpeechSynthesizerClient::kSpeechPropertyPitch,
- DoubleToString(params.pitch),
- &options);
- }
-
- if (params.volume >= 0.0) {
- // The Chrome OS TTS service allows a range of 0 to 5 for speech volume,
- // but 5 clips, so map to a range of 0...4.
- AppendSpeakOption(
- chromeos::SpeechSynthesizerClient::kSpeechPropertyVolume,
- DoubleToString(params.volume * 4),
- &options);
- }
-
- chromeos::SpeechSynthesizerClient* speech_synthesizer_client =
- chromeos::DBusThreadManager::Get()->GetSpeechSynthesizerClient();
-
- speech_synthesizer_client->Speak(utterance, options);
- if (utterance_id_ >= 0) {
- ExtensionTtsController* controller = ExtensionTtsController::GetInstance();
- controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string());
- PollUntilSpeechFinishes(utterance_id_);
- }
-
- return true;
-}
-
-bool ExtensionTtsPlatformImplChromeOs::StopSpeaking() {
- // If we haven't been enabled yet, clear the internal queue.
- if (!enabled_) {
- STLDeleteElements(&queued_utterances_);
- return true;
- }
-
- chromeos::DBusThreadManager::Get()->GetSpeechSynthesizerClient()->
- StopSpeaking();
- return true;
-}
-
-bool ExtensionTtsPlatformImplChromeOs::IsSpeaking() {
- // Defers to controller's utterance based detection of is speaking.
- return false;
-}
-
-bool ExtensionTtsPlatformImplChromeOs::SendsEvent(TtsEventType event_type) {
- return (event_type == TTS_EVENT_START ||
- event_type == TTS_EVENT_END ||
- event_type == TTS_EVENT_ERROR);
-}
-
-void ExtensionTtsPlatformImplChromeOs::Enable() {
- enabled_ = true;
- while (!queued_utterances_.empty()) {
- QueuedUtterance* queued = queued_utterances_.front();
- Speak(queued->utterance_id,
- queued->utterance,
- queued->lang,
- queued->params);
- delete queued;
- queued_utterances_.pop_front();
- }
-}
-
-void ExtensionTtsPlatformImplChromeOs::PollUntilSpeechFinishes(
- int utterance_id) {
- if (utterance_id != utterance_id_) {
- // This utterance must have been interrupted or cancelled.
- return;
- }
- chromeos::SpeechSynthesizerClient* speech_synthesizer_client =
- chromeos::DBusThreadManager::Get()->GetSpeechSynthesizerClient();
- speech_synthesizer_client->IsSpeaking(base::Bind(
- &ExtensionTtsPlatformImplChromeOs::ContinuePollingIfSpeechIsNotFinished,
- weak_ptr_factory_.GetWeakPtr(), utterance_id));
-}
-
-void ExtensionTtsPlatformImplChromeOs::ContinuePollingIfSpeechIsNotFinished(
- int utterance_id, bool is_speaking) {
- if (utterance_id != utterance_id_) {
- // This utterance must have been interrupted or cancelled.
- return;
- }
- if (!is_speaking) {
- ExtensionTtsController* controller = ExtensionTtsController::GetInstance();
- controller->OnTtsEvent(
- utterance_id_, TTS_EVENT_END, utterance_length_, std::string());
- return;
- }
- // Continue polling.
- MessageLoop::current()->PostDelayedTask(
- FROM_HERE, base::Bind(
- &ExtensionTtsPlatformImplChromeOs::PollUntilSpeechFinishes,
- weak_ptr_factory_.GetWeakPtr(),
- utterance_id),
- base::TimeDelta::FromMilliseconds(kSpeechCheckDelayIntervalMs));
-}
-
-void ExtensionTtsPlatformImplChromeOs::AppendSpeakOption(
- std::string key,
- std::string value,
- std::string* options) {
- *options +=
- key +
- chromeos::SpeechSynthesizerClient::kSpeechPropertyEquals +
- value +
- chromeos::SpeechSynthesizerClient::kSpeechPropertyDelimiter;
-}
-
// static
ExtensionTtsPlatformImplChromeOs*
ExtensionTtsPlatformImplChromeOs::GetInstance() {
return Singleton<ExtensionTtsPlatformImplChromeOs>::get();
}
-
-// global
-void EnableChromeOsTts() {
- ExtensionTtsPlatformImplChromeOs::GetInstance()->Enable();
-}
diff --git a/chrome/browser/speech/extension_api/tts_extension_api_chromeos.h b/chrome/browser/speech/extension_api/tts_extension_api_chromeos.h
deleted file mode 100644
index f5edaac..0000000
--- a/chrome/browser/speech/extension_api/tts_extension_api_chromeos.h
+++ /dev/null
@@ -1,14 +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_EXTENSION_API_TTS_EXTENSION_API_CHROMEOS_H_
-#define CHROME_BROWSER_SPEECH_EXTENSION_API_TTS_EXTENSION_API_CHROMEOS_H_
-
-// This is called by AudioMixerAlsa to signal that it's safe to begin
-// using TTS on ChromeOS - this must happen after AudioMixerAlsa has
-// finished its initialization. NotificationService would be cleaner,
-// but it's not available at the point when this code needs to be called.
-void EnableChromeOsTts();
-
-#endif // CHROME_BROWSER_SPEECH_EXTENSION_API_TTS_EXTENSION_API_CHROMEOS_H_
diff --git a/chrome/browser/speech/extension_api/tts_extension_api_controller.cc b/chrome/browser/speech/extension_api/tts_extension_api_controller.cc
index fdbd57eb..10ab0aa 100644
--- a/chrome/browser/speech/extension_api/tts_extension_api_controller.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_api_controller.cc
@@ -196,6 +196,13 @@
utterance->text(),
utterance->lang(),
utterance->continuous_parameters());
+
+ if (!success &&
+ GetPlatformImpl()->LoadBuiltInTtsExtension(utterance->profile())) {
+ utterance_queue_.push(utterance);
+ return;
+ }
+
if (!success) {
utterance->OnTtsEvent(TTS_EVENT_ERROR, kInvalidCharIndex,
GetPlatformImpl()->error());
@@ -304,6 +311,11 @@
}
}
+void ExtensionTtsController::RetrySpeakingQueuedUtterances() {
+ if (current_utterance_ == NULL && !utterance_queue_.empty())
+ SpeakNextUtterance();
+}
+
void ExtensionTtsController::ClearUtteranceQueue(bool send_events) {
while (!utterance_queue_.empty()) {
Utterance* utterance = utterance_queue_.front();
diff --git a/chrome/browser/speech/extension_api/tts_extension_api_controller.h b/chrome/browser/speech/extension_api/tts_extension_api_controller.h
index e1fa714..89201f9 100644
--- a/chrome/browser/speech/extension_api/tts_extension_api_controller.h
+++ b/chrome/browser/speech/extension_api/tts_extension_api_controller.h
@@ -214,6 +214,10 @@
// if supported, and all voices registered by extensions.
base::ListValue* GetVoices(Profile* profile);
+ // Called by TtsExtensionLoaderChromeOs::LoadTtsExtension when it
+ // finishes loading the built-in TTS component extension.
+ void RetrySpeakingQueuedUtterances();
+
// For unit testing.
void SetPlatformImpl(ExtensionTtsPlatformImpl* platform_impl);
int QueueSize();
diff --git a/chrome/browser/speech/extension_api/tts_extension_api_platform.cc b/chrome/browser/speech/extension_api/tts_extension_api_platform.cc
index 3c3e5c9..df3cbb5 100644
--- a/chrome/browser/speech/extension_api/tts_extension_api_platform.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_api_platform.cc
@@ -6,6 +6,10 @@
#include <string>
+bool ExtensionTtsPlatformImpl::LoadBuiltInTtsExtension(Profile* profile) {
+ return false;
+}
+
std::string ExtensionTtsPlatformImpl::gender() {
return std::string();
}
diff --git a/chrome/browser/speech/extension_api/tts_extension_api_platform.h b/chrome/browser/speech/extension_api/tts_extension_api_platform.h
index 03ac59f..5f0a4b62 100644
--- a/chrome/browser/speech/extension_api/tts_extension_api_platform.h
+++ b/chrome/browser/speech/extension_api/tts_extension_api_platform.h
@@ -18,6 +18,13 @@
// 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 ExtensionTtsController::RetrySpeakingQueuedUtterances when
+ // the extension finishes loading.
+ virtual bool LoadBuiltInTtsExtension(Profile* profile);
+
// 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.
diff --git a/chrome/browser/speech/extension_api/tts_extension_loader_chromeos.cc b/chrome/browser/speech/extension_api/tts_extension_loader_chromeos.cc
new file mode 100644
index 0000000..c566ede
--- /dev/null
+++ b/chrome/browser/speech/extension_api/tts_extension_loader_chromeos.cc
@@ -0,0 +1,115 @@
+// 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/extension_api/tts_extension_loader_chromeos.h"
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "chrome/browser/extensions/component_loader.h"
+#include "chrome/browser/extensions/event_router.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_dependency_manager.h"
+#include "chrome/browser/profiles/profile_keyed_service.h"
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+#include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
+#include "chrome/browser/speech/extension_api/tts_extension_api_controller.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "grit/browser_resources.h"
+
+// Factory to load one instance of TtsExtensionLoaderChromeOs per profile.
+class TtsExtensionLoaderChromeOsFactory : public ProfileKeyedServiceFactory {
+ public:
+ static TtsExtensionLoaderChromeOs* GetForProfile(Profile* profile) {
+ return static_cast<TtsExtensionLoaderChromeOs*>(
+ GetInstance()->GetServiceForProfile(profile, true));
+ }
+
+ static TtsExtensionLoaderChromeOsFactory* GetInstance() {
+ return Singleton<TtsExtensionLoaderChromeOsFactory>::get();
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<TtsExtensionLoaderChromeOsFactory>;
+
+ TtsExtensionLoaderChromeOsFactory() : ProfileKeyedServiceFactory(
+ "TtsExtensionLoaderChromeOs",
+ ProfileDependencyManager::GetInstance())
+ {}
+
+ ~TtsExtensionLoaderChromeOsFactory() {}
+
+ ProfileKeyedService* BuildServiceInstanceFor(Profile* profile) const {
+ return new TtsExtensionLoaderChromeOs(profile);
+ }
+};
+
+TtsExtensionLoaderChromeOs*
+TtsExtensionLoaderChromeOs::GetInstance(Profile* profile) {
+ return TtsExtensionLoaderChromeOsFactory::GetInstance()
+ ->GetForProfile(profile);
+}
+
+TtsExtensionLoaderChromeOs::TtsExtensionLoaderChromeOs(
+ Profile* profile)
+ : profile_(profile) {
+ tts_state_ = IsTtsLoadedInThisProfile() ? TTS_LOADED : TTS_NOT_LOADED;
+
+ extensions::ExtensionSystem* system =
+ extensions::ExtensionSystem::Get(profile_);
+ DCHECK(system);
+ extensions::EventRouter* event_router = system->event_router();
+ DCHECK(event_router);
+ event_router->RegisterObserver(this, tts_engine_events::kOnSpeak);
+ event_router->RegisterObserver(this, tts_engine_events::kOnStop);
+}
+
+bool TtsExtensionLoaderChromeOs::LoadTtsExtension() {
+ if (tts_state_ == TTS_LOADED || tts_state_ == TTS_LOADING)
+ return false;
+
+ // Load the component extension into this profile.
+ LOG(INFO) << "Loading TTS component extension.";
+ tts_state_ = TTS_LOADING;
+ ExtensionService* extension_service = profile_->GetExtensionService();
+ DCHECK(extension_service);
+ FilePath path = FilePath(extension_misc::kSpeechSynthesisExtensionPath);
+ extension_service->component_loader()->Add(IDR_SPEECH_SYNTHESIS_MANIFEST,
+ path);
+ return true;
+}
+
+bool TtsExtensionLoaderChromeOs::IsTtsLoadedInThisProfile() {
+ extensions::ExtensionSystem* system =
+ extensions::ExtensionSystem::Get(profile_);
+ DCHECK(system);
+ extensions::EventRouter* event_router = system->event_router();
+ DCHECK(event_router);
+ if (event_router->ExtensionHasEventListener(
+ extension_misc::kSpeechSynthesisExtensionId,
+ tts_engine_events::kOnSpeak) &&
+ event_router->ExtensionHasEventListener(
+ extension_misc::kSpeechSynthesisExtensionId,
+ tts_engine_events::kOnStop)) {
+ return true;
+ }
+
+ return false;
+}
+
+void TtsExtensionLoaderChromeOs::OnListenerAdded(
+ const extensions::EventListenerInfo& details) {
+ if (details.extension_id != extension_misc::kSpeechSynthesisExtensionId)
+ return;
+
+ if (!IsTtsLoadedInThisProfile())
+ return;
+
+ if (tts_state_ == TTS_LOADING) {
+ LOG(INFO) << "TTS component extension loaded, retrying queued utterances.";
+ tts_state_ = TTS_LOADED;
+ ExtensionTtsController::GetInstance()->RetrySpeakingQueuedUtterances();
+ }
+}
diff --git a/chrome/browser/speech/extension_api/tts_extension_loader_chromeos.h b/chrome/browser/speech/extension_api/tts_extension_loader_chromeos.h
new file mode 100644
index 0000000..2cc47b7
--- /dev/null
+++ b/chrome/browser/speech/extension_api/tts_extension_loader_chromeos.h
@@ -0,0 +1,51 @@
+// 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_EXTENSION_API_TTS_EXTENSION_LOADER_CHROMEOS_H_
+#define CHROME_BROWSER_SPEECH_EXTENSION_API_TTS_EXTENSION_LOADER_CHROMEOS_H_
+
+#include "chrome/browser/extensions/event_router.h"
+#include "chrome/browser/profiles/profile_keyed_service.h"
+
+// Profile-keyed class that loads a built-in TTS component extension
+// into a given profile on Chrome OS.
+class TtsExtensionLoaderChromeOs
+ : public ProfileKeyedService,
+ public extensions::EventRouter::Observer {
+ public:
+ static TtsExtensionLoaderChromeOs* GetInstance(Profile* profile);
+
+ // Returns true if the extension was not previously loaded and is now
+ // loading. This class will call
+ // ExtensionTtsController::RetrySpeakingQueuedUtterances when the
+ // extension finishes loading.
+ bool LoadTtsExtension();
+
+ // Implementation of extensions::EventRouter::Observer.
+ virtual void OnListenerAdded(const extensions::EventListenerInfo& details)
+ OVERRIDE;
+
+ private:
+ // The state of TTS for this profile.
+ enum TtsState {
+ TTS_NOT_LOADED,
+ TTS_LOAD_REQUESTED,
+ TTS_LOADING,
+ TTS_LOADED
+ };
+
+ explicit TtsExtensionLoaderChromeOs(Profile* profile);
+ virtual ~TtsExtensionLoaderChromeOs() {}
+
+ bool IsTtsLoadedInThisProfile();
+
+ Profile* profile_;
+ TtsState tts_state_;
+
+ friend class TtsExtensionLoaderChromeOsFactory;
+
+ DISALLOW_COPY_AND_ASSIGN(TtsExtensionLoaderChromeOs);
+};
+
+#endif // CHROME_BROWSER_SPEECH_EXTENSION_API_TTS_EXTENSION_LOADER_CHROMEOS_H_