89db1c5fa9064a5a266fce392adfc976d1947a77
[platform/framework/web/crosswalk.git] / src / chrome / browser / speech / extension_api / tts_extension_api.cc
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.
4
5 #include "chrome/browser/speech/extension_api/tts_extension_api.h"
6
7 #include <string>
8
9 #include "base/lazy_instance.h"
10 #include "base/memory/weak_ptr.h"
11 #include "base/values.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/speech/extension_api/tts_engine_extension_api.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"
19
20 namespace constants = tts_extension_api_constants;
21
22 namespace events {
23 const char kOnEvent[] = "tts.onEvent";
24 };  // namespace events
25
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;
51   }
52 }
53
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;
75
76   NOTREACHED();
77   return TTS_EVENT_ERROR;
78 }
79
80 namespace extensions {
81
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 base::SupportsWeakPtr<TtsExtensionEventHandler> {
87  public:
88   virtual void OnTtsEvent(Utterance* utterance,
89                           TtsEventType event_type,
90                           int char_index,
91                           const std::string& error_message) OVERRIDE;
92 };
93
94 void TtsExtensionEventHandler::OnTtsEvent(Utterance* utterance,
95                                           TtsEventType event_type,
96                                           int char_index,
97                                           const std::string& error_message) {
98   if (utterance->src_id() < 0) {
99     if (utterance->finished())
100       delete this;
101     return;
102   }
103
104   const std::set<TtsEventType>& desired_event_types =
105       utterance->desired_event_types();
106   if (desired_event_types.size() > 0 &&
107       desired_event_types.find(event_type) == desired_event_types.end()) {
108     if (utterance->finished())
109       delete this;
110     return;
111   }
112
113   const char *event_type_string = TtsEventTypeToString(event_type);
114   scoped_ptr<base::DictionaryValue> details(new base::DictionaryValue());
115   if (char_index >= 0)
116     details->SetInteger(constants::kCharIndexKey, char_index);
117   details->SetString(constants::kEventTypeKey, event_type_string);
118   if (event_type == TTS_EVENT_ERROR) {
119     details->SetString(constants::kErrorMessageKey, error_message);
120   }
121   details->SetInteger(constants::kSrcIdKey, utterance->src_id());
122   details->SetBoolean(constants::kIsFinalEventKey, utterance->finished());
123
124   scoped_ptr<base::ListValue> arguments(new base::ListValue());
125   arguments->Set(0, details.release());
126
127   scoped_ptr<extensions::Event> event(
128       new extensions::Event(events::kOnEvent, arguments.Pass()));
129   event->restrict_to_browser_context = utterance->profile();
130   event->event_url = utterance->src_url();
131   extensions::EventRouter::Get(utterance->profile())
132       ->DispatchEventToExtension(utterance->src_extension_id(), event.Pass());
133
134   if (utterance->finished())
135     delete this;
136 }
137
138 bool TtsSpeakFunction::RunAsync() {
139   std::string text;
140   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &text));
141   if (text.size() > 32768) {
142     error_ = constants::kErrorUtteranceTooLong;
143     return false;
144   }
145
146   scoped_ptr<base::DictionaryValue> options(new base::DictionaryValue());
147   if (args_->GetSize() >= 2) {
148     base::DictionaryValue* temp_options = NULL;
149     if (args_->GetDictionary(1, &temp_options))
150       options.reset(temp_options->DeepCopy());
151   }
152
153   std::string voice_name;
154   if (options->HasKey(constants::kVoiceNameKey)) {
155     EXTENSION_FUNCTION_VALIDATE(
156         options->GetString(constants::kVoiceNameKey, &voice_name));
157   }
158
159   std::string lang;
160   if (options->HasKey(constants::kLangKey))
161     EXTENSION_FUNCTION_VALIDATE(options->GetString(constants::kLangKey, &lang));
162   if (!lang.empty() && !l10n_util::IsValidLocaleSyntax(lang)) {
163     error_ = constants::kErrorInvalidLang;
164     return false;
165   }
166
167   std::string gender_str;
168   TtsGenderType gender;
169   if (options->HasKey(constants::kGenderKey))
170     EXTENSION_FUNCTION_VALIDATE(
171         options->GetString(constants::kGenderKey, &gender_str));
172   if (gender_str == constants::kGenderMale) {
173     gender = TTS_GENDER_MALE;
174   } else if (gender_str == constants::kGenderFemale) {
175     gender = TTS_GENDER_FEMALE;
176   } else if (gender_str.empty()) {
177     gender = TTS_GENDER_NONE;
178   } else {
179     error_ = constants::kErrorInvalidGender;
180     return false;
181   }
182
183   double rate = 1.0;
184   if (options->HasKey(constants::kRateKey)) {
185     EXTENSION_FUNCTION_VALIDATE(
186         options->GetDouble(constants::kRateKey, &rate));
187     if (rate < 0.1 || rate > 10.0) {
188       error_ = constants::kErrorInvalidRate;
189       return false;
190     }
191   }
192
193   double pitch = 1.0;
194   if (options->HasKey(constants::kPitchKey)) {
195     EXTENSION_FUNCTION_VALIDATE(
196         options->GetDouble(constants::kPitchKey, &pitch));
197     if (pitch < 0.0 || pitch > 2.0) {
198       error_ = constants::kErrorInvalidPitch;
199       return false;
200     }
201   }
202
203   double volume = 1.0;
204   if (options->HasKey(constants::kVolumeKey)) {
205     EXTENSION_FUNCTION_VALIDATE(
206         options->GetDouble(constants::kVolumeKey, &volume));
207     if (volume < 0.0 || volume > 1.0) {
208       error_ = constants::kErrorInvalidVolume;
209       return false;
210     }
211   }
212
213   bool can_enqueue = false;
214   if (options->HasKey(constants::kEnqueueKey)) {
215     EXTENSION_FUNCTION_VALIDATE(
216         options->GetBoolean(constants::kEnqueueKey, &can_enqueue));
217   }
218
219   std::set<TtsEventType> required_event_types;
220   if (options->HasKey(constants::kRequiredEventTypesKey)) {
221     base::ListValue* list;
222     EXTENSION_FUNCTION_VALIDATE(
223         options->GetList(constants::kRequiredEventTypesKey, &list));
224     for (size_t i = 0; i < list->GetSize(); ++i) {
225       std::string event_type;
226       if (list->GetString(i, &event_type))
227         required_event_types.insert(TtsEventTypeFromString(event_type.c_str()));
228     }
229   }
230
231   std::set<TtsEventType> desired_event_types;
232   if (options->HasKey(constants::kDesiredEventTypesKey)) {
233     base::ListValue* list;
234     EXTENSION_FUNCTION_VALIDATE(
235         options->GetList(constants::kDesiredEventTypesKey, &list));
236     for (size_t i = 0; i < list->GetSize(); ++i) {
237       std::string event_type;
238       if (list->GetString(i, &event_type))
239         desired_event_types.insert(TtsEventTypeFromString(event_type.c_str()));
240     }
241   }
242
243   std::string voice_extension_id;
244   if (options->HasKey(constants::kExtensionIdKey)) {
245     EXTENSION_FUNCTION_VALIDATE(
246         options->GetString(constants::kExtensionIdKey, &voice_extension_id));
247   }
248
249   int src_id = -1;
250   if (options->HasKey(constants::kSrcIdKey)) {
251     EXTENSION_FUNCTION_VALIDATE(
252         options->GetInteger(constants::kSrcIdKey, &src_id));
253   }
254
255   // If we got this far, the arguments were all in the valid format, so
256   // send the success response to the callback now - this ensures that
257   // the callback response always arrives before events, which makes
258   // the behavior more predictable and easier to write unit tests for too.
259   SendResponse(true);
260
261   UtteranceContinuousParameters continuous_params;
262   continuous_params.rate = rate;
263   continuous_params.pitch = pitch;
264   continuous_params.volume = volume;
265
266   Utterance* utterance = new Utterance(GetProfile());
267   utterance->set_text(text);
268   utterance->set_voice_name(voice_name);
269   utterance->set_src_extension_id(extension_id());
270   utterance->set_src_id(src_id);
271   utterance->set_src_url(source_url());
272   utterance->set_lang(lang);
273   utterance->set_gender(gender);
274   utterance->set_continuous_parameters(continuous_params);
275   utterance->set_can_enqueue(can_enqueue);
276   utterance->set_required_event_types(required_event_types);
277   utterance->set_desired_event_types(desired_event_types);
278   utterance->set_extension_id(voice_extension_id);
279   utterance->set_options(options.get());
280   utterance->set_event_delegate(
281       (new TtsExtensionEventHandler())->AsWeakPtr());
282
283   TtsController* controller = TtsController::GetInstance();
284   controller->SpeakOrEnqueue(utterance);
285   return true;
286 }
287
288 bool TtsStopSpeakingFunction::RunSync() {
289   TtsController::GetInstance()->Stop();
290   return true;
291 }
292
293 bool TtsPauseFunction::RunSync() {
294   TtsController::GetInstance()->Pause();
295   return true;
296 }
297
298 bool TtsResumeFunction::RunSync() {
299   TtsController::GetInstance()->Resume();
300   return true;
301 }
302
303 bool TtsIsSpeakingFunction::RunSync() {
304   SetResult(base::Value::CreateBooleanValue(
305       TtsController::GetInstance()->IsSpeaking()));
306   return true;
307 }
308
309 bool TtsGetVoicesFunction::RunSync() {
310   std::vector<VoiceData> voices;
311   TtsController::GetInstance()->GetVoices(GetProfile(), &voices);
312
313   scoped_ptr<base::ListValue> result_voices(new base::ListValue());
314   for (size_t i = 0; i < voices.size(); ++i) {
315     const VoiceData& voice = voices[i];
316     base::DictionaryValue* result_voice = new base::DictionaryValue();
317     result_voice->SetString(constants::kVoiceNameKey, voice.name);
318     result_voice->SetBoolean(constants::kRemoteKey, voice.remote);
319     if (!voice.lang.empty())
320       result_voice->SetString(constants::kLangKey, voice.lang);
321     if (voice.gender == TTS_GENDER_MALE)
322       result_voice->SetString(constants::kGenderKey, constants::kGenderMale);
323     else if (voice.gender == TTS_GENDER_FEMALE)
324       result_voice->SetString(constants::kGenderKey, constants::kGenderFemale);
325     if (!voice.extension_id.empty())
326       result_voice->SetString(constants::kExtensionIdKey, voice.extension_id);
327
328     base::ListValue* event_types = new base::ListValue();
329     for (std::set<TtsEventType>::iterator iter = voice.events.begin();
330          iter != voice.events.end(); ++iter) {
331       const char* event_name_constant = TtsEventTypeToString(*iter);
332       event_types->Append(base::Value::CreateStringValue(event_name_constant));
333     }
334     result_voice->Set(constants::kEventTypesKey, event_types);
335
336     result_voices->Append(result_voice);
337   }
338
339   SetResult(result_voices.release());
340   return true;
341 }
342
343 TtsAPI::TtsAPI(content::BrowserContext* context) {
344   ExtensionFunctionRegistry* registry =
345       ExtensionFunctionRegistry::GetInstance();
346   registry->RegisterFunction<ExtensionTtsEngineSendTtsEventFunction>();
347   registry->RegisterFunction<TtsGetVoicesFunction>();
348   registry->RegisterFunction<TtsIsSpeakingFunction>();
349   registry->RegisterFunction<TtsSpeakFunction>();
350   registry->RegisterFunction<TtsStopSpeakingFunction>();
351   registry->RegisterFunction<TtsPauseFunction>();
352   registry->RegisterFunction<TtsResumeFunction>();
353 }
354
355 TtsAPI::~TtsAPI() {
356 }
357
358 static base::LazyInstance<BrowserContextKeyedAPIFactory<TtsAPI> > g_factory =
359     LAZY_INSTANCE_INITIALIZER;
360
361 BrowserContextKeyedAPIFactory<TtsAPI>* TtsAPI::GetFactoryInstance() {
362   return g_factory.Pointer();
363 }
364
365 }  // namespace extensions