Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / speech / extension_api / tts_engine_extension_api.cc
blobeff4dee29435a7077b023d3382ce46ab4fcf5ba2
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"
7 #include <string>
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_frame_host.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/common/console_message_level.h"
24 #include "extensions/browser/event_router.h"
25 #include "extensions/browser/extension_host.h"
26 #include "extensions/browser/extension_registry.h"
27 #include "extensions/browser/extension_system.h"
28 #include "extensions/browser/process_manager.h"
29 #include "extensions/common/extension.h"
30 #include "extensions/common/extension_set.h"
31 #include "net/base/network_change_notifier.h"
33 using extensions::EventRouter;
34 using extensions::Extension;
35 using extensions::ExtensionSystem;
37 namespace constants = tts_extension_api_constants;
39 namespace tts_engine_events {
40 const char kOnSpeak[] = "ttsEngine.onSpeak";
41 const char kOnStop[] = "ttsEngine.onStop";
42 const char kOnPause[] = "ttsEngine.onPause";
43 const char kOnResume[] = "ttsEngine.onResume";
44 }; // namespace tts_engine_events
46 namespace {
48 void 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;
57 extensions::ExtensionHost* host =
58 extensions::ProcessManager::Get(profile)
59 ->GetBackgroundHostForExtension(extension_id);
60 host->host_contents()->GetMainFrame()->AddMessageToConsole(
61 content::CONSOLE_MESSAGE_LEVEL_WARNING,
62 constants::kErrorMissingPauseOrResume);
65 } // namespace
67 TtsExtensionEngine* TtsExtensionEngine::GetInstance() {
68 return Singleton<TtsExtensionEngine>::get();
71 void TtsExtensionEngine::GetVoices(content::BrowserContext* browser_context,
72 std::vector<VoiceData>* out_voices) {
73 Profile* profile = Profile::FromBrowserContext(browser_context);
74 EventRouter* event_router = EventRouter::Get(profile);
75 DCHECK(event_router);
77 bool is_offline = (net::NetworkChangeNotifier::GetConnectionType() ==
78 net::NetworkChangeNotifier::CONNECTION_NONE);
80 const extensions::ExtensionSet& extensions =
81 extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
82 extensions::ExtensionSet::const_iterator iter;
83 for (iter = extensions.begin(); iter != extensions.end(); ++iter) {
84 const Extension* extension = iter->get();
86 if (!event_router->ExtensionHasEventListener(
87 extension->id(), tts_engine_events::kOnSpeak) ||
88 !event_router->ExtensionHasEventListener(
89 extension->id(), tts_engine_events::kOnStop)) {
90 continue;
93 const std::vector<extensions::TtsVoice>* tts_voices =
94 extensions::TtsVoice::GetTtsVoices(extension);
95 if (!tts_voices)
96 continue;
98 for (size_t i = 0; i < tts_voices->size(); ++i) {
99 const extensions::TtsVoice& voice = tts_voices->at(i);
101 // Don't return remote voices when the system is offline.
102 if (voice.remote && is_offline)
103 continue;
105 out_voices->push_back(VoiceData());
106 VoiceData& result_voice = out_voices->back();
108 result_voice.native = false;
109 result_voice.name = voice.voice_name;
110 result_voice.lang = voice.lang;
111 result_voice.remote = voice.remote;
112 result_voice.extension_id = extension->id();
113 if (voice.gender == constants::kGenderMale)
114 result_voice.gender = TTS_GENDER_MALE;
115 else if (voice.gender == constants::kGenderFemale)
116 result_voice.gender = TTS_GENDER_FEMALE;
117 else
118 result_voice.gender = TTS_GENDER_NONE;
120 for (std::set<std::string>::const_iterator iter =
121 voice.event_types.begin();
122 iter != voice.event_types.end();
123 ++iter) {
124 result_voice.events.insert(TtsEventTypeFromString(*iter));
127 // If the extension sends end events, the controller will handle
128 // queueing and send interrupted and cancelled events.
129 if (voice.event_types.find(constants::kEventTypeEnd) !=
130 voice.event_types.end()) {
131 result_voice.events.insert(TTS_EVENT_CANCELLED);
132 result_voice.events.insert(TTS_EVENT_INTERRUPTED);
138 void TtsExtensionEngine::Speak(Utterance* utterance,
139 const VoiceData& voice) {
140 // See if the engine supports the "end" event; if so, we can keep the
141 // utterance around and track it. If not, we're finished with this
142 // utterance now.
143 bool sends_end_event = voice.events.find(TTS_EVENT_END) != voice.events.end();
145 scoped_ptr<base::ListValue> args(new base::ListValue());
146 args->AppendString(utterance->text());
148 // Pass through most options to the speech engine, but remove some
149 // that are handled internally.
150 scoped_ptr<base::DictionaryValue> options(static_cast<base::DictionaryValue*>(
151 utterance->options()->DeepCopy()));
152 if (options->HasKey(constants::kRequiredEventTypesKey))
153 options->Remove(constants::kRequiredEventTypesKey, NULL);
154 if (options->HasKey(constants::kDesiredEventTypesKey))
155 options->Remove(constants::kDesiredEventTypesKey, NULL);
156 if (sends_end_event && options->HasKey(constants::kEnqueueKey))
157 options->Remove(constants::kEnqueueKey, NULL);
158 if (options->HasKey(constants::kSrcIdKey))
159 options->Remove(constants::kSrcIdKey, NULL);
160 if (options->HasKey(constants::kIsFinalEventKey))
161 options->Remove(constants::kIsFinalEventKey, NULL);
162 if (options->HasKey(constants::kOnEventKey))
163 options->Remove(constants::kOnEventKey, NULL);
165 // Get the volume, pitch, and rate, but only if they weren't already in
166 // the options. TODO(dmazzoni): these shouldn't be redundant.
167 // http://crbug.com/463264
168 if (!options->HasKey(constants::kRateKey)) {
169 options->SetDouble(constants::kRateKey,
170 utterance->continuous_parameters().rate);
172 if (!options->HasKey(constants::kPitchKey)) {
173 options->SetDouble(constants::kPitchKey,
174 utterance->continuous_parameters().pitch);
176 if (!options->HasKey(constants::kVolumeKey)) {
177 options->SetDouble(constants::kVolumeKey,
178 utterance->continuous_parameters().volume);
181 // Add the voice name and language to the options if they're not
182 // already there, since they might have been picked by the TTS controller
183 // rather than directly by the client that requested the speech.
184 if (!options->HasKey(constants::kVoiceNameKey))
185 options->SetString(constants::kVoiceNameKey, voice.name);
186 if (!options->HasKey(constants::kLangKey))
187 options->SetString(constants::kLangKey, voice.lang);
189 args->Append(options.release());
190 args->AppendInteger(utterance->id());
192 std::string json;
193 base::JSONWriter::Write(*args, &json);
195 scoped_ptr<extensions::Event> event(
196 new extensions::Event(extensions::events::TTS_ENGINE_ON_SPEAK,
197 tts_engine_events::kOnSpeak, args.Pass()));
198 Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
199 event->restrict_to_browser_context = profile;
200 EventRouter::Get(profile)
201 ->DispatchEventToExtension(utterance->extension_id(), event.Pass());
204 void TtsExtensionEngine::Stop(Utterance* utterance) {
205 scoped_ptr<base::ListValue> args(new base::ListValue());
206 scoped_ptr<extensions::Event> event(
207 new extensions::Event(extensions::events::TTS_ENGINE_ON_STOP,
208 tts_engine_events::kOnStop, args.Pass()));
209 Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
210 event->restrict_to_browser_context = profile;
211 EventRouter::Get(profile)
212 ->DispatchEventToExtension(utterance->extension_id(), event.Pass());
215 void TtsExtensionEngine::Pause(Utterance* utterance) {
216 scoped_ptr<base::ListValue> args(new base::ListValue());
217 scoped_ptr<extensions::Event> event(
218 new extensions::Event(extensions::events::TTS_ENGINE_ON_PAUSE,
219 tts_engine_events::kOnPause, args.Pass()));
220 Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
221 event->restrict_to_browser_context = profile;
222 EventRouter* event_router = EventRouter::Get(profile);
223 std::string id = utterance->extension_id();
224 event_router->DispatchEventToExtension(id, event.Pass());
225 WarnIfMissingPauseOrResumeListener(profile, event_router, id);
228 void TtsExtensionEngine::Resume(Utterance* utterance) {
229 scoped_ptr<base::ListValue> args(new base::ListValue());
230 scoped_ptr<extensions::Event> event(
231 new extensions::Event(extensions::events::TTS_ENGINE_ON_RESUME,
232 tts_engine_events::kOnResume, args.Pass()));
233 Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
234 event->restrict_to_browser_context = profile;
235 EventRouter* event_router = EventRouter::Get(profile);
236 std::string id = utterance->extension_id();
237 event_router->DispatchEventToExtension(id, event.Pass());
238 WarnIfMissingPauseOrResumeListener(profile, event_router, id);
241 bool TtsExtensionEngine::LoadBuiltInTtsExtension(
242 content::BrowserContext* browser_context) {
243 #if defined(OS_CHROMEOS)
244 Profile* profile = Profile::FromBrowserContext(browser_context);
245 // Check to see if the engine was previously loaded.
246 if (TtsEngineExtensionObserver::GetInstance(profile)->SawExtensionLoad(
247 extension_misc::kSpeechSynthesisExtensionId, true)) {
248 return false;
251 // Load the component extension into this profile.
252 ExtensionService* extension_service =
253 extensions::ExtensionSystem::Get(profile)->extension_service();
254 DCHECK(extension_service);
255 extension_service->component_loader()
256 ->AddChromeOsSpeechSynthesisExtension();
257 return true;
258 #else
259 return false;
260 #endif
263 bool ExtensionTtsEngineSendTtsEventFunction::RunSync() {
264 int utterance_id;
265 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &utterance_id));
267 base::DictionaryValue* event;
268 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &event));
270 std::string event_type;
271 EXTENSION_FUNCTION_VALIDATE(
272 event->GetString(constants::kEventTypeKey, &event_type));
274 int char_index = 0;
275 if (event->HasKey(constants::kCharIndexKey)) {
276 EXTENSION_FUNCTION_VALIDATE(
277 event->GetInteger(constants::kCharIndexKey, &char_index));
280 // Make sure the extension has included this event type in its manifest.
281 bool event_type_allowed = false;
282 const std::vector<extensions::TtsVoice>* tts_voices =
283 extensions::TtsVoice::GetTtsVoices(extension());
284 if (!tts_voices) {
285 error_ = constants::kErrorUndeclaredEventType;
286 return false;
289 for (size_t i = 0; i < tts_voices->size(); i++) {
290 const extensions::TtsVoice& voice = tts_voices->at(i);
291 if (voice.event_types.find(event_type) != voice.event_types.end()) {
292 event_type_allowed = true;
293 break;
296 if (!event_type_allowed) {
297 error_ = constants::kErrorUndeclaredEventType;
298 return false;
301 TtsController* controller = TtsController::GetInstance();
302 if (event_type == constants::kEventTypeStart) {
303 controller->OnTtsEvent(
304 utterance_id, TTS_EVENT_START, char_index, std::string());
305 } else if (event_type == constants::kEventTypeEnd) {
306 controller->OnTtsEvent(
307 utterance_id, TTS_EVENT_END, char_index, std::string());
308 } else if (event_type == constants::kEventTypeWord) {
309 controller->OnTtsEvent(
310 utterance_id, TTS_EVENT_WORD, char_index, std::string());
311 } else if (event_type == constants::kEventTypeSentence) {
312 controller->OnTtsEvent(
313 utterance_id, TTS_EVENT_SENTENCE, char_index, std::string());
314 } else if (event_type == constants::kEventTypeMarker) {
315 controller->OnTtsEvent(
316 utterance_id, TTS_EVENT_MARKER, char_index, std::string());
317 } else if (event_type == constants::kEventTypeError) {
318 std::string error_message;
319 event->GetString(constants::kErrorMessageKey, &error_message);
320 controller->OnTtsEvent(
321 utterance_id, TTS_EVENT_ERROR, char_index, error_message);
322 } else if (event_type == constants::kEventTypePause) {
323 controller->OnTtsEvent(
324 utterance_id, TTS_EVENT_PAUSE, char_index, std::string());
325 } else if (event_type == constants::kEventTypeResume) {
326 controller->OnTtsEvent(
327 utterance_id, TTS_EVENT_RESUME, char_index, std::string());
328 } else {
329 EXTENSION_FUNCTION_VALIDATE(false);
332 return true;