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_engine_extension_api.h" 6 7 #include <string> 8 9 #include "base/json/json_writer.h" 10 #include "base/values.h" 11 #include "chrome/browser/extensions/component_loader.h" 12 #include "chrome/browser/extensions/extension_service.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/speech/extension_api/tts_engine_extension_observer.h" 15 #include "chrome/browser/speech/extension_api/tts_extension_api.h" 16 #include "chrome/browser/speech/extension_api/tts_extension_api_constants.h" 17 #include "chrome/browser/speech/tts_controller.h" 18 #include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h" 19 #include "chrome/common/extensions/extension_constants.h" 20 #include "content/public/browser/render_process_host.h" 21 #include "content/public/browser/render_view_host.h" 22 #include "content/public/common/console_message_level.h" 23 #include "extensions/browser/event_router.h" 24 #include "extensions/browser/extension_host.h" 25 #include "extensions/browser/extension_registry.h" 26 #include "extensions/browser/extension_system.h" 27 #include "extensions/browser/process_manager.h" 28 #include "extensions/common/extension.h" 29 #include "extensions/common/extension_messages.h" 30 #include "extensions/common/extension_set.h" 31 #include "net/base/network_change_notifier.h" 32 33 using extensions::EventRouter; 34 using extensions::Extension; 35 using extensions::ExtensionSystem; 36 37 namespace constants = tts_extension_api_constants; 38 39 namespace tts_engine_events { 40 const char kOnSpeak[] = "ttsEngine.onSpeak"; 41 const char kOnStop[] = "ttsEngine.onStop"; 42 const char kOnPause[] = "ttsEngine.onPause"; 43 const char kOnResume[] = "ttsEngine.onResume"; 44 }; // namespace tts_engine_events 45 46 namespace { 47 48 void WarnIfMissingPauseOrResumeListener( 49 Profile* profile, EventRouter* event_router, std::string extension_id) { 50 bool has_onpause = event_router->ExtensionHasEventListener( 51 extension_id, tts_engine_events::kOnPause); 52 bool has_onresume = event_router->ExtensionHasEventListener( 53 extension_id, tts_engine_events::kOnResume); 54 if (has_onpause == has_onresume) 55 return; 56 57 extensions::ProcessManager* process_manager = 58 ExtensionSystem::Get(profile)->process_manager(); 59 extensions::ExtensionHost* host = 60 process_manager->GetBackgroundHostForExtension(extension_id); 61 host->render_process_host()->Send(new ExtensionMsg_AddMessageToConsole( 62 host->render_view_host()->GetRoutingID(), 63 content::CONSOLE_MESSAGE_LEVEL_WARNING, 64 constants::kErrorMissingPauseOrResume)); 65 } 66 67 } // namespace 68 69 TtsExtensionEngine* TtsExtensionEngine::GetInstance() { 70 return Singleton<TtsExtensionEngine>::get(); 71 } 72 73 void TtsExtensionEngine::GetVoices(content::BrowserContext* browser_context, 74 std::vector<VoiceData>* out_voices) { 75 Profile* profile = Profile::FromBrowserContext(browser_context); 76 EventRouter* event_router = EventRouter::Get(profile); 77 DCHECK(event_router); 78 79 bool is_offline = (net::NetworkChangeNotifier::GetConnectionType() == 80 net::NetworkChangeNotifier::CONNECTION_NONE); 81 82 const extensions::ExtensionSet& extensions = 83 extensions::ExtensionRegistry::Get(profile)->enabled_extensions(); 84 extensions::ExtensionSet::const_iterator iter; 85 for (iter = extensions.begin(); iter != extensions.end(); ++iter) { 86 const Extension* extension = iter->get(); 87 88 if (!event_router->ExtensionHasEventListener( 89 extension->id(), tts_engine_events::kOnSpeak) || 90 !event_router->ExtensionHasEventListener( 91 extension->id(), tts_engine_events::kOnStop)) { 92 continue; 93 } 94 95 const std::vector<extensions::TtsVoice>* tts_voices = 96 extensions::TtsVoice::GetTtsVoices(extension); 97 if (!tts_voices) 98 continue; 99 100 for (size_t i = 0; i < tts_voices->size(); ++i) { 101 const extensions::TtsVoice& voice = tts_voices->at(i); 102 103 // Don't return remote voices when the system is offline. 104 if (voice.remote && is_offline) 105 continue; 106 107 out_voices->push_back(VoiceData()); 108 VoiceData& result_voice = out_voices->back(); 109 110 result_voice.native = false; 111 result_voice.name = voice.voice_name; 112 result_voice.lang = voice.lang; 113 result_voice.remote = voice.remote; 114 result_voice.extension_id = extension->id(); 115 if (voice.gender == constants::kGenderMale) 116 result_voice.gender = TTS_GENDER_MALE; 117 else if (voice.gender == constants::kGenderFemale) 118 result_voice.gender = TTS_GENDER_FEMALE; 119 else 120 result_voice.gender = TTS_GENDER_NONE; 121 122 for (std::set<std::string>::const_iterator iter = 123 voice.event_types.begin(); 124 iter != voice.event_types.end(); 125 ++iter) { 126 result_voice.events.insert(TtsEventTypeFromString(*iter)); 127 } 128 129 // If the extension sends end events, the controller will handle 130 // queueing and send interrupted and cancelled events. 131 if (voice.event_types.find(constants::kEventTypeEnd) != 132 voice.event_types.end()) { 133 result_voice.events.insert(TTS_EVENT_CANCELLED); 134 result_voice.events.insert(TTS_EVENT_INTERRUPTED); 135 } 136 } 137 } 138 } 139 140 void TtsExtensionEngine::Speak(Utterance* utterance, 141 const VoiceData& voice) { 142 // See if the engine supports the "end" event; if so, we can keep the 143 // utterance around and track it. If not, we're finished with this 144 // utterance now. 145 bool sends_end_event = voice.events.find(TTS_EVENT_END) != voice.events.end(); 146 147 scoped_ptr<base::ListValue> args(new base::ListValue()); 148 args->AppendString(utterance->text()); 149 150 // Pass through most options to the speech engine, but remove some 151 // that are handled internally. 152 scoped_ptr<base::DictionaryValue> options(static_cast<base::DictionaryValue*>( 153 utterance->options()->DeepCopy())); 154 if (options->HasKey(constants::kRequiredEventTypesKey)) 155 options->Remove(constants::kRequiredEventTypesKey, NULL); 156 if (options->HasKey(constants::kDesiredEventTypesKey)) 157 options->Remove(constants::kDesiredEventTypesKey, NULL); 158 if (sends_end_event && options->HasKey(constants::kEnqueueKey)) 159 options->Remove(constants::kEnqueueKey, NULL); 160 if (options->HasKey(constants::kSrcIdKey)) 161 options->Remove(constants::kSrcIdKey, NULL); 162 if (options->HasKey(constants::kIsFinalEventKey)) 163 options->Remove(constants::kIsFinalEventKey, NULL); 164 if (options->HasKey(constants::kOnEventKey)) 165 options->Remove(constants::kOnEventKey, NULL); 166 167 // Add the voice name and language to the options if they're not 168 // already there, since they might have been picked by the TTS controller 169 // rather than directly by the client that requested the speech. 170 if (!options->HasKey(constants::kVoiceNameKey)) 171 options->SetString(constants::kVoiceNameKey, voice.name); 172 if (!options->HasKey(constants::kLangKey)) 173 options->SetString(constants::kLangKey, voice.lang); 174 175 args->Append(options.release()); 176 args->AppendInteger(utterance->id()); 177 178 scoped_ptr<extensions::Event> event(new extensions::Event( 179 tts_engine_events::kOnSpeak, args.Pass())); 180 Profile* profile = Profile::FromBrowserContext(utterance->browser_context()); 181 event->restrict_to_browser_context = profile; 182 EventRouter::Get(profile) 183 ->DispatchEventToExtension(utterance->extension_id(), event.Pass()); 184 } 185 186 void TtsExtensionEngine::Stop(Utterance* utterance) { 187 scoped_ptr<base::ListValue> args(new base::ListValue()); 188 scoped_ptr<extensions::Event> event(new extensions::Event( 189 tts_engine_events::kOnStop, args.Pass())); 190 Profile* profile = Profile::FromBrowserContext(utterance->browser_context()); 191 event->restrict_to_browser_context = profile; 192 EventRouter::Get(profile) 193 ->DispatchEventToExtension(utterance->extension_id(), event.Pass()); 194 } 195 196 void TtsExtensionEngine::Pause(Utterance* utterance) { 197 scoped_ptr<base::ListValue> args(new base::ListValue()); 198 scoped_ptr<extensions::Event> event(new extensions::Event( 199 tts_engine_events::kOnPause, args.Pass())); 200 Profile* profile = Profile::FromBrowserContext(utterance->browser_context()); 201 event->restrict_to_browser_context = profile; 202 EventRouter* event_router = EventRouter::Get(profile); 203 std::string id = utterance->extension_id(); 204 event_router->DispatchEventToExtension(id, event.Pass()); 205 WarnIfMissingPauseOrResumeListener(profile, event_router, id); 206 } 207 208 void TtsExtensionEngine::Resume(Utterance* utterance) { 209 scoped_ptr<base::ListValue> args(new base::ListValue()); 210 scoped_ptr<extensions::Event> event(new extensions::Event( 211 tts_engine_events::kOnResume, args.Pass())); 212 Profile* profile = Profile::FromBrowserContext(utterance->browser_context()); 213 event->restrict_to_browser_context = profile; 214 EventRouter* event_router = EventRouter::Get(profile); 215 std::string id = utterance->extension_id(); 216 event_router->DispatchEventToExtension(id, event.Pass()); 217 WarnIfMissingPauseOrResumeListener(profile, event_router, id); 218 } 219 220 bool TtsExtensionEngine::LoadBuiltInTtsExtension( 221 content::BrowserContext* browser_context) { 222 #if defined(OS_CHROMEOS) 223 Profile* profile = Profile::FromBrowserContext(browser_context); 224 // Check to see if the engine was previously loaded. 225 if (TtsEngineExtensionObserver::GetInstance(profile)->SawExtensionLoad( 226 extension_misc::kSpeechSynthesisExtensionId, true)) { 227 return false; 228 } 229 230 // Load the component extension into this profile. 231 ExtensionService* extension_service = 232 extensions::ExtensionSystem::Get(profile)->extension_service(); 233 DCHECK(extension_service); 234 extension_service->component_loader() 235 ->AddChromeOsSpeechSynthesisExtension(); 236 return true; 237 #else 238 return false; 239 #endif 240 } 241 242 bool ExtensionTtsEngineSendTtsEventFunction::RunSync() { 243 int utterance_id; 244 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &utterance_id)); 245 246 base::DictionaryValue* event; 247 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &event)); 248 249 std::string event_type; 250 EXTENSION_FUNCTION_VALIDATE( 251 event->GetString(constants::kEventTypeKey, &event_type)); 252 253 int char_index = 0; 254 if (event->HasKey(constants::kCharIndexKey)) { 255 EXTENSION_FUNCTION_VALIDATE( 256 event->GetInteger(constants::kCharIndexKey, &char_index)); 257 } 258 259 // Make sure the extension has included this event type in its manifest. 260 bool event_type_allowed = false; 261 const std::vector<extensions::TtsVoice>* tts_voices = 262 extensions::TtsVoice::GetTtsVoices(extension()); 263 if (!tts_voices) { 264 error_ = constants::kErrorUndeclaredEventType; 265 return false; 266 } 267 268 for (size_t i = 0; i < tts_voices->size(); i++) { 269 const extensions::TtsVoice& voice = tts_voices->at(i); 270 if (voice.event_types.find(event_type) != voice.event_types.end()) { 271 event_type_allowed = true; 272 break; 273 } 274 } 275 if (!event_type_allowed) { 276 error_ = constants::kErrorUndeclaredEventType; 277 return false; 278 } 279 280 TtsController* controller = TtsController::GetInstance(); 281 if (event_type == constants::kEventTypeStart) { 282 controller->OnTtsEvent( 283 utterance_id, TTS_EVENT_START, char_index, std::string()); 284 } else if (event_type == constants::kEventTypeEnd) { 285 controller->OnTtsEvent( 286 utterance_id, TTS_EVENT_END, char_index, std::string()); 287 } else if (event_type == constants::kEventTypeWord) { 288 controller->OnTtsEvent( 289 utterance_id, TTS_EVENT_WORD, char_index, std::string()); 290 } else if (event_type == constants::kEventTypeSentence) { 291 controller->OnTtsEvent( 292 utterance_id, TTS_EVENT_SENTENCE, char_index, std::string()); 293 } else if (event_type == constants::kEventTypeMarker) { 294 controller->OnTtsEvent( 295 utterance_id, TTS_EVENT_MARKER, char_index, std::string()); 296 } else if (event_type == constants::kEventTypeError) { 297 std::string error_message; 298 event->GetString(constants::kErrorMessageKey, &error_message); 299 controller->OnTtsEvent( 300 utterance_id, TTS_EVENT_ERROR, char_index, error_message); 301 } else if (event_type == constants::kEventTypePause) { 302 controller->OnTtsEvent( 303 utterance_id, TTS_EVENT_PAUSE, char_index, std::string()); 304 } else if (event_type == constants::kEventTypeResume) { 305 controller->OnTtsEvent( 306 utterance_id, TTS_EVENT_RESUME, char_index, std::string()); 307 } else { 308 EXTENSION_FUNCTION_VALIDATE(false); 309 } 310 311 return true; 312 } 313