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