1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
9 #include "base/json/json_writer.h"
10 #include "base/values.h"
11 #include "chrome/browser/extensions/component_loader.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/speech/extension_api/tts_engine_extension_observer.h"
15 #include "chrome/browser/speech/extension_api/tts_extension_api.h"
16 #include "chrome/browser/speech/extension_api/tts_extension_api_constants.h"
17 #include "chrome/browser/speech/tts_controller.h"
18 #include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h"
19 #include "chrome/common/extensions/extension_constants.h"
20 #include "content/public/browser/render_frame_host.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/common/console_message_level.h"
24 #include "extensions/browser/event_router.h"
25 #include "extensions/browser/extension_host.h"
26 #include "extensions/browser/extension_registry.h"
27 #include "extensions/browser/extension_system.h"
28 #include "extensions/browser/process_manager.h"
29 #include "extensions/common/extension.h"
30 #include "extensions/common/extension_set.h"
31 #include "net/base/network_change_notifier.h"
33 using extensions::EventRouter
;
34 using extensions::Extension
;
35 using extensions::ExtensionSystem
;
37 namespace constants
= tts_extension_api_constants
;
39 namespace tts_engine_events
{
40 const char kOnSpeak
[] = "ttsEngine.onSpeak";
41 const char kOnStop
[] = "ttsEngine.onStop";
42 const char kOnPause
[] = "ttsEngine.onPause";
43 const char kOnResume
[] = "ttsEngine.onResume";
44 }; // namespace tts_engine_events
48 void WarnIfMissingPauseOrResumeListener(
49 Profile
* profile
, EventRouter
* event_router
, std::string extension_id
) {
50 bool has_onpause
= event_router
->ExtensionHasEventListener(
51 extension_id
, tts_engine_events::kOnPause
);
52 bool has_onresume
= event_router
->ExtensionHasEventListener(
53 extension_id
, tts_engine_events::kOnResume
);
54 if (has_onpause
== has_onresume
)
57 extensions::ExtensionHost
* host
=
58 extensions::ProcessManager::Get(profile
)
59 ->GetBackgroundHostForExtension(extension_id
);
60 host
->host_contents()->GetMainFrame()->AddMessageToConsole(
61 content::CONSOLE_MESSAGE_LEVEL_WARNING
,
62 constants::kErrorMissingPauseOrResume
);
67 TtsExtensionEngine
* TtsExtensionEngine::GetInstance() {
68 return Singleton
<TtsExtensionEngine
>::get();
71 void TtsExtensionEngine::GetVoices(content::BrowserContext
* browser_context
,
72 std::vector
<VoiceData
>* out_voices
) {
73 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
74 EventRouter
* event_router
= EventRouter::Get(profile
);
77 bool is_offline
= (net::NetworkChangeNotifier::GetConnectionType() ==
78 net::NetworkChangeNotifier::CONNECTION_NONE
);
80 const extensions::ExtensionSet
& extensions
=
81 extensions::ExtensionRegistry::Get(profile
)->enabled_extensions();
82 extensions::ExtensionSet::const_iterator iter
;
83 for (iter
= extensions
.begin(); iter
!= extensions
.end(); ++iter
) {
84 const Extension
* extension
= iter
->get();
86 if (!event_router
->ExtensionHasEventListener(
87 extension
->id(), tts_engine_events::kOnSpeak
) ||
88 !event_router
->ExtensionHasEventListener(
89 extension
->id(), tts_engine_events::kOnStop
)) {
93 const std::vector
<extensions::TtsVoice
>* tts_voices
=
94 extensions::TtsVoice::GetTtsVoices(extension
);
98 for (size_t i
= 0; i
< tts_voices
->size(); ++i
) {
99 const extensions::TtsVoice
& voice
= tts_voices
->at(i
);
101 // Don't return remote voices when the system is offline.
102 if (voice
.remote
&& is_offline
)
105 out_voices
->push_back(VoiceData());
106 VoiceData
& result_voice
= out_voices
->back();
108 result_voice
.native
= false;
109 result_voice
.name
= voice
.voice_name
;
110 result_voice
.lang
= voice
.lang
;
111 result_voice
.remote
= voice
.remote
;
112 result_voice
.extension_id
= extension
->id();
113 if (voice
.gender
== constants::kGenderMale
)
114 result_voice
.gender
= TTS_GENDER_MALE
;
115 else if (voice
.gender
== constants::kGenderFemale
)
116 result_voice
.gender
= TTS_GENDER_FEMALE
;
118 result_voice
.gender
= TTS_GENDER_NONE
;
120 for (std::set
<std::string
>::const_iterator iter
=
121 voice
.event_types
.begin();
122 iter
!= voice
.event_types
.end();
124 result_voice
.events
.insert(TtsEventTypeFromString(*iter
));
127 // If the extension sends end events, the controller will handle
128 // queueing and send interrupted and cancelled events.
129 if (voice
.event_types
.find(constants::kEventTypeEnd
) !=
130 voice
.event_types
.end()) {
131 result_voice
.events
.insert(TTS_EVENT_CANCELLED
);
132 result_voice
.events
.insert(TTS_EVENT_INTERRUPTED
);
138 void TtsExtensionEngine::Speak(Utterance
* utterance
,
139 const VoiceData
& voice
) {
140 // See if the engine supports the "end" event; if so, we can keep the
141 // utterance around and track it. If not, we're finished with this
143 bool sends_end_event
= voice
.events
.find(TTS_EVENT_END
) != voice
.events
.end();
145 scoped_ptr
<base::ListValue
> args(new base::ListValue());
146 args
->AppendString(utterance
->text());
148 // Pass through most options to the speech engine, but remove some
149 // that are handled internally.
150 scoped_ptr
<base::DictionaryValue
> options(static_cast<base::DictionaryValue
*>(
151 utterance
->options()->DeepCopy()));
152 if (options
->HasKey(constants::kRequiredEventTypesKey
))
153 options
->Remove(constants::kRequiredEventTypesKey
, NULL
);
154 if (options
->HasKey(constants::kDesiredEventTypesKey
))
155 options
->Remove(constants::kDesiredEventTypesKey
, NULL
);
156 if (sends_end_event
&& options
->HasKey(constants::kEnqueueKey
))
157 options
->Remove(constants::kEnqueueKey
, NULL
);
158 if (options
->HasKey(constants::kSrcIdKey
))
159 options
->Remove(constants::kSrcIdKey
, NULL
);
160 if (options
->HasKey(constants::kIsFinalEventKey
))
161 options
->Remove(constants::kIsFinalEventKey
, NULL
);
162 if (options
->HasKey(constants::kOnEventKey
))
163 options
->Remove(constants::kOnEventKey
, NULL
);
165 // Get the volume, pitch, and rate, but only if they weren't already in
166 // the options. TODO(dmazzoni): these shouldn't be redundant.
167 // http://crbug.com/463264
168 if (!options
->HasKey(constants::kRateKey
)) {
169 options
->SetDouble(constants::kRateKey
,
170 utterance
->continuous_parameters().rate
);
172 if (!options
->HasKey(constants::kPitchKey
)) {
173 options
->SetDouble(constants::kPitchKey
,
174 utterance
->continuous_parameters().pitch
);
176 if (!options
->HasKey(constants::kVolumeKey
)) {
177 options
->SetDouble(constants::kVolumeKey
,
178 utterance
->continuous_parameters().volume
);
181 // Add the voice name and language to the options if they're not
182 // already there, since they might have been picked by the TTS controller
183 // rather than directly by the client that requested the speech.
184 if (!options
->HasKey(constants::kVoiceNameKey
))
185 options
->SetString(constants::kVoiceNameKey
, voice
.name
);
186 if (!options
->HasKey(constants::kLangKey
))
187 options
->SetString(constants::kLangKey
, voice
.lang
);
189 args
->Append(options
.release());
190 args
->AppendInteger(utterance
->id());
193 base::JSONWriter::Write(*args
, &json
);
195 scoped_ptr
<extensions::Event
> event(
196 new extensions::Event(extensions::events::TTS_ENGINE_ON_SPEAK
,
197 tts_engine_events::kOnSpeak
, args
.Pass()));
198 Profile
* profile
= Profile::FromBrowserContext(utterance
->browser_context());
199 event
->restrict_to_browser_context
= profile
;
200 EventRouter::Get(profile
)
201 ->DispatchEventToExtension(utterance
->extension_id(), event
.Pass());
204 void TtsExtensionEngine::Stop(Utterance
* utterance
) {
205 scoped_ptr
<base::ListValue
> args(new base::ListValue());
206 scoped_ptr
<extensions::Event
> event(
207 new extensions::Event(extensions::events::TTS_ENGINE_ON_STOP
,
208 tts_engine_events::kOnStop
, args
.Pass()));
209 Profile
* profile
= Profile::FromBrowserContext(utterance
->browser_context());
210 event
->restrict_to_browser_context
= profile
;
211 EventRouter::Get(profile
)
212 ->DispatchEventToExtension(utterance
->extension_id(), event
.Pass());
215 void TtsExtensionEngine::Pause(Utterance
* utterance
) {
216 scoped_ptr
<base::ListValue
> args(new base::ListValue());
217 scoped_ptr
<extensions::Event
> event(
218 new extensions::Event(extensions::events::TTS_ENGINE_ON_PAUSE
,
219 tts_engine_events::kOnPause
, args
.Pass()));
220 Profile
* profile
= Profile::FromBrowserContext(utterance
->browser_context());
221 event
->restrict_to_browser_context
= profile
;
222 EventRouter
* event_router
= EventRouter::Get(profile
);
223 std::string id
= utterance
->extension_id();
224 event_router
->DispatchEventToExtension(id
, event
.Pass());
225 WarnIfMissingPauseOrResumeListener(profile
, event_router
, id
);
228 void TtsExtensionEngine::Resume(Utterance
* utterance
) {
229 scoped_ptr
<base::ListValue
> args(new base::ListValue());
230 scoped_ptr
<extensions::Event
> event(
231 new extensions::Event(extensions::events::TTS_ENGINE_ON_RESUME
,
232 tts_engine_events::kOnResume
, args
.Pass()));
233 Profile
* profile
= Profile::FromBrowserContext(utterance
->browser_context());
234 event
->restrict_to_browser_context
= profile
;
235 EventRouter
* event_router
= EventRouter::Get(profile
);
236 std::string id
= utterance
->extension_id();
237 event_router
->DispatchEventToExtension(id
, event
.Pass());
238 WarnIfMissingPauseOrResumeListener(profile
, event_router
, id
);
241 bool TtsExtensionEngine::LoadBuiltInTtsExtension(
242 content::BrowserContext
* browser_context
) {
243 #if defined(OS_CHROMEOS)
244 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
245 // Check to see if the engine was previously loaded.
246 if (TtsEngineExtensionObserver::GetInstance(profile
)->SawExtensionLoad(
247 extension_misc::kSpeechSynthesisExtensionId
, true)) {
251 // Load the component extension into this profile.
252 ExtensionService
* extension_service
=
253 extensions::ExtensionSystem::Get(profile
)->extension_service();
254 DCHECK(extension_service
);
255 extension_service
->component_loader()
256 ->AddChromeOsSpeechSynthesisExtension();
263 bool ExtensionTtsEngineSendTtsEventFunction::RunSync() {
265 EXTENSION_FUNCTION_VALIDATE(args_
->GetInteger(0, &utterance_id
));
267 base::DictionaryValue
* event
;
268 EXTENSION_FUNCTION_VALIDATE(args_
->GetDictionary(1, &event
));
270 std::string event_type
;
271 EXTENSION_FUNCTION_VALIDATE(
272 event
->GetString(constants::kEventTypeKey
, &event_type
));
275 if (event
->HasKey(constants::kCharIndexKey
)) {
276 EXTENSION_FUNCTION_VALIDATE(
277 event
->GetInteger(constants::kCharIndexKey
, &char_index
));
280 // Make sure the extension has included this event type in its manifest.
281 bool event_type_allowed
= false;
282 const std::vector
<extensions::TtsVoice
>* tts_voices
=
283 extensions::TtsVoice::GetTtsVoices(extension());
285 error_
= constants::kErrorUndeclaredEventType
;
289 for (size_t i
= 0; i
< tts_voices
->size(); i
++) {
290 const extensions::TtsVoice
& voice
= tts_voices
->at(i
);
291 if (voice
.event_types
.find(event_type
) != voice
.event_types
.end()) {
292 event_type_allowed
= true;
296 if (!event_type_allowed
) {
297 error_
= constants::kErrorUndeclaredEventType
;
301 TtsController
* controller
= TtsController::GetInstance();
302 if (event_type
== constants::kEventTypeStart
) {
303 controller
->OnTtsEvent(
304 utterance_id
, TTS_EVENT_START
, char_index
, std::string());
305 } else if (event_type
== constants::kEventTypeEnd
) {
306 controller
->OnTtsEvent(
307 utterance_id
, TTS_EVENT_END
, char_index
, std::string());
308 } else if (event_type
== constants::kEventTypeWord
) {
309 controller
->OnTtsEvent(
310 utterance_id
, TTS_EVENT_WORD
, char_index
, std::string());
311 } else if (event_type
== constants::kEventTypeSentence
) {
312 controller
->OnTtsEvent(
313 utterance_id
, TTS_EVENT_SENTENCE
, char_index
, std::string());
314 } else if (event_type
== constants::kEventTypeMarker
) {
315 controller
->OnTtsEvent(
316 utterance_id
, TTS_EVENT_MARKER
, char_index
, std::string());
317 } else if (event_type
== constants::kEventTypeError
) {
318 std::string error_message
;
319 event
->GetString(constants::kErrorMessageKey
, &error_message
);
320 controller
->OnTtsEvent(
321 utterance_id
, TTS_EVENT_ERROR
, char_index
, error_message
);
322 } else if (event_type
== constants::kEventTypePause
) {
323 controller
->OnTtsEvent(
324 utterance_id
, TTS_EVENT_PAUSE
, char_index
, std::string());
325 } else if (event_type
== constants::kEventTypeResume
) {
326 controller
->OnTtsEvent(
327 utterance_id
, TTS_EVENT_RESUME
, char_index
, std::string());
329 EXTENSION_FUNCTION_VALIDATE(false);