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_extension_api.h"
9 #include "base/lazy_instance.h"
10 #include "base/values.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
13 #include "chrome/browser/speech/extension_api/tts_engine_extension_observer.h"
14 #include "chrome/browser/speech/extension_api/tts_extension_api_constants.h"
15 #include "chrome/browser/speech/tts_controller.h"
16 #include "extensions/browser/event_router.h"
17 #include "extensions/browser/extension_function_registry.h"
18 #include "ui/base/l10n/l10n_util.h"
20 namespace constants
= tts_extension_api_constants
;
23 const char kOnEvent
[] = "tts.onEvent";
24 }; // namespace events
26 const char *TtsEventTypeToString(TtsEventType event_type
) {
29 return constants::kEventTypeStart
;
31 return constants::kEventTypeEnd
;
33 return constants::kEventTypeWord
;
34 case TTS_EVENT_SENTENCE
:
35 return constants::kEventTypeSentence
;
36 case TTS_EVENT_MARKER
:
37 return constants::kEventTypeMarker
;
38 case TTS_EVENT_INTERRUPTED
:
39 return constants::kEventTypeInterrupted
;
40 case TTS_EVENT_CANCELLED
:
41 return constants::kEventTypeCancelled
;
43 return constants::kEventTypeError
;
45 return constants::kEventTypePause
;
46 case TTS_EVENT_RESUME
:
47 return constants::kEventTypeResume
;
50 return constants::kEventTypeError
;
54 TtsEventType
TtsEventTypeFromString(const std::string
& str
) {
55 if (str
== constants::kEventTypeStart
)
56 return TTS_EVENT_START
;
57 if (str
== constants::kEventTypeEnd
)
59 if (str
== constants::kEventTypeWord
)
60 return TTS_EVENT_WORD
;
61 if (str
== constants::kEventTypeSentence
)
62 return TTS_EVENT_SENTENCE
;
63 if (str
== constants::kEventTypeMarker
)
64 return TTS_EVENT_MARKER
;
65 if (str
== constants::kEventTypeInterrupted
)
66 return TTS_EVENT_INTERRUPTED
;
67 if (str
== constants::kEventTypeCancelled
)
68 return TTS_EVENT_CANCELLED
;
69 if (str
== constants::kEventTypeError
)
70 return TTS_EVENT_ERROR
;
71 if (str
== constants::kEventTypePause
)
72 return TTS_EVENT_PAUSE
;
73 if (str
== constants::kEventTypeResume
)
74 return TTS_EVENT_RESUME
;
77 return TTS_EVENT_ERROR
;
80 namespace extensions
{
82 // One of these is constructed for each utterance, and deleted
83 // when the utterance gets any final event.
84 class TtsExtensionEventHandler
85 : public UtteranceEventDelegate
{
87 explicit TtsExtensionEventHandler(const std::string
& src_extension_id
);
89 void OnTtsEvent(Utterance
* utterance
,
90 TtsEventType event_type
,
92 const std::string
& error_message
) override
;
95 // The extension ID of the extension that called speak() and should
97 std::string src_extension_id_
;
100 TtsExtensionEventHandler::TtsExtensionEventHandler(
101 const std::string
& src_extension_id
)
102 : src_extension_id_(src_extension_id
) {
105 void TtsExtensionEventHandler::OnTtsEvent(Utterance
* utterance
,
106 TtsEventType event_type
,
108 const std::string
& error_message
) {
109 if (utterance
->src_id() < 0) {
110 if (utterance
->finished())
115 const std::set
<TtsEventType
>& desired_event_types
=
116 utterance
->desired_event_types();
117 if (desired_event_types
.size() > 0 &&
118 desired_event_types
.find(event_type
) == desired_event_types
.end()) {
119 if (utterance
->finished())
124 const char *event_type_string
= TtsEventTypeToString(event_type
);
125 scoped_ptr
<base::DictionaryValue
> details(new base::DictionaryValue());
127 details
->SetInteger(constants::kCharIndexKey
, char_index
);
128 details
->SetString(constants::kEventTypeKey
, event_type_string
);
129 if (event_type
== TTS_EVENT_ERROR
) {
130 details
->SetString(constants::kErrorMessageKey
, error_message
);
132 details
->SetInteger(constants::kSrcIdKey
, utterance
->src_id());
133 details
->SetBoolean(constants::kIsFinalEventKey
, utterance
->finished());
135 scoped_ptr
<base::ListValue
> arguments(new base::ListValue());
136 arguments
->Set(0, details
.release());
138 scoped_ptr
<extensions::Event
> event(
139 new extensions::Event(events::kOnEvent
, arguments
.Pass()));
140 event
->restrict_to_browser_context
= utterance
->browser_context();
141 event
->event_url
= utterance
->src_url();
142 extensions::EventRouter::Get(utterance
->browser_context())
143 ->DispatchEventToExtension(src_extension_id_
, event
.Pass());
145 if (utterance
->finished())
149 bool TtsSpeakFunction::RunAsync() {
151 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &text
));
152 if (text
.size() > 32768) {
153 error_
= constants::kErrorUtteranceTooLong
;
157 scoped_ptr
<base::DictionaryValue
> options(new base::DictionaryValue());
158 if (args_
->GetSize() >= 2) {
159 base::DictionaryValue
* temp_options
= NULL
;
160 if (args_
->GetDictionary(1, &temp_options
))
161 options
.reset(temp_options
->DeepCopy());
164 std::string voice_name
;
165 if (options
->HasKey(constants::kVoiceNameKey
)) {
166 EXTENSION_FUNCTION_VALIDATE(
167 options
->GetString(constants::kVoiceNameKey
, &voice_name
));
171 if (options
->HasKey(constants::kLangKey
))
172 EXTENSION_FUNCTION_VALIDATE(options
->GetString(constants::kLangKey
, &lang
));
173 if (!lang
.empty() && !l10n_util::IsValidLocaleSyntax(lang
)) {
174 error_
= constants::kErrorInvalidLang
;
178 std::string gender_str
;
179 TtsGenderType gender
;
180 if (options
->HasKey(constants::kGenderKey
))
181 EXTENSION_FUNCTION_VALIDATE(
182 options
->GetString(constants::kGenderKey
, &gender_str
));
183 if (gender_str
== constants::kGenderMale
) {
184 gender
= TTS_GENDER_MALE
;
185 } else if (gender_str
== constants::kGenderFemale
) {
186 gender
= TTS_GENDER_FEMALE
;
187 } else if (gender_str
.empty()) {
188 gender
= TTS_GENDER_NONE
;
190 error_
= constants::kErrorInvalidGender
;
195 if (options
->HasKey(constants::kRateKey
)) {
196 EXTENSION_FUNCTION_VALIDATE(
197 options
->GetDouble(constants::kRateKey
, &rate
));
198 if (rate
< 0.1 || rate
> 10.0) {
199 error_
= constants::kErrorInvalidRate
;
205 if (options
->HasKey(constants::kPitchKey
)) {
206 EXTENSION_FUNCTION_VALIDATE(
207 options
->GetDouble(constants::kPitchKey
, &pitch
));
208 if (pitch
< 0.0 || pitch
> 2.0) {
209 error_
= constants::kErrorInvalidPitch
;
215 if (options
->HasKey(constants::kVolumeKey
)) {
216 EXTENSION_FUNCTION_VALIDATE(
217 options
->GetDouble(constants::kVolumeKey
, &volume
));
218 if (volume
< 0.0 || volume
> 1.0) {
219 error_
= constants::kErrorInvalidVolume
;
224 bool can_enqueue
= false;
225 if (options
->HasKey(constants::kEnqueueKey
)) {
226 EXTENSION_FUNCTION_VALIDATE(
227 options
->GetBoolean(constants::kEnqueueKey
, &can_enqueue
));
230 std::set
<TtsEventType
> required_event_types
;
231 if (options
->HasKey(constants::kRequiredEventTypesKey
)) {
232 base::ListValue
* list
;
233 EXTENSION_FUNCTION_VALIDATE(
234 options
->GetList(constants::kRequiredEventTypesKey
, &list
));
235 for (size_t i
= 0; i
< list
->GetSize(); ++i
) {
236 std::string event_type
;
237 if (list
->GetString(i
, &event_type
))
238 required_event_types
.insert(TtsEventTypeFromString(event_type
.c_str()));
242 std::set
<TtsEventType
> desired_event_types
;
243 if (options
->HasKey(constants::kDesiredEventTypesKey
)) {
244 base::ListValue
* list
;
245 EXTENSION_FUNCTION_VALIDATE(
246 options
->GetList(constants::kDesiredEventTypesKey
, &list
));
247 for (size_t i
= 0; i
< list
->GetSize(); ++i
) {
248 std::string event_type
;
249 if (list
->GetString(i
, &event_type
))
250 desired_event_types
.insert(TtsEventTypeFromString(event_type
.c_str()));
254 std::string voice_extension_id
;
255 if (options
->HasKey(constants::kExtensionIdKey
)) {
256 EXTENSION_FUNCTION_VALIDATE(
257 options
->GetString(constants::kExtensionIdKey
, &voice_extension_id
));
261 if (options
->HasKey(constants::kSrcIdKey
)) {
262 EXTENSION_FUNCTION_VALIDATE(
263 options
->GetInteger(constants::kSrcIdKey
, &src_id
));
266 // If we got this far, the arguments were all in the valid format, so
267 // send the success response to the callback now - this ensures that
268 // the callback response always arrives before events, which makes
269 // the behavior more predictable and easier to write unit tests for too.
272 UtteranceContinuousParameters continuous_params
;
273 continuous_params
.rate
= rate
;
274 continuous_params
.pitch
= pitch
;
275 continuous_params
.volume
= volume
;
277 Utterance
* utterance
= new Utterance(GetProfile());
278 utterance
->set_text(text
);
279 utterance
->set_voice_name(voice_name
);
280 utterance
->set_src_id(src_id
);
281 utterance
->set_src_url(source_url());
282 utterance
->set_lang(lang
);
283 utterance
->set_gender(gender
);
284 utterance
->set_continuous_parameters(continuous_params
);
285 utterance
->set_can_enqueue(can_enqueue
);
286 utterance
->set_required_event_types(required_event_types
);
287 utterance
->set_desired_event_types(desired_event_types
);
288 utterance
->set_extension_id(voice_extension_id
);
289 utterance
->set_options(options
.get());
290 utterance
->set_event_delegate(new TtsExtensionEventHandler(extension_id()));
292 TtsController
* controller
= TtsController::GetInstance();
293 controller
->SpeakOrEnqueue(utterance
);
297 bool TtsStopSpeakingFunction::RunSync() {
298 TtsController::GetInstance()->Stop();
302 bool TtsPauseFunction::RunSync() {
303 TtsController::GetInstance()->Pause();
307 bool TtsResumeFunction::RunSync() {
308 TtsController::GetInstance()->Resume();
312 bool TtsIsSpeakingFunction::RunSync() {
314 new base::FundamentalValue(TtsController::GetInstance()->IsSpeaking()));
318 bool TtsGetVoicesFunction::RunSync() {
319 std::vector
<VoiceData
> voices
;
320 TtsController::GetInstance()->GetVoices(GetProfile(), &voices
);
322 scoped_ptr
<base::ListValue
> result_voices(new base::ListValue());
323 for (size_t i
= 0; i
< voices
.size(); ++i
) {
324 const VoiceData
& voice
= voices
[i
];
325 base::DictionaryValue
* result_voice
= new base::DictionaryValue();
326 result_voice
->SetString(constants::kVoiceNameKey
, voice
.name
);
327 result_voice
->SetBoolean(constants::kRemoteKey
, voice
.remote
);
328 if (!voice
.lang
.empty())
329 result_voice
->SetString(constants::kLangKey
, voice
.lang
);
330 if (voice
.gender
== TTS_GENDER_MALE
)
331 result_voice
->SetString(constants::kGenderKey
, constants::kGenderMale
);
332 else if (voice
.gender
== TTS_GENDER_FEMALE
)
333 result_voice
->SetString(constants::kGenderKey
, constants::kGenderFemale
);
334 if (!voice
.extension_id
.empty())
335 result_voice
->SetString(constants::kExtensionIdKey
, voice
.extension_id
);
337 base::ListValue
* event_types
= new base::ListValue();
338 for (std::set
<TtsEventType
>::iterator iter
= voice
.events
.begin();
339 iter
!= voice
.events
.end(); ++iter
) {
340 const char* event_name_constant
= TtsEventTypeToString(*iter
);
341 event_types
->Append(new base::StringValue(event_name_constant
));
343 result_voice
->Set(constants::kEventTypesKey
, event_types
);
345 result_voices
->Append(result_voice
);
348 SetResult(result_voices
.release());
352 TtsAPI::TtsAPI(content::BrowserContext
* context
) {
353 ExtensionFunctionRegistry
* registry
=
354 ExtensionFunctionRegistry::GetInstance();
355 registry
->RegisterFunction
<ExtensionTtsEngineSendTtsEventFunction
>();
356 registry
->RegisterFunction
<TtsGetVoicesFunction
>();
357 registry
->RegisterFunction
<TtsIsSpeakingFunction
>();
358 registry
->RegisterFunction
<TtsSpeakFunction
>();
359 registry
->RegisterFunction
<TtsStopSpeakingFunction
>();
360 registry
->RegisterFunction
<TtsPauseFunction
>();
361 registry
->RegisterFunction
<TtsResumeFunction
>();
363 // Ensure we're observing newly added engines for the given context.
364 TtsEngineExtensionObserver::GetInstance(Profile::FromBrowserContext(context
));
370 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<TtsAPI
> > g_factory
=
371 LAZY_INSTANCE_INITIALIZER
;
373 BrowserContextKeyedAPIFactory
<TtsAPI
>* TtsAPI::GetFactoryInstance() {
374 return g_factory
.Pointer();
377 } // namespace extensions