// 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 #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& 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::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) { // This will only happen during unit testing. Otherwise, an utterance // will always have an associated profile. if (!utterance->profile()) return false; 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& 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::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 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( 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; }