This source file includes following definitions.
- ClearCacheOnNavigationOnUI
- ParseCookieLifetime
- NullableEquals
- NullableEquals
- NullableEquals
- NullableEquals
- NullableEquals
- NullableEquals
- NullableEquals
- NullableEquals
- cancel
- CreateNetLogExtensionIdCallback
- NetLogModificationCallback
- InDecreasingExtensionInstallationTimeOrder
- StringToCharList
- CharListToString
- CalculateOnBeforeRequestDelta
- CalculateOnBeforeSendHeadersDelta
- CalculateOnHeadersReceivedDelta
- CalculateOnAuthRequiredDelta
- MergeCancelOfResponses
- MergeRedirectUrlOfResponsesHelper
- MergeRedirectUrlOfResponses
- MergeOnBeforeRequestResponses
- ParseRequestCookieLine
- SerializeRequestCookieLine
- DoesRequestCookieMatchFilter
- MergeAddRequestCookieModifications
- MergeEditRequestCookieModifications
- MergeRemoveRequestCookieModifications
- MergeCookiesInOnBeforeSendHeadersResponses
- FindSetRequestHeader
- FindRemoveRequestHeader
- MergeOnBeforeSendHeadersResponses
- GetResponseCookies
- StoreResponseCookies
- ApplyResponseCookieModification
- DoesResponseCookieMatchFilter
- MergeAddResponseCookieModifications
- MergeEditResponseCookieModifications
- MergeRemoveResponseCookieModifications
- MergeCookiesInOnHeadersReceivedResponses
- ToLowerCase
- FindRemoveResponseHeader
- MergeOnHeadersReceivedResponses
- MergeOnAuthRequiredResponses
- IsRelevantResourceType
- ResourceTypeToString
- ParseResourceType
- ClearCacheOnNavigation
- NotifyWebRequestAPIUsed
- IsValidHeaderName
- IsValidHeaderValue
#include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
#include <cmath>
#include "base/bind.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/web_request/web_request_api.h"
#include "chrome/browser/extensions/extension_warning_set.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/renderer_host/web_cache_manager.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/runtime_data.h"
#include "net/base/net_log.h"
#include "net/cookies/cookie_util.h"
#include "net/cookies/parsed_cookie.h"
#include "net/http/http_util.h"
#include "net/url_request/url_request.h"
using base::Time;
using extensions::ExtensionWarning;
namespace extension_web_request_api_helpers {
namespace {
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[] = {
"main_frame",
"sub_frame",
"stylesheet",
"script",
"image",
"object",
"xmlhttprequest",
"other",
"other",
};
static ResourceType::Type kResourceTypeValues[] = {
ResourceType::MAIN_FRAME,
ResourceType::SUB_FRAME,
ResourceType::STYLESHEET,
ResourceType::SCRIPT,
ResourceType::IMAGE,
ResourceType::OBJECT,
ResourceType::XHR,
ResourceType::LAST_TYPE,
ResourceType::LAST_TYPE,
};
COMPILE_ASSERT(
arraysize(kResourceTypeStrings) == arraysize(kResourceTypeValues),
keep_resource_types_in_sync);
void ClearCacheOnNavigationOnUI() {
WebCacheManager::GetInstance()->ClearCacheOnNavigation();
}
bool ParseCookieLifetime(net::ParsedCookie* cookie,
int64* seconds_till_expiry) {
if (cookie->HasMaxAge() &&
base::StringToInt64(cookie->MaxAge(), seconds_till_expiry)) {
return true;
}
Time parsed_expiry_time;
if (cookie->HasExpires())
parsed_expiry_time = net::cookie_util::ParseCookieTime(cookie->Expires());
if (!parsed_expiry_time.is_null()) {
*seconds_till_expiry =
ceil((parsed_expiry_time - Time::Now()).InSecondsF());
return *seconds_till_expiry >= 0;
}
return false;
}
bool NullableEquals(const int* a, const int* b) {
if ((a && !b) || (!a && b))
return false;
return (!a) || (*a == *b);
}
bool NullableEquals(const bool* a, const bool* b) {
if ((a && !b) || (!a && b))
return false;
return (!a) || (*a == *b);
}
bool NullableEquals(const std::string* a, const std::string* b) {
if ((a && !b) || (!a && b))
return false;
return (!a) || (*a == *b);
}
}
RequestCookie::RequestCookie() {}
RequestCookie::~RequestCookie() {}
bool NullableEquals(const RequestCookie* a, const RequestCookie* b) {
if ((a && !b) || (!a && b))
return false;
if (!a)
return true;
return NullableEquals(a->name.get(), b->name.get()) &&
NullableEquals(a->value.get(), b->value.get());
}
ResponseCookie::ResponseCookie() {}
ResponseCookie::~ResponseCookie() {}
bool NullableEquals(const ResponseCookie* a, const ResponseCookie* b) {
if ((a && !b) || (!a && b))
return false;
if (!a)
return true;
return NullableEquals(a->name.get(), b->name.get()) &&
NullableEquals(a->value.get(), b->value.get()) &&
NullableEquals(a->expires.get(), b->expires.get()) &&
NullableEquals(a->max_age.get(), b->max_age.get()) &&
NullableEquals(a->domain.get(), b->domain.get()) &&
NullableEquals(a->path.get(), b->path.get()) &&
NullableEquals(a->secure.get(), b->secure.get()) &&
NullableEquals(a->http_only.get(), b->http_only.get());
}
FilterResponseCookie::FilterResponseCookie() {}
FilterResponseCookie::~FilterResponseCookie() {}
bool NullableEquals(const FilterResponseCookie* a,
const FilterResponseCookie* b) {
if ((a && !b) || (!a && b))
return false;
if (!a)
return true;
return NullableEquals(a->age_lower_bound.get(), b->age_lower_bound.get()) &&
NullableEquals(a->age_upper_bound.get(), b->age_upper_bound.get()) &&
NullableEquals(a->session_cookie.get(), b->session_cookie.get());
}
RequestCookieModification::RequestCookieModification() {}
RequestCookieModification::~RequestCookieModification() {}
bool NullableEquals(const RequestCookieModification* a,
const RequestCookieModification* b) {
if ((a && !b) || (!a && b))
return false;
if (!a)
return true;
return NullableEquals(a->filter.get(), b->filter.get()) &&
NullableEquals(a->modification.get(), b->modification.get());
}
ResponseCookieModification::ResponseCookieModification() : type(ADD) {}
ResponseCookieModification::~ResponseCookieModification() {}
bool NullableEquals(const ResponseCookieModification* a,
const ResponseCookieModification* b) {
if ((a && !b) || (!a && b))
return false;
if (!a)
return true;
return a->type == b->type &&
NullableEquals(a->filter.get(), b->filter.get()) &&
NullableEquals(a->modification.get(), b->modification.get());
}
EventResponseDelta::EventResponseDelta(
const std::string& extension_id, const base::Time& extension_install_time)
: extension_id(extension_id),
extension_install_time(extension_install_time),
cancel(false) {
}
EventResponseDelta::~EventResponseDelta() {
}
net::NetLog::ParametersCallback CreateNetLogExtensionIdCallback(
const EventResponseDelta* delta) {
return net::NetLog::StringCallback("extension_id", &delta->extension_id);
}
base::Value* NetLogModificationCallback(
const EventResponseDelta* delta,
net::NetLog::LogLevel log_level) {
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetString("extension_id", delta->extension_id);
base::ListValue* modified_headers = new base::ListValue();
net::HttpRequestHeaders::Iterator modification(
delta->modified_request_headers);
while (modification.GetNext()) {
std::string line = modification.name() + ": " + modification.value();
modified_headers->Append(new base::StringValue(line));
}
dict->Set("modified_headers", modified_headers);
base::ListValue* deleted_headers = new base::ListValue();
for (std::vector<std::string>::const_iterator key =
delta->deleted_request_headers.begin();
key != delta->deleted_request_headers.end();
++key) {
deleted_headers->Append(new base::StringValue(*key));
}
dict->Set("deleted_headers", deleted_headers);
return dict;
}
bool InDecreasingExtensionInstallationTimeOrder(
const linked_ptr<EventResponseDelta>& a,
const linked_ptr<EventResponseDelta>& b) {
return a->extension_install_time > b->extension_install_time;
}
base::ListValue* StringToCharList(const std::string& s) {
base::ListValue* result = new base::ListValue;
for (size_t i = 0, n = s.size(); i < n; ++i) {
result->Append(
new base::FundamentalValue(
*reinterpret_cast<const unsigned char*>(&s[i])));
}
return result;
}
bool CharListToString(const base::ListValue* list, std::string* out) {
if (!list)
return false;
const size_t list_length = list->GetSize();
out->resize(list_length);
int value = 0;
for (size_t i = 0; i < list_length; ++i) {
if (!list->GetInteger(i, &value) || value < 0 || value > 255)
return false;
unsigned char tmp = static_cast<unsigned char>(value);
(*out)[i] = *reinterpret_cast<char*>(&tmp);
}
return true;
}
EventResponseDelta* CalculateOnBeforeRequestDelta(
const std::string& extension_id,
const base::Time& extension_install_time,
bool cancel,
const GURL& new_url) {
EventResponseDelta* result =
new EventResponseDelta(extension_id, extension_install_time);
result->cancel = cancel;
result->new_url = new_url;
return result;
}
EventResponseDelta* CalculateOnBeforeSendHeadersDelta(
const std::string& extension_id,
const base::Time& extension_install_time,
bool cancel,
net::HttpRequestHeaders* old_headers,
net::HttpRequestHeaders* new_headers) {
EventResponseDelta* result =
new EventResponseDelta(extension_id, extension_install_time);
result->cancel = cancel;
if (new_headers) {
{
net::HttpRequestHeaders::Iterator i(*old_headers);
while (i.GetNext()) {
if (!new_headers->HasHeader(i.name())) {
result->deleted_request_headers.push_back(i.name());
}
}
}
{
net::HttpRequestHeaders::Iterator i(*new_headers);
while (i.GetNext()) {
std::string value;
if (!old_headers->GetHeader(i.name(), &value) || i.value() != value) {
result->modified_request_headers.SetHeader(i.name(), i.value());
}
}
}
}
return result;
}
EventResponseDelta* CalculateOnHeadersReceivedDelta(
const std::string& extension_id,
const base::Time& extension_install_time,
bool cancel,
const GURL& new_url,
const net::HttpResponseHeaders* old_response_headers,
ResponseHeaders* new_response_headers) {
EventResponseDelta* result =
new EventResponseDelta(extension_id, extension_install_time);
result->cancel = cancel;
result->new_url = new_url;
if (!new_response_headers)
return result;
{
void* iter = NULL;
std::string name;
std::string value;
while (old_response_headers->EnumerateHeaderLines(&iter, &name, &value)) {
std::string name_lowercase(name);
StringToLowerASCII(&name_lowercase);
bool header_found = false;
for (ResponseHeaders::const_iterator i = new_response_headers->begin();
i != new_response_headers->end(); ++i) {
if (LowerCaseEqualsASCII(i->first, name_lowercase.c_str()) &&
value == i->second) {
header_found = true;
break;
}
}
if (!header_found)
result->deleted_response_headers.push_back(ResponseHeader(name, value));
}
}
{
for (ResponseHeaders::const_iterator i = new_response_headers->begin();
i != new_response_headers->end(); ++i) {
void* iter = NULL;
std::string value;
bool header_found = false;
while (old_response_headers->EnumerateHeader(&iter, i->first, &value) &&
!header_found) {
header_found = (value == i->second);
}
if (!header_found)
result->added_response_headers.push_back(*i);
}
}
return result;
}
EventResponseDelta* CalculateOnAuthRequiredDelta(
const std::string& extension_id,
const base::Time& extension_install_time,
bool cancel,
scoped_ptr<net::AuthCredentials>* auth_credentials) {
EventResponseDelta* result =
new EventResponseDelta(extension_id, extension_install_time);
result->cancel = cancel;
result->auth_credentials.swap(*auth_credentials);
return result;
}
void MergeCancelOfResponses(
const EventResponseDeltas& deltas,
bool* canceled,
const net::BoundNetLog* net_log) {
for (EventResponseDeltas::const_iterator i = deltas.begin();
i != deltas.end(); ++i) {
if ((*i)->cancel) {
*canceled = true;
net_log->AddEvent(
net::NetLog::TYPE_CHROME_EXTENSION_ABORTED_REQUEST,
CreateNetLogExtensionIdCallback(i->get()));
break;
}
}
}
static bool MergeRedirectUrlOfResponsesHelper(
const EventResponseDeltas& deltas,
GURL* new_url,
extensions::ExtensionWarningSet* conflicting_extensions,
const net::BoundNetLog* net_log,
bool consider_only_cancel_scheme_urls) {
bool redirected = false;
std::string winning_extension_id;
EventResponseDeltas::const_iterator delta;
for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
if ((*delta)->new_url.is_empty())
continue;
if (consider_only_cancel_scheme_urls &&
!(*delta)->new_url.SchemeIs(content::kDataScheme) &&
(*delta)->new_url.spec() != "about:blank") {
continue;
}
if (!redirected || *new_url == (*delta)->new_url) {
*new_url = (*delta)->new_url;
winning_extension_id = (*delta)->extension_id;
redirected = true;
net_log->AddEvent(
net::NetLog::TYPE_CHROME_EXTENSION_REDIRECTED_REQUEST,
CreateNetLogExtensionIdCallback(delta->get()));
} else {
conflicting_extensions->insert(
ExtensionWarning::CreateRedirectConflictWarning(
(*delta)->extension_id,
winning_extension_id,
(*delta)->new_url,
*new_url));
net_log->AddEvent(
net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
CreateNetLogExtensionIdCallback(delta->get()));
}
}
return redirected;
}
void MergeRedirectUrlOfResponses(
const EventResponseDeltas& deltas,
GURL* new_url,
extensions::ExtensionWarningSet* conflicting_extensions,
const net::BoundNetLog* net_log) {
if (MergeRedirectUrlOfResponsesHelper(
deltas, new_url, conflicting_extensions, net_log, true)) {
return;
}
MergeRedirectUrlOfResponsesHelper(
deltas, new_url, conflicting_extensions, net_log, false);
}
void MergeOnBeforeRequestResponses(
const EventResponseDeltas& deltas,
GURL* new_url,
extensions::ExtensionWarningSet* conflicting_extensions,
const net::BoundNetLog* net_log) {
MergeRedirectUrlOfResponses(deltas, new_url, conflicting_extensions, net_log);
}
static void ParseRequestCookieLine(
const std::string& header_value,
ParsedRequestCookies* parsed_cookies) {
std::string::const_iterator i = header_value.begin();
while (i != header_value.end()) {
while (i != header_value.end() && *i == ' ') ++i;
if (i == header_value.end()) return;
std::string::const_iterator cookie_name_beginning = i;
while (i != header_value.end() && *i != '=') ++i;
base::StringPiece cookie_name(cookie_name_beginning, i);
base::StringPiece cookie_value;
if (i != header_value.end()) {
++i;
std::string::const_iterator cookie_value_beginning = i;
if (*i == '"') {
++i;
while (i != header_value.end() && *i != '"') ++i;
if (i == header_value.end()) return;
++i;
cookie_value = base::StringPiece(cookie_value_beginning, i);
} else {
while (i != header_value.end() && *i != ';') ++i;
cookie_value = base::StringPiece(cookie_value_beginning, i);
}
}
parsed_cookies->push_back(make_pair(cookie_name, cookie_value));
if (i != header_value.end()) ++i;
}
}
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;
}
static bool MergeAddRequestCookieModifications(
const EventResponseDeltas& deltas,
ParsedRequestCookies* cookies) {
bool modified = false;
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;
}
static bool MergeEditRequestCookieModifications(
const EventResponseDeltas& deltas,
ParsedRequestCookies* cookies) {
bool modified = false;
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 (new_value && cookie->second != *new_value) {
cookie->second = *new_value;
modified = true;
}
}
}
}
return modified;
}
static bool MergeRemoveRequestCookieModifications(
const EventResponseDeltas& deltas,
ParsedRequestCookies* cookies) {
bool modified = false;
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,
extensions::ExtensionWarningSet* conflicting_extensions,
const net::BoundNetLog* net_log) {
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;
std::string cookie_header;
request_headers->GetHeader(net::HttpRequestHeaders::kCookie, &cookie_header);
ParsedRequestCookies cookies;
ParseRequestCookieLine(cookie_header, &cookies);
bool modified = false;
modified |= MergeAddRequestCookieModifications(deltas, &cookies);
modified |= MergeEditRequestCookieModifications(deltas, &cookies);
modified |= MergeRemoveRequestCookieModifications(deltas, &cookies);
if (modified) {
std::string new_cookie_header = SerializeRequestCookieLine(cookies);
request_headers->SetHeader(net::HttpRequestHeaders::kCookie,
new_cookie_header);
}
}
static std::string FindSetRequestHeader(
const EventResponseDeltas& deltas,
const std::string& key,
const std::string& value) {
EventResponseDeltas::const_iterator delta;
for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
net::HttpRequestHeaders::Iterator modification(
(*delta)->modified_request_headers);
while (modification.GetNext()) {
if (key == modification.name() && value == modification.value())
return (*delta)->extension_id;
}
}
return std::string();
}
static std::string FindRemoveRequestHeader(
const EventResponseDeltas& deltas,
const std::string& key) {
EventResponseDeltas::const_iterator delta;
for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
std::vector<std::string>::iterator i;
for (i = (*delta)->deleted_request_headers.begin();
i != (*delta)->deleted_request_headers.end();
++i) {
if (*i == key)
return (*delta)->extension_id;
}
}
return std::string();
}
void MergeOnBeforeSendHeadersResponses(
const EventResponseDeltas& deltas,
net::HttpRequestHeaders* request_headers,
extensions::ExtensionWarningSet* conflicting_extensions,
const net::BoundNetLog* net_log) {
EventResponseDeltas::const_iterator delta;
std::set<std::string> removed_headers;
std::set<std::string> set_headers;
for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
if ((*delta)->modified_request_headers.IsEmpty() &&
(*delta)->deleted_request_headers.empty()) {
continue;
}
bool extension_conflicts = false;
std::string winning_extension_id;
std::string conflicting_header;
{
net::HttpRequestHeaders::Iterator modification(
(*delta)->modified_request_headers);
while (modification.GetNext() && !extension_conflicts) {
const std::string& key = modification.name();
const std::string& value = modification.value();
if (removed_headers.find(key) != removed_headers.end() &&
!extension_conflicts) {
winning_extension_id = FindRemoveRequestHeader(deltas, key);
conflicting_header = key;
extension_conflicts = true;
}
if (set_headers.find(key) != set_headers.end() &&
!extension_conflicts) {
std::string current_value;
if (!request_headers->GetHeader(key, ¤t_value) ||
current_value != value) {
winning_extension_id =
FindSetRequestHeader(deltas, key, current_value);
conflicting_header = key;
extension_conflicts = true;
}
}
}
}
{
std::vector<std::string>::iterator key;
for (key = (*delta)->deleted_request_headers.begin();
key != (*delta)->deleted_request_headers.end() &&
!extension_conflicts;
++key) {
if (set_headers.find(*key) != set_headers.end()) {
std::string current_value;
request_headers->GetHeader(*key, ¤t_value);
winning_extension_id =
FindSetRequestHeader(deltas, *key, current_value);
conflicting_header = *key;
extension_conflicts = true;
}
}
}
if (!extension_conflicts) {
request_headers->MergeFrom((*delta)->modified_request_headers);
{
net::HttpRequestHeaders::Iterator modification(
(*delta)->modified_request_headers);
while (modification.GetNext())
set_headers.insert(modification.name());
}
{
std::vector<std::string>::iterator key;
for (key = (*delta)->deleted_request_headers.begin();
key != (*delta)->deleted_request_headers.end();
++key) {
request_headers->RemoveHeader(*key);
removed_headers.insert(*key);
}
}
net_log->AddEvent(
net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
base::Bind(&NetLogModificationCallback, delta->get()));
} else {
conflicting_extensions->insert(
ExtensionWarning::CreateRequestHeaderConflictWarning(
(*delta)->extension_id, winning_extension_id,
conflicting_header));
net_log->AddEvent(
net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
CreateNetLogExtensionIdCallback(delta->get()));
}
}
MergeCookiesInOnBeforeSendHeadersResponses(deltas, request_headers,
conflicting_extensions, net_log);
}
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;
}
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());
}
}
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,
FilterResponseCookie* 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() : std::string();
if (actual_value != *filter->expires)
return false;
}
if (filter->max_age.get()) {
std::string actual_value =
cookie->HasMaxAge() ? cookie->MaxAge() : std::string();
if (actual_value != base::IntToString(*filter->max_age))
return false;
}
if (filter->domain.get()) {
std::string actual_value =
cookie->HasDomain() ? cookie->Domain() : std::string();
if (actual_value != *filter->domain)
return false;
}
if (filter->path.get()) {
std::string actual_value =
cookie->HasPath() ? cookie->Path() : std::string();
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;
int64 seconds_till_expiry;
bool lifetime_parsed = false;
if (filter->age_upper_bound.get() ||
filter->age_lower_bound.get() ||
(filter->session_cookie.get() && *filter->session_cookie)) {
lifetime_parsed = ParseCookieLifetime(cookie, &seconds_till_expiry);
}
if (filter->age_upper_bound.get()) {
if (seconds_till_expiry > *filter->age_upper_bound)
return false;
}
if (filter->age_lower_bound.get()) {
if (seconds_till_expiry < *filter->age_lower_bound)
return false;
}
if (filter->session_cookie.get() &&
*filter->session_cookie &&
lifetime_parsed) {
return false;
}
return true;
}
static bool MergeAddResponseCookieModifications(
const EventResponseDeltas& deltas,
ParsedResponseCookies* cookies) {
bool modified = false;
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;
linked_ptr<net::ParsedCookie> cookie(
new net::ParsedCookie(std::string()));
ApplyResponseCookieModification((*mod)->modification.get(), cookie.get());
cookies->push_back(cookie);
modified = true;
}
}
return modified;
}
static bool MergeEditResponseCookieModifications(
const EventResponseDeltas& deltas,
ParsedResponseCookies* cookies) {
bool modified = false;
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;
}
static bool MergeRemoveResponseCookieModifications(
const EventResponseDeltas& deltas,
ParsedResponseCookies* cookies) {
bool modified = false;
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,
extensions::ExtensionWarningSet* conflicting_extensions,
const net::BoundNetLog* net_log) {
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;
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);
if (modified)
StoreResponseCookies(cookies, *override_response_headers);
}
static ResponseHeader ToLowerCase(const ResponseHeader& header) {
std::string lower_key(header.first);
StringToLowerASCII(&lower_key);
return ResponseHeader(lower_key, header.second);
}
static std::string FindRemoveResponseHeader(
const EventResponseDeltas& deltas,
const std::string& key) {
std::string lower_key = StringToLowerASCII(key);
EventResponseDeltas::const_iterator delta;
for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
ResponseHeaders::const_iterator i;
for (i = (*delta)->deleted_response_headers.begin();
i != (*delta)->deleted_response_headers.end(); ++i) {
if (StringToLowerASCII(i->first) == lower_key)
return (*delta)->extension_id;
}
}
return std::string();
}
void MergeOnHeadersReceivedResponses(
const EventResponseDeltas& deltas,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
GURL* allowed_unsafe_redirect_url,
extensions::ExtensionWarningSet* conflicting_extensions,
const net::BoundNetLog* net_log) {
EventResponseDeltas::const_iterator delta;
std::set<ResponseHeader> removed_headers;
std::set<ResponseHeader> added_headers;
for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
if ((*delta)->added_response_headers.empty() &&
(*delta)->deleted_response_headers.empty()) {
continue;
}
if (override_response_headers->get() == NULL) {
*override_response_headers = new net::HttpResponseHeaders(
original_response_headers->raw_headers());
}
bool extension_conflicts = false;
std::string conflicting_header;
std::string winning_extension_id;
ResponseHeaders::const_iterator i;
for (i = (*delta)->deleted_response_headers.begin();
i != (*delta)->deleted_response_headers.end(); ++i) {
if (removed_headers.find(ToLowerCase(*i)) != removed_headers.end()) {
winning_extension_id = FindRemoveResponseHeader(deltas, i->first);
conflicting_header = i->first;
extension_conflicts = true;
break;
}
}
if (!extension_conflicts) {
{
for (i = (*delta)->deleted_response_headers.begin();
i != (*delta)->deleted_response_headers.end(); ++i) {
(*override_response_headers)->RemoveHeaderLine(i->first, i->second);
removed_headers.insert(ToLowerCase(*i));
}
}
{
for (i = (*delta)->added_response_headers.begin();
i != (*delta)->added_response_headers.end(); ++i) {
ResponseHeader lowercase_header(ToLowerCase(*i));
if (added_headers.find(lowercase_header) != added_headers.end())
continue;
added_headers.insert(lowercase_header);
(*override_response_headers)->AddHeader(i->first + ": " + i->second);
}
}
net_log->AddEvent(
net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
CreateNetLogExtensionIdCallback(delta->get()));
} else {
conflicting_extensions->insert(
ExtensionWarning::CreateResponseHeaderConflictWarning(
(*delta)->extension_id, winning_extension_id,
conflicting_header));
net_log->AddEvent(
net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
CreateNetLogExtensionIdCallback(delta->get()));
}
}
MergeCookiesInOnHeadersReceivedResponses(deltas, original_response_headers,
override_response_headers, conflicting_extensions, net_log);
GURL new_url;
MergeRedirectUrlOfResponses(
deltas, &new_url, conflicting_extensions, net_log);
if (new_url.is_valid()) {
if (override_response_headers->get() == NULL) {
*override_response_headers = new net::HttpResponseHeaders(
original_response_headers->raw_headers());
}
(*override_response_headers)->ReplaceStatusLine("HTTP/1.1 302 Found");
(*override_response_headers)->RemoveHeader("location");
(*override_response_headers)->AddHeader("Location: " + new_url.spec());
*allowed_unsafe_redirect_url = new_url;
}
}
bool MergeOnAuthRequiredResponses(
const EventResponseDeltas& deltas,
net::AuthCredentials* auth_credentials,
extensions::ExtensionWarningSet* conflicting_extensions,
const net::BoundNetLog* net_log) {
CHECK(auth_credentials);
bool credentials_set = false;
std::string winning_extension_id;
for (EventResponseDeltas::const_iterator delta = deltas.begin();
delta != deltas.end();
++delta) {
if (!(*delta)->auth_credentials.get())
continue;
bool different =
auth_credentials->username() !=
(*delta)->auth_credentials->username() ||
auth_credentials->password() != (*delta)->auth_credentials->password();
if (credentials_set && different) {
conflicting_extensions->insert(
ExtensionWarning::CreateCredentialsConflictWarning(
(*delta)->extension_id, winning_extension_id));
net_log->AddEvent(
net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
CreateNetLogExtensionIdCallback(delta->get()));
} else {
net_log->AddEvent(
net::NetLog::TYPE_CHROME_EXTENSION_PROVIDE_AUTH_CREDENTIALS,
CreateNetLogExtensionIdCallback(delta->get()));
*auth_credentials = *(*delta)->auth_credentials;
credentials_set = true;
winning_extension_id = (*delta)->extension_id;
}
}
return credentials_set;
}
#define ARRAYEND(array) (array + arraysize(array))
bool IsRelevantResourceType(ResourceType::Type type) {
ResourceType::Type* iter =
std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), type);
return iter != ARRAYEND(kResourceTypeValues);
}
const char* ResourceTypeToString(ResourceType::Type type) {
ResourceType::Type* iter =
std::find(kResourceTypeValues, ARRAYEND(kResourceTypeValues), type);
if (iter == ARRAYEND(kResourceTypeValues))
return "other";
return kResourceTypeStrings[iter - kResourceTypeValues];
}
bool ParseResourceType(const std::string& type_str,
ResourceType::Type* type) {
const char** iter =
std::find(kResourceTypeStrings, ARRAYEND(kResourceTypeStrings), type_str);
if (iter == ARRAYEND(kResourceTypeStrings))
return false;
*type = kResourceTypeValues[iter - kResourceTypeStrings];
return true;
}
void ClearCacheOnNavigation() {
if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
ClearCacheOnNavigationOnUI();
} else {
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
base::Bind(&ClearCacheOnNavigationOnUI));
}
}
void NotifyWebRequestAPIUsed(
void* profile_id,
scoped_refptr<const extensions::Extension> extension) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Profile* profile = reinterpret_cast<Profile*>(profile_id);
if (!g_browser_process->profile_manager()->IsValidProfile(profile))
return;
extensions::RuntimeData* runtime_data =
extensions::ExtensionSystem::Get(profile)->runtime_data();
if (runtime_data->HasUsedWebRequest(extension.get()))
return;
runtime_data->SetHasUsedWebRequest(extension.get(), true);
content::BrowserContext* browser_context = profile;
for (content::RenderProcessHost::iterator it =
content::RenderProcessHost::AllHostsIterator();
!it.IsAtEnd(); it.Advance()) {
content::RenderProcessHost* host = it.GetCurrentValue();
if (host->GetBrowserContext() == browser_context)
SendExtensionWebRequestStatusToHost(host);
}
}
bool IsValidHeaderName(const std::string& name) {
return net::HttpUtil::IsToken(name);
}
bool IsValidHeaderValue(const std::string& value) {
return value.find('\0') == std::string::npos &&
value.find("\r\n") == std::string::npos;
}
}