| // 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_webrequest/webrequest_action.h" |
| |
| #include <limits> |
| #include <utility> |
| |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/values.h" |
| #include "content/public/browser/resource_request_info.h" |
| #include "content/public/common/url_constants.h" |
| #include "extensions/browser/api/declarative/deduping_factory.h" |
| #include "extensions/browser/api/declarative_webrequest/request_stage.h" |
| #include "extensions/browser/api/declarative_webrequest/webrequest_condition.h" |
| #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h" |
| #include "extensions/browser/api/web_request/web_request_api_constants.h" |
| #include "extensions/browser/api/web_request/web_request_api_helpers.h" |
| #include "extensions/browser/api/web_request/web_request_permissions.h" |
| #include "extensions/browser/extension_navigation_ui_data.h" |
| #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h" |
| #include "extensions/browser/info_map.h" |
| #include "extensions/common/error_utils.h" |
| #include "extensions/common/extension.h" |
| #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| #include "net/http/http_util.h" |
| #include "third_party/re2/src/re2/re2.h" |
| |
| using content::ResourceRequestInfo; |
| using extension_web_request_api_helpers::EventResponseDelta; |
| |
| namespace extensions { |
| |
| namespace helpers = extension_web_request_api_helpers; |
| namespace keys = declarative_webrequest_constants; |
| |
| namespace { |
| // Error messages. |
| const char kIgnoreRulesRequiresParameterError[] = |
| "IgnoreRules requires at least one parameter."; |
| |
| const char kTransparentImageUrl[] = "data:image/png;base64,iVBORw0KGgoAAAANSUh" |
| "EUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg=="; |
| const char kEmptyDocumentUrl[] = "data:text/html,"; |
| |
| #define INPUT_FORMAT_VALIDATE(test) \ |
| do { \ |
| if (!(test)) { \ |
| *bad_message = true; \ |
| return scoped_refptr<const WebRequestAction>(nullptr); \ |
| } \ |
| } while (0) |
| |
| helpers::RequestCookie ParseRequestCookie(const base::DictionaryValue* dict) { |
| helpers::RequestCookie result; |
| std::string tmp; |
| if (dict->GetString(keys::kNameKey, &tmp)) |
| result.name = tmp; |
| if (dict->GetString(keys::kValueKey, &tmp)) |
| result.value = tmp; |
| return result; |
| } |
| |
| void ParseResponseCookieImpl(const base::DictionaryValue* dict, |
| helpers::ResponseCookie* cookie) { |
| std::string string_tmp; |
| int int_tmp = 0; |
| bool bool_tmp = false; |
| if (dict->GetString(keys::kNameKey, &string_tmp)) |
| cookie->name = string_tmp; |
| if (dict->GetString(keys::kValueKey, &string_tmp)) |
| cookie->value = string_tmp; |
| if (dict->GetString(keys::kExpiresKey, &string_tmp)) |
| cookie->expires = string_tmp; |
| if (dict->GetInteger(keys::kMaxAgeKey, &int_tmp)) |
| cookie->max_age = int_tmp; |
| if (dict->GetString(keys::kDomainKey, &string_tmp)) |
| cookie->domain = string_tmp; |
| if (dict->GetString(keys::kPathKey, &string_tmp)) |
| cookie->path = string_tmp; |
| if (dict->GetBoolean(keys::kSecureKey, &bool_tmp)) |
| cookie->secure = bool_tmp; |
| if (dict->GetBoolean(keys::kHttpOnlyKey, &bool_tmp)) |
| cookie->http_only = bool_tmp; |
| } |
| |
| helpers::ResponseCookie ParseResponseCookie(const base::DictionaryValue* dict) { |
| helpers::ResponseCookie result; |
| ParseResponseCookieImpl(dict, &result); |
| return result; |
| } |
| |
| helpers::FilterResponseCookie ParseFilterResponseCookie( |
| const base::DictionaryValue* dict) { |
| helpers::FilterResponseCookie result; |
| ParseResponseCookieImpl(dict, &result); |
| |
| int int_tmp = 0; |
| bool bool_tmp = false; |
| if (dict->GetInteger(keys::kAgeUpperBoundKey, &int_tmp)) |
| result.age_upper_bound = int_tmp; |
| if (dict->GetInteger(keys::kAgeLowerBoundKey, &int_tmp)) |
| result.age_lower_bound = int_tmp; |
| if (dict->GetBoolean(keys::kSessionCookieKey, &bool_tmp)) |
| result.session_cookie = bool_tmp; |
| return result; |
| } |
| |
| // Helper function for WebRequestActions that can be instantiated by just |
| // calling the constructor. |
| template <class T> |
| scoped_refptr<const WebRequestAction> CallConstructorFactoryMethod( |
| const std::string& instance_type, |
| const base::Value* value, |
| std::string* error, |
| bool* bad_message) { |
| return scoped_refptr<const WebRequestAction>(new T); |
| } |
| |
| scoped_refptr<const WebRequestAction> CreateRedirectRequestAction( |
| const std::string& instance_type, |
| const base::Value* value, |
| std::string* error, |
| bool* bad_message) { |
| const base::DictionaryValue* dict = NULL; |
| CHECK(value->GetAsDictionary(&dict)); |
| std::string redirect_url_string; |
| INPUT_FORMAT_VALIDATE( |
| dict->GetString(keys::kRedirectUrlKey, &redirect_url_string)); |
| GURL redirect_url(redirect_url_string); |
| return base::MakeRefCounted<WebRequestRedirectAction>(redirect_url); |
| } |
| |
| scoped_refptr<const WebRequestAction> CreateRedirectRequestByRegExAction( |
| const std::string& instance_type, |
| const base::Value* value, |
| std::string* error, |
| bool* bad_message) { |
| const base::DictionaryValue* dict = NULL; |
| CHECK(value->GetAsDictionary(&dict)); |
| std::string from; |
| std::string to; |
| INPUT_FORMAT_VALIDATE(dict->GetString(keys::kFromKey, &from)); |
| INPUT_FORMAT_VALIDATE(dict->GetString(keys::kToKey, &to)); |
| |
| to = WebRequestRedirectByRegExAction::PerlToRe2Style(to); |
| |
| RE2::Options options; |
| options.set_case_sensitive(false); |
| std::unique_ptr<RE2> from_pattern = std::make_unique<RE2>(from, options); |
| |
| if (!from_pattern->ok()) { |
| *error = "Invalid pattern '" + from + "' -> '" + to + "'"; |
| return scoped_refptr<const WebRequestAction>(nullptr); |
| } |
| return base::MakeRefCounted<WebRequestRedirectByRegExAction>( |
| std::move(from_pattern), to); |
| } |
| |
| scoped_refptr<const WebRequestAction> CreateSetRequestHeaderAction( |
| const std::string& instance_type, |
| const base::Value* json_value, |
| std::string* error, |
| bool* bad_message) { |
| const base::DictionaryValue* dict = NULL; |
| CHECK(json_value->GetAsDictionary(&dict)); |
| std::string name; |
| std::string value; |
| INPUT_FORMAT_VALIDATE(dict->GetString(keys::kNameKey, &name)); |
| INPUT_FORMAT_VALIDATE(dict->GetString(keys::kValueKey, &value)); |
| if (!net::HttpUtil::IsValidHeaderName(name)) { |
| *error = extension_web_request_api_constants::kInvalidHeaderName; |
| return scoped_refptr<const WebRequestAction>(nullptr); |
| } |
| if (!net::HttpUtil::IsValidHeaderValue(value)) { |
| *error = ErrorUtils::FormatErrorMessage( |
| extension_web_request_api_constants::kInvalidHeaderValue, name); |
| return scoped_refptr<const WebRequestAction>(nullptr); |
| } |
| return base::MakeRefCounted<WebRequestSetRequestHeaderAction>(name, value); |
| } |
| |
| scoped_refptr<const WebRequestAction> CreateRemoveRequestHeaderAction( |
| const std::string& instance_type, |
| const base::Value* value, |
| std::string* error, |
| bool* bad_message) { |
| const base::DictionaryValue* dict = NULL; |
| CHECK(value->GetAsDictionary(&dict)); |
| std::string name; |
| INPUT_FORMAT_VALIDATE(dict->GetString(keys::kNameKey, &name)); |
| if (!net::HttpUtil::IsValidHeaderName(name)) { |
| *error = extension_web_request_api_constants::kInvalidHeaderName; |
| return scoped_refptr<const WebRequestAction>(nullptr); |
| } |
| return base::MakeRefCounted<WebRequestRemoveRequestHeaderAction>(name); |
| } |
| |
| scoped_refptr<const WebRequestAction> CreateAddResponseHeaderAction( |
| const std::string& instance_type, |
| const base::Value* json_value, |
| std::string* error, |
| bool* bad_message) { |
| const base::DictionaryValue* dict = NULL; |
| CHECK(json_value->GetAsDictionary(&dict)); |
| std::string name; |
| std::string value; |
| INPUT_FORMAT_VALIDATE(dict->GetString(keys::kNameKey, &name)); |
| INPUT_FORMAT_VALIDATE(dict->GetString(keys::kValueKey, &value)); |
| if (!net::HttpUtil::IsValidHeaderName(name)) { |
| *error = extension_web_request_api_constants::kInvalidHeaderName; |
| return scoped_refptr<const WebRequestAction>(nullptr); |
| } |
| if (!net::HttpUtil::IsValidHeaderValue(value)) { |
| *error = ErrorUtils::FormatErrorMessage( |
| extension_web_request_api_constants::kInvalidHeaderValue, name); |
| return scoped_refptr<const WebRequestAction>(nullptr); |
| } |
| return base::MakeRefCounted<WebRequestAddResponseHeaderAction>(name, value); |
| } |
| |
| scoped_refptr<const WebRequestAction> CreateRemoveResponseHeaderAction( |
| const std::string& instance_type, |
| const base::Value* json_value, |
| std::string* error, |
| bool* bad_message) { |
| const base::DictionaryValue* dict = NULL; |
| CHECK(json_value->GetAsDictionary(&dict)); |
| std::string name; |
| std::string value; |
| INPUT_FORMAT_VALIDATE(dict->GetString(keys::kNameKey, &name)); |
| bool has_value = dict->GetString(keys::kValueKey, &value); |
| if (!net::HttpUtil::IsValidHeaderName(name)) { |
| *error = extension_web_request_api_constants::kInvalidHeaderName; |
| return scoped_refptr<const WebRequestAction>(nullptr); |
| } |
| if (has_value && !net::HttpUtil::IsValidHeaderValue(value)) { |
| *error = ErrorUtils::FormatErrorMessage( |
| extension_web_request_api_constants::kInvalidHeaderValue, name); |
| return scoped_refptr<const WebRequestAction>(nullptr); |
| } |
| return base::MakeRefCounted<WebRequestRemoveResponseHeaderAction>(name, value, |
| has_value); |
| } |
| |
| scoped_refptr<const WebRequestAction> CreateIgnoreRulesAction( |
| const std::string& instance_type, |
| const base::Value* value, |
| std::string* error, |
| bool* bad_message) { |
| const base::DictionaryValue* dict = NULL; |
| CHECK(value->GetAsDictionary(&dict)); |
| bool has_parameter = false; |
| int minimum_priority = std::numeric_limits<int>::min(); |
| std::string ignore_tag; |
| if (dict->HasKey(keys::kLowerPriorityThanKey)) { |
| INPUT_FORMAT_VALIDATE( |
| dict->GetInteger(keys::kLowerPriorityThanKey, &minimum_priority)); |
| has_parameter = true; |
| } |
| if (dict->HasKey(keys::kHasTagKey)) { |
| INPUT_FORMAT_VALIDATE(dict->GetString(keys::kHasTagKey, &ignore_tag)); |
| has_parameter = true; |
| } |
| if (!has_parameter) { |
| *error = kIgnoreRulesRequiresParameterError; |
| return scoped_refptr<const WebRequestAction>(nullptr); |
| } |
| return base::MakeRefCounted<WebRequestIgnoreRulesAction>(minimum_priority, |
| ignore_tag); |
| } |
| |
| scoped_refptr<const WebRequestAction> CreateRequestCookieAction( |
| const std::string& instance_type, |
| const base::Value* value, |
| std::string* error, |
| bool* bad_message) { |
| using extension_web_request_api_helpers::RequestCookieModification; |
| |
| const base::DictionaryValue* dict = NULL; |
| CHECK(value->GetAsDictionary(&dict)); |
| |
| RequestCookieModification modification; |
| |
| // Get modification type. |
| if (instance_type == keys::kAddRequestCookieType) |
| modification.type = helpers::ADD; |
| else if (instance_type == keys::kEditRequestCookieType) |
| modification.type = helpers::EDIT; |
| else if (instance_type == keys::kRemoveRequestCookieType) |
| modification.type = helpers::REMOVE; |
| else |
| INPUT_FORMAT_VALIDATE(false); |
| |
| // Get filter. |
| if (modification.type == helpers::EDIT || |
| modification.type == helpers::REMOVE) { |
| const base::DictionaryValue* filter = NULL; |
| INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kFilterKey, &filter)); |
| modification.filter = ParseRequestCookie(filter); |
| } |
| |
| // Get new value. |
| if (modification.type == helpers::ADD) { |
| const base::DictionaryValue* value = NULL; |
| INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kCookieKey, &value)); |
| modification.modification = ParseRequestCookie(value); |
| } else if (modification.type == helpers::EDIT) { |
| const base::DictionaryValue* value = NULL; |
| INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kModificationKey, &value)); |
| modification.modification = ParseRequestCookie(value); |
| } |
| |
| return base::MakeRefCounted<WebRequestRequestCookieAction>( |
| std::move(modification)); |
| } |
| |
| scoped_refptr<const WebRequestAction> CreateResponseCookieAction( |
| const std::string& instance_type, |
| const base::Value* value, |
| std::string* error, |
| bool* bad_message) { |
| using extension_web_request_api_helpers::ResponseCookieModification; |
| |
| const base::DictionaryValue* dict = NULL; |
| CHECK(value->GetAsDictionary(&dict)); |
| |
| ResponseCookieModification modification; |
| |
| // Get modification type. |
| if (instance_type == keys::kAddResponseCookieType) |
| modification.type = helpers::ADD; |
| else if (instance_type == keys::kEditResponseCookieType) |
| modification.type = helpers::EDIT; |
| else if (instance_type == keys::kRemoveResponseCookieType) |
| modification.type = helpers::REMOVE; |
| else |
| INPUT_FORMAT_VALIDATE(false); |
| |
| // Get filter. |
| if (modification.type == helpers::EDIT || |
| modification.type == helpers::REMOVE) { |
| const base::DictionaryValue* filter = NULL; |
| INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kFilterKey, &filter)); |
| modification.filter = ParseFilterResponseCookie(filter); |
| } |
| |
| // Get new value. |
| if (modification.type == helpers::ADD) { |
| const base::DictionaryValue* value = NULL; |
| INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kCookieKey, &value)); |
| modification.modification = ParseResponseCookie(value); |
| } else if (modification.type == helpers::EDIT) { |
| const base::DictionaryValue* value = NULL; |
| INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kModificationKey, &value)); |
| modification.modification = ParseResponseCookie(value); |
| } |
| |
| return base::MakeRefCounted<WebRequestResponseCookieAction>( |
| std::move(modification)); |
| } |
| |
| scoped_refptr<const WebRequestAction> CreateSendMessageToExtensionAction( |
| const std::string& name, |
| const base::Value* value, |
| std::string* error, |
| bool* bad_message) { |
| const base::DictionaryValue* dict = NULL; |
| CHECK(value->GetAsDictionary(&dict)); |
| std::string message; |
| INPUT_FORMAT_VALIDATE(dict->GetString(keys::kMessageKey, &message)); |
| return base::MakeRefCounted<WebRequestSendMessageToExtensionAction>(message); |
| } |
| |
| struct WebRequestActionFactory { |
| DedupingFactory<WebRequestAction> factory; |
| |
| WebRequestActionFactory() : factory(5) { |
| factory.RegisterFactoryMethod( |
| keys::kAddRequestCookieType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateRequestCookieAction); |
| factory.RegisterFactoryMethod( |
| keys::kAddResponseCookieType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateResponseCookieAction); |
| factory.RegisterFactoryMethod( |
| keys::kAddResponseHeaderType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateAddResponseHeaderAction); |
| factory.RegisterFactoryMethod( |
| keys::kCancelRequestType, |
| DedupingFactory<WebRequestAction>::IS_NOT_PARAMETERIZED, |
| &CallConstructorFactoryMethod<WebRequestCancelAction>); |
| factory.RegisterFactoryMethod( |
| keys::kEditRequestCookieType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateRequestCookieAction); |
| factory.RegisterFactoryMethod( |
| keys::kEditResponseCookieType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateResponseCookieAction); |
| factory.RegisterFactoryMethod( |
| keys::kRedirectByRegExType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateRedirectRequestByRegExAction); |
| factory.RegisterFactoryMethod( |
| keys::kRedirectRequestType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateRedirectRequestAction); |
| factory.RegisterFactoryMethod( |
| keys::kRedirectToTransparentImageType, |
| DedupingFactory<WebRequestAction>::IS_NOT_PARAMETERIZED, |
| &CallConstructorFactoryMethod< |
| WebRequestRedirectToTransparentImageAction>); |
| factory.RegisterFactoryMethod( |
| keys::kRedirectToEmptyDocumentType, |
| DedupingFactory<WebRequestAction>::IS_NOT_PARAMETERIZED, |
| &CallConstructorFactoryMethod<WebRequestRedirectToEmptyDocumentAction>); |
| factory.RegisterFactoryMethod( |
| keys::kRemoveRequestCookieType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateRequestCookieAction); |
| factory.RegisterFactoryMethod( |
| keys::kRemoveResponseCookieType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateResponseCookieAction); |
| factory.RegisterFactoryMethod( |
| keys::kSetRequestHeaderType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateSetRequestHeaderAction); |
| factory.RegisterFactoryMethod( |
| keys::kRemoveRequestHeaderType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateRemoveRequestHeaderAction); |
| factory.RegisterFactoryMethod( |
| keys::kRemoveResponseHeaderType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateRemoveResponseHeaderAction); |
| factory.RegisterFactoryMethod( |
| keys::kIgnoreRulesType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateIgnoreRulesAction); |
| factory.RegisterFactoryMethod( |
| keys::kSendMessageToExtensionType, |
| DedupingFactory<WebRequestAction>::IS_PARAMETERIZED, |
| &CreateSendMessageToExtensionAction); |
| } |
| }; |
| |
| base::LazyInstance<WebRequestActionFactory>::Leaky |
| g_web_request_action_factory = LAZY_INSTANCE_INITIALIZER; |
| |
| } // namespace |
| |
| // |
| // WebRequestAction |
| // |
| |
| WebRequestAction::~WebRequestAction() {} |
| |
| bool WebRequestAction::Equals(const WebRequestAction* other) const { |
| return type() == other->type(); |
| } |
| |
| bool WebRequestAction::HasPermission(ApplyInfo* apply_info, |
| const std::string& extension_id) const { |
| const InfoMap* extension_info_map = apply_info->extension_info_map; |
| const WebRequestInfo* request = apply_info->request_data.request; |
| if (WebRequestPermissions::HideRequest(extension_info_map, *request)) |
| return false; |
| |
| // In unit tests we don't have an extension_info_map object here and skip host |
| // permission checks. |
| if (!extension_info_map) |
| return true; |
| |
| // The embedder can always access all hosts from within a <webview>. |
| // The same is not true of extensions. |
| if (request->is_web_view) |
| return true; |
| |
| WebRequestPermissions::HostPermissionsCheck permission_check = |
| WebRequestPermissions::REQUIRE_ALL_URLS; |
| switch (host_permissions_strategy()) { |
| case STRATEGY_DEFAULT: // Default value is already set. |
| break; |
| case STRATEGY_NONE: |
| permission_check = WebRequestPermissions::DO_NOT_CHECK_HOST; |
| break; |
| case STRATEGY_HOST: |
| permission_check = WebRequestPermissions::REQUIRE_HOST_PERMISSION_FOR_URL; |
| break; |
| } |
| // TODO(devlin): Pass in the real tab id here. |
| return WebRequestPermissions::CanExtensionAccessURL( |
| extension_info_map, extension_id, request->url, -1, |
| apply_info->crosses_incognito, permission_check, |
| request->initiator) == PermissionsData::PageAccess::kAllowed; |
| } |
| |
| // static |
| scoped_refptr<const WebRequestAction> WebRequestAction::Create( |
| content::BrowserContext* browser_context, |
| const Extension* extension, |
| const base::Value& json_action, |
| std::string* error, |
| bool* bad_message) { |
| *error = ""; |
| *bad_message = false; |
| |
| const base::DictionaryValue* action_dict = NULL; |
| INPUT_FORMAT_VALIDATE(json_action.GetAsDictionary(&action_dict)); |
| |
| std::string instance_type; |
| INPUT_FORMAT_VALIDATE( |
| action_dict->GetString(keys::kInstanceTypeKey, &instance_type)); |
| |
| WebRequestActionFactory& factory = g_web_request_action_factory.Get(); |
| return factory.factory.Instantiate( |
| instance_type, action_dict, error, bad_message); |
| } |
| |
| void WebRequestAction::Apply(const std::string& extension_id, |
| base::Time extension_install_time, |
| ApplyInfo* apply_info) const { |
| if (!HasPermission(apply_info, extension_id)) |
| return; |
| if (stages() & apply_info->request_data.stage) { |
| base::Optional<EventResponseDelta> delta = CreateDelta( |
| apply_info->request_data, extension_id, extension_install_time); |
| if (delta.has_value()) |
| apply_info->deltas->push_back(std::move(delta.value())); |
| if (type() == WebRequestAction::ACTION_IGNORE_RULES) { |
| const WebRequestIgnoreRulesAction* ignore_action = |
| static_cast<const WebRequestIgnoreRulesAction*>(this); |
| if (!ignore_action->ignore_tag().empty()) |
| apply_info->ignored_tags->insert(ignore_action->ignore_tag()); |
| } |
| } |
| } |
| |
| WebRequestAction::WebRequestAction(int stages, |
| Type type, |
| int minimum_priority, |
| HostPermissionsStrategy strategy) |
| : stages_(stages), |
| type_(type), |
| minimum_priority_(minimum_priority), |
| host_permissions_strategy_(strategy) {} |
| |
| // |
| // WebRequestCancelAction |
| // |
| |
| WebRequestCancelAction::WebRequestCancelAction() |
| : WebRequestAction(ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS | |
| ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED, |
| ACTION_CANCEL_REQUEST, |
| std::numeric_limits<int>::min(), |
| STRATEGY_NONE) {} |
| |
| WebRequestCancelAction::~WebRequestCancelAction() {} |
| |
| std::string WebRequestCancelAction::GetName() const { |
| return keys::kCancelRequestType; |
| } |
| |
| base::Optional<EventResponseDelta> WebRequestCancelAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| EventResponseDelta result(extension_id, extension_install_time); |
| result.cancel = true; |
| return result; |
| } |
| |
| // |
| // WebRequestRedirectAction |
| // |
| |
| WebRequestRedirectAction::WebRequestRedirectAction(const GURL& redirect_url) |
| : WebRequestAction(ON_BEFORE_REQUEST | ON_HEADERS_RECEIVED, |
| ACTION_REDIRECT_REQUEST, |
| std::numeric_limits<int>::min(), |
| STRATEGY_DEFAULT), |
| redirect_url_(redirect_url) {} |
| |
| WebRequestRedirectAction::~WebRequestRedirectAction() {} |
| |
| bool WebRequestRedirectAction::Equals(const WebRequestAction* other) const { |
| return WebRequestAction::Equals(other) && |
| redirect_url_ == |
| static_cast<const WebRequestRedirectAction*>(other)->redirect_url_; |
| } |
| |
| std::string WebRequestRedirectAction::GetName() const { |
| return keys::kRedirectRequestType; |
| } |
| |
| base::Optional<EventResponseDelta> WebRequestRedirectAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| if (request_data.request->url == redirect_url_) |
| return base::nullopt; |
| EventResponseDelta result(extension_id, extension_install_time); |
| result.new_url = redirect_url_; |
| return result; |
| } |
| |
| // |
| // WebRequestRedirectToTransparentImageAction |
| // |
| |
| WebRequestRedirectToTransparentImageAction:: |
| WebRequestRedirectToTransparentImageAction() |
| : WebRequestAction(ON_BEFORE_REQUEST | ON_HEADERS_RECEIVED, |
| ACTION_REDIRECT_TO_TRANSPARENT_IMAGE, |
| std::numeric_limits<int>::min(), |
| STRATEGY_NONE) {} |
| |
| WebRequestRedirectToTransparentImageAction:: |
| ~WebRequestRedirectToTransparentImageAction() {} |
| |
| std::string WebRequestRedirectToTransparentImageAction::GetName() const { |
| return keys::kRedirectToTransparentImageType; |
| } |
| |
| base::Optional<EventResponseDelta> |
| WebRequestRedirectToTransparentImageAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| EventResponseDelta result(extension_id, extension_install_time); |
| result.new_url = GURL(kTransparentImageUrl); |
| return result; |
| } |
| |
| // |
| // WebRequestRedirectToEmptyDocumentAction |
| // |
| |
| WebRequestRedirectToEmptyDocumentAction:: |
| WebRequestRedirectToEmptyDocumentAction() |
| : WebRequestAction(ON_BEFORE_REQUEST | ON_HEADERS_RECEIVED, |
| ACTION_REDIRECT_TO_EMPTY_DOCUMENT, |
| std::numeric_limits<int>::min(), |
| STRATEGY_NONE) {} |
| |
| WebRequestRedirectToEmptyDocumentAction:: |
| ~WebRequestRedirectToEmptyDocumentAction() {} |
| |
| std::string WebRequestRedirectToEmptyDocumentAction::GetName() const { |
| return keys::kRedirectToEmptyDocumentType; |
| } |
| |
| base::Optional<EventResponseDelta> |
| WebRequestRedirectToEmptyDocumentAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| EventResponseDelta result(extension_id, extension_install_time); |
| result.new_url = GURL(kEmptyDocumentUrl); |
| return result; |
| } |
| |
| // |
| // WebRequestRedirectByRegExAction |
| // |
| |
| WebRequestRedirectByRegExAction::WebRequestRedirectByRegExAction( |
| std::unique_ptr<RE2> from_pattern, |
| const std::string& to_pattern) |
| : WebRequestAction(ON_BEFORE_REQUEST | ON_HEADERS_RECEIVED, |
| ACTION_REDIRECT_BY_REGEX_DOCUMENT, |
| std::numeric_limits<int>::min(), |
| STRATEGY_DEFAULT), |
| from_pattern_(std::move(from_pattern)), |
| to_pattern_(to_pattern.data(), to_pattern.size()) {} |
| |
| WebRequestRedirectByRegExAction::~WebRequestRedirectByRegExAction() {} |
| |
| // About the syntax of the two languages: |
| // |
| // ICU (Perl) states: |
| // $n The text of capture group n will be substituted for $n. n must be >= 0 |
| // and not greater than the number of capture groups. A $ not followed by a |
| // digit has no special meaning, and will appear in the substitution text |
| // as itself, a $. |
| // \ Treat the following character as a literal, suppressing any special |
| // meaning. Backslash escaping in substitution text is only required for |
| // '$' and '\', but may be used on any other character without bad effects. |
| // |
| // RE2, derived from RE2::Rewrite() |
| // \ May only be followed by a digit or another \. If followed by a single |
| // digit, both characters represent the respective capture group. If followed |
| // by another \, it is used as an escape sequence. |
| |
| // static |
| std::string WebRequestRedirectByRegExAction::PerlToRe2Style( |
| const std::string& perl) { |
| std::string::const_iterator i = perl.begin(); |
| std::string result; |
| while (i != perl.end()) { |
| if (*i == '$') { |
| ++i; |
| if (i == perl.end()) { |
| result += '$'; |
| return result; |
| } else if (isdigit(*i)) { |
| result += '\\'; |
| result += *i; |
| } else { |
| result += '$'; |
| result += *i; |
| } |
| } else if (*i == '\\') { |
| ++i; |
| if (i == perl.end()) { |
| result += '\\'; |
| } else if (*i == '$') { |
| result += '$'; |
| } else if (*i == '\\') { |
| result += "\\\\"; |
| } else { |
| result += *i; |
| } |
| } else { |
| result += *i; |
| } |
| ++i; |
| } |
| return result; |
| } |
| |
| bool WebRequestRedirectByRegExAction::Equals( |
| const WebRequestAction* other) const { |
| if (!WebRequestAction::Equals(other)) |
| return false; |
| const WebRequestRedirectByRegExAction* casted_other = |
| static_cast<const WebRequestRedirectByRegExAction*>(other); |
| return from_pattern_->pattern() == casted_other->from_pattern_->pattern() && |
| to_pattern_ == casted_other->to_pattern_; |
| } |
| |
| std::string WebRequestRedirectByRegExAction::GetName() const { |
| return keys::kRedirectByRegExType; |
| } |
| |
| base::Optional<EventResponseDelta> WebRequestRedirectByRegExAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| CHECK(from_pattern_.get()); |
| |
| const std::string& old_url = request_data.request->url.spec(); |
| std::string new_url = old_url; |
| if (!RE2::Replace(&new_url, *from_pattern_, to_pattern_) || |
| new_url == old_url) { |
| return base::nullopt; |
| } |
| |
| EventResponseDelta result(extension_id, extension_install_time); |
| result.new_url = GURL(new_url); |
| return result; |
| } |
| |
| // |
| // WebRequestSetRequestHeaderAction |
| // |
| |
| WebRequestSetRequestHeaderAction::WebRequestSetRequestHeaderAction( |
| const std::string& name, |
| const std::string& value) |
| : WebRequestAction(ON_BEFORE_SEND_HEADERS, |
| ACTION_SET_REQUEST_HEADER, |
| std::numeric_limits<int>::min(), |
| STRATEGY_DEFAULT), |
| name_(name), |
| value_(value) {} |
| |
| WebRequestSetRequestHeaderAction::~WebRequestSetRequestHeaderAction() {} |
| |
| bool WebRequestSetRequestHeaderAction::Equals( |
| const WebRequestAction* other) const { |
| if (!WebRequestAction::Equals(other)) |
| return false; |
| const WebRequestSetRequestHeaderAction* casted_other = |
| static_cast<const WebRequestSetRequestHeaderAction*>(other); |
| return name_ == casted_other->name_ && value_ == casted_other->value_; |
| } |
| |
| std::string WebRequestSetRequestHeaderAction::GetName() const { |
| return keys::kSetRequestHeaderType; |
| } |
| |
| base::Optional<EventResponseDelta> |
| WebRequestSetRequestHeaderAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| EventResponseDelta result(extension_id, extension_install_time); |
| result.modified_request_headers.SetHeader(name_, value_); |
| return result; |
| } |
| |
| // |
| // WebRequestRemoveRequestHeaderAction |
| // |
| |
| WebRequestRemoveRequestHeaderAction::WebRequestRemoveRequestHeaderAction( |
| const std::string& name) |
| : WebRequestAction(ON_BEFORE_SEND_HEADERS, |
| ACTION_REMOVE_REQUEST_HEADER, |
| std::numeric_limits<int>::min(), |
| STRATEGY_DEFAULT), |
| name_(name) {} |
| |
| WebRequestRemoveRequestHeaderAction::~WebRequestRemoveRequestHeaderAction() {} |
| |
| bool WebRequestRemoveRequestHeaderAction::Equals( |
| const WebRequestAction* other) const { |
| if (!WebRequestAction::Equals(other)) |
| return false; |
| const WebRequestRemoveRequestHeaderAction* casted_other = |
| static_cast<const WebRequestRemoveRequestHeaderAction*>(other); |
| return name_ == casted_other->name_; |
| } |
| |
| std::string WebRequestRemoveRequestHeaderAction::GetName() const { |
| return keys::kRemoveRequestHeaderType; |
| } |
| |
| base::Optional<EventResponseDelta> |
| WebRequestRemoveRequestHeaderAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| EventResponseDelta result(extension_id, extension_install_time); |
| result.deleted_request_headers.push_back(name_); |
| return result; |
| } |
| |
| // |
| // WebRequestAddResponseHeaderAction |
| // |
| |
| WebRequestAddResponseHeaderAction::WebRequestAddResponseHeaderAction( |
| const std::string& name, |
| const std::string& value) |
| : WebRequestAction(ON_HEADERS_RECEIVED, |
| ACTION_ADD_RESPONSE_HEADER, |
| std::numeric_limits<int>::min(), |
| STRATEGY_DEFAULT), |
| name_(name), |
| value_(value) {} |
| |
| WebRequestAddResponseHeaderAction::~WebRequestAddResponseHeaderAction() {} |
| |
| bool WebRequestAddResponseHeaderAction::Equals( |
| const WebRequestAction* other) const { |
| if (!WebRequestAction::Equals(other)) |
| return false; |
| const WebRequestAddResponseHeaderAction* casted_other = |
| static_cast<const WebRequestAddResponseHeaderAction*>(other); |
| return name_ == casted_other->name_ && value_ == casted_other->value_; |
| } |
| |
| std::string WebRequestAddResponseHeaderAction::GetName() const { |
| return keys::kAddResponseHeaderType; |
| } |
| |
| base::Optional<EventResponseDelta> |
| WebRequestAddResponseHeaderAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| const net::HttpResponseHeaders* headers = |
| request_data.original_response_headers; |
| if (!headers) |
| return base::nullopt; |
| |
| // Don't generate the header if it exists already. |
| if (headers->HasHeaderValue(name_, value_)) |
| return base::nullopt; |
| |
| EventResponseDelta result(extension_id, extension_install_time); |
| result.added_response_headers.push_back(make_pair(name_, value_)); |
| return result; |
| } |
| |
| // |
| // WebRequestRemoveResponseHeaderAction |
| // |
| |
| WebRequestRemoveResponseHeaderAction::WebRequestRemoveResponseHeaderAction( |
| const std::string& name, |
| const std::string& value, |
| bool has_value) |
| : WebRequestAction(ON_HEADERS_RECEIVED, |
| ACTION_REMOVE_RESPONSE_HEADER, |
| std::numeric_limits<int>::min(), |
| STRATEGY_DEFAULT), |
| name_(name), |
| value_(value), |
| has_value_(has_value) {} |
| |
| WebRequestRemoveResponseHeaderAction::~WebRequestRemoveResponseHeaderAction() {} |
| |
| bool WebRequestRemoveResponseHeaderAction::Equals( |
| const WebRequestAction* other) const { |
| if (!WebRequestAction::Equals(other)) |
| return false; |
| const WebRequestRemoveResponseHeaderAction* casted_other = |
| static_cast<const WebRequestRemoveResponseHeaderAction*>(other); |
| return name_ == casted_other->name_ && value_ == casted_other->value_ && |
| has_value_ == casted_other->has_value_; |
| } |
| |
| std::string WebRequestRemoveResponseHeaderAction::GetName() const { |
| return keys::kRemoveResponseHeaderType; |
| } |
| |
| base::Optional<EventResponseDelta> |
| WebRequestRemoveResponseHeaderAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| const net::HttpResponseHeaders* headers = |
| request_data.original_response_headers; |
| if (!headers) |
| return base::nullopt; |
| |
| EventResponseDelta result(extension_id, extension_install_time); |
| size_t iter = 0; |
| std::string current_value; |
| while (headers->EnumerateHeader(&iter, name_, ¤t_value)) { |
| if (has_value_ && !base::EqualsCaseInsensitiveASCII(current_value, value_)) |
| continue; |
| result.deleted_response_headers.push_back(make_pair(name_, current_value)); |
| } |
| return result; |
| } |
| |
| // |
| // WebRequestIgnoreRulesAction |
| // |
| |
| WebRequestIgnoreRulesAction::WebRequestIgnoreRulesAction( |
| int minimum_priority, |
| const std::string& ignore_tag) |
| : WebRequestAction(ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS | |
| ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED, |
| ACTION_IGNORE_RULES, |
| minimum_priority, |
| STRATEGY_NONE), |
| ignore_tag_(ignore_tag) {} |
| |
| WebRequestIgnoreRulesAction::~WebRequestIgnoreRulesAction() {} |
| |
| bool WebRequestIgnoreRulesAction::Equals(const WebRequestAction* other) const { |
| if (!WebRequestAction::Equals(other)) |
| return false; |
| const WebRequestIgnoreRulesAction* casted_other = |
| static_cast<const WebRequestIgnoreRulesAction*>(other); |
| return minimum_priority() == casted_other->minimum_priority() && |
| ignore_tag_ == casted_other->ignore_tag_; |
| } |
| |
| std::string WebRequestIgnoreRulesAction::GetName() const { |
| return keys::kIgnoreRulesType; |
| } |
| |
| base::Optional<EventResponseDelta> WebRequestIgnoreRulesAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| return base::nullopt; |
| } |
| |
| // |
| // WebRequestRequestCookieAction |
| // |
| |
| WebRequestRequestCookieAction::WebRequestRequestCookieAction( |
| RequestCookieModification request_cookie_modification) |
| : WebRequestAction(ON_BEFORE_SEND_HEADERS, |
| ACTION_MODIFY_REQUEST_COOKIE, |
| std::numeric_limits<int>::min(), |
| STRATEGY_DEFAULT), |
| request_cookie_modification_(std::move(request_cookie_modification)) {} |
| |
| WebRequestRequestCookieAction::~WebRequestRequestCookieAction() {} |
| |
| bool WebRequestRequestCookieAction::Equals( |
| const WebRequestAction* other) const { |
| if (!WebRequestAction::Equals(other)) |
| return false; |
| const WebRequestRequestCookieAction* casted_other = |
| static_cast<const WebRequestRequestCookieAction*>(other); |
| return request_cookie_modification_ == |
| casted_other->request_cookie_modification_; |
| } |
| |
| std::string WebRequestRequestCookieAction::GetName() const { |
| switch (request_cookie_modification_.type) { |
| case helpers::ADD: |
| return keys::kAddRequestCookieType; |
| case helpers::EDIT: |
| return keys::kEditRequestCookieType; |
| case helpers::REMOVE: |
| return keys::kRemoveRequestCookieType; |
| } |
| NOTREACHED(); |
| return ""; |
| } |
| |
| base::Optional<EventResponseDelta> WebRequestRequestCookieAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| EventResponseDelta result(extension_id, extension_install_time); |
| result.request_cookie_modifications.push_back( |
| request_cookie_modification_.Clone()); |
| return result; |
| } |
| |
| // |
| // WebRequestResponseCookieAction |
| // |
| |
| WebRequestResponseCookieAction::WebRequestResponseCookieAction( |
| ResponseCookieModification response_cookie_modification) |
| : WebRequestAction(ON_HEADERS_RECEIVED, |
| ACTION_MODIFY_RESPONSE_COOKIE, |
| std::numeric_limits<int>::min(), |
| STRATEGY_DEFAULT), |
| response_cookie_modification_(std::move(response_cookie_modification)) {} |
| |
| WebRequestResponseCookieAction::~WebRequestResponseCookieAction() {} |
| |
| bool WebRequestResponseCookieAction::Equals( |
| const WebRequestAction* other) const { |
| if (!WebRequestAction::Equals(other)) |
| return false; |
| const WebRequestResponseCookieAction* casted_other = |
| static_cast<const WebRequestResponseCookieAction*>(other); |
| return response_cookie_modification_ == |
| casted_other->response_cookie_modification_; |
| } |
| |
| std::string WebRequestResponseCookieAction::GetName() const { |
| switch (response_cookie_modification_.type) { |
| case helpers::ADD: |
| return keys::kAddResponseCookieType; |
| case helpers::EDIT: |
| return keys::kEditResponseCookieType; |
| case helpers::REMOVE: |
| return keys::kRemoveResponseCookieType; |
| } |
| NOTREACHED(); |
| return ""; |
| } |
| |
| base::Optional<EventResponseDelta> WebRequestResponseCookieAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| EventResponseDelta result(extension_id, extension_install_time); |
| result.response_cookie_modifications.push_back( |
| response_cookie_modification_.Clone()); |
| return result; |
| } |
| |
| // |
| // WebRequestSendMessageToExtensionAction |
| // |
| |
| WebRequestSendMessageToExtensionAction::WebRequestSendMessageToExtensionAction( |
| const std::string& message) |
| : WebRequestAction(ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS | |
| ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED, |
| ACTION_SEND_MESSAGE_TO_EXTENSION, |
| std::numeric_limits<int>::min(), |
| STRATEGY_HOST), |
| message_(message) {} |
| |
| WebRequestSendMessageToExtensionAction:: |
| ~WebRequestSendMessageToExtensionAction() {} |
| |
| bool WebRequestSendMessageToExtensionAction::Equals( |
| const WebRequestAction* other) const { |
| if (!WebRequestAction::Equals(other)) |
| return false; |
| const WebRequestSendMessageToExtensionAction* casted_other = |
| static_cast<const WebRequestSendMessageToExtensionAction*>(other); |
| return message_ == casted_other->message_; |
| } |
| |
| std::string WebRequestSendMessageToExtensionAction::GetName() const { |
| return keys::kSendMessageToExtensionType; |
| } |
| |
| base::Optional<EventResponseDelta> |
| WebRequestSendMessageToExtensionAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| EventResponseDelta result(extension_id, extension_install_time); |
| result.messages_to_extension.insert(message_); |
| return result; |
| } |
| |
| } // namespace extensions |