Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / speech / extension_api / tts_extension_api.cc
blobf5fbe572fc2fb0031965bebc12eaaf9c850524a6
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"
7 #include <string>
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;
22 namespace events {
23 const char kOnEvent[] = "tts.onEvent";
24 }; // namespace events
26 const char *TtsEventTypeToString(TtsEventType event_type) {
27 switch (event_type) {
28 case TTS_EVENT_START:
29 return constants::kEventTypeStart;
30 case TTS_EVENT_END:
31 return constants::kEventTypeEnd;
32 case TTS_EVENT_WORD:
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;
42 case TTS_EVENT_ERROR:
43 return constants::kEventTypeError;
44 case TTS_EVENT_PAUSE:
45 return constants::kEventTypePause;
46 case TTS_EVENT_RESUME:
47 return constants::kEventTypeResume;
48 default:
49 NOTREACHED();
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)
58 return TTS_EVENT_END;
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;
76 NOTREACHED();
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 {
86 public:
87 explicit TtsExtensionEventHandler(const std::string& src_extension_id);
89 void OnTtsEvent(Utterance* utterance,
90 TtsEventType event_type,
91 int char_index,
92 const std::string& error_message) override;
94 private:
95 // The extension ID of the extension that called speak() and should
96 // receive events.
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,
107 int char_index,
108 const std::string& error_message) {
109 if (utterance->src_id() < 0) {
110 if (utterance->finished())
111 delete this;
112 return;
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())
120 delete this;
121 return;
124 const char *event_type_string = TtsEventTypeToString(event_type);
125 scoped_ptr<base::DictionaryValue> details(new base::DictionaryValue());
126 if (char_index >= 0)
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(::extensions::events::TTS_ON_EVENT,
140 ::events::kOnEvent, arguments.Pass()));
141 event->restrict_to_browser_context = utterance->browser_context();
142 event->event_url = utterance->src_url();
143 extensions::EventRouter::Get(utterance->browser_context())
144 ->DispatchEventToExtension(src_extension_id_, event.Pass());
146 if (utterance->finished())
147 delete this;
150 bool TtsSpeakFunction::RunAsync() {
151 std::string text;
152 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &text));
153 if (text.size() > 32768) {
154 error_ = constants::kErrorUtteranceTooLong;
155 return false;
158 scoped_ptr<base::DictionaryValue> options(new base::DictionaryValue());
159 if (args_->GetSize() >= 2) {
160 base::DictionaryValue* temp_options = NULL;
161 if (args_->GetDictionary(1, &temp_options))
162 options.reset(temp_options->DeepCopy());
165 std::string voice_name;
166 if (options->HasKey(constants::kVoiceNameKey)) {
167 EXTENSION_FUNCTION_VALIDATE(
168 options->GetString(constants::kVoiceNameKey, &voice_name));
171 std::string lang;
172 if (options->HasKey(constants::kLangKey))
173 EXTENSION_FUNCTION_VALIDATE(options->GetString(constants::kLangKey, &lang));
174 if (!lang.empty() && !l10n_util::IsValidLocaleSyntax(lang)) {
175 error_ = constants::kErrorInvalidLang;
176 return false;
179 std::string gender_str;
180 TtsGenderType gender;
181 if (options->HasKey(constants::kGenderKey))
182 EXTENSION_FUNCTION_VALIDATE(
183 options->GetString(constants::kGenderKey, &gender_str));
184 if (gender_str == constants::kGenderMale) {
185 gender = TTS_GENDER_MALE;
186 } else if (gender_str == constants::kGenderFemale) {
187 gender = TTS_GENDER_FEMALE;
188 } else if (gender_str.empty()) {
189 gender = TTS_GENDER_NONE;
190 } else {
191 error_ = constants::kErrorInvalidGender;
192 return false;
195 double rate = 1.0;
196 if (options->HasKey(constants::kRateKey)) {
197 EXTENSION_FUNCTION_VALIDATE(
198 options->GetDouble(constants::kRateKey, &rate));
199 if (rate < 0.1 || rate > 10.0) {
200 error_ = constants::kErrorInvalidRate;
201 return false;
205 double pitch = 1.0;
206 if (options->HasKey(constants::kPitchKey)) {
207 EXTENSION_FUNCTION_VALIDATE(
208 options->GetDouble(constants::kPitchKey, &pitch));
209 if (pitch < 0.0 || pitch > 2.0) {
210 error_ = constants::kErrorInvalidPitch;
211 return false;
215 double volume = 1.0;
216 if (options->HasKey(constants::kVolumeKey)) {
217 EXTENSION_FUNCTION_VALIDATE(
218 options->GetDouble(constants::kVolumeKey, &volume));
219 if (volume < 0.0 || volume > 1.0) {
220 error_ = constants::kErrorInvalidVolume;
221 return false;
225 bool can_enqueue = false;
226 if (options->HasKey(constants::kEnqueueKey)) {
227 EXTENSION_FUNCTION_VALIDATE(
228 options->GetBoolean(constants::kEnqueueKey, &can_enqueue));
231 std::set<TtsEventType> required_event_types;
232 if (options->HasKey(constants::kRequiredEventTypesKey)) {
233 base::ListValue* list;
234 EXTENSION_FUNCTION_VALIDATE(
235 options->GetList(constants::kRequiredEventTypesKey, &list));
236 for (size_t i = 0; i < list->GetSize(); ++i) {
237 std::string event_type;
238 if (list->GetString(i, &event_type))
239 required_event_types.insert(TtsEventTypeFromString(event_type.c_str()));
243 std::set<TtsEventType> desired_event_types;
244 if (options->HasKey(constants::kDesiredEventTypesKey)) {
245 base::ListValue* list;
246 EXTENSION_FUNCTION_VALIDATE(
247 options->GetList(constants::kDesiredEventTypesKey, &list));
248 for (size_t i = 0; i < list->GetSize(); ++i) {
249 std::string event_type;
250 if (list->GetString(i, &event_type))
251 desired_event_types.insert(TtsEventTypeFromString(event_type.c_str()));
255 std::string voice_extension_id;
256 if (options->HasKey(constants::kExtensionIdKey)) {
257 EXTENSION_FUNCTION_VALIDATE(
258 options->GetString(constants::kExtensionIdKey, &voice_extension_id));
261 int src_id = -1;
262 if (options->HasKey(constants::kSrcIdKey)) {
263 EXTENSION_FUNCTION_VALIDATE(
264 options->GetInteger(constants::kSrcIdKey, &src_id));
267 // If we got this far, the arguments were all in the valid format, so
268 // send the success response to the callback now - this ensures that
269 // the callback response always arrives before events, which makes
270 // the behavior more predictable and easier to write unit tests for too.
271 SendResponse(true);
273 Utterance* utterance = new Utterance(GetProfile());
274 utterance->set_text(text);
275 utterance->set_voice_name(voice_name);
276 utterance->set_src_id(src_id);
277 utterance->set_src_url(source_url());
278 utterance->set_lang(lang);
279 utterance->set_gender(gender);
280 utterance->set_continuous_parameters(rate, pitch, volume);
281 utterance->set_can_enqueue(can_enqueue);
282 utterance->set_required_event_types(required_event_types);
283 utterance->set_desired_event_types(desired_event_types);
284 utterance->set_extension_id(voice_extension_id);
285 utterance->set_options(options.get());
286 utterance->set_event_delegate(new TtsExtensionEventHandler(extension_id()));
288 TtsController* controller = TtsController::GetInstance();
289 controller->SpeakOrEnqueue(utterance);
290 return true;
293 bool TtsStopSpeakingFunction::RunSync() {
294 TtsController::GetInstance()->Stop();
295 return true;
298 bool TtsPauseFunction::RunSync() {
299 TtsController::GetInstance()->Pause();
300 return true;
303 bool TtsResumeFunction::RunSync() {
304 TtsController::GetInstance()->Resume();
305 return true;
308 bool TtsIsSpeakingFunction::RunSync() {
309 SetResult(
310 new base::FundamentalValue(TtsController::GetInstance()->IsSpeaking()));
311 return true;
314 bool TtsGetVoicesFunction::RunSync() {
315 std::vector<VoiceData> voices;
316 TtsController::GetInstance()->GetVoices(GetProfile(), &voices);
318 scoped_ptr<base::ListValue> result_voices(new base::ListValue());
319 for (size_t i = 0; i < voices.size(); ++i) {
320 const VoiceData& voice = voices[i];
321 base::DictionaryValue* result_voice = new base::DictionaryValue();
322 result_voice->SetString(constants::kVoiceNameKey, voice.name);
323 result_voice->SetBoolean(constants::kRemoteKey, voice.remote);
324 if (!voice.lang.empty())
325 result_voice->SetString(constants::kLangKey, voice.lang);
326 if (voice.gender == TTS_GENDER_MALE)
327 result_voice->SetString(constants::kGenderKey, constants::kGenderMale);
328 else if (voice.gender == TTS_GENDER_FEMALE)
329 result_voice->SetString(constants::kGenderKey, constants::kGenderFemale);
330 if (!voice.extension_id.empty())
331 result_voice->SetString(constants::kExtensionIdKey, voice.extension_id);
333 base::ListValue* event_types = new base::ListValue();
334 for (std::set<TtsEventType>::iterator iter = voice.events.begin();
335 iter != voice.events.end(); ++iter) {
336 const char* event_name_constant = TtsEventTypeToString(*iter);
337 event_types->Append(new base::StringValue(event_name_constant));
339 result_voice->Set(constants::kEventTypesKey, event_types);
341 result_voices->Append(result_voice);
344 SetResult(result_voices.release());
345 return true;
348 TtsAPI::TtsAPI(content::BrowserContext* context) {
349 ExtensionFunctionRegistry* registry =
350 ExtensionFunctionRegistry::GetInstance();
351 registry->RegisterFunction<ExtensionTtsEngineSendTtsEventFunction>();
352 registry->RegisterFunction<TtsGetVoicesFunction>();
353 registry->RegisterFunction<TtsIsSpeakingFunction>();
354 registry->RegisterFunction<TtsSpeakFunction>();
355 registry->RegisterFunction<TtsStopSpeakingFunction>();
356 registry->RegisterFunction<TtsPauseFunction>();
357 registry->RegisterFunction<TtsResumeFunction>();
359 // Ensure we're observing newly added engines for the given context.
360 TtsEngineExtensionObserver::GetInstance(Profile::FromBrowserContext(context));
363 TtsAPI::~TtsAPI() {
366 static base::LazyInstance<BrowserContextKeyedAPIFactory<TtsAPI> > g_factory =
367 LAZY_INSTANCE_INITIALIZER;
369 BrowserContextKeyedAPIFactory<TtsAPI>* TtsAPI::GetFactoryInstance() {
370 return g_factory.Pointer();
373 } // namespace extensions