910e62b5创建于 1月15日历史提交
// 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/browser/api/declarative/declarative_api.h"

#include <stddef.h>

#include <utility>
#include <vector>

#include "base/base64.h"
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/values.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/api/declarative/rules_registry_service.h"
#include "extensions/browser/api/extensions_api_client.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/rules_registry_ids.h"
#include "extensions/common/api/events.h"
#include "extensions/common/extension_api.h"
#include "extensions/common/permissions/permissions_data.h"

#if BUILDFLAG(ENABLE_GUEST_VIEW)
#include "extensions/browser/guest_view/web_view/web_view_constants.h"
#include "extensions/browser/guest_view/web_view/web_view_guest.h"
#endif

using extensions::api::events::Rule;

namespace AddRules = extensions::api::events::Event::AddRules;
namespace GetRules = extensions::api::events::Event::GetRules;
namespace RemoveRules = extensions::api::events::Event::RemoveRules;

namespace extensions {

namespace {

#if BUILDFLAG(ENABLE_GUEST_VIEW)
constexpr char kDeclarativeEventPrefix[] = "declarative";
#endif

constexpr char kDeclarativeContentEventPrefix[] = "declarativeContent.";
constexpr char kDeclarativeWebRequestEventPrefix[] = "declarativeWebRequest.";
constexpr char kDeclarativeWebRequestWebViewEventPrefix[] =
    "webViewInternal.declarativeWebRequest.";

// The type of Declarative API. To collect more granular metrics, a distinction
// is made when the declarative web request API is used from a webview.
enum class DeclarativeAPIType {
  kContent,
  kWebRequest,
  kWebRequestWebview,
  kUnknown,
};

// Describes the possible types of declarative API function calls.
// These values are recorded as UMA. New enum values can be added, but existing
// enum values must never be renumbered or deleted and reused.
enum class DeclarativeAPIFunctionType {
  kDeclarativeContentAddRules = 0,
  kDeclarativeContentRemoveRules = 1,
  kDeclarativeContentGetRules = 2,
  kDeclarativeWebRequestAddRules = 3,
  kDeclarativeWebRequestRemoveRules = 4,
  kDeclarativeWebRequestGetRules = 5,
  kDeclarativeWebRequestWebviewAddRules = 6,
  kDeclarativeWebRequestWebviewRemoveRules = 7,
  kDeclarativeWebRequestWebviewGetRules = 8,

