910e62b5创建于 1月15日历史提交
// Copyright 2017 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/declarative_net_request/test_utils.h"

#include <utility>

#include "base/files/file_util.h"
#include "base/json/json_file_value_serializer.h"
#include "extensions/common/api/declarative_net_request.h"
#include "extensions/common/constants.h"
#include "extensions/common/manifest_constants.h"

namespace extensions {

namespace keys = manifest_keys;
namespace dnr_api = api::declarative_net_request;

namespace declarative_net_request {

namespace {

const base::FilePath::CharType kBackgroundScriptFilepath[] =
    FILE_PATH_LITERAL("background.js");

base::Value ToValue(const std::string& t) {
  return base::Value(t);
}

base::Value ToValue(int t) {
  return base::Value(t);
}

base::Value ToValue(bool t) {
  return base::Value(t);
}

base::Value ToValue(const DictionarySource& source) {
  return base::Value(source.ToValue());
}

base::Value ToValue(const TestRulesetInfo& info) {
  return base::Value(info.GetManifestValue());
}

template <typename T>
base::Value::List ToValue(const std::vector<T>& vec) {
  base::Value::List builder;
  for (const T& t : vec)
    builder.Append(ToValue(t));
  return builder;
}

template <typename T>
void SetValue(base::Value::Dict& dict,
              const char* key,
              const std::optional<T>& value) {
  if (!value)
    return;

  dict.Set(key, ToValue(*value));
}

}  // namespace

TestRuleCondition::TestRuleCondition() = default;
TestRuleCondition::~TestRuleCondition() = default;
TestRuleCondition::TestRuleCondition(const TestRuleCondition&) = default;
TestRuleCondition& TestRuleCondition::operator=(const TestRuleCondition&) =
    default;

base::Value::Dict TestRuleCondition::ToValue() const {
  base::Value::Dict dict;
  SetValue(dict, kUrlFilterKey, url_filter);
  SetValue(dict, kRegexFilterKey, regex_filter);
  SetValue(dict, kIsUrlFilterCaseSensitiveKey, is_url_filter_case_sensitive);
  SetValue(dict, kDomainsKey, domains);
  SetValue(dict, kExcludedDomainsKey, excluded_domains);
  SetValue(dict, kInitiatorDomainsKey, initiator_domains);
  SetValue(dict, kExcludedInitiatorDomainsKey, excluded_initiator_domains);
  SetValue(dict, kRequestDomainsKey, request_domains);
  SetValue(dict, kExcludedRequestDomainsKey, excluded_request_domains);
  SetValue(dict, kTopDomainsKey, top_domains);
  SetValue(dict, kExcludedTopDomainsKey, excluded_top_domains);
  SetValue(dict, kRequestMethodsKey, request_methods);
  SetValue(dict, kExcludedRequestMethodsKey, excluded_request_methods);
  SetValue(dict, kResourceTypesKey, resource_types);
  SetValue(dict, kExcludedResourceTypesKey, excluded_resource_types);
  SetValue(dict, kTabIdsKey, tab_ids);
  SetValue(dict, kExcludedTabIdsKey, excluded_tab_ids);
  SetValue(dict, kDomainTypeKey, domain_type);
  SetValue(dict, kResponseHeadersKey, response_headers);
  SetValue(dict, kExcludedResponseHeadersKey, excluded_response_headers);

  return dict;
}

TestRuleQueryKeyValue::TestRuleQueryKeyValue() = default;
TestRuleQueryKeyValue::~TestRuleQueryKeyValue() = default;
TestRuleQueryKeyValue::TestRuleQueryKeyValue(const TestRuleQueryKeyValue&) =
    default;
TestRuleQueryKeyValue& TestRuleQueryKeyValue::operator=(
    const TestRuleQueryKeyValue&) = default;

base::Value::Dict TestRuleQueryKeyValue::ToValue() const {
  base::Value::Dict dict;
  SetValue(dict, kQueryKeyKey, key);
  SetValue(dict, kQueryValueKey, value);
  SetValue(dict, kQueryReplaceOnlyKey, replace_only);
  return dict;
}

TestRuleQueryTransform::TestRuleQueryTransform() = default;
TestRuleQueryTransform::~TestRuleQueryTransform() = default;
TestRuleQueryTransform::TestRuleQueryTransform(const TestRuleQueryTransform&) =
    default;
TestRuleQueryTransform& TestRuleQueryTransform::operator=(
    const TestRuleQueryTransform&) = default;

base::Value::Dict TestRuleQueryTransform::ToValue() const {
  base::Value::Dict dict;
  SetValue(dict, kQueryTransformRemoveParamsKey, remove_params);
  SetValue(dict, kQueryTransformAddReplaceParamsKey, add_or_replace_params);
  return dict;
}

TestRuleTransform::TestRuleTransform() = default;
TestRuleTransform::~TestRuleTransform() = default;
TestRuleTransform::TestRuleTransform(const TestRuleTransform&) = default;
TestRuleTransform& TestRuleTransform::operator=(const TestRuleTransform&) =
    default;

base::Value::Dict TestRuleTransform::ToValue() const {
  base::Value::Dict dict;
  SetValue(dict, kTransformSchemeKey, scheme);
  SetValue(dict, kTransformHostKey, host);
  SetValue(dict, kTransformPortKey, port);
  SetValue(dict, kTransformPathKey, path);
  SetValue(dict, kTransformQueryKey, query);
  SetValue(dict, kTransformQueryTransformKey, query_transform);
  SetValue(dict, kTransformFragmentKey, fragment);
  SetValue(dict, kTransformUsernameKey, username);
  SetValue(dict, kTransformPasswordKey, password);
  return dict;
}

TestRuleRedirect::TestRuleRedirect() = default;
TestRuleRedirect::~TestRuleRedirect() = default;
TestRuleRedirect::TestRuleRedirect(const TestRuleRedirect&) = default;
TestRuleRedirect& TestRuleRedirect::operator=(const TestRuleRedirect&) =
    default;

base::Value::Dict TestRuleRedirect::ToValue() const {
  base::Value::Dict dict;
  SetValue(dict, kExtensionPathKey, extension_path);
  SetValue(dict, kTransformKey, transform);
  SetValue(dict, kRedirectUrlKey, url);
  SetValue(dict, kRegexSubstitutionKey, regex_substitution);
  return dict;
}

TestHeaderInfo::TestHeaderInfo(std::string header,
                               std::string operation,
                               std::optional<std::string> value)
    : header(std::move(header)),
      operation(std::move(operation)),
      value(std::move(value)) {}
TestHeaderInfo::~TestHeaderInfo() = default;
TestHeaderInfo::TestHeaderInfo(const TestHeaderInfo&) = default;
TestHeaderInfo& TestHeaderInfo::operator=(const TestHeaderInfo&) = default;

base::Value::Dict TestHeaderInfo::ToValue() const {
  base::Value::Dict dict;
  SetValue(dict, kHeaderNameKey, header);
  SetValue(dict, kHeaderOperationKey, operation);
  SetValue(dict, kHeaderValueKey, value);
  return dict;
}

TestHeaderCondition::TestHeaderCondition(
    std::string header,
    std::vector<std::string> values,
    std::vector<std::string> excluded_values)
    : header(std::move(header)),
      values(std::move(values)),
      excluded_values(std::move(excluded_values)) {}
TestHeaderCondition::~TestHeaderCondition() = default;
TestHeaderCondition::TestHeaderCondition(const TestHeaderCondition&) = default;
TestHeaderCondition& TestHeaderCondition::operator=(
    const TestHeaderCondition&) = default;

base::Value::Dict TestHeaderCondition::ToValue() const {
  base::Value::Dict dict;
  SetValue(dict, kHeaderNameKey, header);
  SetValue(dict, kHeaderValuesKey, values);
  SetValue(dict, kHeaderExcludedValuesKey, excluded_values);
  return dict;
}

TestRuleAction::TestRuleAction() = default;
TestRuleAction::~TestRuleAction() = default;
TestRuleAction::TestRuleAction(const TestRuleAction&) = default;
TestRuleAction& TestRuleAction::operator=(const TestRuleAction&) = default;

base::Value::Dict TestRuleAction::ToValue() const {
  base::Value::Dict dict;
  SetValue(dict, kRuleActionTypeKey, type);
  SetValue(dict, kRequestHeadersKey, request_headers);
  SetValue(dict, kResponseHeadersKey, response_headers);
  SetValue(dict, kRedirectKey, redirect);
  return dict;
}

TestRule::TestRule() = default;
TestRule::~TestRule() = default;
TestRule::TestRule(const TestRule&) = default;
TestRule& TestRule::operator=(const TestRule&) = default;

base::Value::Dict TestRule::ToValue() const {
  base::Value::Dict dict;
  SetValue(dict, kIDKey, id);
  SetValue(dict, kPriorityKey, priority);
  SetValue(dict, kRuleConditionKey, condition);
  SetValue(dict, kRuleActionKey, action);
  return dict;
}

TestRule CreateGenericRule(int id) {
  TestRuleCondition condition;
  condition.url_filter = std::string("filter");
  TestRuleAction action;
  action.type = std::string("block");
  TestRule rule;
  rule.id = id;
  rule.priority = kMinValidPriority;
  rule.action = action;
  rule.condition = condition;
  return rule;
}

TestRule CreateRegexRule(int id) {
  TestRule rule = CreateGenericRule(id);
  rule.condition->url_filter.reset();
  rule.condition->regex_filter = std::string("filter");
  return rule;
}

TestRulesetInfo::TestRulesetInfo(const std::string& manifest_id_and_path,
                                 base::Value::List rules_value,
                                 bool enabled)
    : TestRulesetInfo(manifest_id_and_path,
                      manifest_id_and_path,
                      std::move(rules_value),
                      enabled) {}

TestRulesetInfo::TestRulesetInfo(const std::string& manifest_id,
                                 const std::string& relative_file_path,
                                 base::Value::List rules_value,
                                 bool enabled)
    : manifest_id(manifest_id),
      relative_file_path(relative_file_path),
      rules_value(std::move(rules_value)),
      enabled(enabled) {}

TestRulesetInfo::TestRulesetInfo(const std::string& manifest_id,
                                 const std::string& relative_file_path,
                                 const base::Value& rules_value,
                                 bool enabled)
    : manifest_id(manifest_id),
      relative_file_path(relative_file_path),
      rules_value(rules_value.Clone()),
      enabled(enabled) {}

TestRulesetInfo::TestRulesetInfo(const TestRulesetInfo& info)
    : TestRulesetInfo(info.manifest_id,
                      info.relative_file_path,
                      info.rules_value.Clone(),
                      info.enabled) {}

base::Value::Dict TestRulesetInfo::GetManifestValue() const {
  dnr_api::Ruleset ruleset;
  ruleset.id = manifest_id;
  ruleset.path = relative_file_path;
  ruleset.enabled = enabled;
  return ruleset.ToValue();
}

base::Value::Dict CreateManifest(
    const std::vector<TestRulesetInfo>& ruleset_info,
    const std::vector<std::string>& hosts,
    unsigned flags,
    const std::string& extension_name) {
  base::Value::Dict manifest_builder;

  bool is_manifest_version_2 = flags & kConfig_DEPRECATED_ManifestVersion2;

  // Set 'manifest_version' manifest entry.
  if (is_manifest_version_2) {
    manifest_builder.Set(keys::kManifestVersion, 2);
  } else {
    manifest_builder.Set(keys::kManifestVersion, 3);
  }

  // Set 'permissions' and 'host_permissions' manifest entries.
  std::vector<std::string> permissions;
  if (!hosts.empty()) {
    if (is_manifest_version_2) {
      permissions = hosts;
    } else {
      manifest_builder.Set(keys::kHostPermissions, ToValue(hosts));
    }
  }
  if (flags & kConfig_DEPRECATED_HasWebRequestBlockingPermission) {
    // 'webRequestBlocking' requires Manifest Version 2.
    DCHECK(is_manifest_version_2);
    permissions.push_back("webRequestBlocking");
  }
  if (flags & kConfig_HasWebRequestPermission) {
    permissions.push_back("webRequest");
  }
  if (!(flags & kConfig_OmitDeclarativeNetRequestPermission)) {
    permissions.push_back(kDeclarativeNetRequestPermission);
  }
  if (flags & kConfig_HasFeedbackPermission) {
    permissions.push_back(kFeedbackAPIPermission);
  }
  if (flags & kConfig_HasActiveTab) {
    permissions.push_back("activeTab");
  }
  if (flags & kConfig_HasDelarativeNetRequestWithHostAccessPermission) {
    permissions.push_back("declarativeNetRequestWithHostAccess");
  }
  if (!permissions.empty()) {
    manifest_builder.Set(keys::kPermissions, ToValue(std::move(permissions)));
  }

  // Set 'action' manifest key to empty object to activate chrome.action API.
  if (flags & kConfig_HasAction) {
    // Manifest Version 2 does not support 'action' manifest key.
    DCHECK(!is_manifest_version_2);
    manifest_builder.Set(keys::kAction, base::Value::Dict());
  }

  // Set 'background' manifest entry.
  if (flags & kConfig_HasBackgroundScript) {
    if (is_manifest_version_2) {
      // Set 'background.scripts' manifest entry on Manifest Version 2.
      manifest_builder.SetByDottedPath(
          keys::kBackgroundScripts,
          ToValue(std::vector<std::string>({"background.js"})));
    } else {
      // Set 'background.service_worker' manifest entry on modern manifest
      // version.
      manifest_builder.SetByDottedPath(keys::kBackgroundServiceWorkerScript,
                                       "background.js");
    }
  }

  // Set 'declarative_net_request.rule_resources' manifest entry.
  if (flags & kConfig_OmitDeclarativeNetRequestKey) {
    DCHECK(ruleset_info.empty());
  } else {
    manifest_builder.Set(
        dnr_api::ManifestKeys::kDeclarativeNetRequest,
        base::Value::Dict().Set(dnr_api::DNRInfo::kRuleResources,
                                ToValue(ruleset_info)));
  }

  // Set 'sandbox.pages' manifest entry.
  if (flags & kConfig_HasManifestSandbox) {
    manifest_builder.SetByDottedPath(
        keys::kSandboxedPages,
        base::Value::List().Append(kManifestSandboxPageFilepath));
  }

  // std::move() to trigger rvalue overloads.
  return std::move(manifest_builder)
      .Set(keys::kName, extension_name)
      .Set(keys::kVersion, "1.0");
}

base::Value::List ToListValue(const std::vector<std::string>& vec) {
  return ToValue(vec);
}

base::Value::List ToListValue(const std::vector<TestRule>& rules) {
  return ToValue(rules);
}

void WriteManifestAndRulesets(const base::FilePath& extension_dir,
                              const std::vector<TestRulesetInfo>& ruleset_info,
                              const std::vector<std::string>& hosts,
                              unsigned flags,
                              const std::string& extension_name) {
  // Persist JSON rules files.
  for (const TestRulesetInfo& info : ruleset_info) {
    JSONFileValueSerializer(extension_dir.AppendASCII(info.relative_file_path))
        .Serialize(info.rules_value);
  }

  // Persist a background script if needed.
  if (flags & ConfigFlag::kConfig_HasBackgroundScript) {
    static constexpr char kScriptWithOnUpdateAvailable[] =
        "chrome.runtime.onUpdateAvailable.addListener(() => {});"
        "chrome.test.sendMessage('ready');";

    std::string content = flags & ConfigFlag::kConfig_ListenForOnUpdateAvailable
                              ? kScriptWithOnUpdateAvailable
                              : "chrome.test.sendMessage('ready');";
    CHECK(base::WriteFile(extension_dir.Append(kBackgroundScriptFilepath),
                          content));
  }

  // Persist a manifest sandbox page if needed.
  if (flags & ConfigFlag::kConfig_HasManifestSandbox) {
    static constexpr char kManifestSandboxPage[] = "<html></html>";

    CHECK(
        base::WriteFile(extension_dir.AppendASCII(kManifestSandboxPageFilepath),
                        kManifestSandboxPage));
  }

  // Persist manifest file.
  JSONFileValueSerializer(extension_dir.Append(kManifestFilename))
      .Serialize(CreateManifest(ruleset_info, hosts, flags, extension_name));
}

void WriteManifestAndRuleset(const base::FilePath& extension_dir,
                             const TestRulesetInfo& info,
                             const std::vector<std::string>& hosts,
                             unsigned flags,
                             const std::string& extension_name) {
  WriteManifestAndRulesets(extension_dir, {info}, hosts, flags, extension_name);
}

}  // namespace declarative_net_request
}  // namespace extensions