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