blob: c9e4a735b4becfa563c9bc3d42a79d368852c701 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// 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 "base/base64.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_util.h"
#include "base/task_runner_util.h"
#include "base/values.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.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/guest_view/web_view/web_view_constants.h"
#include "extensions/browser/guest_view/web_view/web_view_guest.h"
#include "extensions/common/api/events.h"
#include "extensions/common/extension_api.h"
#include "extensions/common/permissions/permissions_data.h"
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 {
constexpr char kDeclarativeEventPrefix[] = "declarative";
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 DeclarativeAPIFunctionType {
kDeclarativeContentAddRules = 0,
kDeclarativeContentRemoveRules = 1,
kDeclarativeContentGetRules = 2,
kDeclarativeWebRequestAddRules = 3,
kDeclarativeWebRequestRemoveRules = 4,
kDeclarativeWebRequestGetRules = 5,
kDeclarativeWebRequestWebviewAddRules = 6,
kDeclarativeWebRequestWebviewRemoveRules = 7,
kDeclarativeWebRequestWebviewGetRules = 8,
kDeclarativeApiFunctionCallTypeMax,
};
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_LT(type, kDeclarativeApiFunctionCallTypeMax);
UMA_HISTOGRAM_ENUMERATION("Extensions.DeclarativeAPIFunctionCalls", type,
kDeclarativeApiFunctionCallTypeMax);
}
void ConvertBinaryDictionaryValuesToBase64(base::DictionaryValue* dict);
// Encodes |binary| as base64 and returns a new StringValue populated with the
// encoded string.
std::unique_ptr<base::Value> ConvertBinaryToBase64(base::Value* binary) {
std::string binary_data(binary->GetBlob().data(), binary->GetBlob().size());
std::string data64;
base::Base64Encode(binary_data, &data64);
return std::unique_ptr<base::Value>(new base::Value(data64));
}
// Parses through |args| replacing any BinaryValues with base64 encoded
// StringValues. Recurses over any nested ListValues, and calls
// ConvertBinaryDictionaryValuesToBase64 for any nested DictionaryValues.
void ConvertBinaryListElementsToBase64(base::ListValue* args) {
size_t index = 0;
for (base::ListValue::iterator iter = args->begin(); iter != args->end();
++iter, ++index) {
if (iter->IsType(base::Value::Type::BINARY)) {
base::Value* binary = NULL;
if (args->GetBinary(index, &binary))
args->Set(index, ConvertBinaryToBase64(binary));
} else if (iter->IsType(base::Value::Type::LIST)) {
base::ListValue* list;
iter->GetAsList(&list);
ConvertBinaryListElementsToBase64(list);
} else if (iter->IsType(base::Value::Type::DICTIONARY)) {
base::DictionaryValue* dict;
iter->GetAsDictionary(&dict);
ConvertBinaryDictionaryValuesToBase64(dict);
}
}
}
// Parses through |dict| replacing any BinaryValues with base64 encoded
// StringValues. Recurses over any nested DictionaryValues, and calls
// ConvertBinaryListElementsToBase64 for any nested ListValues.
void ConvertBinaryDictionaryValuesToBase64(base::DictionaryValue* dict) {
for (base::DictionaryValue::Iterator iter(*dict); !iter.IsAtEnd();
iter.Advance()) {
if (iter.value().IsType(base::Value::Type::BINARY)) {
base::Value* binary = NULL;
if (dict->GetBinary(iter.key(), &binary))
dict->Set(iter.key(), ConvertBinaryToBase64(binary));
} else if (iter.value().IsType(base::Value::Type::LIST)) {
const base::ListValue* list;
iter.value().GetAsList(&list);
ConvertBinaryListElementsToBase64(const_cast<base::ListValue*>(list));
} else if (iter.value().IsType(base::Value::Type::DICTIONARY)) {
const base::DictionaryValue* dict;
iter.value().GetAsDictionary(&dict);
ConvertBinaryDictionaryValuesToBase64(
const_cast<base::DictionaryValue*>(dict));
}
}
}
} // namespace
RulesFunction::RulesFunction()
: rules_registry_(NULL) {
}
RulesFunction::~RulesFunction() {}
bool RulesFunction::HasPermission() {
std::string event_name;
EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &event_name));
int web_view_instance_id = 0;
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(1, &web_view_instance_id));
// <webview> embedders use the declarativeWebRequest API via
// <webview>.onRequest.
if (web_view_instance_id != 0 &&
extension_->permissions_data()->HasAPIPermission(
extensions::APIPermission::kWebView))
return true;
return ExtensionFunction::HasPermission();
}
bool RulesFunction::RunAsync() {
std::string event_name;
EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &event_name));
int web_view_instance_id = 0;
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(1, &web_view_instance_id));
int embedder_process_id = render_frame_host()->GetProcess()->GetID();
RecordUMA(event_name);
bool from_web_view = web_view_instance_id != 0;
// If we are not operating on a particular <webview>, then the key is 0.
int rules_registry_id = RulesRegistryService::kDefaultRulesRegistryID;
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(
embedder_process_id, web_view_instance_id);
}
// 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());
if (content::BrowserThread::CurrentlyOn(rules_registry_->owner_thread())) {
bool success = RunAsyncOnCorrectThread();
SendResponse(success);
} else {
scoped_refptr<base::SingleThreadTaskRunner> thread_task_runner =
content::BrowserThread::GetTaskRunnerForThread(
rules_registry_->owner_thread());
base::PostTaskAndReplyWithResult(
thread_task_runner.get(), FROM_HERE,
base::Bind(&RulesFunction::RunAsyncOnCorrectThread, this),
base::Bind(&RulesFunction::SendResponse, this));
}
return true;
}
bool EventsEventAddRulesFunction::RunAsyncOnCorrectThread() {
ConvertBinaryListElementsToBase64(args_.get());
std::unique_ptr<AddRules::Params> params(AddRules::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
// TODO(devlin): Remove the dependency on linked_ptr here.
std::vector<linked_ptr<api::events::Rule>> linked_rules;
for (api::events::Rule& rule : params->rules) {
linked_rules.push_back(
make_linked_ptr(new api::events::Rule(std::move(rule))));
}
error_ = rules_registry_->AddRules(extension_id(), linked_rules);
if (error_.empty()) {
std::unique_ptr<base::ListValue> rules_value(new base::ListValue());
for (const auto& rule : linked_rules)
rules_value->Append(rule->ToValue());
SetResult(std::move(rules_value));
}
return error_.empty();
}
void EventsEventAddRulesFunction::RecordUMA(
const std::string& event_name) const {
DeclarativeAPIFunctionType type = kDeclarativeApiFunctionCallTypeMax;
switch (GetDeclarativeAPIType(event_name)) {
case DeclarativeAPIType::kContent:
type = kDeclarativeContentAddRules;
break;
case DeclarativeAPIType::kWebRequest:
type = kDeclarativeWebRequestAddRules;
break;
case DeclarativeAPIType::kWebRequestWebview:
type = kDeclarativeWebRequestWebviewAddRules;
break;
case DeclarativeAPIType::kUnknown:
NOTREACHED();
return;
}
RecordUMAHelper(type);
}
bool EventsEventRemoveRulesFunction::RunAsyncOnCorrectThread() {
std::unique_ptr<RemoveRules::Params> params(
RemoveRules::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
if (params->rule_identifiers.get()) {
error_ = rules_registry_->RemoveRules(extension_id(),
*params->rule_identifiers);
} else {
error_ = rules_registry_->RemoveAllRules(extension_id());
}
return error_.empty();
}
void EventsEventRemoveRulesFunction::RecordUMA(
const std::string& event_name) const {
DeclarativeAPIFunctionType type = kDeclarativeApiFunctionCallTypeMax;
switch (GetDeclarativeAPIType(event_name)) {
case DeclarativeAPIType::kContent:
type = kDeclarativeContentRemoveRules;
break;
case DeclarativeAPIType::kWebRequest:
type = kDeclarativeWebRequestRemoveRules;
break;
case DeclarativeAPIType::kWebRequestWebview:
type = kDeclarativeWebRequestWebviewRemoveRules;
break;
case DeclarativeAPIType::kUnknown:
NOTREACHED();
return;
}
RecordUMAHelper(type);
}
bool EventsEventGetRulesFunction::RunAsyncOnCorrectThread() {
std::unique_ptr<GetRules::Params> params(GetRules::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
std::vector<linked_ptr<Rule> > rules;
if (params->rule_identifiers.get()) {
rules_registry_->GetRules(
extension_id(), *params->rule_identifiers, &rules);
} else {
rules_registry_->GetAllRules(extension_id(), &rules);
}
std::unique_ptr<base::ListValue> rules_value(new base::ListValue());
for (const auto& rule : rules)
rules_value->Append(rule->ToValue());
SetResult(std::move(rules_value));
return true;
}
void EventsEventGetRulesFunction::RecordUMA(
const std::string& event_name) const {
DeclarativeAPIFunctionType type = kDeclarativeApiFunctionCallTypeMax;
switch (GetDeclarativeAPIType(event_name)) {
case DeclarativeAPIType::kContent:
type = kDeclarativeContentGetRules;
break;
case DeclarativeAPIType::kWebRequest:
type = kDeclarativeWebRequestGetRules;
break;
case DeclarativeAPIType::kWebRequestWebview:
type = kDeclarativeWebRequestWebviewGetRules;
break;
case DeclarativeAPIType::kUnknown:
NOTREACHED();
return;
}
RecordUMAHelper(type);
}
} // namespace extensions
OSZAR »