Fire an error if a pref used in the UI is missing once all prefs are fetched.
[chromium-blink-merge.git] / chrome / browser / speech / extension_api / tts_extension_api.cc
blobe7b65e175c62026c0ab86247117388c380fb16f0
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(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())
146 delete this;
149 bool TtsSpeakFunction::RunAsync() {
150 std::string text;
151 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &text));
152 if (text.size() > 32768) {
153 error_ = constants::kErrorUtteranceTooLong;
154 return false;
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));
170 std::string lang;
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;
175 return false;
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;
189 } else {
190 error_ = constants::kErrorInvalidGender;
191 return false;
194 double rate = 1.0;
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;
200 return false;
204 double pitch = 1.0;
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;
210 return false;
214 double volume = 1.0;
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;
220 return false;
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));
260 int src_id = -1;
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.
270 SendResponse(true);
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);
294 return true;
297 bool TtsStopSpeakingFunction::RunSync() {
298 TtsController::GetInstance()->Stop();
299 return true;
302 bool TtsPauseFunction::RunSync() {
303 TtsController::GetInstance()->Pause();
304 return true;
307 bool TtsResumeFunction::RunSync() {
308 TtsController::GetInstance()->Resume();
309 return true;
312 bool TtsIsSpeakingFunction::RunSync() {
313 SetResult(
314 new base::FundamentalValue(TtsController::GetInstance()->IsSpeaking()));
315 return true;
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());
349 return true;
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));
367 TtsAPI::~TtsAPI() {
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