  kMaxValue = kDeclarativeWebRequestWebviewGetRules,
};

DeclarativeAPIType GetDeclarativeAPIType(const std::string& event_name) {
  if (base::StartsWith(event_name, kDeclarativeContentEventPrefix,
                       base::CompareCase::SENSITIVE))
    return DeclarativeAPIType::kContent;
  if (base::StartsWith(event_name, kDeclarativeWebRequestEventPrefix,
                       base::CompareCase::SENSITIVE))
    return DeclarativeAPIType::kWebRequest;
  if (base::StartsWith(event_name, kDeclarativeWebRequestWebViewEventPrefix,
                       base::CompareCase::SENSITIVE))
    return DeclarativeAPIType::kWebRequestWebview;
  return DeclarativeAPIType::kUnknown;
}

void RecordUMAHelper(DeclarativeAPIFunctionType type) {
  DCHECK_LE(type, DeclarativeAPIFunctionType::kMaxValue);
  base::UmaHistogramEnumeration("Extensions.DeclarativeAPIFunctionCalls", type);
}

void ConvertBinaryDictValuesToBase64(base::Value::Dict& dict);

// Encodes |binary| as base64 and returns a new string value populated with the
// encoded string.
base::Value ConvertBinaryToBase64(const base::Value& binary) {
  return base::Value(base::Base64Encode(binary.GetBlob()));
}

// Parses through |args| replacing any binary values with base64 encoded
// string values. Recurses over any nested List values, and calls
// ConvertBinaryDictValuesToBase64 for any nested Dict values.
void ConvertBinaryListElementsToBase64(base::Value::List& args) {
  for (auto& value : args) {
    if (value.is_blob()) {
      value = ConvertBinaryToBase64(value);
    } else if (value.is_list() && !value.GetList().empty()) {
      ConvertBinaryListElementsToBase64(value.GetList());
    } else if (value.is_dict()) {
      ConvertBinaryDictValuesToBase64(value.GetDict());
    }
  }
}

// Parses through |dict| replacing any BinaryValues with base64 encoded
// string values. Recurses over any nested Dict values, and calls
// ConvertBinaryListElementsToBase64 for any nested List values.
void ConvertBinaryDictValuesToBase64(base::Value::Dict& dict) {
  for (auto it : dict) {
    auto& value = it.second;
    if (value.is_blob()) {
      value = ConvertBinaryToBase64(value);
    } else if (value.is_list() && !value.GetList().empty()) {
      ConvertBinaryListElementsToBase64(value.GetList());
    } else if (value.is_dict()) {
      ConvertBinaryDictValuesToBase64(value.GetDict());
    }
  }
}

}  // namespace

RulesFunction::RulesFunction() = default;

RulesFunction::~RulesFunction() = default;

ExtensionFunction::ResponseAction RulesFunction::Run() {
  EXTENSION_FUNCTION_VALIDATE(CreateParams());
  EXTENSION_FUNCTION_VALIDATE(args().size() >= 2);
  const auto& event_name_value = args()[0];
  const auto& web_view_instance_id_value = args()[1];
  EXTENSION_FUNCTION_VALIDATE(event_name_value.is_string());
  EXTENSION_FUNCTION_VALIDATE(web_view_instance_id_value.is_int());
  std::string event_name = event_name_value.GetString();
  int web_view_instance_id = web_view_instance_id_value.GetInt();

  EXTENSION_FUNCTION_VALIDATE(extension_);

  // <webview> embedders use the declarativeWebRequest API via
  // <webview>.onRequest.
  if (web_view_instance_id && !extension_->permissions_data()->HasAPIPermission(
                                  mojom::APIPermissionID::kWebView)) {
    return RespondNow(Error("Missing webview permission"));
  }

  RecordUMA(event_name);

  // If we are not operating on a particular <webview>, then the key is 0.
  int rules_registry_id = rules_registry_ids::kDefaultRulesRegistryID;

#if BUILDFLAG(ENABLE_GUEST_VIEW)
  bool from_web_view = web_view_instance_id != 0;
  if (from_web_view) {
    // Sample event names:
    // webViewInternal.declarativeWebRequest.onRequest.
    // webViewInternal.declarativeWebRequest.onMessage.
    // The "webViewInternal." prefix is removed from the event name.
    std::size_t found = event_name.find(kDeclarativeEventPrefix);
    EXTENSION_FUNCTION_VALIDATE(found != std::string::npos);
    event_name = event_name.substr(found);

    rules_registry_id = WebViewGuest::GetOrGenerateRulesRegistryID(
        source_process_id(), web_view_instance_id);
  }
#endif

  // The following call will return a NULL pointer for apps_shell, but should
  // never be called there anyways.
  rules_registry_ = RulesRegistryService::Get(browser_context())->
      GetRulesRegistry(rules_registry_id, event_name);
  DCHECK(rules_registry_.get());
  // Raw access to this function is not available to extensions, therefore
  // there should never be a request for a nonexisting rules registry.
  EXTENSION_FUNCTION_VALIDATE(rules_registry_.get());

  return RespondNow(RunInternal());
}

void RulesFunction::SendResponse(ResponseValue response) {
  Respond(std::move(response));
}

EventsEventAddRulesFunction::EventsEventAddRulesFunction() = default;

EventsEventAddRulesFunction::~EventsEventAddRulesFunction() = default;

bool EventsEventAddRulesFunction::CreateParams() {
  ConvertBinaryListElementsToBase64(GetMutableArgs());
  params_ = AddRules::Params::Create(args());
  return params_.has_value();
}

ExtensionFunction::ResponseValue EventsEventAddRulesFunction::RunInternal() {
  std::vector<const api::events::Rule*> rules_out;
  std::string error = rules_registry_->AddRules(
      extension_id(), std::move(params_->rules), &rules_out);
  if (!error.empty()) {
    return Error(error);
  }

  base::Value::List rules_value;
  rules_value.reserve(rules_out.size());
  for (const auto* rule : rules_out)
    rules_value.Append(rule->ToValue());
  return WithArguments(std::move(rules_value));
}

void EventsEventAddRulesFunction::RecordUMA(
    const std::string& event_name) const {
  DeclarativeAPIFunctionType type;
  switch (GetDeclarativeAPIType(event_name)) {
    case DeclarativeAPIType::kContent:
      type = DeclarativeAPIFunctionType::kDeclarativeContentAddRules;
      break;
    case DeclarativeAPIType::kWebRequest:
      type = DeclarativeAPIFunctionType::kDeclarativeWebRequestAddRules;
      break;
    case DeclarativeAPIType::kWebRequestWebview:
      type = DeclarativeAPIFunctionType::kDeclarativeWebRequestWebviewAddRules;
      break;
    case DeclarativeAPIType::kUnknown:
      NOTREACHED();
  }
  RecordUMAHelper(type);
}

EventsEventRemoveRulesFunction::EventsEventRemoveRulesFunction() = default;

EventsEventRemoveRulesFunction::~EventsEventRemoveRulesFunction() = default;

bool EventsEventRemoveRulesFunction::CreateParams() {
  params_ = RemoveRules::Params::Create(args());
  return params_.has_value();
}

ExtensionFunction::ResponseValue EventsEventRemoveRulesFunction::RunInternal() {
  std::string error;
  if (params_->rule_identifiers) {
    error = rules_registry_->RemoveRules(extension_id(),
                                         *params_->rule_identifiers);
  } else {
    error = rules_registry_->RemoveAllRules(extension_id());
  }

  return error.empty() ? NoArguments() : Error(error);
}

void EventsEventRemoveRulesFunction::RecordUMA(
    const std::string& event_name) const {
  DeclarativeAPIFunctionType type;
  switch (GetDeclarativeAPIType(event_name)) {
    case DeclarativeAPIType::kContent:
      type = DeclarativeAPIFunctionType::kDeclarativeContentRemoveRules;
      break;
    case DeclarativeAPIType::kWebRequest:
      type = DeclarativeAPIFunctionType::kDeclarativeWebRequestRemoveRules;
      break;
    case DeclarativeAPIType::kWebRequestWebview:
      type =
          DeclarativeAPIFunctionType::kDeclarativeWebRequestWebviewRemoveRules;
      break;
    case DeclarativeAPIType::kUnknown:
      NOTREACHED();
  }
  RecordUMAHelper(type);
}

EventsEventGetRulesFunction::EventsEventGetRulesFunction() = default;

EventsEventGetRulesFunction::~EventsEventGetRulesFunction() = default;

bool EventsEventGetRulesFunction::CreateParams() {
  params_ = GetRules::Params::Create(args());
  return params_.has_value();
}

ExtensionFunction::ResponseValue EventsEventGetRulesFunction::RunInternal() {
  std::vector<const Rule*> rules;
  if (params_->rule_identifiers) {
    rules_registry_->GetRules(extension_id(), *params_->rule_identifiers,
                              &rules);
  } else {
    rules_registry_->GetAllRules(extension_id(), &rules);
  }

  base::Value::List rules_value;
  rules_value.reserve(rules.size());
  for (const auto* rule : rules)
    rules_value.Append(rule->ToValue());
  return WithArguments(std::move(rules_value));
}

void EventsEventGetRulesFunction::RecordUMA(
    const std::string& event_name) const {
  DeclarativeAPIFunctionType type;
  switch (GetDeclarativeAPIType(event_name)) {
    case DeclarativeAPIType::kContent:
      type = DeclarativeAPIFunctionType::kDeclarativeContentGetRules;
      break;
    case DeclarativeAPIType::kWebRequest:
      type = DeclarativeAPIFunctionType::kDeclarativeWebRequestGetRules;
      break;
    case DeclarativeAPIType::kWebRequestWebview:
      type = DeclarativeAPIFunctionType::kDeclarativeWebRequestWebviewGetRules;
      break;
    case DeclarativeAPIType::kUnknown:
      NOTREACHED();
  }
  RecordUMAHelper(type);
}

}  // namespace extensions