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/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_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/extension_host.h"
25#include "extensions/browser/extension_registry.h"
26#include "extensions/browser/extension_system.h"
27#include "extensions/browser/process_manager.h"
28#include "extensions/common/extension.h"
29#include "extensions/common/extension_messages.h"
30#include "extensions/common/extension_set.h"
31#include "net/base/network_change_notifier.h"
32
33using extensions::EventRouter;
34using extensions::Extension;
35using extensions::ExtensionSystem;
36
37namespace constants = tts_extension_api_constants;
38
39namespace tts_engine_events {
40const char kOnSpeak[] = "ttsEngine.onSpeak";
41const char kOnStop[] = "ttsEngine.onStop";
42const char kOnPause[] = "ttsEngine.onPause";
43const char kOnResume[] = "ttsEngine.onResume";
44};  // namespace tts_engine_events
45
46namespace {
47
48void 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;
56
57  extensions::ProcessManager* process_manager =
58      ExtensionSystem::Get(profile)->process_manager();
59  extensions::ExtensionHost* host =
60      process_manager->GetBackgroundHostForExtension(extension_id);
61  host->render_process_host()->Send(new ExtensionMsg_AddMessageToConsole(
62      host->render_view_host()->GetRoutingID(),
63      content::CONSOLE_MESSAGE_LEVEL_WARNING,
64      constants::kErrorMissingPauseOrResume));
65}
66
67}  // namespace
68
69TtsExtensionEngine* TtsExtensionEngine::GetInstance() {
70  return Singleton<TtsExtensionEngine>::get();
71}
72
73void TtsExtensionEngine::GetVoices(content::BrowserContext* browser_context,
74    std::vector<VoiceData>* out_voices) {
75  Profile* profile = Profile::FromBrowserContext(browser_context);
76  EventRouter* event_router = EventRouter::Get(profile);
77  DCHECK(event_router);
78
79  bool is_offline = (net::NetworkChangeNotifier::GetConnectionType() ==
80                     net::NetworkChangeNotifier::CONNECTION_NONE);
81
82  const extensions::ExtensionSet& extensions =
83      extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
84  extensions::ExtensionSet::const_iterator iter;
85  for (iter = extensions.begin(); iter != extensions.end(); ++iter) {
86    const Extension* extension = iter->get();
87
88    if (!event_router->ExtensionHasEventListener(
89            extension->id(), tts_engine_events::kOnSpeak) ||
90        !event_router->ExtensionHasEventListener(
91            extension->id(), tts_engine_events::kOnStop)) {
92      continue;
93    }
94
95    const std::vector<extensions::TtsVoice>* tts_voices =
96        extensions::TtsVoice::GetTtsVoices(extension);
97    if (!tts_voices)
98      continue;
99
100    for (size_t i = 0; i < tts_voices->size(); ++i) {
101      const extensions::TtsVoice& voice = tts_voices->at(i);
102
103      // Don't return remote voices when the system is offline.
104      if (voice.remote && is_offline)
105        continue;
106
107      out_voices->push_back(VoiceData());
108      VoiceData& result_voice = out_voices->back();
109
110      result_voice.native = false;
111      result_voice.name = voice.voice_name;
112      result_voice.lang = voice.lang;
113      result_voice.remote = voice.remote;
114      result_voice.extension_id = extension->id();
115      if (voice.gender == constants::kGenderMale)
116        result_voice.gender = TTS_GENDER_MALE;
117      else if (voice.gender == constants::kGenderFemale)
118        result_voice.gender = TTS_GENDER_FEMALE;
119      else
120        result_voice.gender = TTS_GENDER_NONE;
121
122      for (std::set<std::string>::const_iterator iter =
123               voice.event_types.begin();
124           iter != voice.event_types.end();
125           ++iter) {
126        result_voice.events.insert(TtsEventTypeFromString(*iter));
127      }
128
129      // If the extension sends end events, the controller will handle
130      // queueing and send interrupted and cancelled events.
131      if (voice.event_types.find(constants::kEventTypeEnd) !=
132          voice.event_types.end()) {
133        result_voice.events.insert(TTS_EVENT_CANCELLED);
134        result_voice.events.insert(TTS_EVENT_INTERRUPTED);
135      }
136    }
137  }
138}
139
140void TtsExtensionEngine::Speak(Utterance* utterance,
141                               const VoiceData& voice) {
142  // See if the engine supports the "end" event; if so, we can keep the
143  // utterance around and track it. If not, we're finished with this
144  // utterance now.
145  bool sends_end_event = voice.events.find(TTS_EVENT_END) != voice.events.end();
146
147  scoped_ptr<base::ListValue> args(new base::ListValue());
148  args->AppendString(utterance->text());
149
150  // Pass through most options to the speech engine, but remove some
151  // that are handled internally.
152  scoped_ptr<base::DictionaryValue> options(static_cast<base::DictionaryValue*>(
153      utterance->options()->DeepCopy()));
154  if (options->HasKey(constants::kRequiredEventTypesKey))
155    options->Remove(constants::kRequiredEventTypesKey, NULL);
156  if (options->HasKey(constants::kDesiredEventTypesKey))
157    options->Remove(constants::kDesiredEventTypesKey, NULL);
158  if (sends_end_event && options->HasKey(constants::kEnqueueKey))
159    options->Remove(constants::kEnqueueKey, NULL);
160  if (options->HasKey(constants::kSrcIdKey))
161    options->Remove(constants::kSrcIdKey, NULL);
162  if (options->HasKey(constants::kIsFinalEventKey))
163    options->Remove(constants::kIsFinalEventKey, NULL);
164  if (options->HasKey(constants::kOnEventKey))
165    options->Remove(constants::kOnEventKey, NULL);
166
167  // Add the voice name and language to the options if they're not
168  // already there, since they might have been picked by the TTS controller
169  // rather than directly by the client that requested the speech.
170  if (!options->HasKey(constants::kVoiceNameKey))
171    options->SetString(constants::kVoiceNameKey, voice.name);
172  if (!options->HasKey(constants::kLangKey))
173    options->SetString(constants::kLangKey, voice.lang);
174
175  args->Append(options.release());
176  args->AppendInteger(utterance->id());
177
178  scoped_ptr<extensions::Event> event(new extensions::Event(
179      tts_engine_events::kOnSpeak, args.Pass()));
180  Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
181  event->restrict_to_browser_context = profile;
182  EventRouter::Get(profile)
183      ->DispatchEventToExtension(utterance->extension_id(), event.Pass());
184}
185
186void TtsExtensionEngine::Stop(Utterance* utterance) {
187  scoped_ptr<base::ListValue> args(new base::ListValue());
188  scoped_ptr<extensions::Event> event(new extensions::Event(
189      tts_engine_events::kOnStop, args.Pass()));
190  Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
191  event->restrict_to_browser_context = profile;
192  EventRouter::Get(profile)
193      ->DispatchEventToExtension(utterance->extension_id(), event.Pass());
194}
195
196void TtsExtensionEngine::Pause(Utterance* utterance) {
197  scoped_ptr<base::ListValue> args(new base::ListValue());
198  scoped_ptr<extensions::Event> event(new extensions::Event(
199      tts_engine_events::kOnPause, args.Pass()));
200  Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
201  event->restrict_to_browser_context = profile;
202  EventRouter* event_router = EventRouter::Get(profile);
203  std::string id = utterance->extension_id();
204  event_router->DispatchEventToExtension(id, event.Pass());
205  WarnIfMissingPauseOrResumeListener(profile, event_router, id);
206}
207
208void TtsExtensionEngine::Resume(Utterance* utterance) {
209  scoped_ptr<base::ListValue> args(new base::ListValue());
210  scoped_ptr<extensions::Event> event(new extensions::Event(
211      tts_engine_events::kOnResume, args.Pass()));
212  Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
213  event->restrict_to_browser_context = profile;
214  EventRouter* event_router = EventRouter::Get(profile);
215  std::string id = utterance->extension_id();
216  event_router->DispatchEventToExtension(id, event.Pass());
217  WarnIfMissingPauseOrResumeListener(profile, event_router, id);
218}
219
220bool TtsExtensionEngine::LoadBuiltInTtsExtension(
221    content::BrowserContext* browser_context) {
222#if defined(OS_CHROMEOS)
223  Profile* profile = Profile::FromBrowserContext(browser_context);
224  // Check to see if the engine was previously loaded.
225  if (TtsEngineExtensionObserver::GetInstance(profile)->SawExtensionLoad(
226          extension_misc::kSpeechSynthesisExtensionId, true)) {
227    return false;
228  }
229
230  // Load the component extension into this profile.
231  ExtensionService* extension_service =
232      extensions::ExtensionSystem::Get(profile)->extension_service();
233  DCHECK(extension_service);
234  extension_service->component_loader()
235      ->AddChromeOsSpeechSynthesisExtension();
236  return true;
237#else
238  return false;
239#endif
240}
241
242bool ExtensionTtsEngineSendTtsEventFunction::RunSync() {
243  int utterance_id;
244  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &utterance_id));
245
246  base::DictionaryValue* event;
247  EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &event));
248
249  std::string event_type;
250  EXTENSION_FUNCTION_VALIDATE(
251      event->GetString(constants::kEventTypeKey, &event_type));
252
253  int char_index = 0;
254  if (event->HasKey(constants::kCharIndexKey)) {
255    EXTENSION_FUNCTION_VALIDATE(
256        event->GetInteger(constants::kCharIndexKey, &char_index));
257  }
258
259  // Make sure the extension has included this event type in its manifest.
260  bool event_type_allowed = false;
261  const std::vector<extensions::TtsVoice>* tts_voices =
262      extensions::TtsVoice::GetTtsVoices(extension());
263  if (!tts_voices) {
264    error_ = constants::kErrorUndeclaredEventType;
265    return false;
266  }
267
268  for (size_t i = 0; i < tts_voices->size(); i++) {
269    const extensions::TtsVoice& voice = tts_voices->at(i);
270    if (voice.event_types.find(event_type) != voice.event_types.end()) {
271      event_type_allowed = true;
272      break;
273    }
274  }
275  if (!event_type_allowed) {
276    error_ = constants::kErrorUndeclaredEventType;
277    return false;
278  }
279
280  TtsController* controller = TtsController::GetInstance();
281  if (event_type == constants::kEventTypeStart) {
282    controller->OnTtsEvent(
283        utterance_id, TTS_EVENT_START, char_index, std::string());
284  } else if (event_type == constants::kEventTypeEnd) {
285    controller->OnTtsEvent(
286        utterance_id, TTS_EVENT_END, char_index, std::string());
287  } else if (event_type == constants::kEventTypeWord) {
288    controller->OnTtsEvent(
289        utterance_id, TTS_EVENT_WORD, char_index, std::string());
290  } else if (event_type == constants::kEventTypeSentence) {
291    controller->OnTtsEvent(
292        utterance_id, TTS_EVENT_SENTENCE, char_index, std::string());
293  } else if (event_type == constants::kEventTypeMarker) {
294    controller->OnTtsEvent(
295        utterance_id, TTS_EVENT_MARKER, char_index, std::string());
296  } else if (event_type == constants::kEventTypeError) {
297    std::string error_message;
298    event->GetString(constants::kErrorMessageKey, &error_message);
299    controller->OnTtsEvent(
300        utterance_id, TTS_EVENT_ERROR, char_index, error_message);
301  } else if (event_type == constants::kEventTypePause) {
302    controller->OnTtsEvent(
303        utterance_id, TTS_EVENT_PAUSE, char_index, std::string());
304  } else if (event_type == constants::kEventTypeResume) {
305    controller->OnTtsEvent(
306        utterance_id, TTS_EVENT_RESUME, char_index, std::string());
307  } else {
308    EXTENSION_FUNCTION_VALIDATE(false);
309  }
310
311  return true;
312}
313