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