blob: fe845932c9a3049b49306352dcc194402dac4a4e [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/extension_tts_engine_api.h"
#include <string>
#include "base/json/json_writer.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_tts_api_constants.h"
#include "chrome/browser/extensions/extension_tts_api_controller.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension.h"
namespace constants = extension_tts_api_constants;
namespace events {
const char kOnSpeak[] = "ttsEngine.onSpeak";
const char kOnStop[] = "ttsEngine.onStop";
}; // namespace events
void GetExtensionVoices(Profile* profile, ListValue* result_voices) {
ExtensionService* service = profile->GetExtensionService();
DCHECK(service);
ExtensionEventRouter* event_router = profile->GetExtensionEventRouter();
DCHECK(event_router);
const ExtensionList* extensions = service->extensions();
ExtensionList::const_iterator iter;
for (iter = extensions->begin(); iter != extensions->end(); ++iter) {
const Extension* extension = *iter;
if (!event_router->ExtensionHasEventListener(
extension->id(), events::kOnSpeak) ||
!event_router->ExtensionHasEventListener(
extension->id(), events::kOnStop)) {
continue;
}
const std::vector<Extension::TtsVoice>& tts_voices =
extension->tts_voices();
for (size_t i = 0; i < tts_voices.size(); ++i) {
const Extension::TtsVoice& voice = tts_voices[i];
DictionaryValue* result_voice = new DictionaryValue();
if (!voice.voice_name.empty())
result_voice->SetString(constants::kVoiceNameKey, voice.voice_name);
if (!voice.lang.empty())
result_voice->SetString(constants::kLangKey, voice.lang);
if (!voice.gender.empty())
result_voice->SetString(constants::kGenderKey, voice.gender);
result_voice->SetString(constants::kExtensionIdKey, extension->id());
ListValue* event_types = new ListValue();
for (std::set<std::string>::const_iterator iter =
voice.event_types.begin();
iter != voice.event_types.end();
++iter) {
event_types->Append(Value::CreateStringValue(*iter));
}
// If the extension sends end events, the controller will handle
// queueing and send interrupted and cancelled events.
if (voice.event_types.find(constants::kEventTypeEnd) !=
voice.event_types.end()) {
event_types->Append(
Value::CreateStringValue(constants::kEventTypeCancelled));
event_types->Append(Value::CreateStringValue(
constants::kEventTypeInterrupted));
}
result_voice->Set(constants::kEventTypesKey, event_types);
result_voices->Append(result_voice);
}
}
}
bool GetMatchingExtensionVoice(
Utterance* utterance,
const Extension** matching_extension,
size_t* voice_index) {
ExtensionService* service = utterance->profile()->GetExtensionService();
DCHECK(service);
ExtensionEventRouter* event_router =
utterance->profile()->GetExtensionEventRouter();
DCHECK(event_router);
*matching_extension = NULL;
*voice_index = -1;
const ExtensionList* extensions = service->extensions();
ExtensionList::const_iterator iter;
for (iter = extensions->begin(); iter != extensions->end(); ++iter) {
const Extension* extension = *iter;
if (!event_router->ExtensionHasEventListener(
extension->id(), events::kOnSpeak) ||
!event_router->ExtensionHasEventListener(
extension->id(), events::kOnStop)) {
continue;
}
if (!utterance->extension_id().empty() &&
utterance->extension_id() != extension->id()) {
continue;
}
const std::vector<Extension::TtsVoice>& tts_voices =
extension->tts_voices();
for (size_t i = 0; i < tts_voices.size(); ++i) {
const Extension::TtsVoice& voice = tts_voices[i];
if (!voice.voice_name.empty() &&
!utterance->voice_name().empty() &&
voice.voice_name != utterance->voice_name()) {
continue;
}
if (!voice.lang.empty() &&
!utterance->lang().empty() &&
voice.lang != utterance->lang()) {
continue;
}
if (!voice.gender.empty() &&
!utterance->gender().empty() &&
voice.gender != utterance->gender()) {
continue;
}
if (utterance->required_event_types().size() > 0) {
bool has_all_required_event_types = true;
for (std::set<std::string>::const_iterator iter =
utterance->required_event_types().begin();
iter != utterance->required_event_types().end();
++iter) {
if (voice.event_types.find(*iter) == voice.event_types.end()) {
has_all_required_event_types = false;
break;
}
}
if (!has_all_required_event_types)
continue;
}
*matching_extension = extension;
*voice_index = i;
return true;
}
}
return false;
}
void ExtensionTtsEngineSpeak(Utterance* utterance,
const Extension* extension,
size_t voice_index) {
// See if the engine supports the "end" event; if so, we can keep the
// utterance around and track it. If not, we're finished with this
// utterance now.
const std::set<std::string> event_types =
extension->tts_voices()[voice_index].event_types;
bool sends_end_event =
(event_types.find(constants::kEventTypeEnd) != event_types.end());
ListValue args;
args.Set(0, Value::CreateStringValue(utterance->text()));
// Pass through most options to the speech engine, but remove some
// that are handled internally.
DictionaryValue* options = static_cast<DictionaryValue*>(
utterance->options()->DeepCopy());
if (options->HasKey(constants::kRequiredEventTypesKey))
options->Remove(constants::kRequiredEventTypesKey, NULL);
if (options->HasKey(constants::kDesiredEventTypesKey))
options->Remove(constants::kDesiredEventTypesKey, NULL);
if (sends_end_event && options->HasKey(constants::kEnqueueKey))
options->Remove(constants::kEnqueueKey, NULL);
if (options->HasKey(constants::kSrcIdKey))
options->Remove(constants::kSrcIdKey, NULL);
if (options->HasKey(constants::kIsFinalEventKey))
options->Remove(constants::kIsFinalEventKey, NULL);
args.Set(1, options);
args.Set(2, Value::CreateIntegerValue(utterance->id()));
std::string json_args;
base::JSONWriter::Write(&args, false, &json_args);
utterance->profile()->GetExtensionEventRouter()->DispatchEventToExtension(
extension->id(),
events::kOnSpeak,
json_args,
utterance->profile(),
GURL());
}
void ExtensionTtsEngineStop(Utterance* utterance) {
utterance->profile()->GetExtensionEventRouter()->DispatchEventToExtension(
utterance->extension_id(),
events::kOnStop,
"[]",
utterance->profile(),
GURL());
}
bool ExtensionTtsEngineSendTtsEventFunction::RunImpl() {
int utterance_id;
std::string error_message;
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &utterance_id));
DictionaryValue* event;
EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &event));
std::string event_type;
EXTENSION_FUNCTION_VALIDATE(
event->GetString(constants::kEventTypeKey, &event_type));
int char_index = 0;
if (event->HasKey(constants::kCharIndexKey)) {
EXTENSION_FUNCTION_VALIDATE(
event->GetInteger(constants::kCharIndexKey, &char_index));
}
// Make sure the extension has included this event type in its manifest.
bool event_type_allowed = false;
const Extension* extension = GetExtension();
for (size_t i = 0; i < extension->tts_voices().size(); i++) {
const Extension::TtsVoice& voice = extension->tts_voices()[i];
if (voice.event_types.find(event_type) != voice.event_types.end()) {
event_type_allowed = true;
break;
}
}
if (!event_type_allowed) {
error_ = constants::kErrorUndeclaredEventType;
return false;
}
ExtensionTtsController* controller = ExtensionTtsController::GetInstance();
if (event_type == constants::kEventTypeStart) {
controller->OnTtsEvent(
utterance_id, TTS_EVENT_START, char_index, std::string());
} else if (event_type == constants::kEventTypeEnd) {
controller->OnTtsEvent(
utterance_id, TTS_EVENT_END, char_index, std::string());
} else if (event_type == constants::kEventTypeWord) {
controller->OnTtsEvent(
utterance_id, TTS_EVENT_WORD, char_index, std::string());
} else if (event_type == constants::kEventTypeSentence) {
controller->OnTtsEvent(
utterance_id, TTS_EVENT_SENTENCE, char_index, std::string());
} else if (event_type == constants::kEventTypeMarker) {
controller->OnTtsEvent(
utterance_id, TTS_EVENT_MARKER, char_index, std::string());
} else if (event_type == constants::kEventTypeError) {
std::string error_message;
event->GetString(constants::kErrorMessageKey, &error_message);
controller->OnTtsEvent(
utterance_id, TTS_EVENT_ERROR, char_index, error_message);
} else {
EXTENSION_FUNCTION_VALIDATE(false);
}
return true;
}