• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
WarnIfMissingPauseOrResumeListener(Profile * profile,EventRouter * event_router,std::string extension_id)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 
GetExtensionVoices(Profile * profile,std::vector<VoiceData> * out_voices)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 
ExtensionTtsEngineSpeak(Utterance * utterance,const VoiceData & voice)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 
ExtensionTtsEngineStop(Utterance * utterance)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 
ExtensionTtsEnginePause(Utterance * utterance)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 
ExtensionTtsEngineResume(Utterance * utterance)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 
RunImpl()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