| // 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 "chrome/browser/extensions/api/declarative_webrequest/webrequest_action.h" |
| |
| #include <limits> |
| |
| #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 "chrome/browser/extensions/api/declarative/deduping_factory.h" |
| #include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h" |
| #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h" |
| #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h" |
| #include "chrome/browser/extensions/api/web_request/web_request_api_constants.h" |
| #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h" |
| #include "chrome/browser/extensions/api/web_request/web_request_permissions.h" |
| #include "chrome/browser/guest_view/web_view/web_view_renderer_state.h" |
| #include "content/public/browser/resource_request_info.h" |
| #include "content/public/common/url_constants.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/url_request/url_request.h" |
| #include "third_party/re2/re2/re2.h" |
| |
| using content::ResourceRequestInfo; |
| |
| 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>(NULL); \ |
| } \ |
| } while (0) |
| |
| scoped_ptr<helpers::RequestCookie> ParseRequestCookie( |
| const base::DictionaryValue* dict) { |
| scoped_ptr<helpers::RequestCookie> result(new helpers::RequestCookie); |
| std::string tmp; |
| if (dict->GetString(keys::kNameKey, &tmp)) |
| result->name.reset(new std::string(tmp)); |
| if (dict->GetString(keys::kValueKey, &tmp)) |
| result->value.reset(new std::string(tmp)); |
| return result.Pass(); |
| } |
| |
| 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.reset(new std::string(string_tmp)); |
| if (dict->GetString(keys::kValueKey, &string_tmp)) |
| cookie->value.reset(new std::string(string_tmp)); |
| if (dict->GetString(keys::kExpiresKey, &string_tmp)) |
| cookie->expires.reset(new std::string(string_tmp)); |
| if (dict->GetInteger(keys::kMaxAgeKey, &int_tmp)) |
| cookie->max_age.reset(new int(int_tmp)); |
| if (dict->GetString(keys::kDomainKey, &string_tmp)) |
| cookie->domain.reset(new std::string(string_tmp)); |
| if (dict->GetString(keys::kPathKey, &string_tmp)) |
| cookie->path.reset(new std::string(string_tmp)); |
| if (dict->GetBoolean(keys::kSecureKey, &bool_tmp)) |
| cookie->secure.reset(new bool(bool_tmp)); |
| if (dict->GetBoolean(keys::kHttpOnlyKey, &bool_tmp)) |
| cookie->http_only.reset(new bool(bool_tmp)); |
| } |
| |
| scoped_ptr<helpers::ResponseCookie> ParseResponseCookie( |
| const base::DictionaryValue* dict) { |
| scoped_ptr<helpers::ResponseCookie> result(new helpers::ResponseCookie); |
| ParseResponseCookieImpl(dict, result.get()); |
| return result.Pass(); |
| } |
| |
| scoped_ptr<helpers::FilterResponseCookie> ParseFilterResponseCookie( |
| const base::DictionaryValue* dict) { |
| scoped_ptr<helpers::FilterResponseCookie> result( |
| new helpers::FilterResponseCookie); |
| ParseResponseCookieImpl(dict, result.get()); |
| |
| int int_tmp = 0; |
| bool bool_tmp = false; |
| if (dict->GetInteger(keys::kAgeUpperBoundKey, &int_tmp)) |
| result->age_upper_bound.reset(new int(int_tmp)); |
| if (dict->GetInteger(keys::kAgeLowerBoundKey, &int_tmp)) |
| result->age_lower_bound.reset(new int(int_tmp)); |
| if (dict->GetBoolean(keys::kSessionCookieKey, &bool_tmp)) |
| result->session_cookie.reset(new bool(bool_tmp)); |
| return result.Pass(); |
| } |
| |
| // 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 scoped_refptr<const WebRequestAction>( |
| new 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); |
| scoped_ptr<RE2> from_pattern(new RE2(from, options)); |
| |
| if (!from_pattern->ok()) { |
| *error = "Invalid pattern '" + from + "' -> '" + to + "'"; |
| return scoped_refptr<const WebRequestAction>(NULL); |
| } |
| return scoped_refptr<const WebRequestAction>( |
| new WebRequestRedirectByRegExAction(from_pattern.Pass(), 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 (!helpers::IsValidHeaderName(name)) { |
| *error = extension_web_request_api_constants::kInvalidHeaderName; |
| return scoped_refptr<const WebRequestAction>(NULL); |
| } |
| if (!helpers::IsValidHeaderValue(value)) { |
| *error = ErrorUtils::FormatErrorMessage( |
| extension_web_request_api_constants::kInvalidHeaderValue, name); |
| return scoped_refptr<const WebRequestAction>(NULL); |
| } |
| return scoped_refptr<const WebRequestAction>( |
| new 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 (!helpers::IsValidHeaderName(name)) { |
| *error = extension_web_request_api_constants::kInvalidHeaderName; |
| return scoped_refptr<const WebRequestAction>(NULL); |
| } |
| return scoped_refptr<const WebRequestAction>( |
| new 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 (!helpers::IsValidHeaderName(name)) { |
| *error = extension_web_request_api_constants::kInvalidHeaderName; |
| return scoped_refptr<const WebRequestAction>(NULL); |
| } |
| if (!helpers::IsValidHeaderValue(value)) { |
| *error = ErrorUtils::FormatErrorMessage( |
| extension_web_request_api_constants::kInvalidHeaderValue, name); |
| return scoped_refptr<const WebRequestAction>(NULL); |
| } |
| return scoped_refptr<const WebRequestAction>( |
| new 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 (!helpers::IsValidHeaderName(name)) { |
| *error = extension_web_request_api_constants::kInvalidHeaderName; |
| return scoped_refptr<const WebRequestAction>(NULL); |
| } |
| if (has_value && !helpers::IsValidHeaderValue(value)) { |
| *error = ErrorUtils::FormatErrorMessage( |
| extension_web_request_api_constants::kInvalidHeaderValue, name); |
| return scoped_refptr<const WebRequestAction>(NULL); |
| } |
| return scoped_refptr<const WebRequestAction>( |
| new 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>(NULL); |
| } |
| return scoped_refptr<const WebRequestAction>( |
| new 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)); |
| |
| linked_ptr<RequestCookieModification> modification( |
| new RequestCookieModification); |
| |
| // 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 scoped_refptr<const WebRequestAction>( |
| new WebRequestRequestCookieAction(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)); |
| |
| linked_ptr<ResponseCookieModification> modification( |
| new ResponseCookieModification); |
| |
| // 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 scoped_refptr<const WebRequestAction>( |
| new WebRequestResponseCookieAction(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 scoped_refptr<const WebRequestAction>( |
| new 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(const InfoMap* extension_info_map, |
| const std::string& extension_id, |
| const net::URLRequest* request, |
| bool crosses_incognito) const { |
| 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; |
| |
| const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); |
| int process_id = info ? info->GetChildID() : 0; |
| |
| // The embedder can always access all hosts from within a <webview>. |
| // The same is not true of extensions. |
| if (WebViewRendererState::GetInstance()->IsGuest(process_id)) |
| 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; |
| break; |
| } |
| return WebRequestPermissions::CanExtensionAccessURL( |
| extension_info_map, extension_id, request->url(), crosses_incognito, |
| permission_check); |
| } |
| |
| // static |
| scoped_refptr<const WebRequestAction> WebRequestAction::Create( |
| 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_info_map, extension_id, |
| apply_info->request_data.request, |
| apply_info->crosses_incognito)) |
| return; |
| if (stages() & apply_info->request_data.stage) { |
| LinkedPtrEventResponseDelta delta = CreateDelta( |
| apply_info->request_data, extension_id, extension_install_time); |
| if (delta.get()) |
| apply_info->deltas->push_back(delta); |
| 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; |
| } |
| |
| LinkedPtrEventResponseDelta WebRequestCancelAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| LinkedPtrEventResponseDelta result( |
| new helpers::EventResponseDelta(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; |
| } |
| |
| LinkedPtrEventResponseDelta 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 LinkedPtrEventResponseDelta(NULL); |
| LinkedPtrEventResponseDelta result( |
| new helpers::EventResponseDelta(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; |
| } |
| |
| LinkedPtrEventResponseDelta |
| WebRequestRedirectToTransparentImageAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| LinkedPtrEventResponseDelta result( |
| new helpers::EventResponseDelta(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; |
| } |
| |
| LinkedPtrEventResponseDelta |
| WebRequestRedirectToEmptyDocumentAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| LinkedPtrEventResponseDelta result( |
| new helpers::EventResponseDelta(extension_id, extension_install_time)); |
| result->new_url = GURL(kEmptyDocumentUrl); |
| return result; |
| } |
| |
| // |
| // WebRequestRedirectByRegExAction |
| // |
| |
| WebRequestRedirectByRegExAction::WebRequestRedirectByRegExAction( |
| scoped_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_(from_pattern.Pass()), |
| 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; |
| } |
| |
| LinkedPtrEventResponseDelta 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 LinkedPtrEventResponseDelta(NULL); |
| } |
| |
| LinkedPtrEventResponseDelta result( |
| new extension_web_request_api_helpers::EventResponseDelta( |
| 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; |
| } |
| |
| |
| LinkedPtrEventResponseDelta |
| WebRequestSetRequestHeaderAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| LinkedPtrEventResponseDelta result( |
| new helpers::EventResponseDelta(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; |
| } |
| |
| LinkedPtrEventResponseDelta |
| WebRequestRemoveRequestHeaderAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| LinkedPtrEventResponseDelta result( |
| new helpers::EventResponseDelta(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; |
| } |
| |
| LinkedPtrEventResponseDelta |
| 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 LinkedPtrEventResponseDelta(NULL); |
| |
| // Don't generate the header if it exists already. |
| if (headers->HasHeaderValue(name_, value_)) |
| return LinkedPtrEventResponseDelta(NULL); |
| |
| LinkedPtrEventResponseDelta result( |
| new helpers::EventResponseDelta(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; |
| } |
| |
| LinkedPtrEventResponseDelta |
| 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 LinkedPtrEventResponseDelta(NULL); |
| |
| LinkedPtrEventResponseDelta result( |
| new helpers::EventResponseDelta(extension_id, extension_install_time)); |
| void* iter = NULL; |
| std::string current_value; |
| while (headers->EnumerateHeader(&iter, name_, ¤t_value)) { |
| if (has_value_ && |
| (current_value.size() != value_.size() || |
| !std::equal(current_value.begin(), current_value.end(), |
| value_.begin(), |
| base::CaseInsensitiveCompare<char>()))) { |
| 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; |
| } |
| |
| LinkedPtrEventResponseDelta WebRequestIgnoreRulesAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| return LinkedPtrEventResponseDelta(NULL); |
| } |
| |
| // |
| // WebRequestRequestCookieAction |
| // |
| |
| WebRequestRequestCookieAction::WebRequestRequestCookieAction( |
| linked_ptr<RequestCookieModification> request_cookie_modification) |
| : WebRequestAction(ON_BEFORE_SEND_HEADERS, |
| ACTION_MODIFY_REQUEST_COOKIE, |
| std::numeric_limits<int>::min(), |
| STRATEGY_DEFAULT), |
| request_cookie_modification_(request_cookie_modification) { |
| CHECK(request_cookie_modification_.get()); |
| } |
| |
| 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 helpers::NullableEquals( |
| request_cookie_modification_.get(), |
| casted_other->request_cookie_modification_.get()); |
| } |
| |
| 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 ""; |
| } |
| |
| LinkedPtrEventResponseDelta WebRequestRequestCookieAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| LinkedPtrEventResponseDelta result( |
| new extension_web_request_api_helpers::EventResponseDelta( |
| extension_id, extension_install_time)); |
| result->request_cookie_modifications.push_back( |
| request_cookie_modification_); |
| return result; |
| } |
| |
| // |
| // WebRequestResponseCookieAction |
| // |
| |
| WebRequestResponseCookieAction::WebRequestResponseCookieAction( |
| linked_ptr<ResponseCookieModification> response_cookie_modification) |
| : WebRequestAction(ON_HEADERS_RECEIVED, |
| ACTION_MODIFY_RESPONSE_COOKIE, |
| std::numeric_limits<int>::min(), |
| STRATEGY_DEFAULT), |
| response_cookie_modification_(response_cookie_modification) { |
| CHECK(response_cookie_modification_.get()); |
| } |
| |
| 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 helpers::NullableEquals( |
| response_cookie_modification_.get(), |
| casted_other->response_cookie_modification_.get()); |
| } |
| |
| 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 ""; |
| } |
| |
| LinkedPtrEventResponseDelta WebRequestResponseCookieAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| LinkedPtrEventResponseDelta result( |
| new extension_web_request_api_helpers::EventResponseDelta( |
| extension_id, extension_install_time)); |
| result->response_cookie_modifications.push_back( |
| response_cookie_modification_); |
| 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; |
| } |
| |
| LinkedPtrEventResponseDelta WebRequestSendMessageToExtensionAction::CreateDelta( |
| const WebRequestData& request_data, |
| const std::string& extension_id, |
| const base::Time& extension_install_time) const { |
| CHECK(request_data.stage & stages()); |
| LinkedPtrEventResponseDelta result( |
| new extension_web_request_api_helpers::EventResponseDelta( |
| extension_id, extension_install_time)); |
| result->messages_to_extension.insert(message_); |
| return result; |
| } |
| |
| } // namespace extensions |