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_engine_extension_observer.h"
15#include "chrome/browser/speech/extension_api/tts_extension_api_constants.h"
16#include "chrome/browser/speech/tts_controller.h"
17#include "extensions/browser/event_router.h"
18#include "extensions/browser/extension_function_registry.h"
19#include "ui/base/l10n/l10n_util.h"
20
21namespace constants = tts_extension_api_constants;
22
23namespace events {
24const char kOnEvent[] = "tts.onEvent";
25};  // namespace events
26
27const char *TtsEventTypeToString(TtsEventType event_type) {
28  switch (event_type) {
29    case TTS_EVENT_START:
30      return constants::kEventTypeStart;
31    case TTS_EVENT_END:
32      return constants::kEventTypeEnd;
33    case TTS_EVENT_WORD:
34      return constants::kEventTypeWord;
35    case TTS_EVENT_SENTENCE:
36      return constants::kEventTypeSentence;
37    case TTS_EVENT_MARKER:
38      return constants::kEventTypeMarker;
39    case TTS_EVENT_INTERRUPTED:
40      return constants::kEventTypeInterrupted;
41    case TTS_EVENT_CANCELLED:
42      return constants::kEventTypeCancelled;
43    case TTS_EVENT_ERROR:
44      return constants::kEventTypeError;
45    case TTS_EVENT_PAUSE:
46      return constants::kEventTypePause;
47    case TTS_EVENT_RESUME:
48      return constants::kEventTypeResume;
49    default:
50      NOTREACHED();
51      return constants::kEventTypeError;
52  }
53}
54
55TtsEventType TtsEventTypeFromString(const std::string& str) {
56  if (str == constants::kEventTypeStart)
57    return TTS_EVENT_START;
58  if (str == constants::kEventTypeEnd)
59    return TTS_EVENT_END;
60  if (str == constants::kEventTypeWord)
61    return TTS_EVENT_WORD;
62  if (str == constants::kEventTypeSentence)
63    return TTS_EVENT_SENTENCE;
64  if (str == constants::kEventTypeMarker)
65    return TTS_EVENT_MARKER;
66  if (str == constants::kEventTypeInterrupted)
67    return TTS_EVENT_INTERRUPTED;
68  if (str == constants::kEventTypeCancelled)
69    return TTS_EVENT_CANCELLED;
70  if (str == constants::kEventTypeError)
71    return TTS_EVENT_ERROR;
72  if (str == constants::kEventTypePause)
73    return TTS_EVENT_PAUSE;
74  if (str == constants::kEventTypeResume)
75    return TTS_EVENT_RESUME;
76
77  NOTREACHED();
78  return TTS_EVENT_ERROR;
79}
80
81namespace extensions {
82
83// One of these is constructed for each utterance, and deleted
84// when the utterance gets any final event.
85class TtsExtensionEventHandler
86    : public UtteranceEventDelegate,
87      public base::SupportsWeakPtr<TtsExtensionEventHandler> {
88 public:
89  virtual void OnTtsEvent(Utterance* utterance,
90                          TtsEventType event_type,
91                          int char_index,
92                          const std::string& error_message) OVERRIDE;
93};
94
95void TtsExtensionEventHandler::OnTtsEvent(Utterance* utterance,
96                                          TtsEventType event_type,
97                                          int char_index,
98                                          const std::string& error_message) {
99  if (utterance->src_id() < 0) {
100    if (utterance->finished())
101      delete this;
102    return;
103  }
104
105  const std::set<TtsEventType>& desired_event_types =
106      utterance->desired_event_types();
107  if (desired_event_types.size() > 0 &&
108      desired_event_types.find(event_type) == desired_event_types.end()) {
109    if (utterance->finished())
110      delete this;
111    return;
112  }
113
114  const char *event_type_string = TtsEventTypeToString(event_type);
115  scoped_ptr<base::DictionaryValue> details(new base::DictionaryValue());
116  if (char_index >= 0)
117    details->SetInteger(constants::kCharIndexKey, char_index);
118  details->SetString(constants::kEventTypeKey, event_type_string);
119  if (event_type == TTS_EVENT_ERROR) {
120    details->SetString(constants::kErrorMessageKey, error_message);
121  }
122  details->SetInteger(constants::kSrcIdKey, utterance->src_id());
123  details->SetBoolean(constants::kIsFinalEventKey, utterance->finished());
124
125  scoped_ptr<base::ListValue> arguments(new base::ListValue());
126  arguments->Set(0, details.release());
127
128  scoped_ptr<extensions::Event> event(
129      new extensions::Event(events::kOnEvent, arguments.Pass()));
130  event->restrict_to_browser_context = utterance->browser_context();
131  event->event_url = utterance->src_url();
132  extensions::EventRouter::Get(utterance->browser_context())
133      ->DispatchEventToExtension(utterance->src_extension_id(), event.Pass());
134
135  if (utterance->finished())
136    delete this;
137}
138
139bool TtsSpeakFunction::RunAsync() {
140  std::string text;
141  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &text));
142  if (text.size() > 32768) {
143    error_ = constants::kErrorUtteranceTooLong;
144    return false;
145  }
146
147  scoped_ptr<base::DictionaryValue> options(new base::DictionaryValue());
148  if (args_->GetSize() >= 2) {
149    base::DictionaryValue* temp_options = NULL;
150    if (args_->GetDictionary(1, &temp_options))
151      options.reset(temp_options->DeepCopy());
152  }
153
154  std::string voice_name;
155  if (options->HasKey(constants::kVoiceNameKey)) {
156    EXTENSION_FUNCTION_VALIDATE(
157        options->GetString(constants::kVoiceNameKey, &voice_name));
158  }
159
160  std::string lang;
161  if (options->HasKey(constants::kLangKey))
162    EXTENSION_FUNCTION_VALIDATE(options->GetString(constants::kLangKey, &lang));
163  if (!lang.empty() && !l10n_util::IsValidLocaleSyntax(lang)) {
164    error_ = constants::kErrorInvalidLang;
165    return false;
166  }
167
168  std::string gender_str;
169  TtsGenderType gender;
170  if (options->HasKey(constants::kGenderKey))
171    EXTENSION_FUNCTION_VALIDATE(
172        options->GetString(constants::kGenderKey, &gender_str));
173  if (gender_str == constants::kGenderMale) {
174    gender = TTS_GENDER_MALE;
175  } else if (gender_str == constants::kGenderFemale) {
176    gender = TTS_GENDER_FEMALE;
177  } else if (gender_str.empty()) {
178    gender = TTS_GENDER_NONE;
179  } else {
180    error_ = constants::kErrorInvalidGender;
181    return false;
182  }
183
184  double rate = 1.0;
185  if (options->HasKey(constants::kRateKey)) {
186    EXTENSION_FUNCTION_VALIDATE(
187        options->GetDouble(constants::kRateKey, &rate));
188    if (rate < 0.1 || rate > 10.0) {
189      error_ = constants::kErrorInvalidRate;
190      return false;
191    }
192  }
193
194  double pitch = 1.0;
195  if (options->HasKey(constants::kPitchKey)) {
196    EXTENSION_FUNCTION_VALIDATE(
197        options->GetDouble(constants::kPitchKey, &pitch));
198    if (pitch < 0.0 || pitch > 2.0) {
199      error_ = constants::kErrorInvalidPitch;
200      return false;
201    }
202  }
203
204  double volume = 1.0;
205  if (options->HasKey(constants::kVolumeKey)) {
206    EXTENSION_FUNCTION_VALIDATE(
207        options->GetDouble(constants::kVolumeKey, &volume));
208    if (volume < 0.0 || volume > 1.0) {
209      error_ = constants::kErrorInvalidVolume;
210      return false;
211    }
212  }
213
214  bool can_enqueue = false;
215  if (options->HasKey(constants::kEnqueueKey)) {
216    EXTENSION_FUNCTION_VALIDATE(
217        options->GetBoolean(constants::kEnqueueKey, &can_enqueue));
218  }
219
220  std::set<TtsEventType> required_event_types;
221  if (options->HasKey(constants::kRequiredEventTypesKey)) {
222    base::ListValue* list;
223    EXTENSION_FUNCTION_VALIDATE(
224        options->GetList(constants::kRequiredEventTypesKey, &list));
225    for (size_t i = 0; i < list->GetSize(); ++i) {
226      std::string event_type;
227      if (list->GetString(i, &event_type))
228        required_event_types.insert(TtsEventTypeFromString(event_type.c_str()));
229    }
230  }
231
232  std::set<TtsEventType> desired_event_types;
233  if (options->HasKey(constants::kDesiredEventTypesKey)) {
234    base::ListValue* list;
235    EXTENSION_FUNCTION_VALIDATE(
236        options->GetList(constants::kDesiredEventTypesKey, &list));
237    for (size_t i = 0; i < list->GetSize(); ++i) {
238      std::string event_type;
239      if (list->GetString(i, &event_type))
240        desired_event_types.insert(TtsEventTypeFromString(event_type.c_str()));
241    }
242  }
243
244  std::string voice_extension_id;
245  if (options->HasKey(constants::kExtensionIdKey)) {
246    EXTENSION_FUNCTION_VALIDATE(
247        options->GetString(constants::kExtensionIdKey, &voice_extension_id));
248  }
249
250  int src_id = -1;
251  if (options->HasKey(constants::kSrcIdKey)) {
252    EXTENSION_FUNCTION_VALIDATE(
253        options->GetInteger(constants::kSrcIdKey, &src_id));
254  }
255
256  // If we got this far, the arguments were all in the valid format, so
257  // send the success response to the callback now - this ensures that
258  // the callback response always arrives before events, which makes
259  // the behavior more predictable and easier to write unit tests for too.
260  SendResponse(true);
261
262  UtteranceContinuousParameters continuous_params;
263  continuous_params.rate = rate;
264  continuous_params.pitch = pitch;
265  continuous_params.volume = volume;
266
267  Utterance* utterance = new Utterance(GetProfile());
268  utterance->set_text(text);
269  utterance->set_voice_name(voice_name);
270  utterance->set_src_extension_id(extension_id());
271  utterance->set_src_id(src_id);
272  utterance->set_src_url(source_url());
273  utterance->set_lang(lang);
274  utterance->set_gender(gender);
275  utterance->set_continuous_parameters(continuous_params);
276  utterance->set_can_enqueue(can_enqueue);
277  utterance->set_required_event_types(required_event_types);
278  utterance->set_desired_event_types(desired_event_types);
279  utterance->set_extension_id(voice_extension_id);
280  utterance->set_options(options.get());
281  utterance->set_event_delegate(
282      (new TtsExtensionEventHandler())->AsWeakPtr());
283
284  TtsController* controller = TtsController::GetInstance();
285  controller->SpeakOrEnqueue(utterance);
286  return true;
287}
288
289bool TtsStopSpeakingFunction::RunSync() {
290  TtsController::GetInstance()->Stop();
291  return true;
292}
293
294bool TtsPauseFunction::RunSync() {
295  TtsController::GetInstance()->Pause();
296  return true;
297}
298
299bool TtsResumeFunction::RunSync() {
300  TtsController::GetInstance()->Resume();
301  return true;
302}
303
304bool TtsIsSpeakingFunction::RunSync() {
305  SetResult(
306      new base::FundamentalValue(TtsController::GetInstance()->IsSpeaking()));
307  return true;
308}
309
310bool TtsGetVoicesFunction::RunSync() {
311  std::vector<VoiceData> voices;
312  TtsController::GetInstance()->GetVoices(GetProfile(), &voices);
313
314  scoped_ptr<base::ListValue> result_voices(new base::ListValue());
315  for (size_t i = 0; i < voices.size(); ++i) {
316    const VoiceData& voice = voices[i];
317    base::DictionaryValue* result_voice = new base::DictionaryValue();
318    result_voice->SetString(constants::kVoiceNameKey, voice.name);
319    result_voice->SetBoolean(constants::kRemoteKey, voice.remote);
320    if (!voice.lang.empty())
321      result_voice->SetString(constants::kLangKey, voice.lang);
322    if (voice.gender == TTS_GENDER_MALE)
323      result_voice->SetString(constants::kGenderKey, constants::kGenderMale);
324    else if (voice.gender == TTS_GENDER_FEMALE)
325      result_voice->SetString(constants::kGenderKey, constants::kGenderFemale);
326    if (!voice.extension_id.empty())
327      result_voice->SetString(constants::kExtensionIdKey, voice.extension_id);
328
329    base::ListValue* event_types = new base::ListValue();
330    for (std::set<TtsEventType>::iterator iter = voice.events.begin();
331         iter != voice.events.end(); ++iter) {
332      const char* event_name_constant = TtsEventTypeToString(*iter);
333      event_types->Append(new base::StringValue(event_name_constant));
334    }
335    result_voice->Set(constants::kEventTypesKey, event_types);
336
337    result_voices->Append(result_voice);
338  }
339
340  SetResult(result_voices.release());
341  return true;
342}
343
344TtsAPI::TtsAPI(content::BrowserContext* context) {
345  ExtensionFunctionRegistry* registry =
346      ExtensionFunctionRegistry::GetInstance();
347  registry->RegisterFunction<ExtensionTtsEngineSendTtsEventFunction>();
348  registry->RegisterFunction<TtsGetVoicesFunction>();
349  registry->RegisterFunction<TtsIsSpeakingFunction>();
350  registry->RegisterFunction<TtsSpeakFunction>();
351  registry->RegisterFunction<TtsStopSpeakingFunction>();
352  registry->RegisterFunction<TtsPauseFunction>();
353  registry->RegisterFunction<TtsResumeFunction>();
354
355  // Ensure we're observing newly added engines for the given context.
356  TtsEngineExtensionObserver::GetInstance(Profile::FromBrowserContext(context));
357}
358
359TtsAPI::~TtsAPI() {
360}
361
362static base::LazyInstance<BrowserContextKeyedAPIFactory<TtsAPI> > g_factory =
363    LAZY_INSTANCE_INITIALIZER;
364
365BrowserContextKeyedAPIFactory<TtsAPI>* TtsAPI::GetFactoryInstance() {
366  return g_factory.Pointer();
367}
368
369}  // namespace extensions
370