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 33using extensions::EventRouter; 34using extensions::Extension; 35using extensions::ExtensionSystem; 36 37namespace constants = tts_extension_api_constants; 38 39namespace tts_engine_events { 40const char kOnSpeak[] = "ttsEngine.onSpeak"; 41const char kOnStop[] = "ttsEngine.onStop"; 42const char kOnPause[] = "ttsEngine.onPause"; 43const char kOnResume[] = "ttsEngine.onResume"; 44}; // namespace tts_engine_events 45 46namespace { 47 48void 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 69TtsExtensionEngine* TtsExtensionEngine::GetInstance() { 70 return Singleton<TtsExtensionEngine>::get(); 71} 72 73void 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 140void 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 186void 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 196void 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 208void 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 220bool 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 242bool 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