Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(270)

Side by Side Diff: chrome/browser/extensions/api/web_request/web_request_api_helpers.cc

Issue 566823003: Move declarative_webrequest: action, rules_registry (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixing another weird re-base error. Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
6
7 #include <cmath>
8
9 #include "base/bind.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "base/values.h"
15 #include "chrome/browser/extensions/api/web_request/web_request_api.h"
16 #include "components/web_cache/browser/web_cache_manager.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "extensions/browser/extension_system.h"
20 #include "extensions/browser/runtime_data.h"
21 #include "extensions/browser/warning_set.h"
22 #include "net/base/net_log.h"
23 #include "net/cookies/cookie_util.h"
24 #include "net/cookies/parsed_cookie.h"
25 #include "net/http/http_util.h"
26 #include "net/url_request/url_request.h"
27 #include "url/url_constants.h"
28
29 // TODO(battre): move all static functions into an anonymous namespace at the
30 // top of this file.
31
32 using base::Time;
33 using net::cookie_util::ParsedRequestCookie;
34 using net::cookie_util::ParsedRequestCookies;
35
36 namespace extension_web_request_api_helpers {
37
38 namespace {
39
40 typedef std::vector<linked_ptr<net::ParsedCookie> > ParsedResponseCookies;
41
42 void ClearCacheOnNavigationOnUI() {
43 web_cache::WebCacheManager::GetInstance()->ClearCacheOnNavigation();
44 }
45
46 bool ParseCookieLifetime(net::ParsedCookie* cookie,
47 int64* seconds_till_expiry) {
48 // 'Max-Age' is processed first because according to:
49 // http://tools.ietf.org/html/rfc6265#section-5.3 'Max-Age' attribute
50 // overrides 'Expires' attribute.
51 if (cookie->HasMaxAge() &&
52 base::StringToInt64(cookie->MaxAge(), seconds_till_expiry)) {
53 return true;
54 }
55
56 Time parsed_expiry_time;
57 if (cookie->HasExpires())
58 parsed_expiry_time = net::cookie_util::ParseCookieTime(cookie->Expires());
59
60 if (!parsed_expiry_time.is_null()) {
61 *seconds_till_expiry =
62 ceil((parsed_expiry_time - Time::Now()).InSecondsF());
63 return *seconds_till_expiry >= 0;
64 }
65 return false;
66 }
67
68 bool NullableEquals(const int* a, const int* b) {
69 if ((a && !b) || (!a && b))
70 return false;
71 return (!a) || (*a == *b);
72 }
73
74 bool NullableEquals(const bool* a, const bool* b) {
75 if ((a && !b) || (!a && b))
76 return false;
77 return (!a) || (*a == *b);
78 }
79
80 bool NullableEquals(const std::string* a, const std::string* b) {
81 if ((a && !b) || (!a && b))
82 return false;
83 return (!a) || (*a == *b);
84 }
85
86 } // namespace
87
88 RequestCookie::RequestCookie() {}
89 RequestCookie::~RequestCookie() {}
90
91 bool NullableEquals(const RequestCookie* a, const RequestCookie* b) {
92 if ((a && !b) || (!a && b))
93 return false;
94 if (!a)
95 return true;
96 return NullableEquals(a->name.get(), b->name.get()) &&
97 NullableEquals(a->value.get(), b->value.get());
98 }
99
100 ResponseCookie::ResponseCookie() {}
101 ResponseCookie::~ResponseCookie() {}
102
103 bool NullableEquals(const ResponseCookie* a, const ResponseCookie* b) {
104 if ((a && !b) || (!a && b))
105 return false;
106 if (!a)
107 return true;
108 return NullableEquals(a->name.get(), b->name.get()) &&
109 NullableEquals(a->value.get(), b->value.get()) &&
110 NullableEquals(a->expires.get(), b->expires.get()) &&
111 NullableEquals(a->max_age.get(), b->max_age.get()) &&
112 NullableEquals(a->domain.get(), b->domain.get()) &&
113 NullableEquals(a->path.get(), b->path.get()) &&
114 NullableEquals(a->secure.get(), b->secure.get()) &&
115 NullableEquals(a->http_only.get(), b->http_only.get());
116 }
117
118 FilterResponseCookie::FilterResponseCookie() {}
119 FilterResponseCookie::~FilterResponseCookie() {}
120
121 bool NullableEquals(const FilterResponseCookie* a,
122 const FilterResponseCookie* b) {
123 if ((a && !b) || (!a && b))
124 return false;
125 if (!a)
126 return true;
127 return NullableEquals(a->age_lower_bound.get(), b->age_lower_bound.get()) &&
128 NullableEquals(a->age_upper_bound.get(), b->age_upper_bound.get()) &&
129 NullableEquals(a->session_cookie.get(), b->session_cookie.get());
130 }
131
132 RequestCookieModification::RequestCookieModification() {}
133 RequestCookieModification::~RequestCookieModification() {}
134
135 bool NullableEquals(const RequestCookieModification* a,
136 const RequestCookieModification* b) {
137 if ((a && !b) || (!a && b))
138 return false;
139 if (!a)
140 return true;
141 return NullableEquals(a->filter.get(), b->filter.get()) &&
142 NullableEquals(a->modification.get(), b->modification.get());
143 }
144
145 ResponseCookieModification::ResponseCookieModification() : type(ADD) {}
146 ResponseCookieModification::~ResponseCookieModification() {}
147
148 bool NullableEquals(const ResponseCookieModification* a,
149 const ResponseCookieModification* b) {
150 if ((a && !b) || (!a && b))
151 return false;
152 if (!a)
153 return true;
154 return a->type == b->type &&
155 NullableEquals(a->filter.get(), b->filter.get()) &&
156 NullableEquals(a->modification.get(), b->modification.get());
157 }
158
159 EventResponseDelta::EventResponseDelta(
160 const std::string& extension_id, const base::Time& extension_install_time)
161 : extension_id(extension_id),
162 extension_install_time(extension_install_time),
163 cancel(false) {
164 }
165
166 EventResponseDelta::~EventResponseDelta() {
167 }
168
169
170 // Creates a NetLog callback the returns a Value with the ID of the extension
171 // that caused an event. |delta| must remain valid for the lifetime of the
172 // callback.
173 net::NetLog::ParametersCallback CreateNetLogExtensionIdCallback(
174 const EventResponseDelta* delta) {
175 return net::NetLog::StringCallback("extension_id", &delta->extension_id);
176 }
177
178 // Creates NetLog parameters to indicate that an extension modified a request.
179 // Caller takes ownership of returned value.
180 base::Value* NetLogModificationCallback(
181 const EventResponseDelta* delta,
182 net::NetLog::LogLevel log_level) {
183 base::DictionaryValue* dict = new base::DictionaryValue();
184 dict->SetString("extension_id", delta->extension_id);
185
186 base::ListValue* modified_headers = new base::ListValue();
187 net::HttpRequestHeaders::Iterator modification(
188 delta->modified_request_headers);
189 while (modification.GetNext()) {
190 std::string line = modification.name() + ": " + modification.value();
191 modified_headers->Append(new base::StringValue(line));
192 }
193 dict->Set("modified_headers", modified_headers);
194
195 base::ListValue* deleted_headers = new base::ListValue();
196 for (std::vector<std::string>::const_iterator key =
197 delta->deleted_request_headers.begin();
198 key != delta->deleted_request_headers.end();
199 ++key) {
200 deleted_headers->Append(new base::StringValue(*key));
201 }
202 dict->Set("deleted_headers", deleted_headers);
203 return dict;
204 }
205
206 bool InDecreasingExtensionInstallationTimeOrder(
207 const linked_ptr<EventResponseDelta>& a,
208 const linked_ptr<EventResponseDelta>& b) {
209 return a->extension_install_time > b->extension_install_time;
210 }
211
212 base::ListValue* StringToCharList(const std::string& s) {
213 base::ListValue* result = new base::ListValue;
214 for (size_t i = 0, n = s.size(); i < n; ++i) {
215 result->Append(
216 new base::FundamentalValue(
217 *reinterpret_cast<const unsigned char*>(&s[i])));
218 }
219 return result;
220 }
221
222 bool CharListToString(const base::ListValue* list, std::string* out) {
223 if (!list)
224 return false;
225 const size_t list_length = list->GetSize();
226 out->resize(list_length);
227 int value = 0;
228 for (size_t i = 0; i < list_length; ++i) {
229 if (!list->GetInteger(i, &value) || value < 0 || value > 255)
230 return false;
231 unsigned char tmp = static_cast<unsigned char>(value);
232 (*out)[i] = *reinterpret_cast<char*>(&tmp);
233 }
234 return true;
235 }
236
237 EventResponseDelta* CalculateOnBeforeRequestDelta(
238 const std::string& extension_id,
239 const base::Time& extension_install_time,
240 bool cancel,
241 const GURL& new_url) {
242 EventResponseDelta* result =
243 new EventResponseDelta(extension_id, extension_install_time);
244 result->cancel = cancel;
245 result->new_url = new_url;
246 return result;
247 }
248
249 EventResponseDelta* CalculateOnBeforeSendHeadersDelta(
250 const std::string& extension_id,
251 const base::Time& extension_install_time,
252 bool cancel,
253 net::HttpRequestHeaders* old_headers,
254 net::HttpRequestHeaders* new_headers) {
255 EventResponseDelta* result =
256 new EventResponseDelta(extension_id, extension_install_time);
257 result->cancel = cancel;
258
259 // The event listener might not have passed any new headers if he
260 // just wanted to cancel the request.
261 if (new_headers) {
262 // Find deleted headers.
263 {
264 net::HttpRequestHeaders::Iterator i(*old_headers);
265 while (i.GetNext()) {
266 if (!new_headers->HasHeader(i.name())) {
267 result->deleted_request_headers.push_back(i.name());
268 }
269 }
270 }
271
272 // Find modified headers.
273 {
274 net::HttpRequestHeaders::Iterator i(*new_headers);
275 while (i.GetNext()) {
276 std::string value;
277 if (!old_headers->GetHeader(i.name(), &value) || i.value() != value) {
278 result->modified_request_headers.SetHeader(i.name(), i.value());
279 }
280 }
281 }
282 }
283 return result;
284 }
285
286 EventResponseDelta* CalculateOnHeadersReceivedDelta(
287 const std::string& extension_id,
288 const base::Time& extension_install_time,
289 bool cancel,
290 const GURL& new_url,
291 const net::HttpResponseHeaders* old_response_headers,
292 ResponseHeaders* new_response_headers) {
293 EventResponseDelta* result =
294 new EventResponseDelta(extension_id, extension_install_time);
295 result->cancel = cancel;
296 result->new_url = new_url;
297
298 if (!new_response_headers)
299 return result;
300
301 // Find deleted headers (header keys are treated case insensitively).
302 {
303 void* iter = NULL;
304 std::string name;
305 std::string value;
306 while (old_response_headers->EnumerateHeaderLines(&iter, &name, &value)) {
307 std::string name_lowercase(name);
308 base::StringToLowerASCII(&name_lowercase);
309
310 bool header_found = false;
311 for (ResponseHeaders::const_iterator i = new_response_headers->begin();
312 i != new_response_headers->end(); ++i) {
313 if (LowerCaseEqualsASCII(i->first, name_lowercase.c_str()) &&
314 value == i->second) {
315 header_found = true;
316 break;
317 }
318 }
319 if (!header_found)
320 result->deleted_response_headers.push_back(ResponseHeader(name, value));
321 }
322 }
323
324 // Find added headers (header keys are treated case insensitively).
325 {
326 for (ResponseHeaders::const_iterator i = new_response_headers->begin();
327 i != new_response_headers->end(); ++i) {
328 void* iter = NULL;
329 std::string value;
330 bool header_found = false;
331 while (old_response_headers->EnumerateHeader(&iter, i->first, &value) &&
332 !header_found) {
333 header_found = (value == i->second);
334 }
335 if (!header_found)
336 result->added_response_headers.push_back(*i);
337 }
338 }
339
340 return result;
341 }
342
343 EventResponseDelta* CalculateOnAuthRequiredDelta(
344 const std::string& extension_id,
345 const base::Time& extension_install_time,
346 bool cancel,
347 scoped_ptr<net::AuthCredentials>* auth_credentials) {
348 EventResponseDelta* result =
349 new EventResponseDelta(extension_id, extension_install_time);
350 result->cancel = cancel;
351 result->auth_credentials.swap(*auth_credentials);
352 return result;
353 }
354
355 void MergeCancelOfResponses(
356 const EventResponseDeltas& deltas,
357 bool* canceled,
358 const net::BoundNetLog* net_log) {
359 for (EventResponseDeltas::const_iterator i = deltas.begin();
360 i != deltas.end(); ++i) {
361 if ((*i)->cancel) {
362 *canceled = true;
363 net_log->AddEvent(
364 net::NetLog::TYPE_CHROME_EXTENSION_ABORTED_REQUEST,
365 CreateNetLogExtensionIdCallback(i->get()));
366 break;
367 }
368 }
369 }
370
371 // Helper function for MergeRedirectUrlOfResponses() that allows ignoring
372 // all redirects but those to data:// urls and about:blank. This is important
373 // to treat these URLs as "cancel urls", i.e. URLs that extensions redirect
374 // to if they want to express that they want to cancel a request. This reduces
375 // the number of conflicts that we need to flag, as canceling is considered
376 // a higher precedence operation that redirects.
377 // Returns whether a redirect occurred.
378 static bool MergeRedirectUrlOfResponsesHelper(
379 const EventResponseDeltas& deltas,
380 GURL* new_url,
381 extensions::WarningSet* conflicting_extensions,
382 const net::BoundNetLog* net_log,
383 bool consider_only_cancel_scheme_urls) {
384 bool redirected = false;
385
386 // Extension that determines the |new_url|.
387 std::string winning_extension_id;
388 EventResponseDeltas::const_iterator delta;
389 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
390 if ((*delta)->new_url.is_empty())
391 continue;
392 if (consider_only_cancel_scheme_urls &&
393 !(*delta)->new_url.SchemeIs(url::kDataScheme) &&
394 (*delta)->new_url.spec() != "about:blank") {
395 continue;
396 }
397
398 if (!redirected || *new_url == (*delta)->new_url) {
399 *new_url = (*delta)->new_url;
400 winning_extension_id = (*delta)->extension_id;
401 redirected = true;
402 net_log->AddEvent(
403 net::NetLog::TYPE_CHROME_EXTENSION_REDIRECTED_REQUEST,
404 CreateNetLogExtensionIdCallback(delta->get()));
405 } else {
406 conflicting_extensions->insert(
407 extensions::Warning::CreateRedirectConflictWarning(
408 (*delta)->extension_id,
409 winning_extension_id,
410 (*delta)->new_url,
411 *new_url));
412 net_log->AddEvent(
413 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
414 CreateNetLogExtensionIdCallback(delta->get()));
415 }
416 }
417 return redirected;
418 }
419
420 void MergeRedirectUrlOfResponses(
421 const EventResponseDeltas& deltas,
422 GURL* new_url,
423 extensions::WarningSet* conflicting_extensions,
424 const net::BoundNetLog* net_log) {
425
426 // First handle only redirects to data:// URLs and about:blank. These are a
427 // special case as they represent a way of cancelling a request.
428 if (MergeRedirectUrlOfResponsesHelper(
429 deltas, new_url, conflicting_extensions, net_log, true)) {
430 // If any extension cancelled a request by redirecting to a data:// URL or
431 // about:blank, we don't consider the other redirects.
432 return;
433 }
434
435 // Handle all other redirects.
436 MergeRedirectUrlOfResponsesHelper(
437 deltas, new_url, conflicting_extensions, net_log, false);
438 }
439
440 void MergeOnBeforeRequestResponses(
441 const EventResponseDeltas& deltas,
442 GURL* new_url,
443 extensions::WarningSet* conflicting_extensions,
444 const net::BoundNetLog* net_log) {
445 MergeRedirectUrlOfResponses(deltas, new_url, conflicting_extensions, net_log);
446 }
447
448 static bool DoesRequestCookieMatchFilter(
449 const ParsedRequestCookie& cookie,
450 RequestCookie* filter) {
451 if (!filter) return true;
452 if (filter->name.get() && cookie.first != *filter->name) return false;
453 if (filter->value.get() && cookie.second != *filter->value) return false;
454 return true;
455 }
456
457 // Applies all CookieModificationType::ADD operations for request cookies of
458 // |deltas| to |cookies|. Returns whether any cookie was added.
459 static bool MergeAddRequestCookieModifications(
460 const EventResponseDeltas& deltas,
461 ParsedRequestCookies* cookies) {
462 bool modified = false;
463 // We assume here that the deltas are sorted in decreasing extension
464 // precedence (i.e. decreasing extension installation time).
465 EventResponseDeltas::const_reverse_iterator delta;
466 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
467 const RequestCookieModifications& modifications =
468 (*delta)->request_cookie_modifications;
469 for (RequestCookieModifications::const_iterator mod = modifications.begin();
470 mod != modifications.end(); ++mod) {
471 if ((*mod)->type != ADD || !(*mod)->modification.get())
472 continue;
473 std::string* new_name = (*mod)->modification->name.get();
474 std::string* new_value = (*mod)->modification->value.get();
475 if (!new_name || !new_value)
476 continue;
477
478 bool cookie_with_same_name_found = false;
479 for (ParsedRequestCookies::iterator cookie = cookies->begin();
480 cookie != cookies->end() && !cookie_with_same_name_found; ++cookie) {
481 if (cookie->first == *new_name) {
482 if (cookie->second != *new_value) {
483 cookie->second = *new_value;
484 modified = true;
485 }
486 cookie_with_same_name_found = true;
487 }
488 }
489 if (!cookie_with_same_name_found) {
490 cookies->push_back(std::make_pair(base::StringPiece(*new_name),
491 base::StringPiece(*new_value)));
492 modified = true;
493 }
494 }
495 }
496 return modified;
497 }
498
499 // Applies all CookieModificationType::EDIT operations for request cookies of
500 // |deltas| to |cookies|. Returns whether any cookie was modified.
501 static bool MergeEditRequestCookieModifications(
502 const EventResponseDeltas& deltas,
503 ParsedRequestCookies* cookies) {
504 bool modified = false;
505 // We assume here that the deltas are sorted in decreasing extension
506 // precedence (i.e. decreasing extension installation time).
507 EventResponseDeltas::const_reverse_iterator delta;
508 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
509 const RequestCookieModifications& modifications =
510 (*delta)->request_cookie_modifications;
511 for (RequestCookieModifications::const_iterator mod = modifications.begin();
512 mod != modifications.end(); ++mod) {
513 if ((*mod)->type != EDIT || !(*mod)->modification.get())
514 continue;
515
516 std::string* new_value = (*mod)->modification->value.get();
517 RequestCookie* filter = (*mod)->filter.get();
518 for (ParsedRequestCookies::iterator cookie = cookies->begin();
519 cookie != cookies->end(); ++cookie) {
520 if (!DoesRequestCookieMatchFilter(*cookie, filter))
521 continue;
522 // If the edit operation tries to modify the cookie name, we just ignore
523 // this. We only modify the cookie value.
524 if (new_value && cookie->second != *new_value) {
525 cookie->second = *new_value;
526 modified = true;
527 }
528 }
529 }
530 }
531 return modified;
532 }
533
534 // Applies all CookieModificationType::REMOVE operations for request cookies of
535 // |deltas| to |cookies|. Returns whether any cookie was deleted.
536 static bool MergeRemoveRequestCookieModifications(
537 const EventResponseDeltas& deltas,
538 ParsedRequestCookies* cookies) {
539 bool modified = false;
540 // We assume here that the deltas are sorted in decreasing extension
541 // precedence (i.e. decreasing extension installation time).
542 EventResponseDeltas::const_reverse_iterator delta;
543 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
544 const RequestCookieModifications& modifications =
545 (*delta)->request_cookie_modifications;
546 for (RequestCookieModifications::const_iterator mod = modifications.begin();
547 mod != modifications.end(); ++mod) {
548 if ((*mod)->type != REMOVE)
549 continue;
550
551 RequestCookie* filter = (*mod)->filter.get();
552 ParsedRequestCookies::iterator i = cookies->begin();
553 while (i != cookies->end()) {
554 if (DoesRequestCookieMatchFilter(*i, filter)) {
555 i = cookies->erase(i);
556 modified = true;
557 } else {
558 ++i;
559 }
560 }
561 }
562 }
563 return modified;
564 }
565
566 void MergeCookiesInOnBeforeSendHeadersResponses(
567 const EventResponseDeltas& deltas,
568 net::HttpRequestHeaders* request_headers,
569 extensions::WarningSet* conflicting_extensions,
570 const net::BoundNetLog* net_log) {
571 // Skip all work if there are no registered cookie modifications.
572 bool cookie_modifications_exist = false;
573 EventResponseDeltas::const_iterator delta;
574 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
575 cookie_modifications_exist |=
576 !(*delta)->request_cookie_modifications.empty();
577 }
578 if (!cookie_modifications_exist)
579 return;
580
581 // Parse old cookie line.
582 std::string cookie_header;
583 request_headers->GetHeader(net::HttpRequestHeaders::kCookie, &cookie_header);
584 ParsedRequestCookies cookies;
585 net::cookie_util::ParseRequestCookieLine(cookie_header, &cookies);
586
587 // Modify cookies.
588 bool modified = false;
589 modified |= MergeAddRequestCookieModifications(deltas, &cookies);
590 modified |= MergeEditRequestCookieModifications(deltas, &cookies);
591 modified |= MergeRemoveRequestCookieModifications(deltas, &cookies);
592
593 // Reassemble and store new cookie line.
594 if (modified) {
595 std::string new_cookie_header =
596 net::cookie_util::SerializeRequestCookieLine(cookies);
597 request_headers->SetHeader(net::HttpRequestHeaders::kCookie,
598 new_cookie_header);
599 }
600 }
601
602 // Returns the extension ID of the first extension in |deltas| that sets the
603 // request header identified by |key| to |value|.
604 static std::string FindSetRequestHeader(
605 const EventResponseDeltas& deltas,
606 const std::string& key,
607 const std::string& value) {
608 EventResponseDeltas::const_iterator delta;
609 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
610 net::HttpRequestHeaders::Iterator modification(
611 (*delta)->modified_request_headers);
612 while (modification.GetNext()) {
613 if (key == modification.name() && value == modification.value())
614 return (*delta)->extension_id;
615 }
616 }
617 return std::string();
618 }
619
620 // Returns the extension ID of the first extension in |deltas| that removes the
621 // request header identified by |key|.
622 static std::string FindRemoveRequestHeader(
623 const EventResponseDeltas& deltas,
624 const std::string& key) {
625 EventResponseDeltas::const_iterator delta;
626 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
627 std::vector<std::string>::iterator i;
628 for (i = (*delta)->deleted_request_headers.begin();
629 i != (*delta)->deleted_request_headers.end();
630 ++i) {
631 if (*i == key)
632 return (*delta)->extension_id;
633 }
634 }
635 return std::string();
636 }
637
638 void MergeOnBeforeSendHeadersResponses(
639 const EventResponseDeltas& deltas,
640 net::HttpRequestHeaders* request_headers,
641 extensions::WarningSet* conflicting_extensions,
642 const net::BoundNetLog* net_log) {
643 EventResponseDeltas::const_iterator delta;
644
645 // Here we collect which headers we have removed or set to new values
646 // so far due to extensions of higher precedence.
647 std::set<std::string> removed_headers;
648 std::set<std::string> set_headers;
649
650 // We assume here that the deltas are sorted in decreasing extension
651 // precedence (i.e. decreasing extension installation time).
652 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
653 if ((*delta)->modified_request_headers.IsEmpty() &&
654 (*delta)->deleted_request_headers.empty()) {
655 continue;
656 }
657
658 // Check whether any modification affects a request header that
659 // has been modified differently before. As deltas is sorted by decreasing
660 // extension installation order, this takes care of precedence.
661 bool extension_conflicts = false;
662 std::string winning_extension_id;
663 std::string conflicting_header;
664 {
665 net::HttpRequestHeaders::Iterator modification(
666 (*delta)->modified_request_headers);
667 while (modification.GetNext() && !extension_conflicts) {
668 // This modification sets |key| to |value|.
669 const std::string& key = modification.name();
670 const std::string& value = modification.value();
671
672 // We must not delete anything that has been modified before.
673 if (removed_headers.find(key) != removed_headers.end() &&
674 !extension_conflicts) {
675 winning_extension_id = FindRemoveRequestHeader(deltas, key);
676 conflicting_header = key;
677 extension_conflicts = true;
678 }
679
680 // We must not modify anything that has been set to a *different*
681 // value before.
682 if (set_headers.find(key) != set_headers.end() &&
683 !extension_conflicts) {
684 std::string current_value;
685 if (!request_headers->GetHeader(key, &current_value) ||
686 current_value != value) {
687 winning_extension_id =
688 FindSetRequestHeader(deltas, key, current_value);
689 conflicting_header = key;
690 extension_conflicts = true;
691 }
692 }
693 }
694 }
695
696 // Check whether any deletion affects a request header that has been
697 // modified before.
698 {
699 std::vector<std::string>::iterator key;
700 for (key = (*delta)->deleted_request_headers.begin();
701 key != (*delta)->deleted_request_headers.end() &&
702 !extension_conflicts;
703 ++key) {
704 if (set_headers.find(*key) != set_headers.end()) {
705 std::string current_value;
706 request_headers->GetHeader(*key, &current_value);
707 winning_extension_id =
708 FindSetRequestHeader(deltas, *key, current_value);
709 conflicting_header = *key;
710 extension_conflicts = true;
711 }
712 }
713 }
714
715 // Now execute the modifications if there were no conflicts.
716 if (!extension_conflicts) {
717 // Copy all modifications into the original headers.
718 request_headers->MergeFrom((*delta)->modified_request_headers);
719 {
720 // Record which keys were changed.
721 net::HttpRequestHeaders::Iterator modification(
722 (*delta)->modified_request_headers);
723 while (modification.GetNext())
724 set_headers.insert(modification.name());
725 }
726
727 // Perform all deletions and record which keys were deleted.
728 {
729 std::vector<std::string>::iterator key;
730 for (key = (*delta)->deleted_request_headers.begin();
731 key != (*delta)->deleted_request_headers.end();
732 ++key) {
733 request_headers->RemoveHeader(*key);
734 removed_headers.insert(*key);
735 }
736 }
737 net_log->AddEvent(
738 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
739 base::Bind(&NetLogModificationCallback, delta->get()));
740 } else {
741 conflicting_extensions->insert(
742 extensions::Warning::CreateRequestHeaderConflictWarning(
743 (*delta)->extension_id, winning_extension_id,
744 conflicting_header));
745 net_log->AddEvent(
746 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
747 CreateNetLogExtensionIdCallback(delta->get()));
748 }
749 }
750
751 MergeCookiesInOnBeforeSendHeadersResponses(deltas, request_headers,
752 conflicting_extensions, net_log);
753 }
754
755 // Retrives all cookies from |override_response_headers|.
756 static ParsedResponseCookies GetResponseCookies(
757 scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
758 ParsedResponseCookies result;
759
760 void* iter = NULL;
761 std::string value;
762 while (override_response_headers->EnumerateHeader(&iter, "Set-Cookie",
763 &value)) {
764 result.push_back(make_linked_ptr(new net::ParsedCookie(value)));
765 }
766 return result;
767 }
768
769 // Stores all |cookies| in |override_response_headers| deleting previously
770 // existing cookie definitions.
771 static void StoreResponseCookies(
772 const ParsedResponseCookies& cookies,
773 scoped_refptr<net::HttpResponseHeaders> override_response_headers) {
774 override_response_headers->RemoveHeader("Set-Cookie");
775 for (ParsedResponseCookies::const_iterator i = cookies.begin();
776 i != cookies.end(); ++i) {
777 override_response_headers->AddHeader("Set-Cookie: " + (*i)->ToCookieLine());
778 }
779 }
780
781 // Modifies |cookie| according to |modification|. Each value that is set in
782 // |modification| is applied to |cookie|.
783 static bool ApplyResponseCookieModification(ResponseCookie* modification,
784 net::ParsedCookie* cookie) {
785 bool modified = false;
786 if (modification->name.get())
787 modified |= cookie->SetName(*modification->name);
788 if (modification->value.get())
789 modified |= cookie->SetValue(*modification->value);
790 if (modification->expires.get())
791 modified |= cookie->SetExpires(*modification->expires);
792 if (modification->max_age.get())
793 modified |= cookie->SetMaxAge(base::IntToString(*modification->max_age));
794 if (modification->domain.get())
795 modified |= cookie->SetDomain(*modification->domain);
796 if (modification->path.get())
797 modified |= cookie->SetPath(*modification->path);
798 if (modification->secure.get())
799 modified |= cookie->SetIsSecure(*modification->secure);
800 if (modification->http_only.get())
801 modified |= cookie->SetIsHttpOnly(*modification->http_only);
802 return modified;
803 }
804
805 static bool DoesResponseCookieMatchFilter(net::ParsedCookie* cookie,
806 FilterResponseCookie* filter) {
807 if (!cookie->IsValid()) return false;
808 if (!filter) return true;
809 if (filter->name && cookie->Name() != *filter->name)
810 return false;
811 if (filter->value && cookie->Value() != *filter->value)
812 return false;
813 if (filter->expires) {
814 std::string actual_value =
815 cookie->HasExpires() ? cookie->Expires() : std::string();
816 if (actual_value != *filter->expires)
817 return false;
818 }
819 if (filter->max_age) {
820 std::string actual_value =
821 cookie->HasMaxAge() ? cookie->MaxAge() : std::string();
822 if (actual_value != base::IntToString(*filter->max_age))
823 return false;
824 }
825 if (filter->domain) {
826 std::string actual_value =
827 cookie->HasDomain() ? cookie->Domain() : std::string();
828 if (actual_value != *filter->domain)
829 return false;
830 }
831 if (filter->path) {
832 std::string actual_value =
833 cookie->HasPath() ? cookie->Path() : std::string();
834 if (actual_value != *filter->path)
835 return false;
836 }
837 if (filter->secure && cookie->IsSecure() != *filter->secure)
838 return false;
839 if (filter->http_only && cookie->IsHttpOnly() != *filter->http_only)
840 return false;
841 if (filter->age_upper_bound || filter->age_lower_bound ||
842 (filter->session_cookie && *filter->session_cookie)) {
843 int64 seconds_to_expiry;
844 bool lifetime_parsed = ParseCookieLifetime(cookie, &seconds_to_expiry);
845 if (filter->age_upper_bound && seconds_to_expiry > *filter->age_upper_bound)
846 return false;
847 if (filter->age_lower_bound && seconds_to_expiry < *filter->age_lower_bound)
848 return false;
849 if (filter->session_cookie && *filter->session_cookie && lifetime_parsed)
850 return false;
851 }
852 return true;
853 }
854
855 // Applies all CookieModificationType::ADD operations for response cookies of
856 // |deltas| to |cookies|. Returns whether any cookie was added.
857 static bool MergeAddResponseCookieModifications(
858 const EventResponseDeltas& deltas,
859 ParsedResponseCookies* cookies) {
860 bool modified = false;
861 // We assume here that the deltas are sorted in decreasing extension
862 // precedence (i.e. decreasing extension installation time).
863 EventResponseDeltas::const_reverse_iterator delta;
864 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
865 const ResponseCookieModifications& modifications =
866 (*delta)->response_cookie_modifications;
867 for (ResponseCookieModifications::const_iterator mod =
868 modifications.begin(); mod != modifications.end(); ++mod) {
869 if ((*mod)->type != ADD || !(*mod)->modification.get())
870 continue;
871 // Cookie names are not unique in response cookies so we always append
872 // and never override.
873 linked_ptr<net::ParsedCookie> cookie(
874 new net::ParsedCookie(std::string()));
875 ApplyResponseCookieModification((*mod)->modification.get(), cookie.get());
876 cookies->push_back(cookie);
877 modified = true;
878 }
879 }
880 return modified;
881 }
882
883 // Applies all CookieModificationType::EDIT operations for response cookies of
884 // |deltas| to |cookies|. Returns whether any cookie was modified.
885 static bool MergeEditResponseCookieModifications(
886 const EventResponseDeltas& deltas,
887 ParsedResponseCookies* cookies) {
888 bool modified = false;
889 // We assume here that the deltas are sorted in decreasing extension
890 // precedence (i.e. decreasing extension installation time).
891 EventResponseDeltas::const_reverse_iterator delta;
892 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
893 const ResponseCookieModifications& modifications =
894 (*delta)->response_cookie_modifications;
895 for (ResponseCookieModifications::const_iterator mod =
896 modifications.begin(); mod != modifications.end(); ++mod) {
897 if ((*mod)->type != EDIT || !(*mod)->modification.get())
898 continue;
899
900 for (ParsedResponseCookies::iterator cookie = cookies->begin();
901 cookie != cookies->end(); ++cookie) {
902 if (DoesResponseCookieMatchFilter(cookie->get(),
903 (*mod)->filter.get())) {
904 modified |= ApplyResponseCookieModification(
905 (*mod)->modification.get(), cookie->get());
906 }
907 }
908 }
909 }
910 return modified;
911 }
912
913 // Applies all CookieModificationType::REMOVE operations for response cookies of
914 // |deltas| to |cookies|. Returns whether any cookie was deleted.
915 static bool MergeRemoveResponseCookieModifications(
916 const EventResponseDeltas& deltas,
917 ParsedResponseCookies* cookies) {
918 bool modified = false;
919 // We assume here that the deltas are sorted in decreasing extension
920 // precedence (i.e. decreasing extension installation time).
921 EventResponseDeltas::const_reverse_iterator delta;
922 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
923 const ResponseCookieModifications& modifications =
924 (*delta)->response_cookie_modifications;
925 for (ResponseCookieModifications::const_iterator mod =
926 modifications.begin(); mod != modifications.end(); ++mod) {
927 if ((*mod)->type != REMOVE)
928 continue;
929
930 ParsedResponseCookies::iterator i = cookies->begin();
931 while (i != cookies->end()) {
932 if (DoesResponseCookieMatchFilter(i->get(),
933 (*mod)->filter.get())) {
934 i = cookies->erase(i);
935 modified = true;
936 } else {
937 ++i;
938 }
939 }
940 }
941 }
942 return modified;
943 }
944
945 void MergeCookiesInOnHeadersReceivedResponses(
946 const EventResponseDeltas& deltas,
947 const net::HttpResponseHeaders* original_response_headers,
948 scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
949 extensions::WarningSet* conflicting_extensions,
950 const net::BoundNetLog* net_log) {
951 // Skip all work if there are no registered cookie modifications.
952 bool cookie_modifications_exist = false;
953 EventResponseDeltas::const_reverse_iterator delta;
954 for (delta = deltas.rbegin(); delta != deltas.rend(); ++delta) {
955 cookie_modifications_exist |=
956 !(*delta)->response_cookie_modifications.empty();
957 }
958 if (!cookie_modifications_exist)
959 return;
960
961 // Only create a copy if we really want to modify the response headers.
962 if (override_response_headers->get() == NULL) {
963 *override_response_headers = new net::HttpResponseHeaders(
964 original_response_headers->raw_headers());
965 }
966
967 ParsedResponseCookies cookies =
968 GetResponseCookies(*override_response_headers);
969
970 bool modified = false;
971 modified |= MergeAddResponseCookieModifications(deltas, &cookies);
972 modified |= MergeEditResponseCookieModifications(deltas, &cookies);
973 modified |= MergeRemoveResponseCookieModifications(deltas, &cookies);
974
975 // Store new value.
976 if (modified)
977 StoreResponseCookies(cookies, *override_response_headers);
978 }
979
980 // Converts the key of the (key, value) pair to lower case.
981 static ResponseHeader ToLowerCase(const ResponseHeader& header) {
982 std::string lower_key(header.first);
983 base::StringToLowerASCII(&lower_key);
984 return ResponseHeader(lower_key, header.second);
985 }
986
987 // Returns the extension ID of the first extension in |deltas| that removes the
988 // request header identified by |key|.
989 static std::string FindRemoveResponseHeader(
990 const EventResponseDeltas& deltas,
991 const std::string& key) {
992 std::string lower_key = base::StringToLowerASCII(key);
993 EventResponseDeltas::const_iterator delta;
994 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
995 ResponseHeaders::const_iterator i;
996 for (i = (*delta)->deleted_response_headers.begin();
997 i != (*delta)->deleted_response_headers.end(); ++i) {
998 if (base::StringToLowerASCII(i->first) == lower_key)
999 return (*delta)->extension_id;
1000 }
1001 }
1002 return std::string();
1003 }
1004
1005 void MergeOnHeadersReceivedResponses(
1006 const EventResponseDeltas& deltas,
1007 const net::HttpResponseHeaders* original_response_headers,
1008 scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
1009 GURL* allowed_unsafe_redirect_url,
1010 extensions::WarningSet* conflicting_extensions,
1011 const net::BoundNetLog* net_log) {
1012 EventResponseDeltas::const_iterator delta;
1013
1014 // Here we collect which headers we have removed or added so far due to
1015 // extensions of higher precedence. Header keys are always stored as
1016 // lower case.
1017 std::set<ResponseHeader> removed_headers;
1018 std::set<ResponseHeader> added_headers;
1019
1020 // We assume here that the deltas are sorted in decreasing extension
1021 // precedence (i.e. decreasing extension installation time).
1022 for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
1023 if ((*delta)->added_response_headers.empty() &&
1024 (*delta)->deleted_response_headers.empty()) {
1025 continue;
1026 }
1027
1028 // Only create a copy if we really want to modify the response headers.
1029 if (override_response_headers->get() == NULL) {
1030 *override_response_headers = new net::HttpResponseHeaders(
1031 original_response_headers->raw_headers());
1032 }
1033
1034 // We consider modifications as pairs of (delete, add) operations.
1035 // If a header is deleted twice by different extensions we assume that the
1036 // intention was to modify it to different values and consider this a
1037 // conflict. As deltas is sorted by decreasing extension installation order,
1038 // this takes care of precedence.
1039 bool extension_conflicts = false;
1040 std::string conflicting_header;
1041 std::string winning_extension_id;
1042 ResponseHeaders::const_iterator i;
1043 for (i = (*delta)->deleted_response_headers.begin();
1044 i != (*delta)->deleted_response_headers.end(); ++i) {
1045 if (removed_headers.find(ToLowerCase(*i)) != removed_headers.end()) {
1046 winning_extension_id = FindRemoveResponseHeader(deltas, i->first);
1047 conflicting_header = i->first;
1048 extension_conflicts = true;
1049 break;
1050 }
1051 }
1052
1053 // Now execute the modifications if there were no conflicts.
1054 if (!extension_conflicts) {
1055 // Delete headers
1056 {
1057 for (i = (*delta)->deleted_response_headers.begin();
1058 i != (*delta)->deleted_response_headers.end(); ++i) {
1059 (*override_response_headers)->RemoveHeaderLine(i->first, i->second);
1060 removed_headers.insert(ToLowerCase(*i));
1061 }
1062 }
1063
1064 // Add headers.
1065 {
1066 for (i = (*delta)->added_response_headers.begin();
1067 i != (*delta)->added_response_headers.end(); ++i) {
1068 ResponseHeader lowercase_header(ToLowerCase(*i));
1069 if (added_headers.find(lowercase_header) != added_headers.end())
1070 continue;
1071 added_headers.insert(lowercase_header);
1072 (*override_response_headers)->AddHeader(i->first + ": " + i->second);
1073 }
1074 }
1075 net_log->AddEvent(
1076 net::NetLog::TYPE_CHROME_EXTENSION_MODIFIED_HEADERS,
1077 CreateNetLogExtensionIdCallback(delta->get()));
1078 } else {
1079 conflicting_extensions->insert(
1080 extensions::Warning::CreateResponseHeaderConflictWarning(
1081 (*delta)->extension_id, winning_extension_id,
1082 conflicting_header));
1083 net_log->AddEvent(
1084 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
1085 CreateNetLogExtensionIdCallback(delta->get()));
1086 }
1087 }
1088
1089 MergeCookiesInOnHeadersReceivedResponses(deltas, original_response_headers,
1090 override_response_headers, conflicting_extensions, net_log);
1091
1092 GURL new_url;
1093 MergeRedirectUrlOfResponses(
1094 deltas, &new_url, conflicting_extensions, net_log);
1095 if (new_url.is_valid()) {
1096 // Only create a copy if we really want to modify the response headers.
1097 if (override_response_headers->get() == NULL) {
1098 *override_response_headers = new net::HttpResponseHeaders(
1099 original_response_headers->raw_headers());
1100 }
1101 (*override_response_headers)->ReplaceStatusLine("HTTP/1.1 302 Found");
1102 (*override_response_headers)->RemoveHeader("location");
1103 (*override_response_headers)->AddHeader("Location: " + new_url.spec());
1104 // Explicitly mark the URL as safe for redirection, to prevent the request
1105 // from being blocked because of net::ERR_UNSAFE_REDIRECT.
1106 *allowed_unsafe_redirect_url = new_url;
1107 }
1108 }
1109
1110 bool MergeOnAuthRequiredResponses(
1111 const EventResponseDeltas& deltas,
1112 net::AuthCredentials* auth_credentials,
1113 extensions::WarningSet* conflicting_extensions,
1114 const net::BoundNetLog* net_log) {
1115 CHECK(auth_credentials);
1116 bool credentials_set = false;
1117 std::string winning_extension_id;
1118
1119 for (EventResponseDeltas::const_iterator delta = deltas.begin();
1120 delta != deltas.end();
1121 ++delta) {
1122 if (!(*delta)->auth_credentials.get())
1123 continue;
1124 bool different =
1125 auth_credentials->username() !=
1126 (*delta)->auth_credentials->username() ||
1127 auth_credentials->password() != (*delta)->auth_credentials->password();
1128 if (credentials_set && different) {
1129 conflicting_extensions->insert(
1130 extensions::Warning::CreateCredentialsConflictWarning(
1131 (*delta)->extension_id, winning_extension_id));
1132 net_log->AddEvent(
1133 net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
1134 CreateNetLogExtensionIdCallback(delta->get()));
1135 } else {
1136 net_log->AddEvent(
1137 net::NetLog::TYPE_CHROME_EXTENSION_PROVIDE_AUTH_CREDENTIALS,
1138 CreateNetLogExtensionIdCallback(delta->get()));
1139 *auth_credentials = *(*delta)->auth_credentials;
1140 credentials_set = true;
1141 winning_extension_id = (*delta)->extension_id;
1142 }
1143 }
1144 return credentials_set;
1145 }
1146
1147 void ClearCacheOnNavigation() {
1148 if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
1149 ClearCacheOnNavigationOnUI();
1150 } else {
1151 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
1152 base::Bind(&ClearCacheOnNavigationOnUI));
1153 }
1154 }
1155
1156 void NotifyWebRequestAPIUsed(
1157 void* browser_context_id,
1158 scoped_refptr<const extensions::Extension> extension) {
1159 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1160 content::BrowserContext* browser_context =
1161 reinterpret_cast<content::BrowserContext*>(browser_context_id);
1162 if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(
1163 browser_context))
1164 return;
1165
1166 extensions::RuntimeData* runtime_data =
1167 extensions::ExtensionSystem::Get(browser_context)->runtime_data();
1168 if (runtime_data->HasUsedWebRequest(extension.get()))
1169 return;
1170 runtime_data->SetHasUsedWebRequest(extension.get(), true);
1171
1172 for (content::RenderProcessHost::iterator it =
1173 content::RenderProcessHost::AllHostsIterator();
1174 !it.IsAtEnd(); it.Advance()) {
1175 content::RenderProcessHost* host = it.GetCurrentValue();
1176 if (host->GetBrowserContext() == browser_context)
1177 SendExtensionWebRequestStatusToHost(host);
1178 }
1179 }
1180
1181 } // namespace extension_web_request_api_helpers
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698