Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / speech / extension_api / tts_extension_api.cc
blob823b849d36723c895d1f3e3d87e2ec45a15345c8
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_extension_api.h"
7 #include <string>
9 #include "base/lazy_instance.h"
10 #include "base/memory/weak_ptr.h"
11 #include "base/values.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
14 #include "chrome/browser/speech/extension_api/tts_engine_extension_observer.h"
15 #include "chrome/browser/speech/extension_api/tts_extension_api_constants.h"
16 #include "chrome/browser/speech/tts_controller.h"
17 #include "extensions/browser/event_router.h"
18 #include "extensions/browser/extension_function_registry.h"
19 #include "ui/base/l10n/l10n_util.h"
21 namespace constants = tts_extension_api_constants;
23 namespace events {
24 const char kOnEvent[] = "tts.onEvent";
25 }; // namespace events
27 const char *TtsEventTypeToString(TtsEventType event_type) {
28 switch (event_type) {
29 case TTS_EVENT_START:
30 return constants::kEventTypeStart;
31 case TTS_EVENT_END:
32 return constants::kEventTypeEnd;
33 case TTS_EVENT_WORD:
34 return constants::kEventTypeWord;
35 case TTS_EVENT_SENTENCE:
36 return constants::kEventTypeSentence;
37 case TTS_EVENT_MARKER:
38 return constants::kEventTypeMarker;
39 case TTS_EVENT_INTERRUPTED:
40 return constants::kEventTypeInterrupted;
41 case TTS_EVENT_CANCELLED:
42 return constants::kEventTypeCancelled;
43 case TTS_EVENT_ERROR:
44 return constants::kEventTypeError;
45 case TTS_EVENT_PAUSE:
46 return constants::kEventTypePause;
47 case TTS_EVENT_RESUME:
48 return constants::kEventTypeResume;
49 default:
50 NOTREACHED();
51 return constants::kEventTypeError;
55 TtsEventType TtsEventTypeFromString(const std::string& str) {
56 if (str == constants::kEventTypeStart)
57 return TTS_EVENT_START;
58 if (str == constants::kEventTypeEnd)
59 return TTS_EVENT_END;
60 if (str == constants::kEventTypeWord)
61 return TTS_EVENT_WORD;
62 if (str == constants::kEventTypeSentence)
63 return TTS_EVENT_SENTENCE;
64 if (str == constants::kEventTypeMarker)
65 return TTS_EVENT_MARKER;
66 if (str == constants::kEventTypeInterrupted)
67 return TTS_EVENT_INTERRUPTED;
68 if (str == constants::kEventTypeCancelled)
69 return TTS_EVENT_CANCELLED;
70 if (str == constants::kEventTypeError)
71 return TTS_EVENT_ERROR;
72 if (str == constants::kEventTypePause)
73 return TTS_EVENT_PAUSE;
74 if (str == constants::kEventTypeResume)
75 return TTS_EVENT_RESUME;
77 NOTREACHED();
78 return TTS_EVENT_ERROR;
81 namespace extensions {
83 // One of these is constructed for each utterance, and deleted
84 // when the utterance gets any final event.
85 class TtsExtensionEventHandler
86 : public UtteranceEventDelegate,
87 public base::SupportsWeakPtr<TtsExtensionEventHandler> {
88 public:
89 virtual void OnTtsEvent(Utterance* utterance,
90 TtsEventType event_type,
91 int char_index,
92 const std::string& error_message) override;
95 void TtsExtensionEventHandler::OnTtsEvent(Utterance* utterance,
96 TtsEventType event_type,
97 int char_index,
98 const std::string& error_message) {
99 if (utterance->src_id() < 0) {
100 if (utterance->finished())
101 delete this;
102 return;
105 const std::set<TtsEventType>& desired_event_types =
106 utterance->desired_event_types();
107 if (desired_event_types.size() > 0 &&
108 desired_event_types.find(event_type) == desired_event_types.end()) {
109 if (utterance->finished())
110 delete this;
111 return;
114 const char *event_type_string = TtsEventTypeToString(event_type);
115 scoped_ptr<base::DictionaryValue> details(new base::DictionaryValue());
116 if (char_index >= 0)
117 details->SetInteger(constants::kCharIndexKey, char_index);
118 details->SetString(constants::kEventTypeKey, event_type_string);
119 if (event_type == TTS_EVENT_ERROR) {
120 details->SetString(constants::kErrorMessageKey, error_message);
122 details->SetInteger(constants::kSrcIdKey, utterance->src_id());
123 details->SetBoolean(constants::kIsFinalEventKey, utterance->finished());
125 scoped_ptr<base::ListValue> arguments(new base::ListValue());
126 arguments->Set(0, details.release());
128 scoped_ptr<extensions::Event> event(
129 new extensions::Event(events::kOnEvent, arguments.Pass()));
130 event->restrict_to_browser_context = utterance->browser_context();
131 event->event_url = utterance->src_url();
132 extensions::EventRouter::Get(utterance->browser_context())
133 ->DispatchEventToExtension(utterance->src_extension_id(), event.Pass());
135 if (utterance->finished())
136 delete this;
139 bool TtsSpeakFunction::RunAsync() {
140 std::string text;
141 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &text));
142 if (text.size() > 32768) {
143 error_ = constants::kErrorUtteranceTooLong;
144 return false;
147 scoped_ptr<base::DictionaryValue> options(new base::DictionaryValue());
148 if (args_->GetSize() >= 2) {
149 base::DictionaryValue* temp_options = NULL;
150 if (args_->GetDictionary(1, &temp_options))
151 options.reset(temp_options->DeepCopy());
154 std::string voice_name;
155 if (options->HasKey(constants::kVoiceNameKey)) {
156 EXTENSION_FUNCTION_VALIDATE(
157 options->GetString(constants::kVoiceNameKey, &voice_name));
160 std::string lang;
161 if (options->HasKey(constants::kLangKey))
162 EXTENSION_FUNCTION_VALIDATE(options->GetString(constants::kLangKey, &lang));
163 if (!lang.empty() && !l10n_util::IsValidLocaleSyntax(lang)) {
164 error_ = constants::kErrorInvalidLang;
165 return false;
168 std::string gender_str;
169 TtsGenderType gender;
170 if (options->HasKey(constants::kGenderKey))
171 EXTENSION_FUNCTION_VALIDATE(
172 options->GetString(constants::kGenderKey, &gender_str));
173 if (gender_str == constants::kGenderMale) {
174 gender = TTS_GENDER_MALE;
175 } else if (gender_str == constants::kGenderFemale) {
176 gender = TTS_GENDER_FEMALE;
177 } else if (gender_str.empty()) {
178 gender = TTS_GENDER_NONE;
179 } else {
180 error_ = constants::kErrorInvalidGender;
181 return false;
184 double rate = 1.0;
185 if (options->HasKey(constants::kRateKey)) {
186 EXTENSION_FUNCTION_VALIDATE(
187 options->GetDouble(constants::kRateKey, &rate));
188 if (rate < 0.1 || rate > 10.0) {
189 error_ = constants::kErrorInvalidRate;
190 return false;
194 double pitch = 1.0;
195 if (options->HasKey(constants::kPitchKey)) {
196 EXTENSION_FUNCTION_VALIDATE(
197 options->GetDouble(constants::kPitchKey, &pitch));
198 if (pitch < 0.0 || pitch > 2.0) {
199 error_ = constants::kErrorInvalidPitch;
200 return false;
204 double volume = 1.0;
205 if (options->HasKey(constants::kVolumeKey)) {
206 EXTENSION_FUNCTION_VALIDATE(
207 options->GetDouble(constants::kVolumeKey, &volume));
208 if (volume < 0.0 || volume > 1.0) {
209 error_ = constants::kErrorInvalidVolume;
210 return false;
214 bool can_enqueue = false;
215 if (options->HasKey(constants::kEnqueueKey)) {
216 EXTENSION_FUNCTION_VALIDATE(
217 options->GetBoolean(constants::kEnqueueKey, &can_enqueue));
220 std::set<TtsEventType> required_event_types;
221 if (options->HasKey(constants::kRequiredEventTypesKey)) {
222 base::ListValue* list;
223 EXTENSION_FUNCTION_VALIDATE(
224 options->GetList(constants::kRequiredEventTypesKey, &list));
225 for (size_t i = 0; i < list->GetSize(); ++i) {
226 std::string event_type;
227 if (list->GetString(i, &event_type))
228 required_event_types.insert(TtsEventTypeFromString(event_type.c_str()));
232 std::set<TtsEventType> desired_event_types;
233 if (options->HasKey(constants::kDesiredEventTypesKey)) {
234 base::ListValue* list;
235 EXTENSION_FUNCTION_VALIDATE(
236 options->GetList(constants::kDesiredEventTypesKey, &list));
237 for (size_t i = 0; i < list->GetSize(); ++i) {
238 std::string event_type;
239 if (list->GetString(i, &event_type))
240 desired_event_types.insert(TtsEventTypeFromString(event_type.c_str()));
244 std::string voice_extension_id;
245 if (options->HasKey(constants::kExtensionIdKey)) {
246 EXTENSION_FUNCTION_VALIDATE(
247 options->GetString(constants::kExtensionIdKey, &voice_extension_id));
250 int src_id = -1;
251 if (options->HasKey(constants::kSrcIdKey)) {
252 EXTENSION_FUNCTION_VALIDATE(
253 options->GetInteger(constants::kSrcIdKey, &src_id));
256 // If we got this far, the arguments were all in the valid format, so
257 // send the success response to the callback now - this ensures that
258 // the callback response always arrives before events, which makes
259 // the behavior more predictable and easier to write unit tests for too.
260 SendResponse(true);
262 UtteranceContinuousParameters continuous_params;
263 continuous_params.rate = rate;
264 continuous_params.pitch = pitch;
265 continuous_params.volume = volume;
267 Utterance* utterance = new Utterance(GetProfile());
268 utterance->set_text(text);
269 utterance->set_voice_name(voice_name);
270 utterance->set_src_extension_id(extension_id());
271 utterance->set_src_id(src_id);
272 utterance->set_src_url(source_url());
273 utterance->set_lang(lang);
274 utterance->set_gender(gender);
275 utterance->set_continuous_parameters(continuous_params);
276 utterance->set_can_enqueue(can_enqueue);
277 utterance->set_required_event_types(required_event_types);
278 utterance->set_desired_event_types(desired_event_types);
279 utterance->set_extension_id(voice_extension_id);
280 utterance->set_options(options.get());
281 utterance->set_event_delegate(
282 (new TtsExtensionEventHandler())->AsWeakPtr());
284 TtsController* controller = TtsController::GetInstance();
285 controller->SpeakOrEnqueue(utterance);
286 return true;
289 bool TtsStopSpeakingFunction::RunSync() {
290 TtsController::GetInstance()->Stop();
291 return true;
294 bool TtsPauseFunction::RunSync() {
295 TtsController::GetInstance()->Pause();
296 return true;
299 bool TtsResumeFunction::RunSync() {
300 TtsController::GetInstance()->Resume();
301 return true;
304 bool TtsIsSpeakingFunction::RunSync() {
305 SetResult(
306 new base::FundamentalValue(TtsController::GetInstance()->IsSpeaking()));
307 return true;
310 bool TtsGetVoicesFunction::RunSync() {
311 std::vector<VoiceData> voices;
312 TtsController::GetInstance()->GetVoices(GetProfile(), &voices);
314 scoped_ptr<base::ListValue> result_voices(new base::ListValue());
315 for (size_t i = 0; i < voices.size(); ++i) {
316 const VoiceData& voice = voices[i];
317 base::DictionaryValue* result_voice = new base::DictionaryValue();
318 result_voice->SetString(constants::kVoiceNameKey, voice.name);
319 result_voice->SetBoolean(constants::kRemoteKey, voice.remote);
320 if (!voice.lang.empty())
321 result_voice->SetString(constants::kLangKey, voice.lang);
322 if (voice.gender == TTS_GENDER_MALE)
323 result_voice->SetString(constants::kGenderKey, constants::kGenderMale);
324 else if (voice.gender == TTS_GENDER_FEMALE)
325 result_voice->SetString(constants::kGenderKey, constants::kGenderFemale);
326 if (!voice.extension_id.empty())
327 result_voice->SetString(constants::kExtensionIdKey, voice.extension_id);
329 base::ListValue* event_types = new base::ListValue();
330 for (std::set<TtsEventType>::iterator iter = voice.events.begin();
331 iter != voice.events.end(); ++iter) {
332 const char* event_name_constant = TtsEventTypeToString(*iter);
333 event_types->Append(new base::StringValue(event_name_constant));
335 result_voice->Set(constants::kEventTypesKey, event_types);
337 result_voices->Append(result_voice);
340 SetResult(result_voices.release());
341 return true;
344 TtsAPI::TtsAPI(content::BrowserContext* context) {
345 ExtensionFunctionRegistry* registry =
346 ExtensionFunctionRegistry::GetInstance();
347 registry->RegisterFunction<ExtensionTtsEngineSendTtsEventFunction>();
348 registry->RegisterFunction<TtsGetVoicesFunction>();
349 registry->RegisterFunction<TtsIsSpeakingFunction>();
350 registry->RegisterFunction<TtsSpeakFunction>();
351 registry->RegisterFunction<TtsStopSpeakingFunction>();
352 registry->RegisterFunction<TtsPauseFunction>();
353 registry->RegisterFunction<TtsResumeFunction>();
355 // Ensure we're observing newly added engines for the given context.
356 TtsEngineExtensionObserver::GetInstance(Profile::FromBrowserContext(context));
359 TtsAPI::~TtsAPI() {
362 static base::LazyInstance<BrowserContextKeyedAPIFactory<TtsAPI> > g_factory =
363 LAZY_INSTANCE_INITIALIZER;
365 BrowserContextKeyedAPIFactory<TtsAPI>* TtsAPI::GetFactoryInstance() {
366 return g_factory.Pointer();
369 } // namespace extensions