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 366