Commit 95b0d8a2 authored by battre@chromium.org's avatar battre@chromium.org

Support Cookie modifications in Declarative WebRequest API


BUG=112155
TEST=no
TBR=ben@chromium.org

Review URL: https://chromiumcodereview.appspot.com/10447090

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149838 0039d316-1c4b-4281-b951-d872f2087c98
parent 4b74884a
...@@ -41,6 +41,42 @@ const char kEmptyDocumentUrl[] = "data:text/html,"; ...@@ -41,6 +41,42 @@ const char kEmptyDocumentUrl[] = "data:text/html,";
} \ } \
} while (0) } while (0)
scoped_ptr<helpers::RequestCookie> ParseRequestCookie(
const 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();
}
scoped_ptr<helpers::ResponseCookie> ParseResponseCookie(
const DictionaryValue* dict) {
scoped_ptr<helpers::ResponseCookie> result(new helpers::ResponseCookie);
std::string string_tmp;
int int_tmp = 0;
bool bool_tmp = false;
if (dict->GetString(keys::kNameKey, &string_tmp))
result->name.reset(new std::string(string_tmp));
if (dict->GetString(keys::kValueKey, &string_tmp))
result->value.reset(new std::string(string_tmp));
if (dict->GetString(keys::kExpiresKey, &string_tmp))
result->expires.reset(new std::string(string_tmp));
if (dict->GetInteger(keys::kMaxAgeKey, &int_tmp))
result->max_age.reset(new int(int_tmp));
if (dict->GetString(keys::kDomainKey, &string_tmp))
result->domain.reset(new std::string(string_tmp));
if (dict->GetString(keys::kPathKey, &string_tmp))
result->path.reset(new std::string(string_tmp));
if (dict->GetBoolean(keys::kSecureKey, &bool_tmp))
result->secure.reset(new bool(bool_tmp));
if (dict->GetBoolean(keys::kHttpOnlyKey, &bool_tmp))
result->http_only.reset(new bool(bool_tmp));
return result.Pass();
}
// Helper function for WebRequestActions that can be instantiated by just // Helper function for WebRequestActions that can be instantiated by just
// calling the constructor. // calling the constructor.
template <class T> template <class T>
...@@ -145,6 +181,96 @@ scoped_ptr<WebRequestAction> CreateIgnoreRulesAction( ...@@ -145,6 +181,96 @@ scoped_ptr<WebRequestAction> CreateIgnoreRulesAction(
new WebRequestIgnoreRulesAction(minium_priority)); new WebRequestIgnoreRulesAction(minium_priority));
} }
scoped_ptr<WebRequestAction> CreateRequestCookieAction(
const base::DictionaryValue* dict,
std::string* error,
bool* bad_message) {
using extension_web_request_api_helpers::RequestCookieModification;
linked_ptr<RequestCookieModification> modification(
new RequestCookieModification);
// Get modification type.
std::string instance_type;
INPUT_FORMAT_VALIDATE(
dict->GetString(keys::kInstanceTypeKey, &instance_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 DictionaryValue* filter = NULL;
INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kFilterKey, &filter));
modification->filter = ParseRequestCookie(filter);
}
// Get new value.
if (modification->type == helpers::ADD) {
const DictionaryValue* value = NULL;
INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kCookieKey, &value));
modification->modification = ParseRequestCookie(value);
} else if (modification->type == helpers::EDIT) {
const DictionaryValue* value = NULL;
INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kModificationKey, &value));
modification->modification = ParseRequestCookie(value);
}
return scoped_ptr<WebRequestAction>(
new WebRequestRequestCookieAction(modification));
}
scoped_ptr<WebRequestAction> CreateResponseCookieAction(
const base::DictionaryValue* dict,
std::string* error,
bool* bad_message) {
using extension_web_request_api_helpers::ResponseCookieModification;
linked_ptr<ResponseCookieModification> modification(
new ResponseCookieModification);
// Get modification type.
std::string instance_type;
INPUT_FORMAT_VALIDATE(
dict->GetString(keys::kInstanceTypeKey, &instance_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 DictionaryValue* filter = NULL;
INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kFilterKey, &filter));
modification->filter = ParseResponseCookie(filter);
}
// Get new value.
if (modification->type == helpers::ADD) {
const DictionaryValue* value = NULL;
INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kCookieKey, &value));
modification->modification = ParseResponseCookie(value);
} else if (modification->type == helpers::EDIT) {
const DictionaryValue* value = NULL;
INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kModificationKey, &value));
modification->modification = ParseResponseCookie(value);
}
return scoped_ptr<WebRequestAction>(
new WebRequestResponseCookieAction(modification));
}
struct WebRequestActionFactory { struct WebRequestActionFactory {
// Factory methods for WebRequestAction instances. |dict| contains the json // Factory methods for WebRequestAction instances. |dict| contains the json
// dictionary that describes the action. |error| is used to return error // dictionary that describes the action. |error| is used to return error
...@@ -158,10 +284,18 @@ struct WebRequestActionFactory { ...@@ -158,10 +284,18 @@ struct WebRequestActionFactory {
std::map<std::string, FactoryMethod> factory_methods; std::map<std::string, FactoryMethod> factory_methods;
WebRequestActionFactory() { WebRequestActionFactory() {
factory_methods[keys::kAddRequestCookieType] =
&CreateRequestCookieAction;
factory_methods[keys::kAddResponseCookieType] =
&CreateResponseCookieAction;
factory_methods[keys::kAddResponseHeaderType] = factory_methods[keys::kAddResponseHeaderType] =
&CreateAddResponseHeaderAction; &CreateAddResponseHeaderAction;
factory_methods[keys::kCancelRequestType] = factory_methods[keys::kCancelRequestType] =
&CallConstructorFactoryMethod<WebRequestCancelAction>; &CallConstructorFactoryMethod<WebRequestCancelAction>;
factory_methods[keys::kEditRequestCookieType] =
&CreateRequestCookieAction;
factory_methods[keys::kEditResponseCookieType] =
&CreateResponseCookieAction;
factory_methods[keys::kRedirectByRegExType] = factory_methods[keys::kRedirectByRegExType] =
&CreateRedirectRequestByRegExAction; &CreateRedirectRequestByRegExAction;
factory_methods[keys::kRedirectRequestType] = factory_methods[keys::kRedirectRequestType] =
...@@ -171,6 +305,10 @@ struct WebRequestActionFactory { ...@@ -171,6 +305,10 @@ struct WebRequestActionFactory {
WebRequestRedirectToTransparentImageAction>; WebRequestRedirectToTransparentImageAction>;
factory_methods[keys::kRedirectToEmptyDocumentType] = factory_methods[keys::kRedirectToEmptyDocumentType] =
&CallConstructorFactoryMethod<WebRequestRedirectToEmptyDocumentAction>; &CallConstructorFactoryMethod<WebRequestRedirectToEmptyDocumentAction>;
factory_methods[keys::kRemoveRequestCookieType] =
&CreateRequestCookieAction;
factory_methods[keys::kRemoveResponseCookieType] =
&CreateResponseCookieAction;
factory_methods[keys::kSetRequestHeaderType] = factory_methods[keys::kSetRequestHeaderType] =
&CreateSetRequestHeaderAction; &CreateSetRequestHeaderAction;
factory_methods[keys::kRemoveRequestHeaderType] = factory_methods[keys::kRemoveRequestHeaderType] =
...@@ -762,4 +900,74 @@ LinkedPtrEventResponseDelta WebRequestIgnoreRulesAction::CreateDelta( ...@@ -762,4 +900,74 @@ LinkedPtrEventResponseDelta WebRequestIgnoreRulesAction::CreateDelta(
return LinkedPtrEventResponseDelta(NULL); return LinkedPtrEventResponseDelta(NULL);
} }
//
// WebRequestRequestCookieAction
//
WebRequestRequestCookieAction::WebRequestRequestCookieAction(
linked_ptr<RequestCookieModification> request_cookie_modification)
: request_cookie_modification_(request_cookie_modification) {
CHECK(request_cookie_modification_.get());
}
WebRequestRequestCookieAction::~WebRequestRequestCookieAction() {}
int WebRequestRequestCookieAction::GetStages() const {
return ON_BEFORE_SEND_HEADERS;
}
WebRequestAction::Type WebRequestRequestCookieAction::GetType() const {
return WebRequestAction::ACTION_MODIFY_REQUEST_COOKIE;
}
LinkedPtrEventResponseDelta WebRequestRequestCookieAction::CreateDelta(
net::URLRequest* request,
RequestStages request_stage,
const WebRequestRule::OptionalRequestData& optional_request_data,
const std::string& extension_id,
const base::Time& extension_install_time) const {
CHECK(request_stage & GetStages());
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)
: response_cookie_modification_(response_cookie_modification) {
CHECK(response_cookie_modification_.get());
}
WebRequestResponseCookieAction::~WebRequestResponseCookieAction() {}
int WebRequestResponseCookieAction::GetStages() const {
return ON_HEADERS_RECEIVED;
}
WebRequestAction::Type WebRequestResponseCookieAction::GetType() const {
return WebRequestAction::ACTION_MODIFY_RESPONSE_COOKIE;
}
LinkedPtrEventResponseDelta WebRequestResponseCookieAction::CreateDelta(
net::URLRequest* request,
RequestStages request_stage,
const WebRequestRule::OptionalRequestData& optional_request_data,
const std::string& extension_id,
const base::Time& extension_install_time) const {
CHECK(request_stage & GetStages());
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;
}
} // namespace extensions } // namespace extensions
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/memory/linked_ptr.h" #include "base/memory/linked_ptr.h"
#include "chrome/browser/extensions/api/declarative_webrequest/request_stages.h" #include "chrome/browser/extensions/api/declarative_webrequest/request_stages.h"
#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rule.h" #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rule.h"
#include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
#include "chrome/common/extensions/api/events.h" #include "chrome/common/extensions/api/events.h"
#include "googleurl/src/gurl.h" #include "googleurl/src/gurl.h"
#include "unicode/regex.h" #include "unicode/regex.h"
...@@ -43,7 +44,6 @@ typedef linked_ptr<extension_web_request_api_helpers::EventResponseDelta> ...@@ -43,7 +44,6 @@ typedef linked_ptr<extension_web_request_api_helpers::EventResponseDelta>
LinkedPtrEventResponseDelta; LinkedPtrEventResponseDelta;
// Base class for all WebRequestActions of the declarative Web Request API. // Base class for all WebRequestActions of the declarative Web Request API.
//
class WebRequestAction { class WebRequestAction {
public: public:
// Type identifiers for concrete WebRequestActions. // Type identifiers for concrete WebRequestActions.
...@@ -58,6 +58,8 @@ class WebRequestAction { ...@@ -58,6 +58,8 @@ class WebRequestAction {
ACTION_ADD_RESPONSE_HEADER, ACTION_ADD_RESPONSE_HEADER,
ACTION_REMOVE_RESPONSE_HEADER, ACTION_REMOVE_RESPONSE_HEADER,
ACTION_IGNORE_RULES, ACTION_IGNORE_RULES,
ACTION_MODIFY_REQUEST_COOKIE,
ACTION_MODIFY_RESPONSE_COOKIE,
}; };
WebRequestAction(); WebRequestAction();
...@@ -389,8 +391,55 @@ class WebRequestIgnoreRulesAction : public WebRequestAction { ...@@ -389,8 +391,55 @@ class WebRequestIgnoreRulesAction : public WebRequestAction {
DISALLOW_COPY_AND_ASSIGN(WebRequestIgnoreRulesAction); DISALLOW_COPY_AND_ASSIGN(WebRequestIgnoreRulesAction);
}; };
// TODO(battre) Implement further actions: // Action that instructs to modify (add, edit, remove) a request cookie.
// Redirect by RegEx, Cookie manipulations, ... class WebRequestRequestCookieAction : public WebRequestAction {
public:
typedef extension_web_request_api_helpers::RequestCookieModification
RequestCookieModification;
explicit WebRequestRequestCookieAction(
linked_ptr<RequestCookieModification> request_cookie_modification);
virtual ~WebRequestRequestCookieAction();
// Implementation of WebRequestAction:
virtual int GetStages() const OVERRIDE;
virtual Type GetType() const OVERRIDE;
virtual LinkedPtrEventResponseDelta CreateDelta(
net::URLRequest* request,
RequestStages request_stage,
const WebRequestRule::OptionalRequestData& optional_request_data,
const std::string& extension_id,
const base::Time& extension_install_time) const OVERRIDE;
private:
linked_ptr<RequestCookieModification> request_cookie_modification_;
DISALLOW_COPY_AND_ASSIGN(WebRequestRequestCookieAction);
};
// Action that instructs to modify (add, edit, remove) a response cookie.
class WebRequestResponseCookieAction : public WebRequestAction {
public:
typedef extension_web_request_api_helpers::ResponseCookieModification
ResponseCookieModification;
explicit WebRequestResponseCookieAction(
linked_ptr<ResponseCookieModification> response_cookie_modification);
virtual ~WebRequestResponseCookieAction();
// Implementation of WebRequestAction:
virtual int GetStages() const OVERRIDE;
virtual Type GetType() const OVERRIDE;
virtual LinkedPtrEventResponseDelta CreateDelta(
net::URLRequest* request,
RequestStages request_stage,
const WebRequestRule::OptionalRequestData& optional_request_data,
const std::string& extension_id,
const base::Time& extension_install_time) const OVERRIDE;
private:
linked_ptr<ResponseCookieModification> response_cookie_modification_;
DISALLOW_COPY_AND_ASSIGN(WebRequestResponseCookieAction);
};
} // namespace extensions } // namespace extensions
......
...@@ -11,20 +11,34 @@ namespace declarative_webrequest_constants { ...@@ -11,20 +11,34 @@ namespace declarative_webrequest_constants {
const char kOnRequest[] = "declarativeWebRequest.onRequest"; const char kOnRequest[] = "declarativeWebRequest.onRequest";
// Keys of dictionaries. // Keys of dictionaries.
const char kCookieKey[] = "cookie";
const char kDirectionKey[] = "direction";
const char kDomainKey[] = "domain";
const char kExpiresKey[] = "expires";
const char kFilterKey[] ="filter";
const char kFromKey[] = "from"; const char kFromKey[] = "from";
const char kHttpOnlyKey[] = "httpOnly";
const char kInstanceTypeKey[] = "instanceType"; const char kInstanceTypeKey[] = "instanceType";
const char kLowerPriorityThanKey[] = "lowerPriorityThan"; const char kLowerPriorityThanKey[] = "lowerPriorityThan";
const char kMaxAgeKey[] = "maxAge";
const char kModificationKey[] = "modification";
const char kNameKey[] = "name"; const char kNameKey[] = "name";
const char kPathKey[] = "path";
const char kRedirectUrlKey[] = "redirectUrl"; const char kRedirectUrlKey[] = "redirectUrl";
const char kResourceTypeKey[] = "resourceType"; const char kResourceTypeKey[] = "resourceType";
const char kSecureKey[] = "secure";
const char kToKey[] = "to"; const char kToKey[] = "to";
const char kUrlKey[] = "url"; const char kUrlKey[] = "url";
const char kValueKey[] = "value"; const char kValueKey[] = "value";
// Values of dictionaries, in particular instance types // Values of dictionaries, in particular instance types
const char kAddResponseHeaderType[] = const char kAddRequestCookieType[] = "declarativeWebRequest.AddRequestCookie";
"declarativeWebRequest.AddResponseHeader"; const char kAddResponseCookieType[] = "declarativeWebRequest.AddResponseCookie";
const char kAddResponseHeaderType[] = "declarativeWebRequest.AddResponseHeader";
const char kCancelRequestType[] = "declarativeWebRequest.CancelRequest"; const char kCancelRequestType[] = "declarativeWebRequest.CancelRequest";
const char kEditRequestCookieType[] = "declarativeWebRequest.EditRequestCookie";
const char kEditResponseCookieType[] =
"declarativeWebRequest.EditResponseCookie";
const char kIgnoreRulesType[] = "declarativeWebRequest.IgnoreRules"; const char kIgnoreRulesType[] = "declarativeWebRequest.IgnoreRules";
const char kRedirectRequestType[] = "declarativeWebRequest.RedirectRequest"; const char kRedirectRequestType[] = "declarativeWebRequest.RedirectRequest";
const char kRedirectByRegExType[] = const char kRedirectByRegExType[] =
...@@ -33,11 +47,16 @@ const char kRedirectToEmptyDocumentType[] = ...@@ -33,11 +47,16 @@ const char kRedirectToEmptyDocumentType[] =
"declarativeWebRequest.RedirectToEmptyDocument"; "declarativeWebRequest.RedirectToEmptyDocument";
const char kRedirectToTransparentImageType[] = const char kRedirectToTransparentImageType[] =
"declarativeWebRequest.RedirectToTransparentImage"; "declarativeWebRequest.RedirectToTransparentImage";
const char kRemoveRequestCookieType[] =
"declarativeWebRequest.RemoveRequestCookie";
const char kRemoveRequestHeaderType[] = const char kRemoveRequestHeaderType[] =
"declarativeWebRequest.RemoveRequestHeader"; "declarativeWebRequest.RemoveRequestHeader";
const char kRemoveResponseCookieType[] =
"declarativeWebRequest.RemoveResponseCookie";
const char kRemoveResponseHeaderType[] = const char kRemoveResponseHeaderType[] =
"declarativeWebRequest.RemoveResponseHeader"; "declarativeWebRequest.RemoveResponseHeader";
const char kRequestMatcherType[] = "declarativeWebRequest.RequestMatcher"; const char kRequestMatcherType[] = "declarativeWebRequest.RequestMatcher";
const char kSetRequestHeaderType[] = "declarativeWebRequest.SetRequestHeader"; const char kSetRequestHeaderType[] = "declarativeWebRequest.SetRequestHeader";
} // namespace declarative_webrequest_constants } // namespace declarative_webrequest_constants
} // namespace extensions } // namespace extensions
...@@ -14,25 +14,41 @@ namespace declarative_webrequest_constants { ...@@ -14,25 +14,41 @@ namespace declarative_webrequest_constants {
extern const char kOnRequest[]; extern const char kOnRequest[];
// Keys of dictionaries. // Keys of dictionaries.
extern const char kCookieKey[];
extern const char kDirectionKey[];
extern const char kDomainKey[];
extern const char kExpiresKey[];
extern const char kFilterKey[];
extern const char kFromKey[]; extern const char kFromKey[];
extern const char kHttpOnlyKey[];
extern const char kInstanceTypeKey[]; extern const char kInstanceTypeKey[];
extern const char kLowerPriorityThanKey[]; extern const char kLowerPriorityThanKey[];
extern const char kMaxAgeKey[];
extern const char kModificationKey[];
extern const char kNameKey[]; extern const char kNameKey[];
extern const char kPathKey[];
extern const char kRedirectUrlKey[]; extern const char kRedirectUrlKey[];
extern const char kResourceTypeKey[]; extern const char kResourceTypeKey[];
extern const char kSecureKey[];
extern const char kToKey[]; extern const char kToKey[];
extern const char kUrlKey[]; extern const char kUrlKey[];
extern const char kValueKey[]; extern const char kValueKey[];
// Values of dictionaries, in particular instance types // Values of dictionaries, in particular instance types
extern const char kAddRequestCookieType[];
extern const char kAddResponseCookieType[];
extern const char kAddResponseHeaderType[]; extern const char kAddResponseHeaderType[];
extern const char kCancelRequestType[]; extern const char kCancelRequestType[];
extern const char kEditRequestCookieType[];
extern const char kEditResponseCookieType[];
extern const char kIgnoreRulesType[]; extern const char kIgnoreRulesType[];
extern const char kRedirectByRegExType[]; extern const char kRedirectByRegExType[];
extern const char kRedirectRequestType[]; extern const char kRedirectRequestType[];
extern const char kRedirectToEmptyDocumentType[]; extern const char kRedirectToEmptyDocumentType[];
extern const char kRedirectToTransparentImageType[]; extern const char kRedirectToTransparentImageType[];
extern const char kRemoveRequestCookieType[];
extern const char kRemoveRequestHeaderType[]; extern const char kRemoveRequestHeaderType[];
extern const char kRemoveResponseCookieType[];
extern const char kRemoveResponseHeaderType[]; extern const char kRemoveResponseHeaderType[];
extern const char kRequestMatcherType[]; extern const char kRequestMatcherType[];
extern const char kSetRequestHeaderType[]; extern const char kSetRequestHeaderType[];
......
...@@ -5,12 +5,14 @@ ...@@ -5,12 +5,14 @@
#include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h" #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h" #include "base/string_util.h"
#include "base/stringprintf.h" #include "base/stringprintf.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/extensions/api/web_request/web_request_api.h" #include "chrome/browser/extensions/api/web_request/web_request_api.h"
#include "chrome/common/url_constants.h" #include "chrome/common/url_constants.h"
#include "net/base/net_log.h" #include "net/base/net_log.h"
#include "net/cookies/parsed_cookie.h"
#include "net/http/http_util.h" #include "net/http/http_util.h"
#include "net/url_request/url_request.h" #include "net/url_request/url_request.h"
...@@ -18,6 +20,11 @@ namespace extension_web_request_api_helpers { ...@@ -18,6 +20,11 @@ namespace extension_web_request_api_helpers {
namespace { namespace {
// A ParsedRequestCookie consists of the key and value of the cookie.
typedef std::pair<base::StringPiece, base::StringPiece> ParsedRequestCookie;
typedef std::vector<ParsedRequestCookie> ParsedRequestCookies;
typedef std::vector<linked_ptr<net::ParsedCookie> > ParsedResponseCookies;
static const char* kResourceTypeStrings[] = { static const char* kResourceTypeStrings[] = {
"main_frame", "main_frame",
"sub_frame", "sub_frame",
...@@ -53,6 +60,17 @@ COMPILE_ASSERT( ...@@ -53,6 +60,17 @@ COMPILE_ASSERT(
} // namespace } // namespace
RequestCookie::RequestCookie() {}
RequestCookie::~RequestCookie() {}
ResponseCookie::ResponseCookie() {}
ResponseCookie::~ResponseCookie() {}
RequestCookieModification::RequestCookieModification() {}
RequestCookieModification::~RequestCookieModification() {}
ResponseCookieModification::ResponseCookieModification() : type(ADD) {}
ResponseCookieModification::~ResponseCookieModification() {}
EventResponseDelta::EventResponseDelta( EventResponseDelta::EventResponseDelta(
const std::string& extension_id, const base::Time& extension_install_time) const std::string& extension_id, const base::Time& extension_install_time)
...@@ -325,6 +343,219 @@ void MergeOnBeforeRequestResponses( ...@@ -325,6 +343,219 @@ void MergeOnBeforeRequestResponses(
deltas, new_url, conflicting_extensions, net_log, false); deltas, new_url, conflicting_extensions, net_log, false);
} }
// Assumes that |header_value| is the cookie header value of a HTTP Request
// following the cookie-string schema of RFC 6265, section 4.2.1, and returns
// cookie name/value pairs. If cookie values are presented in double quotes,
// these will appear in |parsed| as well. We can assume that the cookie header
// is written by Chromium and therefore, well-formed.
static void ParseRequestCookieLine(
const std::string& header_value,
ParsedRequestCookies* parsed_cookies) {
std::string::const_iterator i = header_value.begin();
while (i != header_value.end()) {
// Here we are at the beginning of a cookie.
// Eat whitespace.
while (i != header_value.end() && *i == ' ') ++i;
if (i == header_value.end()) return;
// Find cookie name.
std::string::const_iterator cookie_name_beginning = i;
while (i != header_value.end() && *i != '=') ++i;
base::StringPiece cookie_name(cookie_name_beginning, i);
// Find cookie value.
base::StringPiece cookie_value;
if (i != header_value.end()) { // Cookies may have no value.
++i; // Skip '='.
std::string::const_iterator cookie_value_beginning = i;
if (*i == '"') {
while (i != header_value.end() && *i != '"') ++i;
if (i == header_value.end()) return;
++i; // Skip '"'.
cookie_value = base::StringPiece(cookie_value_beginning, i);
// i points to character after '"', potentially a ';'
} else {
while (i != header_value.end() && *i != ';') ++i;
cookie_value = base::StringPiece(cookie_value_beginning, i);
// i points to ';' or end of string.
}
}
parsed_cookies->push_back(make_pair(cookie_name, cookie_value));
// Eat ';'
if (i != header_value.end()) ++i;
}
}
// Writes all cookies of |parsed_cookies| into a HTTP Request header value
// that belongs to the "Cookie" header.
static std::string SerializeRequestCookieLine(
const ParsedRequestCookies& parsed_cookies) {
std::string buffer;
for (ParsedRequestCookies::const_iterator i = parsed_cookies.begin();
i != parsed_cookies.end(); ++i) {
if (!buffer.empty())
buffer += "; ";
buffer += i->first.as_string();
if (!i->second.empty())
buffer += "=" + i->second.as_string();
}
return buffer;
}
static bool DoesRequestCookieMatchFilter(
const ParsedRequestCookie& cookie,
RequestCookie* filter) {
if (!filter) return true;
if (filter->name.get() && cookie.first != *filter->name) return false;
if (filter->value.get() && cookie.second != *filter->value) return false;
return true;
}
// Applies all CookieModificationType::ADD operations for request cookies of
// |deltas| to |cookies|. Returns whether any cookie was added.
static bool MergeAddRequestCookieModifications(
const EventResponseDeltas& deltas,
ParsedRequestCookies* cookies) {
bool modified = false;
// We assume here that the deltas are sorted in decreasing extension
// precedence (i.e. decreasing extension installation time).
EventResponseDeltas::const_reverse_iterator delta;
for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
const RequestCookieModifications& modifications =
(*delta)->request_cookie_modifications;
for (RequestCookieModifications::const_iterator mod = modifications.begin();
mod != modifications.end(); ++mod) {
if ((*mod)->type != ADD || !(*mod)->modification.get())
continue;
std::string* new_name = (*mod)->modification->name.get();
std::string* new_value = (*mod)->modification->value.get();
if (!new_name || !new_value)
continue;
bool cookie_with_same_name_found = false;
for (ParsedRequestCookies::iterator cookie = cookies->begin();
cookie != cookies->end() && !cookie_with_same_name_found; ++cookie) {
if (cookie->first == *new_name) {
if (cookie->second != *new_value) {
cookie->second = *new_value;
modified = true;
}
cookie_with_same_name_found = true;
}
}
if (!cookie_with_same_name_found) {
cookies->push_back(std::make_pair(base::StringPiece(*new_name),
base::StringPiece(*new_value)));
modified = true;
}
}
}
return modified;
}
// Applies all CookieModificationType::EDIT operations for request cookies of
// |deltas| to |cookies|. Returns whether any cookie was modified.
static bool MergeEditRequestCookieModifications(
const EventResponseDeltas& deltas,
ParsedRequestCookies* cookies) {
bool modified = false;
// We assume here that the deltas are sorted in decreasing extension
// precedence (i.e. decreasing extension installation time).
EventResponseDeltas::const_reverse_iterator delta;
for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
const RequestCookieModifications& modifications =
(*delta)->request_cookie_modifications;
for (RequestCookieModifications::const_iterator mod = modifications.begin();
mod != modifications.end(); ++mod) {
if ((*mod)->type != EDIT || !(*mod)->modification.get())
continue;
std::string* new_value = (*mod)->modification->value.get();
RequestCookie* filter = (*mod)->filter.get();
for (ParsedRequestCookies::iterator cookie = cookies->begin();
cookie != cookies->end(); ++cookie) {
if (!DoesRequestCookieMatchFilter(*cookie, filter))
continue;
// If the edit operation tries to modify the cookie name, we just ignore
// this. We only modify the cookie value.
if (new_value && cookie->second != *new_value) {
cookie->second = *new_value;
modified = true;
}
}
}
}
return modified;
}
// Applies all CookieModificationType::REMOVE operations for request cookies of
// |deltas| to |cookies|. Returns whether any cookie was deleted.
static bool MergeRemoveRequestCookieModifications(
const EventResponseDeltas& deltas,
ParsedRequestCookies* cookies) {
bool modified = false;
// We assume here that the deltas are sorted in decreasing extension
// precedence (i.e. decreasing extension installation time).
EventResponseDeltas::const_reverse_iterator delta;
for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
const RequestCookieModifications& modifications =
(*delta)->request_cookie_modifications;
for (RequestCookieModifications::const_iterator mod = modifications.begin();
mod != modifications.end(); ++mod) {
if ((*mod)->type != REMOVE)
continue;
RequestCookie* filter = (*mod)->filter.get();
ParsedRequestCookies::iterator i = cookies->begin();
while (i != cookies->end()) {
if (DoesRequestCookieMatchFilter(*i, filter)) {
i = cookies->erase(i);
modified = true;
} else {
++i;
}
}
}
}
return modified;
}
void MergeCookiesInOnBeforeSendHeadersResponses(
const EventResponseDeltas& deltas,
net::HttpRequestHeaders* request_headers,
std::set<std::string>* conflicting_extensions,
const net::BoundNetLog* net_log) {
// Skip all work if there are no registered cookie modifications.
bool cookie_modifications_exist = false;
EventResponseDeltas::const_iterator delta;
for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
cookie_modifications_exist |=
!(*delta)->request_cookie_modifications.empty();
}
if (!cookie_modifications_exist)
return;
// Parse old cookie line.
std::string cookie_header;
request_headers->GetHeader(net::HttpRequestHeaders::kCookie, &cookie_header);
ParsedRequestCookies cookies;
ParseRequestCookieLine(cookie_header, &cookies);
// Modify cookies.
bool modified = false;
modified |= MergeAddRequestCookieModifications(deltas, &cookies);
modified |= MergeEditRequestCookieModifications(deltas, &cookies);
modified |= MergeRemoveRequestCookieModifications(deltas, &cookies);
// Reassemble and store new cookie line.
if (modified) {
std::string new_cookie_header = SerializeRequestCookieLine(cookies);
request_headers->SetHeader(net::HttpRequestHeaders::kCookie,
new_cookie_header);
}
}
void MergeOnBeforeSendHeadersResponses( void MergeOnBeforeSendHeadersResponses(
const EventResponseDeltas& deltas, const EventResponseDeltas& deltas,
net::HttpRequestHeaders* request_headers, net::HttpRequestHeaders* request_headers,
...@@ -418,6 +649,216 @@ void MergeOnBeforeSendHeadersResponses( ...@@ -418,6 +649,216 @@ void MergeOnBeforeSendHeadersResponses(
CreateNetLogExtensionIdCallback(delta->get())); CreateNetLogExtensionIdCallback(delta->get()));
} }
} }
MergeCookiesInOnBeforeSendHeadersResponses(deltas, request_headers,
conflicting_extensions, net_log);
}
// Retrives all cookies from |override_response_headers|.
static ParsedResponseCookies GetResponseCookies(
scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
ParsedResponseCookies result;
void* iter = NULL;
std::string value;
while (override_response_headers->EnumerateHeader(&iter, "Set-Cookie",
&value)) {
result.push_back(make_linked_ptr(new net::ParsedCookie(value)));
}
return result;
}
// Stores all |cookies| in |override_response_headers| deleting previously
// existing cookie definitions.
static void StoreResponseCookies(
const ParsedResponseCookies& cookies,
scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
override_response_headers->RemoveHeader("Set-Cookie");
for (ParsedResponseCookies::const_iterator i = cookies.begin();
i != cookies.end(); ++i) {
override_response_headers->AddHeader("Set-Cookie: " + (*i)->ToCookieLine());
}
}
// Modifies |cookie| according to |modification|. Each value that is set in
// |modification| is applied to |cookie|.
static bool ApplyResponseCookieModification(ResponseCookie* modification,
net::ParsedCookie* cookie) {
bool modified = false;
if (modification->name.get())
modified |= cookie->SetName(*modification->name);
if (modification->value.get())
modified |= cookie->SetValue(*modification->value);
if (modification->expires.get())
modified |= cookie->SetExpires(*modification->expires);
if (modification->max_age.get())
modified |= cookie->SetMaxAge(base::IntToString(*modification->max_age));
if (modification->domain.get())
modified |= cookie->SetDomain(*modification->domain);
if (modification->path.get())
modified |= cookie->SetPath(*modification->path);
if (modification->secure.get())
modified |= cookie->SetIsSecure(*modification->secure);
if (modification->http_only.get())
modified |= cookie->SetIsHttpOnly(*modification->http_only);
return modified;
}
static bool DoesResponseCookieMatchFilter(net::ParsedCookie* cookie,
ResponseCookie* filter) {
if (!cookie->IsValid()) return false;
if (!filter) return true;
if (filter->name.get() && cookie->Name() != *filter->name) return false;
if (filter->value.get() && cookie->Value() != *filter->value) return false;
if (filter->expires.get()) {
std::string actual_value = cookie->HasExpires() ? cookie->Expires() : "";
if (actual_value != *filter->expires)
return false;
}
if (filter->max_age.get()) {
std::string actual_value = cookie->HasMaxAge() ? cookie->MaxAge() : "";
if (actual_value != base::IntToString(*filter->max_age))
return false;
}
if (filter->domain.get()) {
std::string actual_value = cookie->HasDomain() ? cookie->Domain() : "";
if (actual_value != *filter->domain)
return false;
}
if (filter->path.get()) {
std::string actual_value = cookie->HasPath() ? cookie->Path() : "";
if (actual_value != *filter->path)
return false;
}
if (filter->secure.get() && cookie->IsSecure() != *filter->secure)
return false;
if (filter->http_only.get() && cookie->IsHttpOnly() != *filter->http_only)
return false;
return true;
}
// Applies all CookieModificationType::ADD operations for response cookies of
// |deltas| to |cookies|. Returns whether any cookie was added.
static bool MergeAddResponseCookieModifications(
const EventResponseDeltas& deltas,
ParsedResponseCookies* cookies) {
bool modified = false;
// We assume here that the deltas are sorted in decreasing extension
// precedence (i.e. decreasing extension installation time).
EventResponseDeltas::const_reverse_iterator delta;
for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
const ResponseCookieModifications& modifications =
(*delta)->response_cookie_modifications;
for (ResponseCookieModifications::const_iterator mod =
modifications.begin(); mod != modifications.end(); ++mod) {
if ((*mod)->type != ADD || !(*mod)->modification.get())
continue;
// Cookie names are not unique in response cookies so we always append
// and never override.
linked_ptr<net::ParsedCookie> cookie(new net::ParsedCookie(""));
ApplyResponseCookieModification((*mod)->modification.get(), cookie.get());
cookies->push_back(cookie);
modified = true;
}
}
return modified;
}
// Applies all CookieModificationType::EDIT operations for response cookies of
// |deltas| to |cookies|. Returns whether any cookie was modified.
static bool MergeEditResponseCookieModifications(
const EventResponseDeltas& deltas,
ParsedResponseCookies* cookies) {
bool modified = false;
// We assume here that the deltas are sorted in decreasing extension
// precedence (i.e. decreasing extension installation time).
EventResponseDeltas::const_reverse_iterator delta;
for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
const ResponseCookieModifications& modifications =
(*delta)->response_cookie_modifications;
for (ResponseCookieModifications::const_iterator mod =
modifications.begin(); mod != modifications.end(); ++mod) {
if ((*mod)->type != EDIT || !(*mod)->modification.get())
continue;
for (ParsedResponseCookies::iterator cookie = cookies->begin();
cookie != cookies->end(); ++cookie) {
if (DoesResponseCookieMatchFilter(cookie->get(),
(*mod)->filter.get())) {
modified |= ApplyResponseCookieModification(
(*mod)->modification.get(), cookie->get());
}
}
}
}
return modified;
}
// Applies all CookieModificationType::REMOVE operations for response cookies of
// |deltas| to |cookies|. Returns whether any cookie was deleted.
static bool MergeRemoveResponseCookieModifications(
const EventResponseDeltas& deltas,
ParsedResponseCookies* cookies) {
bool modified = false;
// We assume here that the deltas are sorted in decreasing extension
// precedence (i.e. decreasing extension installation time).
EventResponseDeltas::const_reverse_iterator delta;
for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
const ResponseCookieModifications& modifications =
(*delta)->response_cookie_modifications;
for (ResponseCookieModifications::const_iterator mod =
modifications.begin(); mod != modifications.end(); ++mod) {
if ((*mod)->type != REMOVE)
continue;
ParsedResponseCookies::iterator i = cookies->begin();
while (i != cookies->end()) {
if (DoesResponseCookieMatchFilter(i->get(),
(*mod)->filter.get())) {
i = cookies->erase(i);
modified = true;
} else {
++i;
}
}
}
}
return modified;
}
void MergeCookiesInOnHeadersReceivedResponses(
const EventResponseDeltas& deltas,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
std::set<std::string>* conflicting_extensions,
const net::BoundNetLog* net_log) {
// Skip all work if there are no registered cookie modifications.
bool cookie_modifications_exist = false;
EventResponseDeltas::const_reverse_iterator delta;
for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
cookie_modifications_exist |=
!(*delta)->response_cookie_modifications.empty();
}
if (!cookie_modifications_exist)
return;
// Only create a copy if we really want to modify the response headers.
if (override_response_headers->get() == NULL) {
*override_response_headers = new net::HttpResponseHeaders(
original_response_headers->raw_headers());
}
ParsedResponseCookies cookies =
GetResponseCookies(*override_response_headers);
bool modified = false;
modified |= MergeAddResponseCookieModifications(deltas, &cookies);
modified |= MergeEditResponseCookieModifications(deltas, &cookies);
modified |= MergeRemoveResponseCookieModifications(deltas, &cookies);
// Store new value.
if (modified)
StoreResponseCookies(cookies, *override_response_headers);
} }
// Converts the key of the (key, value) pair to lower case. // Converts the key of the (key, value) pair to lower case.
...@@ -502,6 +943,9 @@ void MergeOnHeadersReceivedResponses( ...@@ -502,6 +943,9 @@ void MergeOnHeadersReceivedResponses(
CreateNetLogExtensionIdCallback(delta->get())); CreateNetLogExtensionIdCallback(delta->get()));
} }
} }
MergeCookiesInOnHeadersReceivedResponses(deltas, original_response_headers,
override_response_headers, conflicting_extensions, net_log);
} }
bool MergeOnAuthRequiredResponses( bool MergeOnAuthRequiredResponses(
......
...@@ -40,6 +40,69 @@ namespace extension_web_request_api_helpers { ...@@ -40,6 +40,69 @@ namespace extension_web_request_api_helpers {
typedef std::pair<std::string, std::string> ResponseHeader; typedef std::pair<std::string, std::string> ResponseHeader;
typedef std::vector<ResponseHeader> ResponseHeaders; typedef std::vector<ResponseHeader> ResponseHeaders;
// Data container for RequestCookies as defined in the declarative WebRequest
// API definition.
struct RequestCookie {
RequestCookie();
~RequestCookie();
scoped_ptr<std::string> name;
scoped_ptr<std::string> value;
private:
DISALLOW_COPY_AND_ASSIGN(RequestCookie);
};
// Data container for ResponseCookies as defined in the declarative WebRequest
// API definition.
struct ResponseCookie {
ResponseCookie();
~ResponseCookie();
scoped_ptr<std::string> name;
scoped_ptr<std::string> value;
scoped_ptr<std::string> expires;
scoped_ptr<int> max_age;
scoped_ptr<std::string> domain;
scoped_ptr<std::string> path;
scoped_ptr<bool> secure;
scoped_ptr<bool> http_only;
private:
DISALLOW_COPY_AND_ASSIGN(ResponseCookie);
};
enum CookieModificationType {
ADD,
EDIT,
REMOVE,
};
struct RequestCookieModification {
RequestCookieModification();
~RequestCookieModification();
CookieModificationType type;
// Used for EDIT and REMOVE. NULL for ADD.
scoped_ptr<RequestCookie> filter;
// Used for ADD and EDIT. NULL for REMOVE.
scoped_ptr<RequestCookie> modification;
private:
DISALLOW_COPY_AND_ASSIGN(RequestCookieModification);
};
struct ResponseCookieModification {
ResponseCookieModification();
~ResponseCookieModification();
CookieModificationType type;
// Used for EDIT and REMOVE.
scoped_ptr<ResponseCookie> filter;
// Used for ADD and EDIT.
scoped_ptr<ResponseCookie> modification;
private:
DISALLOW_COPY_AND_ASSIGN(ResponseCookieModification);
};
typedef std::vector<linked_ptr<RequestCookieModification> >
RequestCookieModifications;
typedef std::vector<linked_ptr<ResponseCookieModification> >
ResponseCookieModifications;
// Contains the modification an extension wants to perform on an event. // Contains the modification an extension wants to perform on an event.
struct EventResponseDelta { struct EventResponseDelta {
// ID of the extension that sent this response. // ID of the extension that sent this response.
...@@ -70,6 +133,12 @@ struct EventResponseDelta { ...@@ -70,6 +133,12 @@ struct EventResponseDelta {
// Authentication Credentials to use. // Authentication Credentials to use.
scoped_ptr<net::AuthCredentials> auth_credentials; scoped_ptr<net::AuthCredentials> auth_credentials;
// Modifications to cookies in request headers.
RequestCookieModifications request_cookie_modifications;
// Modifications to cookies in response headers.
ResponseCookieModifications response_cookie_modifications;
EventResponseDelta(const std::string& extension_id, EventResponseDelta(const std::string& extension_id,
const base::Time& extension_install_time); const base::Time& extension_install_time);
~EventResponseDelta(); ~EventResponseDelta();
...@@ -144,6 +213,14 @@ void MergeOnBeforeRequestResponses( ...@@ -144,6 +213,14 @@ void MergeOnBeforeRequestResponses(
GURL* new_url, GURL* new_url,
std::set<std::string>* conflicting_extensions, std::set<std::string>* conflicting_extensions,
const net::BoundNetLog* net_log); const net::BoundNetLog* net_log);
// Modifies the "Cookie" header in |request_headers| according to
// |deltas.request_cookie_modifications|. Conflicts are currently ignored
// silently.
void MergeCookiesInOnBeforeSendHeadersResponses(
const EventResponseDeltas& deltas,
net::HttpRequestHeaders* request_headers,
std::set<std::string>* conflicting_extensions,
const net::BoundNetLog* net_log);
// Modifies the headers in |request_headers| according to |deltas|. Conflicts // Modifies the headers in |request_headers| according to |deltas|. Conflicts
// are tried to be resolved. // are tried to be resolved.
void MergeOnBeforeSendHeadersResponses( void MergeOnBeforeSendHeadersResponses(
...@@ -151,6 +228,16 @@ void MergeOnBeforeSendHeadersResponses( ...@@ -151,6 +228,16 @@ void MergeOnBeforeSendHeadersResponses(
net::HttpRequestHeaders* request_headers, net::HttpRequestHeaders* request_headers,
std::set<std::string>* conflicting_extensions, std::set<std::string>* conflicting_extensions,
const net::BoundNetLog* net_log); const net::BoundNetLog* net_log);
// Modifies the "Set-Cookie" headers in |override_response_headers| according to
// |deltas.response_cookie_modifications|. If |override_response_headers| is
// NULL, a copy of |original_response_headers| is created. Conflicts are
// currently ignored silently.
void MergeCookiesInOnHeadersReceivedResponses(
const EventResponseDeltas& deltas,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
std::set<std::string>* conflicting_extensions,
const net::BoundNetLog* net_log);
// Stores a copy of |original_response_header| into |override_response_headers| // Stores a copy of |original_response_header| into |override_response_headers|
// that is modified according to |deltas|. If |deltas| does not instruct to // that is modified according to |deltas|. If |deltas| does not instruct to
// modify the response headers, |override_response_headers| remains empty. // modify the response headers, |override_response_headers| remains empty.
......
...@@ -48,6 +48,8 @@ using helpers::EventResponseDeltas; ...@@ -48,6 +48,8 @@ using helpers::EventResponseDeltas;
using helpers::InDecreasingExtensionInstallationTimeOrder; using helpers::InDecreasingExtensionInstallationTimeOrder;
using helpers::MergeCancelOfResponses; using helpers::MergeCancelOfResponses;
using helpers::MergeOnBeforeRequestResponses; using helpers::MergeOnBeforeRequestResponses;
using helpers::RequestCookieModification;
using helpers::ResponseCookieModification;
using helpers::ResponseHeader; using helpers::ResponseHeader;
using helpers::ResponseHeaders; using helpers::ResponseHeaders;
using helpers::StringToCharList; using helpers::StringToCharList;
...@@ -1281,6 +1283,162 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { ...@@ -1281,6 +1283,162 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) {
EXPECT_EQ(3u, capturing_net_log.GetSize()); EXPECT_EQ(3u, capturing_net_log.GetSize());
} }
TEST(ExtensionWebRequestHelpersTest,
TestMergeOnBeforeSendHeadersResponses_Cookies) {
net::HttpRequestHeaders base_headers;
base_headers.AddHeaderFromString(
"Cookie: name=value; name2=value2; name3=value3");
net::CapturingBoundNetLog capturing_net_log;
net::BoundNetLog net_log = capturing_net_log.bound();
std::set<std::string> conflicting_extensions;
std::string header_value;
EventResponseDeltas deltas;
linked_ptr<RequestCookieModification> add_cookie =
make_linked_ptr(new RequestCookieModification);
add_cookie->type = helpers::ADD;
add_cookie->modification.reset(new helpers::RequestCookie);
add_cookie->modification->name.reset(new std::string("name4"));
add_cookie->modification->value.reset(new std::string("\"value 4\""));
linked_ptr<RequestCookieModification> add_cookie_2 =
make_linked_ptr(new RequestCookieModification);
add_cookie_2->type = helpers::ADD;
add_cookie_2->modification.reset(new helpers::RequestCookie);
add_cookie_2->modification->name.reset(new std::string("name"));
add_cookie_2->modification->value.reset(new std::string("new value"));
linked_ptr<RequestCookieModification> edit_cookie =
make_linked_ptr(new RequestCookieModification);
edit_cookie->type = helpers::EDIT;
edit_cookie->filter.reset(new helpers::RequestCookie);
edit_cookie->filter->name.reset(new std::string("name2"));
edit_cookie->modification.reset(new helpers::RequestCookie);
edit_cookie->modification->value.reset(new std::string("new value"));
linked_ptr<RequestCookieModification> remove_cookie =
make_linked_ptr(new RequestCookieModification);
remove_cookie->type = helpers::REMOVE;
remove_cookie->filter.reset(new helpers::RequestCookie);
remove_cookie->filter->name.reset(new std::string("name3"));
linked_ptr<RequestCookieModification> operations[] = {
add_cookie, add_cookie_2, edit_cookie, remove_cookie
};
for (size_t i = 0; i < arraysize(operations); ++i) {
linked_ptr<EventResponseDelta> delta(
new EventResponseDelta("extid0", base::Time::FromInternalValue(i * 5)));
delta->request_cookie_modifications.push_back(operations[i]);
deltas.push_back(delta);
}
deltas.sort(&InDecreasingExtensionInstallationTimeOrder);
net::HttpRequestHeaders headers1;
headers1.MergeFrom(base_headers);
MergeOnBeforeSendHeadersResponses(
deltas, &headers1, &conflicting_extensions, &net_log);
EXPECT_TRUE(headers1.HasHeader("Cookie"));
ASSERT_TRUE(headers1.GetHeader("Cookie", &header_value));
EXPECT_EQ("name=new value; name2=new value; name4=\"value 4\"", header_value);
EXPECT_EQ(0u, conflicting_extensions.size());
EXPECT_EQ(0u, capturing_net_log.GetSize());
}
TEST(ExtensionWebRequestHelpersTest,
TestMergeCookiesInOnHeadersReceivedResponses) {
net::CapturingBoundNetLog capturing_net_log;
net::BoundNetLog net_log = capturing_net_log.bound();
std::set<std::string> conflicting_extensions;
std::string header_value;
EventResponseDeltas deltas;
char base_headers_string[] =
"HTTP/1.0 200 OK\r\n"
"Foo: Bar\r\n"
"Set-Cookie: name=value; DOMAIN=google.com; Secure\r\n"
"Set-Cookie: name2=value2\r\n"
"Set-Cookie: name3=value3\r\n"
"\r\n";
scoped_refptr<net::HttpResponseHeaders> base_headers(
new net::HttpResponseHeaders(
net::HttpUtil::AssembleRawHeaders(
base_headers_string, sizeof(base_headers_string))));
// Check that we can handle if not touching the response headers.
linked_ptr<EventResponseDelta> d0(
new EventResponseDelta("extid0", base::Time::FromInternalValue(3000)));
deltas.push_back(d0);
scoped_refptr<net::HttpResponseHeaders> new_headers0;
MergeCookiesInOnHeadersReceivedResponses(
deltas, base_headers.get(), &new_headers0, &conflicting_extensions,
&net_log);
EXPECT_FALSE(new_headers0.get());
EXPECT_EQ(0u, conflicting_extensions.size());
EXPECT_EQ(0u, capturing_net_log.GetSize());
linked_ptr<ResponseCookieModification> add_cookie =
make_linked_ptr(new ResponseCookieModification);
add_cookie->type = helpers::ADD;
add_cookie->modification.reset(new helpers::ResponseCookie);
add_cookie->modification->name.reset(new std::string("name4"));
add_cookie->modification->value.reset(new std::string("\"value4\""));
linked_ptr<ResponseCookieModification> edit_cookie =
make_linked_ptr(new ResponseCookieModification);
edit_cookie->type = helpers::EDIT;
edit_cookie->filter.reset(new helpers::ResponseCookie);
edit_cookie->filter->name.reset(new std::string("name2"));
edit_cookie->modification.reset(new helpers::ResponseCookie);
edit_cookie->modification->value.reset(new std::string("new value"));
linked_ptr<ResponseCookieModification> edit_cookie_2 =
make_linked_ptr(new ResponseCookieModification);
edit_cookie_2->type = helpers::EDIT;
edit_cookie_2->filter.reset(new helpers::ResponseCookie);
edit_cookie_2->filter->secure.reset(new bool(false));
edit_cookie_2->modification.reset(new helpers::ResponseCookie);
edit_cookie_2->modification->secure.reset(new bool(true));
linked_ptr<ResponseCookieModification> remove_cookie =
make_linked_ptr(new ResponseCookieModification);
remove_cookie->type = helpers::REMOVE;
remove_cookie->filter.reset(new helpers::ResponseCookie);
remove_cookie->filter->name.reset(new std::string("name3"));
linked_ptr<ResponseCookieModification> operations[] = {
add_cookie, edit_cookie, edit_cookie_2, remove_cookie
};
for (size_t i = 0; i < arraysize(operations); ++i) {
linked_ptr<EventResponseDelta> delta(
new EventResponseDelta("extid0", base::Time::FromInternalValue(i * 5)));
delta->response_cookie_modifications.push_back(operations[i]);
deltas.push_back(delta);
}
deltas.sort(&InDecreasingExtensionInstallationTimeOrder);
scoped_refptr<net::HttpResponseHeaders> headers1(
new net::HttpResponseHeaders(
net::HttpUtil::AssembleRawHeaders(
base_headers_string, sizeof(base_headers_string))));
scoped_refptr<net::HttpResponseHeaders> new_headers1;
MergeCookiesInOnHeadersReceivedResponses(
deltas, headers1.get(), &new_headers1, &conflicting_extensions, &net_log);
EXPECT_TRUE(new_headers1->HasHeader("Foo"));
void* iter = NULL;
std::string cookie_string;
std::set<std::string> expected_cookies;
expected_cookies.insert("name=value; domain=google.com; secure");
expected_cookies.insert("name2=value2; secure");
expected_cookies.insert("name4=\"value4\"; secure");
std::set<std::string> actual_cookies;
while (new_headers1->EnumerateHeader(&iter, "Set-Cookie", &cookie_string))
actual_cookies.insert(cookie_string);
EXPECT_EQ(expected_cookies, actual_cookies);
EXPECT_EQ(0u, conflicting_extensions.size());
EXPECT_EQ(0u, capturing_net_log.GetSize());
}
TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) { TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) {
net::CapturingBoundNetLog capturing_net_log; net::CapturingBoundNetLog capturing_net_log;
net::BoundNetLog net_log = capturing_net_log.bound(); net::BoundNetLog net_log = capturing_net_log.bound();
......
...@@ -180,6 +180,168 @@ ...@@ -180,6 +180,168 @@
"description": "If set, rules with a lower priority than the specified value are ignored. This boundary is not persited, it affects only rules and their actions of the same network request stage. TODO(battre): Explain network request stages." "description": "If set, rules with a lower priority than the specified value are ignored. This boundary is not persited, it affects only rules and their actions of the same network request stage. TODO(battre): Explain network request stages."
} }
} }
},
{
"id": "declarativeWebRequest.RequestCookie",
"description": "A filter or specification of a cookie in HTTP Requests.",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of a cookie.",
"optional": true
},
"value": {
"type": "string",
"description": "Value of a cookie, may be padded in double-quotes.",
"optional": true
}
}
},
{
"id": "declarativeWebRequest.ResponseCookie",
"description": "A filter or specification of a cookie in HTTP Responses.",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of a cookie.",
"optional": true
},
"value": {
"type": "string",
"description": "Value of a cookie, may be padded in double-quotes.",
"optional": true
},
"expires": {
"type": "string",
"description": "Value of the Expires cookie attribute.",
"optional": true
},
"maxAge": {
"type": "number",
"description": "Value of the Max-Age cookie attribute",
"optional": true
},
"domain": {
"type": "string",
"description": "Value of the Domain cookie attribute.",
"optional": true
},
"path": {
"type": "string",
"description": "Value of the Path cookie attribute.",
"optional": true
},
"secure": {
"type": "string",
"description": "Existence of the Secure cookie attribute.",
"optional": true
},
"httpOnly": {
"type": "string",
"description": "Existence of the HttpOnly cookie attribute.",
"optional": true
}
}
},
{
"id": "declarativeWebRequest.AddRequestCookie",
"description": "Adds a cookie to the request or overrides a cookie, in case another cookie of the same name exists already. Note that it is preferred to use the Cookies API because this is computationally less expensive.",
"type": "object",
"properties": {
"instanceType": {
"type": "string", "enum": ["declarativeWebRequest.AddRequestCookie"],
"nodoc": true
},
"cookie": {
"$ref": "declarativeWebRequest.RequestCookie",
"description": "Cookie to be added to the request. No field may be undefined."
}
}
},
{
"id": "declarativeWebRequest.AddResponseCookie",
"description": "Adds a cookie to the response or overrides a cookie, in case another cookie of the same name exists already. Note that it is preferred to use the Cookies API because this is computationally less expensive.",
"type": "object",
"properties": {
"instanceType": {
"type": "string", "enum": ["declarativeWebRequest.AddResponseCookie"],
"nodoc": true
},
"cookie": {
"$ref": "declarativeWebRequest.ResponseCookie",
"description": "Cookie to be added to the response. The name and value need to be specified."
}
}
},
{
"id": "declarativeWebRequest.EditRequestCookie",
"description": "Edits one or more cookies of request. Note that it is preferred to use the Cookies API because this is computationally less expensive.",
"type": "object",
"properties": {
"instanceType": {
"type": "string", "enum": ["declarativeWebRequest.EditRequestCookie"],
"nodoc": true
},
"filter": {
"$ref": "declarativeWebRequest.RequestCookie",
"description": "Filter for cookies that will be modified. All empty entries are ignored."
},
"modification": {
"$ref": "declarativeWebRequest.RequestCookie",
"description": "Attributes that shall be overridden in cookies that machted the filter. Attributes that are set to an empty string are removed."
}
}
},
{
"id": "declarativeWebRequest.EditResponseCookie",
"description": "Edits one or more cookies of response. Note that it is preferred to use the Cookies API because this is computationally less expensive.",
"type": "object",
"properties": {
"instanceType": {
"type": "string", "enum": ["declarativeWebRequest.EditResponseCookie"],
"nodoc": true
},
"filter": {
"$ref": "declarativeWebRequest.ResponseCookie",
"description": "Filter for cookies that will be modified. All empty entries are ignored."
},
"modification": {
"$ref": "declarativeWebRequest.ResponseCookie",
"description": "Attributes that shall be overridden in cookies that machted the filter. Attributes that are set to an empty string are removed."
}
}
},
{
"id": "declarativeWebRequest.RemoveRequestCookie",
"description": "Removes one or more cookies of request. Note that it is preferred to use the Cookies API because this is computationally less expensive.",
"type": "object",
"properties": {
"instanceType": {
"type": "string", "enum": ["declarativeWebRequest.RemoveRequestCookie"],
"nodoc": true
},
"filter": {
"$ref": "declarativeWebRequest.RequestCookie",
"description": "Filter for cookies that will be removed. All empty entries are ignored."
}
}
},
{
"id": "declarativeWebRequest.RemoveResponseCookie",
"description": "Removes one or more cookies of response. Note that it is preferred to use the Cookies API because this is computationally less expensive.",
"type": "object",
"properties": {
"instanceType": {
"type": "string", "enum": ["declarativeWebRequest.RemoveResponseCookie"],
"nodoc": true
},
"filter": {
"$ref": "declarativeWebRequest.ResponseCookie",
"description": "Filter for cookies that will be removed. All empty entries are ignored."
}
}
} }
], ],
"functions": [ "functions": [
...@@ -192,15 +354,21 @@ ...@@ -192,15 +354,21 @@
"supportsRules": true, "supportsRules": true,
"conditions": ["declarativeWebRequest.RequestMatcher"], "conditions": ["declarativeWebRequest.RequestMatcher"],
"actions": [ "actions": [
"declarativeWebRequest.AddRequestCookie",
"declarativeWebRequest.AddResponseCookie",
"declarativeWebRequest.AddResponseHeader", "declarativeWebRequest.AddResponseHeader",
"declarativeWebRequest.CancelRequest", "declarativeWebRequest.CancelRequest",
"declarativeWebRequest.EditRequestCookie",
"declarativeWebRequest.EditResponseCookie",
"declarativeWebRequest.RedirectRequest", "declarativeWebRequest.RedirectRequest",
"declarativeWebRequest.RedirectToTransparentImage", "declarativeWebRequest.RedirectToTransparentImage",
"declarativeWebRequest.RedirectToEmptyDocument", "declarativeWebRequest.RedirectToEmptyDocument",
"declarativeWebRequest.RedirectByRegEx", "declarativeWebRequest.RedirectByRegEx",
"declarativeWebRequest.SetRequestHeader", "declarativeWebRequest.RemoveRequestCookie",
"declarativeWebRequest.RemoveResponseCookie",
"declarativeWebRequest.RemoveRequestHeader", "declarativeWebRequest.RemoveRequestHeader",
"declarativeWebRequest.RemoveResponseHeader", "declarativeWebRequest.RemoveResponseHeader",
"declarativeWebRequest.SetRequestHeader",
"declarativeWebRequest.IgnoreRules" "declarativeWebRequest.IgnoreRules"
] ]
} }
......
...@@ -226,7 +226,7 @@ ...@@ -226,7 +226,7 @@
<a href="#types">Types</a> <a href="#types">Types</a>
<ol> <ol>
<li> <li>
<a href="#type-declarativeWebRequest.RedirectByRegEx">RedirectByRegEx</a> <a href="#type-declarativeWebRequest.RemoveResponseHeader">RemoveResponseHeader</a>
<ol> <ol>
</ol> </ol>
</li><li> </li><li>
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
<ol> <ol>
</ol> </ol>
</li><li> </li><li>
<a href="#type-declarativeWebRequest.CancelRequest">CancelRequest</a> <a href="#type-declarativeWebRequest.RedirectByRegEx">RedirectByRegEx</a>
<ol> <ol>
</ol> </ol>
</li><li> </li><li>
...@@ -262,13 +262,45 @@ ...@@ -262,13 +262,45 @@
<ol> <ol>
</ol> </ol>
</li><li> </li><li>
<a href="#type-declarativeWebRequest.RemoveResponseHeader">RemoveResponseHeader</a> <a href="#type-declarativeWebRequest.CancelRequest">CancelRequest</a>
<ol> <ol>
</ol> </ol>
</li><li> </li><li>
<a href="#type-declarativeWebRequest.IgnoreRules">IgnoreRules</a> <a href="#type-declarativeWebRequest.IgnoreRules">IgnoreRules</a>
<ol> <ol>
</ol> </ol>
</li><li>
<a href="#type-declarativeWebRequest.RequestCookie">RequestCookie</a>
<ol>
</ol>
</li><li>
<a href="#type-declarativeWebRequest.ResponseCookie">ResponseCookie</a>
<ol>
</ol>
</li><li>
<a href="#type-declarativeWebRequest.AddRequestCookie">AddRequestCookie</a>
<ol>
</ol>
</li><li>
<a href="#type-declarativeWebRequest.AddResponseCookie">AddResponseCookie</a>
<ol>
</ol>
</li><li>
<a href="#type-declarativeWebRequest.EditRequestCookie">EditRequestCookie</a>
<ol>
</ol>
</li><li>
<a href="#type-declarativeWebRequest.EditResponseCookie">EditResponseCookie</a>
<ol>
</ol>
</li><li>
<a href="#type-declarativeWebRequest.RemoveRequestCookie">RemoveRequestCookie</a>
<ol>
</ol>
</li><li>
<a href="#type-declarativeWebRequest.RemoveResponseCookie">RemoveResponseCookie</a>
<ol>
</ol>
</li> </li>
</ol> </ol>
</li> </li>
...@@ -436,6 +468,24 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -436,6 +468,24 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<dl> <dl>
<div> <div>
<div> <div>
<dt>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.AddRequestCookie">AddRequestCookie</a>
</dt>
<dd class="todo">
Undocumented.
</dd>
</div>
</div><div>
<div>
<dt>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.AddResponseCookie">AddResponseCookie</a>
</dt>
<dd class="todo">
Undocumented.
</dd>
</div>
</div><div>
<div>
<dt> <dt>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.AddResponseHeader">AddResponseHeader</a> <a href="declarativeWebRequest.html#type-declarativeWebRequest.AddResponseHeader">AddResponseHeader</a>
</dt> </dt>
...@@ -451,6 +501,24 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -451,6 +501,24 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<dd class="todo"> <dd class="todo">
Undocumented. Undocumented.
</dd> </dd>
</div>
</div><div>
<div>
<dt>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.EditRequestCookie">EditRequestCookie</a>
</dt>
<dd class="todo">
Undocumented.
</dd>
</div>
</div><div>
<div>
<dt>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.EditResponseCookie">EditResponseCookie</a>
</dt>
<dd class="todo">
Undocumented.
</dd>
</div> </div>
</div><div> </div><div>
<div> <div>
...@@ -491,7 +559,16 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -491,7 +559,16 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div><div> </div><div>
<div> <div>
<dt> <dt>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.SetRequestHeader">SetRequestHeader</a> <a href="declarativeWebRequest.html#type-declarativeWebRequest.RemoveRequestCookie">RemoveRequestCookie</a>
</dt>
<dd class="todo">
Undocumented.
</dd>
</div>
</div><div>
<div>
<dt>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.RemoveResponseCookie">RemoveResponseCookie</a>
</dt> </dt>
<dd class="todo"> <dd class="todo">
Undocumented. Undocumented.
...@@ -514,6 +591,15 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -514,6 +591,15 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<dd class="todo"> <dd class="todo">
Undocumented. Undocumented.
</dd> </dd>
</div>
</div><div>
<div>
<dt>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.SetRequestHeader">SetRequestHeader</a>
</dt>
<dd class="todo">
Undocumented.
</dd>
</div> </div>
</div><div> </div><div>
<div> <div>
...@@ -536,8 +622,8 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -536,8 +622,8 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<h3 id="types">Types</h3> <h3 id="types">Types</h3>
<!-- iterates over all types --> <!-- iterates over all types -->
<div class="apiItem"> <div class="apiItem">
<a name="type-declarativeWebRequest.RedirectByRegEx"></a> <a name="type-declarativeWebRequest.RemoveResponseHeader"></a>
<h4>declarativeWebRequest.RedirectByRegEx</h4> <h4>declarativeWebRequest.RemoveResponseHeader</h4>
<div> <div>
<dt> <dt>
<em> <em>
...@@ -553,14 +639,14 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -553,14 +639,14 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div> </div>
</em> </em>
</dt> </dt>
<dd>Redirects a request by applying a regular expression on the URL. The regular expressions use the <a href="http://code.google.com/p/re2/wiki/Syntax">RE2 syntax</a>.</dd> <dd>Removes all response headers of the specified names and values.</dd>
<!-- OBJECT PROPERTIES --> <!-- OBJECT PROPERTIES -->
<dd> <dd>
<dl> <dl>
<div> <div>
<div> <div>
<dt> <dt>
<var>from</var> <var>name</var>
<em> <em>
<!-- TYPE --> <!-- TYPE -->
<div style="display:inline"> <div style="display:inline">
...@@ -574,7 +660,7 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -574,7 +660,7 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div> </div>
</em> </em>
</dt> </dt>
<dd>A match pattern that may contain capture groups. Capture groups are referenced in the Perl syntax ($1, $2, ...) instead of the RE2 syntax (\1, \2, ...) in order to be closer to JavaScript Regular Expressions.</dd> <dd>HTTP request header name (case-insensitive).</dd>
<!-- OBJECT PROPERTIES --> <!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS --> <!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS --> <!-- OBJECT EVENT FIELDS -->
...@@ -583,11 +669,12 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -583,11 +669,12 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div><div> </div><div>
<div> <div>
<dt> <dt>
<var>to</var> <var>value</var>
<em> <em>
<!-- TYPE --> <!-- TYPE -->
<div style="display:inline"> <div style="display:inline">
( (
<span class="optional">optional</span>
<span id="typeTemplate"> <span id="typeTemplate">
<span> <span>
<span>string</span> <span>string</span>
...@@ -597,7 +684,7 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -597,7 +684,7 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div> </div>
</em> </em>
</dt> </dt>
<dd>Destination pattern.</dd> <dd>HTTP request header value (case-insensitive).</dd>
<!-- OBJECT PROPERTIES --> <!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS --> <!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS --> <!-- OBJECT EVENT FIELDS -->
...@@ -803,8 +890,8 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -803,8 +890,8 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<!-- FUNCTION PARAMETERS --> <!-- FUNCTION PARAMETERS -->
</div> </div>
</div><div class="apiItem"> </div><div class="apiItem">
<a name="type-declarativeWebRequest.CancelRequest"></a> <a name="type-declarativeWebRequest.RedirectByRegEx"></a>
<h4>declarativeWebRequest.CancelRequest</h4> <h4>declarativeWebRequest.RedirectByRegEx</h4>
<div> <div>
<dt> <dt>
<em> <em>
...@@ -820,10 +907,57 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -820,10 +907,57 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div> </div>
</em> </em>
</dt> </dt>
<dd>Declarative event action that cancels a network request.</dd> <dd>Redirects a request by applying a regular expression on the URL. The regular expressions use the <a href="http://code.google.com/p/re2/wiki/Syntax">RE2 syntax</a>.</dd>
<!-- OBJECT PROPERTIES --> <!-- OBJECT PROPERTIES -->
<dd> <dd>
<dl> <dl>
<div>
<div>
<dt>
<var>from</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<span>string</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>A match pattern that may contain capture groups. Capture groups are referenced in the Perl syntax ($1, $2, ...) instead of the RE2 syntax (\1, \2, ...) in order to be closer to JavaScript Regular Expressions.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div>
<div>
<dt>
<var>to</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<span>string</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Destination pattern.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div>
</dl> </dl>
</dd> </dd>
<!-- OBJECT METHODS --> <!-- OBJECT METHODS -->
...@@ -1033,8 +1167,8 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -1033,8 +1167,8 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<!-- FUNCTION PARAMETERS --> <!-- FUNCTION PARAMETERS -->
</div> </div>
</div><div class="apiItem"> </div><div class="apiItem">
<a name="type-declarativeWebRequest.RemoveResponseHeader"></a> <a name="type-declarativeWebRequest.CancelRequest"></a>
<h4>declarativeWebRequest.RemoveResponseHeader</h4> <h4>declarativeWebRequest.CancelRequest</h4>
<div> <div>
<dt> <dt>
<em> <em>
...@@ -1050,7 +1184,87 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -1050,7 +1184,87 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div> </div>
</em> </em>
</dt> </dt>
<dd>Removes all response headers of the specified names and values.</dd> <dd>Declarative event action that cancels a network request.</dd>
<!-- OBJECT PROPERTIES -->
<dd>
<dl>
</dl>
</dd>
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div class="apiItem">
<a name="type-declarativeWebRequest.IgnoreRules"></a>
<h4>declarativeWebRequest.IgnoreRules</h4>
<div>
<dt>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<span>object</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Masks all rules that match the specified criteria.</dd>
<!-- OBJECT PROPERTIES -->
<dd>
<dl>
<div>
<div>
<dt>
<var>lowerPriorityThan</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<span>integer</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>If set, rules with a lower priority than the specified value are ignored. This boundary is not persited, it affects only rules and their actions of the same network request stage. TODO(battre): Explain network request stages.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div>
</dl>
</dd>
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div class="apiItem">
<a name="type-declarativeWebRequest.RequestCookie"></a>
<h4>declarativeWebRequest.RequestCookie</h4>
<div>
<dt>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<span>object</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>A filter or specification of a cookie in HTTP Requests.</dd>
<!-- OBJECT PROPERTIES --> <!-- OBJECT PROPERTIES -->
<dd> <dd>
<dl> <dl>
...@@ -1062,6 +1276,7 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -1062,6 +1276,7 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<!-- TYPE --> <!-- TYPE -->
<div style="display:inline"> <div style="display:inline">
( (
<span class="optional">optional</span>
<span id="typeTemplate"> <span id="typeTemplate">
<span> <span>
<span>string</span> <span>string</span>
...@@ -1071,7 +1286,7 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -1071,7 +1286,7 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div> </div>
</em> </em>
</dt> </dt>
<dd>HTTP request header name (case-insensitive).</dd> <dd>Name of a cookie.</dd>
<!-- OBJECT PROPERTIES --> <!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS --> <!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS --> <!-- OBJECT EVENT FIELDS -->
...@@ -1095,7 +1310,7 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -1095,7 +1310,7 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div> </div>
</em> </em>
</dt> </dt>
<dd>HTTP request header value (case-insensitive).</dd> <dd>Value of a cookie, may be padded in double-quotes.</dd>
<!-- OBJECT PROPERTIES --> <!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS --> <!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS --> <!-- OBJECT EVENT FIELDS -->
...@@ -1109,8 +1324,8 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -1109,8 +1324,8 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<!-- FUNCTION PARAMETERS --> <!-- FUNCTION PARAMETERS -->
</div> </div>
</div><div class="apiItem"> </div><div class="apiItem">
<a name="type-declarativeWebRequest.IgnoreRules"></a> <a name="type-declarativeWebRequest.ResponseCookie"></a>
<h4>declarativeWebRequest.IgnoreRules</h4> <h4>declarativeWebRequest.ResponseCookie</h4>
<div> <div>
<dt> <dt>
<em> <em>
...@@ -1126,28 +1341,555 @@ very fast URL matching algorithm for hundreds of thousands of URLs. ...@@ -1126,28 +1341,555 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div> </div>
</em> </em>
</dt> </dt>
<dd>Masks all rules that match the specified criteria.</dd> <dd>A filter or specification of a cookie in HTTP Responses.</dd>
<!-- OBJECT PROPERTIES --> <!-- OBJECT PROPERTIES -->
<dd> <dd>
<dl> <dl>
<div> <div>
<div> <div>
<dt> <dt>
<var>lowerPriorityThan</var> <var>name</var>
<em> <em>
<!-- TYPE --> <!-- TYPE -->
<div style="display:inline"> <div style="display:inline">
( (
<span class="optional">optional</span>
<span id="typeTemplate"> <span id="typeTemplate">
<span> <span>
<span>integer</span> <span>string</span>
</span> </span>
</span> </span>
) )
</div> </div>
</em> </em>
</dt> </dt>
<dd>If set, rules with a lower priority than the specified value are ignored. This boundary is not persited, it affects only rules and their actions of the same network request stage. TODO(battre): Explain network request stages.</dd> <dd>Name of a cookie.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div>
<div>
<dt>
<var>value</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span class="optional">optional</span>
<span id="typeTemplate">
<span>
<span>string</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Value of a cookie, may be padded in double-quotes.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div>
<div>
<dt>
<var>expires</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span class="optional">optional</span>
<span id="typeTemplate">
<span>
<span>string</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Value of the Expires cookie attribute.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div>
<div>
<dt>
<var>maxAge</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span class="optional">optional</span>
<span id="typeTemplate">
<span>
<span>number</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Value of the Max-Age cookie attribute</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div>
<div>
<dt>
<var>domain</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span class="optional">optional</span>
<span id="typeTemplate">
<span>
<span>string</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Value of the Domain cookie attribute.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div>
<div>
<dt>
<var>path</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span class="optional">optional</span>
<span id="typeTemplate">
<span>
<span>string</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Value of the Path cookie attribute.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div>
<div>
<dt>
<var>secure</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span class="optional">optional</span>
<span id="typeTemplate">
<span>
<span>string</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Existence of the Secure cookie attribute.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div>
<div>
<dt>
<var>httpOnly</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span class="optional">optional</span>
<span id="typeTemplate">
<span>
<span>string</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Existence of the HttpOnly cookie attribute.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div>
</dl>
</dd>
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div class="apiItem">
<a name="type-declarativeWebRequest.AddRequestCookie"></a>
<h4>declarativeWebRequest.AddRequestCookie</h4>
<div>
<dt>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<span>object</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Adds a cookie to the request or overrides a cookie, in case another cookie of the same name exists already. Note that it is preferred to use the Cookies API because this is computationally less expensive.</dd>
<!-- OBJECT PROPERTIES -->
<dd>
<dl>
<div>
<div>
<dt>
<var>cookie</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.RequestCookie">declarativeWebRequest.RequestCookie</a>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Cookie to be added to the request. No field may be undefined.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div>
</dl>
</dd>
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div class="apiItem">
<a name="type-declarativeWebRequest.AddResponseCookie"></a>
<h4>declarativeWebRequest.AddResponseCookie</h4>
<div>
<dt>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<span>object</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Adds a cookie to the response or overrides a cookie, in case another cookie of the same name exists already. Note that it is preferred to use the Cookies API because this is computationally less expensive.</dd>
<!-- OBJECT PROPERTIES -->
<dd>
<dl>
<div>
<div>
<dt>
<var>cookie</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.ResponseCookie">declarativeWebRequest.ResponseCookie</a>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Cookie to be added to the response. The name and value need to be specified.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div>
</dl>
</dd>
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div class="apiItem">
<a name="type-declarativeWebRequest.EditRequestCookie"></a>
<h4>declarativeWebRequest.EditRequestCookie</h4>
<div>
<dt>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<span>object</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Edits one or more cookies of request. Note that it is preferred to use the Cookies API because this is computationally less expensive.</dd>
<!-- OBJECT PROPERTIES -->
<dd>
<dl>
<div>
<div>
<dt>
<var>filter</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.RequestCookie">declarativeWebRequest.RequestCookie</a>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Filter for cookies that will be modified. All empty entries are ignored.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div>
<div>
<dt>
<var>modification</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.RequestCookie">declarativeWebRequest.RequestCookie</a>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Attributes that shall be overridden in cookies that machted the filter. Attributes that are set to an empty string are removed.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div>
</dl>
</dd>
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div class="apiItem">
<a name="type-declarativeWebRequest.EditResponseCookie"></a>
<h4>declarativeWebRequest.EditResponseCookie</h4>
<div>
<dt>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<span>object</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Edits one or more cookies of response. Note that it is preferred to use the Cookies API because this is computationally less expensive.</dd>
<!-- OBJECT PROPERTIES -->
<dd>
<dl>
<div>
<div>
<dt>
<var>filter</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.ResponseCookie">declarativeWebRequest.ResponseCookie</a>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Filter for cookies that will be modified. All empty entries are ignored.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div>
<div>
<dt>
<var>modification</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.ResponseCookie">declarativeWebRequest.ResponseCookie</a>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Attributes that shall be overridden in cookies that machted the filter. Attributes that are set to an empty string are removed.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div>
</dl>
</dd>
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div class="apiItem">
<a name="type-declarativeWebRequest.RemoveRequestCookie"></a>
<h4>declarativeWebRequest.RemoveRequestCookie</h4>
<div>
<dt>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<span>object</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Removes one or more cookies of request. Note that it is preferred to use the Cookies API because this is computationally less expensive.</dd>
<!-- OBJECT PROPERTIES -->
<dd>
<dl>
<div>
<div>
<dt>
<var>filter</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.RequestCookie">declarativeWebRequest.RequestCookie</a>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Filter for cookies that will be removed. All empty entries are ignored.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div>
</dl>
</dd>
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
</div><div class="apiItem">
<a name="type-declarativeWebRequest.RemoveResponseCookie"></a>
<h4>declarativeWebRequest.RemoveResponseCookie</h4>
<div>
<dt>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<span>object</span>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Removes one or more cookies of response. Note that it is preferred to use the Cookies API because this is computationally less expensive.</dd>
<!-- OBJECT PROPERTIES -->
<dd>
<dl>
<div>
<div>
<dt>
<var>filter</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span id="typeTemplate">
<span>
<a href="declarativeWebRequest.html#type-declarativeWebRequest.ResponseCookie">declarativeWebRequest.ResponseCookie</a>
</span>
</span>
)
</div>
</em>
</dt>
<dd>Filter for cookies that will be removed. All empty entries are ignored.</dd>
<!-- OBJECT PROPERTIES --> <!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS --> <!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS --> <!-- OBJECT EVENT FIELDS -->
......
...@@ -68,4 +68,22 @@ chromeHidden.registerCustomHook('declarativeWebRequest', function(api) { ...@@ -68,4 +68,22 @@ chromeHidden.registerCustomHook('declarativeWebRequest', function(api) {
chrome.declarativeWebRequest.IgnoreRules = function(parameters) { chrome.declarativeWebRequest.IgnoreRules = function(parameters) {
setupInstance(this, parameters, 'IgnoreRules'); setupInstance(this, parameters, 'IgnoreRules');
}; };
chrome.declarativeWebRequest.AddRequestCookie = function(parameters) {
setupInstance(this, parameters, 'AddRequestCookie');
};
chrome.declarativeWebRequest.AddResponseCookie = function(parameters) {
setupInstance(this, parameters, 'AddResponseCookie');
};
chrome.declarativeWebRequest.EditRequestCookie = function(parameters) {
setupInstance(this, parameters, 'EditRequestCookie');
};
chrome.declarativeWebRequest.EditResponseCookie = function(parameters) {
setupInstance(this, parameters, 'EditResponseCookie');
};
chrome.declarativeWebRequest.RemoveRequestCookie = function(parameters) {
setupInstance(this, parameters, 'RemoveRequestCookie');
};
chrome.declarativeWebRequest.RemoveResponseCookie = function(parameters) {
setupInstance(this, parameters, 'RemoveResponseCookie');
};
}); });
...@@ -21,6 +21,12 @@ var RemoveResponseHeader = ...@@ -21,6 +21,12 @@ var RemoveResponseHeader =
chrome.declarativeWebRequest.RemoveResponseHeader; chrome.declarativeWebRequest.RemoveResponseHeader;
var IgnoreRules = var IgnoreRules =
chrome.declarativeWebRequest.IgnoreRules; chrome.declarativeWebRequest.IgnoreRules;
var AddRequestCookie = chrome.declarativeWebRequest.AddRequestCookie;
var AddResponseCookie = chrome.declarativeWebRequest.AddResponseCookie;
var EditRequestCookie = chrome.declarativeWebRequest.EditRequestCookie;
var EditResponseCookie = chrome.declarativeWebRequest.EditResponseCookie;
var RemoveRequestCookie = chrome.declarativeWebRequest.RemoveRequestCookie;
var RemoveResponseCookie = chrome.declarativeWebRequest.RemoveResponseCookie;
// Constants as functions, not to be called until after runTests. // Constants as functions, not to be called until after runTests.
function getURLEchoUserAgent() { function getURLEchoUserAgent() {
...@@ -49,10 +55,20 @@ function getURLSetCookie() { ...@@ -49,10 +55,20 @@ function getURLSetCookie() {
return getServerURL('set-cookie?Foo=Bar'); return getServerURL('set-cookie?Foo=Bar');
} }
function getURLSetCookie2() {
return getServerURL('set-cookie?passedCookie=Foo&editedCookie=Foo&' +
'deletedCookie=Foo');
}
function getURLEchoCookie() {
return getServerURL('echoheader?Cookie');
}
function getURLHttpXHRData() { function getURLHttpXHRData() {
return getServerURL("files/extensions/api_test/webrequest/xhr/data.json", return getServerURL("files/extensions/api_test/webrequest/xhr/data.json",
"b.com"); "b.com");
} }
function getURLHttpSimpleOnB() { function getURLHttpSimpleOnB() {
return getServerURL("files/extensions/api_test/webrequest/simpleLoad/a.html", return getServerURL("files/extensions/api_test/webrequest/simpleLoad/a.html",
"b.com"); "b.com");
...@@ -342,6 +358,82 @@ runTests([ ...@@ -342,6 +358,82 @@ runTests([
); );
}, },
function testEditRequestCookies() {
ignoreUnexpected = true;
expect();
var cookie1 = {name: "requestCookie1", value: "foo"};
var cookie2 = {name: "requestCookie2", value: "foo"};
onRequest.addRules(
[ {conditions: [new RequestMatcher({})],
actions: [
// We exploit the fact that cookies are first added, then modified
// and finally removed.
new AddRequestCookie({cookie: cookie1}),
new AddRequestCookie({cookie: cookie2}),
new EditRequestCookie({filter: {name: "requestCookie1"},
modification: {value: "bar"}}),
new RemoveRequestCookie({filter: {name: "requestCookie2"}})
]}
],
function() {
navigateAndWait(getURLEchoCookie(), function() {
chrome.test.listenOnce(chrome.extension.onRequest, function(request) {
chrome.test.assertTrue(request.pass, "Invalid cookies. " +
JSON.stringify(request.cookies));
});
chrome.tabs.executeScript(tabId, {code:
"function hasCookie(name, value) {" +
" var entry = name + '=' + value;" +
" return document.body.innerText.indexOf(entry) >= 0;" +
"};" +
"var result = {};" +
"result.pass = hasCookie('requestCookie1', 'bar') && " +
" !hasCookie('requestCookie1', 'foo') && " +
" !hasCookie('requestCookie2', 'foo');" +
"result.cookies = document.body.innerText;" +
"chrome.extension.sendRequest(result);"});
});
}
);
},
function testEditResponseCookies() {
ignoreUnexpected = true;
expect();
onRequest.addRules(
[ {conditions: [new RequestMatcher({})],
actions: [
new AddResponseCookie({cookie: {name: "addedCookie", value: "Foo"}}),
new EditResponseCookie({filter: {name: "editedCookie"},
modification: {value: "bar"}}),
new RemoveResponseCookie({filter: {name: "deletedCookie"}})
]}
],
function() {
navigateAndWait(getURLSetCookie2(), function() {
chrome.test.listenOnce(chrome.extension.onRequest, function(request) {
chrome.test.assertTrue(request.pass, "Invalid cookies. " +
JSON.stringify(request.cookies));
});
chrome.tabs.executeScript(tabId, {code:
"var cookies = document.cookie.split('; ');" +
"var cookieMap = {};" +
"for (var i = 0; i < cookies.length; ++i) {" +
" var cookieParts = cookies[i].split('=');" +
" cookieMap[cookieParts[0]] = cookieParts[1];" +
"}" +
"var result = {};" +
"result.cookies = cookieMap;" +
"result.pass = (cookieMap.passedCookie === 'Foo') &&" +
" (cookieMap.addedCookie === 'Foo') &&" +
" (cookieMap.editedCookie === 'bar') &&" +
" !cookieMap.hasOwnProperty('deletedCookie');" +
"chrome.extension.sendRequest(result);"});
});
}
);
},
function testPermission() { function testPermission() {
// Test that a redirect is ignored if the extension has no permission. // Test that a redirect is ignored if the extension has no permission.
// we load a.html from a.com and issue an XHR to b.com, which is not // we load a.html from a.com and issue an XHR to b.com, which is not
...@@ -375,5 +467,4 @@ runTests([ ...@@ -375,5 +467,4 @@ runTests([
} }
); );
}, },
]); ]);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment