| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "extensions/common/api/speech/tts_engine_manifest_handler.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "extensions/common/error_utils.h" |
| #include "extensions/common/manifest.h" |
| #include "extensions/common/manifest_constants.h" |
| #include "media/base/limits.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| namespace extensions { |
| |
| namespace keys = manifest_keys; |
| namespace errors = manifest_errors; |
| |
| TtsVoice::TtsVoice() : remote(false) {} |
| |
| TtsVoice::TtsVoice(const TtsVoice& other) = default; |
| |
| TtsVoice::~TtsVoice() = default; |
| |
| TtsEngine::TtsEngine() = default; |
| TtsEngine::~TtsEngine() = default; |
| |
| // static |
| bool TtsEngine::Parse(const base::Value::List& tts_voices, |
| TtsEngine* out_engine, |
| std::u16string* error) { |
| CHECK(out_engine->voices.empty()); |
| for (const base::Value& one_tts_voice_val : tts_voices) { |
| if (!one_tts_voice_val.is_dict()) { |
| *error = errors::kInvalidTtsVoices; |
| return false; |
| } |
| |
| const base::Value::Dict& one_tts_voice = one_tts_voice_val.GetDict(); |
| TtsVoice voice_data; |
| const base::Value* name = one_tts_voice.Find(keys::kTtsVoicesVoiceName); |
| if (name) { |
| if (!name->is_string()) { |
| *error = errors::kInvalidTtsVoicesVoiceName; |
| return false; |
| } |
| voice_data.voice_name = name->GetString(); |
| } |
| |
| const base::Value* lang = one_tts_voice.Find(keys::kTtsVoicesLang); |
| if (lang) { |
| if (!lang->is_string() || |
| !l10n_util::IsValidLocaleSyntax(lang->GetString())) { |
| *error = errors::kInvalidTtsVoicesLang; |
| return false; |
| } |
| voice_data.lang = lang->GetString(); |
| } |
| |
| const base::Value* remote = one_tts_voice.Find(keys::kTtsVoicesRemote); |
| if (remote) { |
| if (!remote->is_bool()) { |
| *error = errors::kInvalidTtsVoicesRemote; |
| return false; |
| } |
| voice_data.remote = remote->GetBool(); |
| } |
| |
| const base::Value* event_types = |
| one_tts_voice.Find(keys::kTtsVoicesEventTypes); |
| if (event_types) { |
| if (!event_types->is_list()) { |
| *error = errors::kInvalidTtsVoicesEventTypes; |
| return false; |
| } |
| for (const base::Value& event_type_val : event_types->GetList()) { |
| if (!event_type_val.is_string()) { |
| *error = errors::kInvalidTtsVoicesEventTypes; |
| return false; |
| } |
| const std::string& event_type = event_type_val.GetString(); |
| if (event_type != keys::kTtsVoicesEventTypeEnd && |
| event_type != keys::kTtsVoicesEventTypeError && |
| event_type != keys::kTtsVoicesEventTypeMarker && |
| event_type != keys::kTtsVoicesEventTypeSentence && |
| event_type != keys::kTtsVoicesEventTypeStart && |
| event_type != keys::kTtsVoicesEventTypeWord) { |
| *error = errors::kInvalidTtsVoicesEventTypes; |
| return false; |
| } |
| if (voice_data.event_types.find(event_type) != |
| voice_data.event_types.end()) { |
| *error = errors::kInvalidTtsVoicesEventTypes; |
| return false; |
| } |
| voice_data.event_types.insert(event_type); |
| } |
| } |
| out_engine->voices.push_back(voice_data); |
| } |
| return true; |
| } |
| |
| // static |
| const std::vector<TtsVoice>* TtsEngine::GetTtsVoices( |
| const Extension* extension) { |
| const TtsEngine* engine = TtsEngine::GetTtsEngineInfo(extension); |
| return engine ? &engine->voices : nullptr; |
| } |
| |
| // static |
| const TtsEngine* TtsEngine::GetTtsEngineInfo(const Extension* extension) { |
| TtsEngine* info = |
| static_cast<TtsEngine*>(extension->GetManifestData(keys::kTtsVoices)); |
| return info; |
| } |
| |
| TtsEngineManifestHandler::TtsEngineManifestHandler() = default; |
| |
| TtsEngineManifestHandler::~TtsEngineManifestHandler() = default; |
| |
| bool TtsEngineManifestHandler::Parse(Extension* extension, |
| std::u16string* error) { |
| auto info = std::make_unique<TtsEngine>(); |
| const base::Value::Dict* tts_dict = |
| extension->manifest()->available_values().FindDict(keys::kTtsEngine); |
| if (!tts_dict) { |
| *error = errors::kInvalidTts; |
| return false; |
| } |
| |
| const base::Value* tts_voices = tts_dict->Find(keys::kTtsVoices); |
| if (!tts_voices) { |
| return true; |
| } |
| |
| if (!tts_voices->is_list()) { |
| *error = errors::kInvalidTtsVoices; |
| return false; |
| } |
| |
| if (!TtsEngine::Parse(tts_voices->GetList(), info.get(), error)) { |
| return false; |
| } |
| |
| const base::Value* tts_engine_sample_rate = |
| tts_dict->Find(keys::kTtsEngineSampleRate); |
| if (tts_engine_sample_rate) { |
| if (!tts_engine_sample_rate->GetIfInt()) { |
| *error = errors::kInvalidTtsSampleRateFormat; |
| return false; |
| } |
| |
| info->sample_rate = tts_engine_sample_rate->GetInt(); |
| if (info->sample_rate < media::limits::kMinSampleRate || |
| info->sample_rate > media::limits::kMaxSampleRate) { |
| *error = base::ASCIIToUTF16(base::StringPrintf( |
| errors::kInvalidTtsSampleRateRange, media::limits::kMinSampleRate, |
| media::limits::kMaxSampleRate)); |
| return false; |
| } |
| } |
| |
| const base::Value* tts_engine_buffer_size = |
| tts_dict->Find(keys::kTtsEngineBufferSize); |
| if (tts_engine_buffer_size) { |
| if (!tts_engine_buffer_size->GetIfInt()) { |
| *error = errors::kInvalidTtsBufferSizeFormat; |
| return false; |
| } |
| |
| // The limits of the buffer size should match those of those found in |
| // AudioParameters::IsValid (as should the sample rate limits above). |
| constexpr int kMinBufferSize = 1; |
| info->buffer_size = tts_engine_buffer_size->GetInt(); |
| if (info->buffer_size < kMinBufferSize || |
| info->buffer_size > media::limits::kMaxSamplesPerPacket) { |
| *error = base::ASCIIToUTF16( |
| base::StringPrintf(errors::kInvalidTtsBufferSizeRange, kMinBufferSize, |
| media::limits::kMaxSamplesPerPacket)); |
| return false; |
| } |
| } |
| |
| if ((!tts_engine_sample_rate && tts_engine_buffer_size) || |
| (tts_engine_sample_rate && !tts_engine_buffer_size)) { |
| *error = errors::kInvalidTtsRequiresSampleRateAndBufferSize; |
| return false; |
| } |
| |
| extension->SetManifestData(keys::kTtsVoices, std::move(info)); |
| return true; |
| } |
| |
| base::span<const char* const> TtsEngineManifestHandler::Keys() const { |
| static constexpr const char* kKeys[] = {keys::kTtsEngine}; |
| return kKeys; |
| } |
| |
| } // namespace extensions |