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