// 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/manifest_handlers/input_components_handler.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handlers/options_page_info.h"
namespace extensions {
namespace keys = manifest_keys;
namespace errors = manifest_errors;
InputComponentInfo::InputComponentInfo() = default;
InputComponentInfo::InputComponentInfo(const InputComponentInfo& other) =
default;
InputComponentInfo::~InputComponentInfo() = default;
InputComponents::InputComponents() = default;
InputComponents::~InputComponents() = default;
// static
const std::vector<InputComponentInfo>* InputComponents::GetInputComponents(
const Extension* extension) {
InputComponents* info = static_cast<InputComponents*>(
extension->GetManifestData(keys::kInputComponents));
return info ? &info->input_components : nullptr;
}
InputComponentsHandler::InputComponentsHandler() = default;
InputComponentsHandler::~InputComponentsHandler() = default;
bool InputComponentsHandler::Parse(Extension* extension,
std::u16string* error) {
const base::Value* list_value;
if (!extension->manifest()->GetList(keys::kInputComponents, &list_value)) {
*error = errors::kInvalidInputComponents16;
return false;
}
auto info = std::make_unique<InputComponents>();
for (size_t i = 0; i < list_value->GetList().size(); ++i) {
const base::Value::Dict* module_value =
list_value->GetList()[i].GetIfDict();
if (!module_value) {
*error = errors::kInvalidInputComponents16;
return false;
}
// Get input_components[i].name.
const std::string* name_str = module_value->FindString(keys::kName);
if (!name_str) {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kInvalidInputComponentName, base::NumberToString(i));
return false;
}
// Get input_components[i].id.
std::string id_str;
const std::string* maybe_id_str = module_value->FindString(keys::kId);
if (maybe_id_str) {
id_str = *maybe_id_str;
}
// Get input_components[i].language.
// Both string and list of string are allowed to be compatibile with old
// input_ime manifest specification.
std::set<std::string> languages;
const base::Value* language_value = module_value->Find(keys::kLanguage);
if (language_value) {
if (language_value->is_string()) {
languages.insert(language_value->GetString());
} else if (language_value->is_list()) {
for (const auto& language : language_value->GetList()) {
if (language.is_string())
languages.insert(language.GetString());
}
}
}
std::set<std::string> layouts;
const base::Value::List* layouts_value =
module_value->FindList(keys::kLayouts);
if (layouts_value) {
for (size_t j = 0; j < layouts_value->size(); ++j) {
const base::Value& layout = (*layouts_value)[j];
if (!layout.is_string()) {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kInvalidInputComponentLayoutName, base::NumberToString(i),
base::NumberToString(j));
return false;
}
layouts.insert(layout.GetString());
}
}
// Get input_components[i].input_view_url.
// Note: 'input_view' is optional in manifest.
GURL input_view_url;
const std::string* input_view_str =
module_value->FindString(keys::kInputView);
if (input_view_str) {
input_view_url = extension->GetResourceURL(*input_view_str);
if (!input_view_url.is_valid()) {
*error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidInputView,
base::NumberToString(i));
return false;
}
}
// Get input_components[i].options_page_url.
// Note: 'options_page' is optional in manifest.
GURL options_page_url;
const std::string* options_page_str =
module_value->FindString(keys::kImeOptionsPage);
if (options_page_str) {
options_page_url = extension->GetResourceURL(*options_page_str);
if (!options_page_url.is_valid()) {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kInvalidOptionsPage, base::NumberToString(i));
return false;
}
} else {
// Fall back to extension's options page for backward compatibility.
options_page_url = extensions::OptionsPageInfo::GetOptionsPage(extension);
}
InputComponentInfo component;
component.name = *name_str;
component.id = std::move(id_str);
component.languages = std::move(languages);
component.layouts = std::move(layouts);
component.options_page_url = std::move(options_page_url);
component.input_view_url = std::move(input_view_url);
info->input_components.push_back(std::move(component));
}
extension->SetManifestData(keys::kInputComponents, std::move(info));
return true;
}
const std::vector<std::string>
InputComponentsHandler::PrerequisiteKeys() const {
return SingleKey(keys::kOptionsPage);
}
base::span<const char* const> InputComponentsHandler::Keys() const {
static constexpr const char* kKeys[] = {keys::kInputComponents};
return kKeys;
}
} // namespace extensions