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