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.
5 #include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
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"
27 using extensions::EventRouter;
28 using extensions::Extension;
29 using extensions::ExtensionSystem;
31 namespace constants = tts_extension_api_constants;
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
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)
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));
59 } // anonymous namespace
61 void GetExtensionVoices(Profile* profile, std::vector<VoiceData>* out_voices) {
62 ExtensionService* service = profile->GetExtensionService();
64 EventRouter* event_router =
65 ExtensionSystem::Get(profile)->event_router();
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();
73 if (!event_router->ExtensionHasEventListener(
74 extension->id(), tts_engine_events::kOnSpeak) ||
75 !event_router->ExtensionHasEventListener(
76 extension->id(), tts_engine_events::kOnStop)) {
80 const std::vector<extensions::TtsVoice>* tts_voices =
81 extensions::TtsVoice::GetTtsVoices(extension);
85 for (size_t i = 0; i < tts_voices->size(); ++i) {
86 const extensions::TtsVoice& voice = tts_voices->at(i);
88 out_voices->push_back(VoiceData());
89 VoiceData& result_voice = out_voices->back();
91 result_voice.native = false;
92 result_voice.name = voice.voice_name;
93 result_voice.lang = voice.lang;
94 result_voice.remote = voice.remote;
95 result_voice.extension_id = extension->id();
96 if (voice.gender == constants::kGenderMale)
97 result_voice.gender = TTS_GENDER_MALE;
98 else if (voice.gender == constants::kGenderFemale)
99 result_voice.gender = TTS_GENDER_FEMALE;
101 result_voice.gender = TTS_GENDER_NONE;
103 for (std::set<std::string>::const_iterator iter =
104 voice.event_types.begin();
105 iter != voice.event_types.end();
107 result_voice.events.insert(TtsEventTypeFromString(*iter));
110 // If the extension sends end events, the controller will handle
111 // queueing and send interrupted and cancelled events.
112 if (voice.event_types.find(constants::kEventTypeEnd) !=
113 voice.event_types.end()) {
114 result_voice.events.insert(TTS_EVENT_CANCELLED);
115 result_voice.events.insert(TTS_EVENT_INTERRUPTED);
121 void ExtensionTtsEngineSpeak(Utterance* utterance, const VoiceData& voice) {
122 // See if the engine supports the "end" event; if so, we can keep the
123 // utterance around and track it. If not, we're finished with this
125 bool sends_end_event = voice.events.find(TTS_EVENT_END) != voice.events.end();
127 scoped_ptr<ListValue> args(new ListValue());
128 args->Set(0, Value::CreateStringValue(utterance->text()));
130 // Pass through most options to the speech engine, but remove some
131 // that are handled internally.
132 scoped_ptr<DictionaryValue> options(static_cast<DictionaryValue*>(
133 utterance->options()->DeepCopy()));
134 if (options->HasKey(constants::kRequiredEventTypesKey))
135 options->Remove(constants::kRequiredEventTypesKey, NULL);
136 if (options->HasKey(constants::kDesiredEventTypesKey))
137 options->Remove(constants::kDesiredEventTypesKey, NULL);
138 if (sends_end_event && options->HasKey(constants::kEnqueueKey))
139 options->Remove(constants::kEnqueueKey, NULL);
140 if (options->HasKey(constants::kSrcIdKey))
141 options->Remove(constants::kSrcIdKey, NULL);
142 if (options->HasKey(constants::kIsFinalEventKey))
143 options->Remove(constants::kIsFinalEventKey, NULL);
144 if (options->HasKey(constants::kOnEventKey))
145 options->Remove(constants::kOnEventKey, NULL);
147 args->Set(1, options.release());
148 args->Set(2, Value::CreateIntegerValue(utterance->id()));
150 scoped_ptr<extensions::Event> event(new extensions::Event(
151 tts_engine_events::kOnSpeak, args.Pass()));
152 event->restrict_to_profile = utterance->profile();
153 ExtensionSystem::Get(utterance->profile())->event_router()->
154 DispatchEventToExtension(utterance->extension_id(), event.Pass());
157 void ExtensionTtsEngineStop(Utterance* utterance) {
158 scoped_ptr<ListValue> args(new ListValue());
159 scoped_ptr<extensions::Event> event(new extensions::Event(
160 tts_engine_events::kOnStop, args.Pass()));
161 event->restrict_to_profile = utterance->profile();
162 ExtensionSystem::Get(utterance->profile())->event_router()->
163 DispatchEventToExtension(utterance->extension_id(), event.Pass());
166 void ExtensionTtsEnginePause(Utterance* utterance) {
167 scoped_ptr<ListValue> args(new ListValue());
168 scoped_ptr<extensions::Event> event(new extensions::Event(
169 tts_engine_events::kOnPause, args.Pass()));
170 Profile* profile = utterance->profile();
171 event->restrict_to_profile = profile;
172 EventRouter* event_router = ExtensionSystem::Get(profile)->event_router();
173 std::string id = utterance->extension_id();
174 event_router->DispatchEventToExtension(id, event.Pass());
175 WarnIfMissingPauseOrResumeListener(profile, event_router, id);
178 void ExtensionTtsEngineResume(Utterance* utterance) {
179 scoped_ptr<ListValue> args(new ListValue());
180 scoped_ptr<extensions::Event> event(new extensions::Event(
181 tts_engine_events::kOnResume, args.Pass()));
182 Profile* profile = utterance->profile();
183 event->restrict_to_profile = profile;
184 EventRouter* event_router = ExtensionSystem::Get(profile)->event_router();
185 std::string id = utterance->extension_id();
186 event_router->DispatchEventToExtension(id, event.Pass());
187 WarnIfMissingPauseOrResumeListener(profile, event_router, id);
190 bool ExtensionTtsEngineSendTtsEventFunction::RunImpl() {
192 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &utterance_id));
194 DictionaryValue* event;
195 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &event));
197 std::string event_type;
198 EXTENSION_FUNCTION_VALIDATE(
199 event->GetString(constants::kEventTypeKey, &event_type));
202 if (event->HasKey(constants::kCharIndexKey)) {
203 EXTENSION_FUNCTION_VALIDATE(
204 event->GetInteger(constants::kCharIndexKey, &char_index));
207 // Make sure the extension has included this event type in its manifest.
208 bool event_type_allowed = false;
209 const Extension* extension = GetExtension();
210 const std::vector<extensions::TtsVoice>* tts_voices =
211 extensions::TtsVoice::GetTtsVoices(extension);
213 error_ = constants::kErrorUndeclaredEventType;
217 for (size_t i = 0; i < tts_voices->size(); i++) {
218 const extensions::TtsVoice& voice = tts_voices->at(i);
219 if (voice.event_types.find(event_type) != voice.event_types.end()) {
220 event_type_allowed = true;
224 if (!event_type_allowed) {
225 error_ = constants::kErrorUndeclaredEventType;
229 TtsController* controller = TtsController::GetInstance();
230 if (event_type == constants::kEventTypeStart) {
231 controller->OnTtsEvent(
232 utterance_id, TTS_EVENT_START, char_index, std::string());
233 } else if (event_type == constants::kEventTypeEnd) {
234 controller->OnTtsEvent(
235 utterance_id, TTS_EVENT_END, char_index, std::string());
236 } else if (event_type == constants::kEventTypeWord) {
237 controller->OnTtsEvent(
238 utterance_id, TTS_EVENT_WORD, char_index, std::string());
239 } else if (event_type == constants::kEventTypeSentence) {
240 controller->OnTtsEvent(
241 utterance_id, TTS_EVENT_SENTENCE, char_index, std::string());
242 } else if (event_type == constants::kEventTypeMarker) {
243 controller->OnTtsEvent(
244 utterance_id, TTS_EVENT_MARKER, char_index, std::string());
245 } else if (event_type == constants::kEventTypeError) {
246 std::string error_message;
247 event->GetString(constants::kErrorMessageKey, &error_message);
248 controller->OnTtsEvent(
249 utterance_id, TTS_EVENT_ERROR, char_index, error_message);
250 } else if (event_type == constants::kEventTypePause) {
251 controller->OnTtsEvent(
252 utterance_id, TTS_EVENT_PAUSE, char_index, std::string());
253 } else if (event_type == constants::kEventTypeResume) {
254 controller->OnTtsEvent(
255 utterance_id, TTS_EVENT_RESUME, char_index, std::string());
257 EXTENSION_FUNCTION_VALIDATE(false);