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