chromium/extensions/common/manifest_handlers/input_components_handler.cc

// 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