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/event_router.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/extension_system.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/speech/extension_api/tts_extension_api_constants.h"
16 #include "chrome/browser/speech/tts_controller.h"
17 #include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h"
18 #include "chrome/common/extensions/extension.h"
20 using extensions::Extension
;
22 namespace constants
= tts_extension_api_constants
;
24 namespace tts_engine_events
{
25 const char kOnSpeak
[] = "ttsEngine.onSpeak";
26 const char kOnStop
[] = "ttsEngine.onStop";
27 }; // namespace tts_engine_events
30 // Given a language/region code of the form 'fr-FR', returns just the basic
31 // language portion, e.g. 'fr'.
32 std::string
TrimLanguageCode(std::string lang
) {
33 if (lang
.size() >= 5 && lang
[2] == '-')
34 return lang
.substr(0, 2);
40 void GetExtensionVoices(Profile
* profile
, ListValue
* result_voices
) {
41 ExtensionService
* service
= profile
->GetExtensionService();
43 extensions::EventRouter
* event_router
=
44 extensions::ExtensionSystem::Get(profile
)->event_router();
47 const ExtensionSet
* extensions
= service
->extensions();
48 ExtensionSet::const_iterator iter
;
49 for (iter
= extensions
->begin(); iter
!= extensions
->end(); ++iter
) {
50 const Extension
* extension
= *iter
;
52 if (!event_router
->ExtensionHasEventListener(
53 extension
->id(), tts_engine_events::kOnSpeak
) ||
54 !event_router
->ExtensionHasEventListener(
55 extension
->id(), tts_engine_events::kOnStop
)) {
59 const std::vector
<extensions::TtsVoice
>* tts_voices
=
60 extensions::TtsVoice::GetTtsVoices(extension
);
64 for (size_t i
= 0; i
< tts_voices
->size(); ++i
) {
65 const extensions::TtsVoice
& voice
= tts_voices
->at(i
);
66 DictionaryValue
* result_voice
= new DictionaryValue();
67 if (!voice
.voice_name
.empty())
68 result_voice
->SetString(constants::kVoiceNameKey
, voice
.voice_name
);
69 if (!voice
.lang
.empty())
70 result_voice
->SetString(constants::kLangKey
, voice
.lang
);
71 if (!voice
.gender
.empty())
72 result_voice
->SetString(constants::kGenderKey
, voice
.gender
);
73 result_voice
->SetString(constants::kExtensionIdKey
, extension
->id());
75 ListValue
* event_types
= new ListValue();
76 for (std::set
<std::string
>::const_iterator iter
=
77 voice
.event_types
.begin();
78 iter
!= voice
.event_types
.end();
80 event_types
->Append(Value::CreateStringValue(*iter
));
82 // If the extension sends end events, the controller will handle
83 // queueing and send interrupted and cancelled events.
84 if (voice
.event_types
.find(constants::kEventTypeEnd
) !=
85 voice
.event_types
.end()) {
87 Value::CreateStringValue(constants::kEventTypeCancelled
));
88 event_types
->Append(Value::CreateStringValue(
89 constants::kEventTypeInterrupted
));
92 result_voice
->Set(constants::kEventTypesKey
, event_types
);
93 result_voices
->Append(result_voice
);
98 bool GetMatchingExtensionVoice(
100 const Extension
** matching_extension
,
101 size_t* voice_index
) {
102 // This will only happen during unit testing. Otherwise, an utterance
103 // will always have an associated profile.
104 if (!utterance
->profile())
107 ExtensionService
* service
= utterance
->profile()->GetExtensionService();
109 // If speech is generated when Chrome OS first starts up, it's possible
110 // the extension service isn't even available.
114 extensions::EventRouter
* event_router
=
115 extensions::ExtensionSystem::Get(utterance
->profile())->event_router();
116 DCHECK(event_router
);
118 *matching_extension
= NULL
;
120 const ExtensionSet
* extensions
= service
->extensions();
121 ExtensionSet::const_iterator iter
;
123 // Make two passes: the first time, do strict language matching
124 // ('fr-FR' does not match 'fr-CA'). The second time, do prefix
125 // language matching ('fr-FR' matches 'fr' and 'fr-CA')
126 for (int pass
= 0; pass
< 2; ++pass
) {
127 for (iter
= extensions
->begin(); iter
!= extensions
->end(); ++iter
) {
128 const Extension
* extension
= *iter
;
130 if (!event_router
->ExtensionHasEventListener(
131 extension
->id(), tts_engine_events::kOnSpeak
) ||
132 !event_router
->ExtensionHasEventListener(
133 extension
->id(), tts_engine_events::kOnStop
)) {
137 if (!utterance
->extension_id().empty() &&
138 utterance
->extension_id() != extension
->id()) {
142 const std::vector
<extensions::TtsVoice
>* tts_voices
=
143 extensions::TtsVoice::GetTtsVoices(extension
);
147 for (size_t i
= 0; i
< tts_voices
->size(); ++i
) {
148 const extensions::TtsVoice
& voice
= tts_voices
->at(i
);
149 if (!voice
.voice_name
.empty() &&
150 !utterance
->voice_name().empty() &&
151 voice
.voice_name
!= utterance
->voice_name()) {
154 if (!voice
.lang
.empty() && !utterance
->lang().empty()) {
155 std::string voice_lang
= voice
.lang
;
156 std::string utterance_lang
= utterance
->lang();
158 voice_lang
= TrimLanguageCode(voice_lang
);
159 utterance_lang
= TrimLanguageCode(utterance_lang
);
161 if (voice_lang
!= utterance_lang
)
164 if (!voice
.gender
.empty() &&
165 !utterance
->gender().empty() &&
166 voice
.gender
!= utterance
->gender()) {
169 if (utterance
->required_event_types().size() > 0) {
170 bool has_all_required_event_types
= true;
171 for (std::set
<std::string
>::const_iterator iter
=
172 utterance
->required_event_types().begin();
173 iter
!= utterance
->required_event_types().end();
175 if (voice
.event_types
.find(*iter
) == voice
.event_types
.end()) {
176 has_all_required_event_types
= false;
180 if (!has_all_required_event_types
)
184 *matching_extension
= extension
;
194 void ExtensionTtsEngineSpeak(Utterance
* utterance
,
195 const Extension
* extension
,
196 size_t voice_index
) {
197 // See if the engine supports the "end" event; if so, we can keep the
198 // utterance around and track it. If not, we're finished with this
200 const std::vector
<extensions::TtsVoice
>* tts_voices
=
201 extensions::TtsVoice::GetTtsVoices(extension
);
202 std::set
<std::string
> event_types
;
204 event_types
= tts_voices
->at(voice_index
).event_types
;
206 bool sends_end_event
=
207 (event_types
.find(constants::kEventTypeEnd
) != event_types
.end());
209 scoped_ptr
<ListValue
> args(new ListValue());
210 args
->Set(0, Value::CreateStringValue(utterance
->text()));
212 // Pass through most options to the speech engine, but remove some
213 // that are handled internally.
214 DictionaryValue
* options
= static_cast<DictionaryValue
*>(
215 utterance
->options()->DeepCopy());
216 if (options
->HasKey(constants::kRequiredEventTypesKey
))
217 options
->Remove(constants::kRequiredEventTypesKey
, NULL
);
218 if (options
->HasKey(constants::kDesiredEventTypesKey
))
219 options
->Remove(constants::kDesiredEventTypesKey
, NULL
);
220 if (sends_end_event
&& options
->HasKey(constants::kEnqueueKey
))
221 options
->Remove(constants::kEnqueueKey
, NULL
);
222 if (options
->HasKey(constants::kSrcIdKey
))
223 options
->Remove(constants::kSrcIdKey
, NULL
);
224 if (options
->HasKey(constants::kIsFinalEventKey
))
225 options
->Remove(constants::kIsFinalEventKey
, NULL
);
226 if (options
->HasKey(constants::kOnEventKey
))
227 options
->Remove(constants::kOnEventKey
, NULL
);
229 args
->Set(1, options
);
230 args
->Set(2, Value::CreateIntegerValue(utterance
->id()));
232 scoped_ptr
<extensions::Event
> event(new extensions::Event(
233 tts_engine_events::kOnSpeak
, args
.Pass()));
234 event
->restrict_to_profile
= utterance
->profile();
235 extensions::ExtensionSystem::Get(utterance
->profile())->event_router()->
236 DispatchEventToExtension(utterance
->extension_id(), event
.Pass());
239 void ExtensionTtsEngineStop(Utterance
* utterance
) {
240 scoped_ptr
<ListValue
> args(new ListValue());
241 scoped_ptr
<extensions::Event
> event(new extensions::Event(
242 tts_engine_events::kOnStop
, args
.Pass()));
243 event
->restrict_to_profile
= utterance
->profile();
244 extensions::ExtensionSystem::Get(utterance
->profile())->event_router()->
245 DispatchEventToExtension(utterance
->extension_id(), event
.Pass());
248 bool ExtensionTtsEngineSendTtsEventFunction::RunImpl() {
250 std::string error_message
;
251 EXTENSION_FUNCTION_VALIDATE(args_
->GetInteger(0, &utterance_id
));
253 DictionaryValue
* event
;
254 EXTENSION_FUNCTION_VALIDATE(args_
->GetDictionary(1, &event
));
256 std::string event_type
;
257 EXTENSION_FUNCTION_VALIDATE(
258 event
->GetString(constants::kEventTypeKey
, &event_type
));
261 if (event
->HasKey(constants::kCharIndexKey
)) {
262 EXTENSION_FUNCTION_VALIDATE(
263 event
->GetInteger(constants::kCharIndexKey
, &char_index
));
266 // Make sure the extension has included this event type in its manifest.
267 bool event_type_allowed
= false;
268 const Extension
* extension
= GetExtension();
269 const std::vector
<extensions::TtsVoice
>* tts_voices
=
270 extensions::TtsVoice::GetTtsVoices(extension
);
272 error_
= constants::kErrorUndeclaredEventType
;
276 for (size_t i
= 0; i
< tts_voices
->size(); i
++) {
277 const extensions::TtsVoice
& voice
= tts_voices
->at(i
);
278 if (voice
.event_types
.find(event_type
) != voice
.event_types
.end()) {
279 event_type_allowed
= true;
283 if (!event_type_allowed
) {
284 error_
= constants::kErrorUndeclaredEventType
;
288 TtsController
* controller
= TtsController::GetInstance();
289 if (event_type
== constants::kEventTypeStart
) {
290 controller
->OnTtsEvent(
291 utterance_id
, TTS_EVENT_START
, char_index
, std::string());
292 } else if (event_type
== constants::kEventTypeEnd
) {
293 controller
->OnTtsEvent(
294 utterance_id
, TTS_EVENT_END
, char_index
, std::string());
295 } else if (event_type
== constants::kEventTypeWord
) {
296 controller
->OnTtsEvent(
297 utterance_id
, TTS_EVENT_WORD
, char_index
, std::string());
298 } else if (event_type
== constants::kEventTypeSentence
) {
299 controller
->OnTtsEvent(
300 utterance_id
, TTS_EVENT_SENTENCE
, char_index
, std::string());
301 } else if (event_type
== constants::kEventTypeMarker
) {
302 controller
->OnTtsEvent(
303 utterance_id
, TTS_EVENT_MARKER
, char_index
, std::string());
304 } else if (event_type
== constants::kEventTypeError
) {
305 std::string error_message
;
306 event
->GetString(constants::kErrorMessageKey
, &error_message
);
307 controller
->OnTtsEvent(
308 utterance_id
, TTS_EVENT_ERROR
, char_index
, error_message
);
310 EXTENSION_FUNCTION_VALIDATE(false);