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/declarative_webrequest/webrequest_action.h"
6
7#include <limits>
8
9#include "base/lazy_instance.h"
10#include "base/logging.h"
11#include "base/strings/string_util.h"
12#include "base/strings/stringprintf.h"
13#include "base/values.h"
14#include "chrome/browser/extensions/api/declarative/deduping_factory.h"
15#include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h"
16#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h"
17#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h"
18#include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
19#include "chrome/browser/extensions/api/web_request/web_request_permissions.h"
20#include "content/public/common/url_constants.h"
21#include "extensions/browser/info_map.h"
22#include "extensions/common/extension.h"
23#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
24#include "net/url_request/url_request.h"
25#include "third_party/re2/re2/re2.h"
26
27namespace extensions {
28
29namespace helpers = extension_web_request_api_helpers;
30namespace keys = declarative_webrequest_constants;
31
32namespace {
33// Error messages.
34const char kIgnoreRulesRequiresParameterError[] =
35    "IgnoreRules requires at least one parameter.";
36
37const char kTransparentImageUrl[] = "data:image/png;base64,iVBORw0KGgoAAAANSUh"
38    "EUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==";
39const char kEmptyDocumentUrl[] = "data:text/html,";
40
41#define INPUT_FORMAT_VALIDATE(test) do { \
42    if (!(test)) { \
43      *bad_message = true; \
44      return scoped_refptr<const WebRequestAction>(NULL); \
45    } \
46  } while (0)
47
48scoped_ptr<helpers::RequestCookie> ParseRequestCookie(
49    const base::DictionaryValue* dict) {
50  scoped_ptr<helpers::RequestCookie> result(new helpers::RequestCookie);
51  std::string tmp;
52  if (dict->GetString(keys::kNameKey, &tmp))
53    result->name.reset(new std::string(tmp));
54  if (dict->GetString(keys::kValueKey, &tmp))
55    result->value.reset(new std::string(tmp));
56  return result.Pass();
57}
58
59void ParseResponseCookieImpl(const base::DictionaryValue* dict,
60                             helpers::ResponseCookie* cookie) {
61  std::string string_tmp;
62  int int_tmp = 0;
63  bool bool_tmp = false;
64  if (dict->GetString(keys::kNameKey, &string_tmp))
65    cookie->name.reset(new std::string(string_tmp));
66  if (dict->GetString(keys::kValueKey, &string_tmp))
67    cookie->value.reset(new std::string(string_tmp));
68  if (dict->GetString(keys::kExpiresKey, &string_tmp))
69    cookie->expires.reset(new std::string(string_tmp));
70  if (dict->GetInteger(keys::kMaxAgeKey, &int_tmp))
71    cookie->max_age.reset(new int(int_tmp));
72  if (dict->GetString(keys::kDomainKey, &string_tmp))
73    cookie->domain.reset(new std::string(string_tmp));
74  if (dict->GetString(keys::kPathKey, &string_tmp))
75    cookie->path.reset(new std::string(string_tmp));
76  if (dict->GetBoolean(keys::kSecureKey, &bool_tmp))
77    cookie->secure.reset(new bool(bool_tmp));
78  if (dict->GetBoolean(keys::kHttpOnlyKey, &bool_tmp))
79    cookie->http_only.reset(new bool(bool_tmp));
80}
81
82scoped_ptr<helpers::ResponseCookie> ParseResponseCookie(
83    const base::DictionaryValue* dict) {
84  scoped_ptr<helpers::ResponseCookie> result(new helpers::ResponseCookie);
85  ParseResponseCookieImpl(dict, result.get());
86  return result.Pass();
87}
88
89scoped_ptr<helpers::FilterResponseCookie> ParseFilterResponseCookie(
90    const base::DictionaryValue* dict) {
91  scoped_ptr<helpers::FilterResponseCookie> result(
92      new helpers::FilterResponseCookie);
93  ParseResponseCookieImpl(dict, result.get());
94
95  int int_tmp = 0;
96  bool bool_tmp = false;
97  if (dict->GetInteger(keys::kAgeUpperBoundKey, &int_tmp))
98    result->age_upper_bound.reset(new int(int_tmp));
99  if (dict->GetInteger(keys::kAgeLowerBoundKey, &int_tmp))
100    result->age_lower_bound.reset(new int(int_tmp));
101  if (dict->GetBoolean(keys::kSessionCookieKey, &bool_tmp))
102    result->session_cookie.reset(new bool(bool_tmp));
103  return result.Pass();
104}
105
106// Helper function for WebRequestActions that can be instantiated by just
107// calling the constructor.
108template <class T>
109scoped_refptr<const WebRequestAction> CallConstructorFactoryMethod(
110    const std::string& instance_type,
111    const base::Value* value,
112    std::string* error,
113    bool* bad_message) {
114  return scoped_refptr<const WebRequestAction>(new T);
115}
116
117scoped_refptr<const WebRequestAction> CreateRedirectRequestAction(
118    const std::string& instance_type,
119    const base::Value* value,
120    std::string* error,
121    bool* bad_message) {
122  const base::DictionaryValue* dict = NULL;
123  CHECK(value->GetAsDictionary(&dict));
124  std::string redirect_url_string;
125  INPUT_FORMAT_VALIDATE(
126      dict->GetString(keys::kRedirectUrlKey, &redirect_url_string));
127  GURL redirect_url(redirect_url_string);
128  return scoped_refptr<const WebRequestAction>(
129      new WebRequestRedirectAction(redirect_url));
130}
131
132scoped_refptr<const WebRequestAction> CreateRedirectRequestByRegExAction(
133    const std::string& instance_type,
134    const base::Value* value,
135    std::string* error,
136    bool* bad_message) {
137  const base::DictionaryValue* dict = NULL;
138  CHECK(value->GetAsDictionary(&dict));
139  std::string from;
140  std::string to;
141  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kFromKey, &from));
142  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kToKey, &to));
143
144  to = WebRequestRedirectByRegExAction::PerlToRe2Style(to);
145
146  RE2::Options options;
147  options.set_case_sensitive(false);
148  scoped_ptr<RE2> from_pattern(new RE2(from, options));
149
150  if (!from_pattern->ok()) {
151    *error = "Invalid pattern '" + from + "' -> '" + to + "'";
152    return scoped_refptr<const WebRequestAction>(NULL);
153  }
154  return scoped_refptr<const WebRequestAction>(
155      new WebRequestRedirectByRegExAction(from_pattern.Pass(), to));
156}
157
158scoped_refptr<const WebRequestAction> CreateSetRequestHeaderAction(
159    const std::string& instance_type,
160    const base::Value* json_value,
161    std::string* error,
162    bool* bad_message) {
163  const base::DictionaryValue* dict = NULL;
164  CHECK(json_value->GetAsDictionary(&dict));
165  std::string name;
166  std::string value;
167  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kNameKey, &name));
168  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kValueKey, &value));
169  return scoped_refptr<const WebRequestAction>(
170      new WebRequestSetRequestHeaderAction(name, value));
171}
172
173scoped_refptr<const WebRequestAction> CreateRemoveRequestHeaderAction(
174    const std::string& instance_type,
175    const base::Value* value,
176    std::string* error,
177    bool* bad_message) {
178  const base::DictionaryValue* dict = NULL;
179  CHECK(value->GetAsDictionary(&dict));
180  std::string name;
181  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kNameKey, &name));
182  return scoped_refptr<const WebRequestAction>(
183      new WebRequestRemoveRequestHeaderAction(name));
184}
185
186scoped_refptr<const WebRequestAction> CreateAddResponseHeaderAction(
187    const std::string& instance_type,
188    const base::Value* json_value,
189    std::string* error,
190    bool* bad_message) {
191  const base::DictionaryValue* dict = NULL;
192  CHECK(json_value->GetAsDictionary(&dict));
193  std::string name;
194  std::string value;
195  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kNameKey, &name));
196  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kValueKey, &value));
197  return scoped_refptr<const WebRequestAction>(
198      new WebRequestAddResponseHeaderAction(name, value));
199}
200
201scoped_refptr<const WebRequestAction> CreateRemoveResponseHeaderAction(
202    const std::string& instance_type,
203    const base::Value* json_value,
204    std::string* error,
205    bool* bad_message) {
206  const base::DictionaryValue* dict = NULL;
207  CHECK(json_value->GetAsDictionary(&dict));
208  std::string name;
209  std::string value;
210  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kNameKey, &name));
211  bool has_value = dict->GetString(keys::kValueKey, &value);
212  return scoped_refptr<const WebRequestAction>(
213      new WebRequestRemoveResponseHeaderAction(name, value, has_value));
214}
215
216scoped_refptr<const WebRequestAction> CreateIgnoreRulesAction(
217    const std::string& instance_type,
218    const base::Value* value,
219    std::string* error,
220    bool* bad_message) {
221  const base::DictionaryValue* dict = NULL;
222  CHECK(value->GetAsDictionary(&dict));
223  bool has_parameter = false;
224  int minimum_priority = std::numeric_limits<int>::min();
225  std::string ignore_tag;
226  if (dict->HasKey(keys::kLowerPriorityThanKey)) {
227    INPUT_FORMAT_VALIDATE(
228        dict->GetInteger(keys::kLowerPriorityThanKey, &minimum_priority));
229    has_parameter = true;
230  }
231  if (dict->HasKey(keys::kHasTagKey)) {
232    INPUT_FORMAT_VALIDATE(dict->GetString(keys::kHasTagKey, &ignore_tag));
233    has_parameter = true;
234  }
235  if (!has_parameter) {
236    *error = kIgnoreRulesRequiresParameterError;
237    return scoped_refptr<const WebRequestAction>(NULL);
238  }
239  return scoped_refptr<const WebRequestAction>(
240      new WebRequestIgnoreRulesAction(minimum_priority, ignore_tag));
241}
242
243scoped_refptr<const WebRequestAction> CreateRequestCookieAction(
244    const std::string& instance_type,
245    const base::Value* value,
246    std::string* error,
247    bool* bad_message) {
248  using extension_web_request_api_helpers::RequestCookieModification;
249
250  const base::DictionaryValue* dict = NULL;
251  CHECK(value->GetAsDictionary(&dict));
252
253  linked_ptr<RequestCookieModification> modification(
254      new RequestCookieModification);
255
256  // Get modification type.
257  if (instance_type == keys::kAddRequestCookieType)
258    modification->type = helpers::ADD;
259  else if (instance_type == keys::kEditRequestCookieType)
260    modification->type = helpers::EDIT;
261  else if (instance_type == keys::kRemoveRequestCookieType)
262    modification->type = helpers::REMOVE;
263  else
264    INPUT_FORMAT_VALIDATE(false);
265
266  // Get filter.
267  if (modification->type == helpers::EDIT ||
268      modification->type == helpers::REMOVE) {
269    const base::DictionaryValue* filter = NULL;
270    INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kFilterKey, &filter));
271    modification->filter = ParseRequestCookie(filter);
272  }
273
274  // Get new value.
275  if (modification->type == helpers::ADD) {
276    const base::DictionaryValue* value = NULL;
277    INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kCookieKey, &value));
278    modification->modification = ParseRequestCookie(value);
279  } else if (modification->type == helpers::EDIT) {
280    const base::DictionaryValue* value = NULL;
281    INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kModificationKey, &value));
282    modification->modification = ParseRequestCookie(value);
283  }
284
285  return scoped_refptr<const WebRequestAction>(
286      new WebRequestRequestCookieAction(modification));
287}
288
289scoped_refptr<const WebRequestAction> CreateResponseCookieAction(
290    const std::string& instance_type,
291    const base::Value* value,
292    std::string* error,
293    bool* bad_message) {
294  using extension_web_request_api_helpers::ResponseCookieModification;
295
296  const base::DictionaryValue* dict = NULL;
297  CHECK(value->GetAsDictionary(&dict));
298
299  linked_ptr<ResponseCookieModification> modification(
300      new ResponseCookieModification);
301
302  // Get modification type.
303  if (instance_type == keys::kAddResponseCookieType)
304    modification->type = helpers::ADD;
305  else if (instance_type == keys::kEditResponseCookieType)
306    modification->type = helpers::EDIT;
307  else if (instance_type == keys::kRemoveResponseCookieType)
308    modification->type = helpers::REMOVE;
309  else
310    INPUT_FORMAT_VALIDATE(false);
311
312  // Get filter.
313  if (modification->type == helpers::EDIT ||
314      modification->type == helpers::REMOVE) {
315    const base::DictionaryValue* filter = NULL;
316    INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kFilterKey, &filter));
317    modification->filter = ParseFilterResponseCookie(filter);
318  }
319
320  // Get new value.
321  if (modification->type == helpers::ADD) {
322    const base::DictionaryValue* value = NULL;
323    INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kCookieKey, &value));
324    modification->modification = ParseResponseCookie(value);
325  } else if (modification->type == helpers::EDIT) {
326    const base::DictionaryValue* value = NULL;
327    INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kModificationKey, &value));
328    modification->modification = ParseResponseCookie(value);
329  }
330
331  return scoped_refptr<const WebRequestAction>(
332      new WebRequestResponseCookieAction(modification));
333}
334
335scoped_refptr<const WebRequestAction> CreateSendMessageToExtensionAction(
336    const std::string& name,
337    const base::Value* value,
338    std::string* error,
339    bool* bad_message) {
340  const base::DictionaryValue* dict = NULL;
341  CHECK(value->GetAsDictionary(&dict));
342  std::string message;
343  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kMessageKey, &message));
344  return scoped_refptr<const WebRequestAction>(
345      new WebRequestSendMessageToExtensionAction(message));
346}
347
348struct WebRequestActionFactory {
349  DedupingFactory<WebRequestAction> factory;
350
351  WebRequestActionFactory() : factory(5) {
352    factory.RegisterFactoryMethod(
353        keys::kAddRequestCookieType,
354        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
355        &CreateRequestCookieAction);
356    factory.RegisterFactoryMethod(
357        keys::kAddResponseCookieType,
358        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
359        &CreateResponseCookieAction);
360    factory.RegisterFactoryMethod(
361        keys::kAddResponseHeaderType,
362        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
363        &CreateAddResponseHeaderAction);
364    factory.RegisterFactoryMethod(
365        keys::kCancelRequestType,
366        DedupingFactory<WebRequestAction>::IS_NOT_PARAMETERIZED,
367        &CallConstructorFactoryMethod<WebRequestCancelAction>);
368    factory.RegisterFactoryMethod(
369        keys::kEditRequestCookieType,
370        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
371        &CreateRequestCookieAction);
372    factory.RegisterFactoryMethod(
373        keys::kEditResponseCookieType,
374        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
375        &CreateResponseCookieAction);
376    factory.RegisterFactoryMethod(
377        keys::kRedirectByRegExType,
378        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
379        &CreateRedirectRequestByRegExAction);
380    factory.RegisterFactoryMethod(
381        keys::kRedirectRequestType,
382        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
383        &CreateRedirectRequestAction);
384    factory.RegisterFactoryMethod(
385        keys::kRedirectToTransparentImageType,
386        DedupingFactory<WebRequestAction>::IS_NOT_PARAMETERIZED,
387        &CallConstructorFactoryMethod<
388            WebRequestRedirectToTransparentImageAction>);
389    factory.RegisterFactoryMethod(
390        keys::kRedirectToEmptyDocumentType,
391        DedupingFactory<WebRequestAction>::IS_NOT_PARAMETERIZED,
392        &CallConstructorFactoryMethod<WebRequestRedirectToEmptyDocumentAction>);
393    factory.RegisterFactoryMethod(
394        keys::kRemoveRequestCookieType,
395        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
396        &CreateRequestCookieAction);
397    factory.RegisterFactoryMethod(
398        keys::kRemoveResponseCookieType,
399        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
400        &CreateResponseCookieAction);
401    factory.RegisterFactoryMethod(
402        keys::kSetRequestHeaderType,
403        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
404        &CreateSetRequestHeaderAction);
405    factory.RegisterFactoryMethod(
406        keys::kRemoveRequestHeaderType,
407        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
408        &CreateRemoveRequestHeaderAction);
409    factory.RegisterFactoryMethod(
410        keys::kRemoveResponseHeaderType,
411        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
412        &CreateRemoveResponseHeaderAction);
413    factory.RegisterFactoryMethod(
414        keys::kIgnoreRulesType,
415        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
416        &CreateIgnoreRulesAction);
417    factory.RegisterFactoryMethod(
418        keys::kSendMessageToExtensionType,
419        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
420        &CreateSendMessageToExtensionAction);
421  }
422};
423
424base::LazyInstance<WebRequestActionFactory>::Leaky
425    g_web_request_action_factory = LAZY_INSTANCE_INITIALIZER;
426
427}  // namespace
428
429//
430// WebRequestAction
431//
432
433WebRequestAction::~WebRequestAction() {}
434
435bool WebRequestAction::Equals(const WebRequestAction* other) const {
436  return type() == other->type();
437}
438
439bool WebRequestAction::HasPermission(const InfoMap* extension_info_map,
440                                     const std::string& extension_id,
441                                     const net::URLRequest* request,
442                                     bool crosses_incognito) const {
443  if (WebRequestPermissions::HideRequest(extension_info_map, request))
444    return false;
445
446  // In unit tests we don't have an extension_info_map object here and skip host
447  // permission checks.
448  if (!extension_info_map)
449    return true;
450
451  WebRequestPermissions::HostPermissionsCheck permission_check =
452      WebRequestPermissions::REQUIRE_ALL_URLS;
453  switch (host_permissions_strategy()) {
454    case STRATEGY_DEFAULT:  // Default value is already set.
455      break;
456    case STRATEGY_NONE:
457      permission_check = WebRequestPermissions::DO_NOT_CHECK_HOST;
458      break;
459    case STRATEGY_HOST:
460      permission_check = WebRequestPermissions::REQUIRE_HOST_PERMISSION;
461      break;
462  }
463  return WebRequestPermissions::CanExtensionAccessURL(
464      extension_info_map, extension_id, request->url(), crosses_incognito,
465      permission_check);
466}
467
468// static
469scoped_refptr<const WebRequestAction> WebRequestAction::Create(
470    const Extension* extension,
471    const base::Value& json_action,
472    std::string* error,
473    bool* bad_message) {
474  *error = "";
475  *bad_message = false;
476
477  const base::DictionaryValue* action_dict = NULL;
478  INPUT_FORMAT_VALIDATE(json_action.GetAsDictionary(&action_dict));
479
480  std::string instance_type;
481  INPUT_FORMAT_VALIDATE(
482      action_dict->GetString(keys::kInstanceTypeKey, &instance_type));
483
484  WebRequestActionFactory& factory = g_web_request_action_factory.Get();
485  return factory.factory.Instantiate(
486      instance_type, action_dict, error, bad_message);
487}
488
489void WebRequestAction::Apply(const std::string& extension_id,
490                             base::Time extension_install_time,
491                             ApplyInfo* apply_info) const {
492  if (!HasPermission(apply_info->extension_info_map, extension_id,
493                     apply_info->request_data.request,
494                     apply_info->crosses_incognito))
495    return;
496  if (stages() & apply_info->request_data.stage) {
497    LinkedPtrEventResponseDelta delta = CreateDelta(
498        apply_info->request_data, extension_id, extension_install_time);
499    if (delta.get())
500      apply_info->deltas->push_back(delta);
501    if (type() == WebRequestAction::ACTION_IGNORE_RULES) {
502      const WebRequestIgnoreRulesAction* ignore_action =
503          static_cast<const WebRequestIgnoreRulesAction*>(this);
504      if (!ignore_action->ignore_tag().empty())
505        apply_info->ignored_tags->insert(ignore_action->ignore_tag());
506    }
507  }
508}
509
510WebRequestAction::WebRequestAction(int stages,
511                                   Type type,
512                                   int minimum_priority,
513                                   HostPermissionsStrategy strategy)
514    : stages_(stages),
515      type_(type),
516      minimum_priority_(minimum_priority),
517      host_permissions_strategy_(strategy) {}
518
519//
520// WebRequestCancelAction
521//
522
523WebRequestCancelAction::WebRequestCancelAction()
524    : WebRequestAction(ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS |
525                           ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED,
526                       ACTION_CANCEL_REQUEST,
527                       std::numeric_limits<int>::min(),
528                       STRATEGY_NONE) {}
529
530WebRequestCancelAction::~WebRequestCancelAction() {}
531
532std::string WebRequestCancelAction::GetName() const {
533  return keys::kCancelRequestType;
534}
535
536LinkedPtrEventResponseDelta WebRequestCancelAction::CreateDelta(
537    const WebRequestData& request_data,
538    const std::string& extension_id,
539    const base::Time& extension_install_time) const {
540  CHECK(request_data.stage & stages());
541  LinkedPtrEventResponseDelta result(
542      new helpers::EventResponseDelta(extension_id, extension_install_time));
543  result->cancel = true;
544  return result;
545}
546
547//
548// WebRequestRedirectAction
549//
550
551WebRequestRedirectAction::WebRequestRedirectAction(const GURL& redirect_url)
552    : WebRequestAction(ON_BEFORE_REQUEST,
553                       ACTION_REDIRECT_REQUEST,
554                       std::numeric_limits<int>::min(),
555                       STRATEGY_DEFAULT),
556      redirect_url_(redirect_url) {}
557
558WebRequestRedirectAction::~WebRequestRedirectAction() {}
559
560bool WebRequestRedirectAction::Equals(const WebRequestAction* other) const {
561  return WebRequestAction::Equals(other) &&
562         redirect_url_ ==
563             static_cast<const WebRequestRedirectAction*>(other)->redirect_url_;
564}
565
566std::string WebRequestRedirectAction::GetName() const {
567  return keys::kRedirectRequestType;
568}
569
570LinkedPtrEventResponseDelta WebRequestRedirectAction::CreateDelta(
571    const WebRequestData& request_data,
572    const std::string& extension_id,
573    const base::Time& extension_install_time) const {
574  CHECK(request_data.stage & stages());
575  if (request_data.request->url() == redirect_url_)
576    return LinkedPtrEventResponseDelta(NULL);
577  LinkedPtrEventResponseDelta result(
578      new helpers::EventResponseDelta(extension_id, extension_install_time));
579  result->new_url = redirect_url_;
580  return result;
581}
582
583//
584// WebRequestRedirectToTransparentImageAction
585//
586
587WebRequestRedirectToTransparentImageAction::
588    WebRequestRedirectToTransparentImageAction()
589    : WebRequestAction(ON_BEFORE_REQUEST,
590                       ACTION_REDIRECT_TO_TRANSPARENT_IMAGE,
591                       std::numeric_limits<int>::min(),
592                       STRATEGY_NONE) {}
593
594WebRequestRedirectToTransparentImageAction::
595~WebRequestRedirectToTransparentImageAction() {}
596
597std::string WebRequestRedirectToTransparentImageAction::GetName() const {
598  return keys::kRedirectToTransparentImageType;
599}
600
601LinkedPtrEventResponseDelta
602WebRequestRedirectToTransparentImageAction::CreateDelta(
603    const WebRequestData& request_data,
604    const std::string& extension_id,
605    const base::Time& extension_install_time) const {
606  CHECK(request_data.stage & stages());
607  LinkedPtrEventResponseDelta result(
608      new helpers::EventResponseDelta(extension_id, extension_install_time));
609  result->new_url = GURL(kTransparentImageUrl);
610  return result;
611}
612
613//
614// WebRequestRedirectToEmptyDocumentAction
615//
616
617WebRequestRedirectToEmptyDocumentAction::
618    WebRequestRedirectToEmptyDocumentAction()
619    : WebRequestAction(ON_BEFORE_REQUEST,
620                       ACTION_REDIRECT_TO_EMPTY_DOCUMENT,
621                       std::numeric_limits<int>::min(),
622                       STRATEGY_NONE) {}
623
624WebRequestRedirectToEmptyDocumentAction::
625~WebRequestRedirectToEmptyDocumentAction() {}
626
627std::string WebRequestRedirectToEmptyDocumentAction::GetName() const {
628  return keys::kRedirectToEmptyDocumentType;
629}
630
631LinkedPtrEventResponseDelta
632WebRequestRedirectToEmptyDocumentAction::CreateDelta(
633    const WebRequestData& request_data,
634    const std::string& extension_id,
635    const base::Time& extension_install_time) const {
636  CHECK(request_data.stage & stages());
637  LinkedPtrEventResponseDelta result(
638      new helpers::EventResponseDelta(extension_id, extension_install_time));
639  result->new_url = GURL(kEmptyDocumentUrl);
640  return result;
641}
642
643//
644// WebRequestRedirectByRegExAction
645//
646
647WebRequestRedirectByRegExAction::WebRequestRedirectByRegExAction(
648    scoped_ptr<RE2> from_pattern,
649    const std::string& to_pattern)
650    : WebRequestAction(ON_BEFORE_REQUEST,
651                       ACTION_REDIRECT_BY_REGEX_DOCUMENT,
652                       std::numeric_limits<int>::min(),
653                       STRATEGY_DEFAULT),
654      from_pattern_(from_pattern.Pass()),
655      to_pattern_(to_pattern.data(), to_pattern.size()) {}
656
657WebRequestRedirectByRegExAction::~WebRequestRedirectByRegExAction() {}
658
659// About the syntax of the two languages:
660//
661// ICU (Perl) states:
662// $n The text of capture group n will be substituted for $n. n must be >= 0
663//    and not greater than the number of capture groups. A $ not followed by a
664//    digit has no special meaning, and will appear in the substitution text
665//    as itself, a $.
666// \  Treat the following character as a literal, suppressing any special
667//    meaning. Backslash escaping in substitution text is only required for
668//    '$' and '\', but may be used on any other character without bad effects.
669//
670// RE2, derived from RE2::Rewrite()
671// \  May only be followed by a digit or another \. If followed by a single
672//    digit, both characters represent the respective capture group. If followed
673//    by another \, it is used as an escape sequence.
674
675// static
676std::string WebRequestRedirectByRegExAction::PerlToRe2Style(
677    const std::string& perl) {
678  std::string::const_iterator i = perl.begin();
679  std::string result;
680  while (i != perl.end()) {
681    if (*i == '$') {
682      ++i;
683      if (i == perl.end()) {
684        result += '$';
685        return result;
686      } else if (isdigit(*i)) {
687        result += '\\';
688        result += *i;
689      } else {
690        result += '$';
691        result += *i;
692      }
693    } else if (*i == '\\') {
694      ++i;
695      if (i == perl.end()) {
696        result += '\\';
697      } else if (*i == '$') {
698        result += '$';
699      } else if (*i == '\\') {
700        result += "\\\\";
701      } else {
702        result += *i;
703      }
704    } else {
705      result += *i;
706    }
707    ++i;
708  }
709  return result;
710}
711
712bool WebRequestRedirectByRegExAction::Equals(
713    const WebRequestAction* other) const {
714  if (!WebRequestAction::Equals(other))
715    return false;
716  const WebRequestRedirectByRegExAction* casted_other =
717      static_cast<const WebRequestRedirectByRegExAction*>(other);
718  return from_pattern_->pattern() == casted_other->from_pattern_->pattern() &&
719         to_pattern_ == casted_other->to_pattern_;
720}
721
722std::string WebRequestRedirectByRegExAction::GetName() const {
723  return keys::kRedirectByRegExType;
724}
725
726LinkedPtrEventResponseDelta WebRequestRedirectByRegExAction::CreateDelta(
727    const WebRequestData& request_data,
728    const std::string& extension_id,
729    const base::Time& extension_install_time) const {
730  CHECK(request_data.stage & stages());
731  CHECK(from_pattern_.get());
732
733  const std::string& old_url = request_data.request->url().spec();
734  std::string new_url = old_url;
735  if (!RE2::Replace(&new_url, *from_pattern_, to_pattern_) ||
736      new_url == old_url) {
737    return LinkedPtrEventResponseDelta(NULL);
738  }
739
740  LinkedPtrEventResponseDelta result(
741      new extension_web_request_api_helpers::EventResponseDelta(
742          extension_id, extension_install_time));
743  result->new_url = GURL(new_url);
744  return result;
745}
746
747//
748// WebRequestSetRequestHeaderAction
749//
750
751WebRequestSetRequestHeaderAction::WebRequestSetRequestHeaderAction(
752    const std::string& name,
753    const std::string& value)
754    : WebRequestAction(ON_BEFORE_SEND_HEADERS,
755                       ACTION_SET_REQUEST_HEADER,
756                       std::numeric_limits<int>::min(),
757                       STRATEGY_DEFAULT),
758      name_(name),
759      value_(value) {}
760
761WebRequestSetRequestHeaderAction::~WebRequestSetRequestHeaderAction() {}
762
763bool WebRequestSetRequestHeaderAction::Equals(
764    const WebRequestAction* other) const {
765  if (!WebRequestAction::Equals(other))
766    return false;
767  const WebRequestSetRequestHeaderAction* casted_other =
768      static_cast<const WebRequestSetRequestHeaderAction*>(other);
769  return name_ == casted_other->name_ && value_ == casted_other->value_;
770}
771
772std::string WebRequestSetRequestHeaderAction::GetName() const {
773  return keys::kSetRequestHeaderType;
774}
775
776
777LinkedPtrEventResponseDelta
778WebRequestSetRequestHeaderAction::CreateDelta(
779    const WebRequestData& request_data,
780    const std::string& extension_id,
781    const base::Time& extension_install_time) const {
782  CHECK(request_data.stage & stages());
783  LinkedPtrEventResponseDelta result(
784      new helpers::EventResponseDelta(extension_id, extension_install_time));
785  result->modified_request_headers.SetHeader(name_, value_);
786  return result;
787}
788
789//
790// WebRequestRemoveRequestHeaderAction
791//
792
793WebRequestRemoveRequestHeaderAction::WebRequestRemoveRequestHeaderAction(
794    const std::string& name)
795    : WebRequestAction(ON_BEFORE_SEND_HEADERS,
796                       ACTION_REMOVE_REQUEST_HEADER,
797                       std::numeric_limits<int>::min(),
798                       STRATEGY_DEFAULT),
799      name_(name) {}
800
801WebRequestRemoveRequestHeaderAction::~WebRequestRemoveRequestHeaderAction() {}
802
803bool WebRequestRemoveRequestHeaderAction::Equals(
804    const WebRequestAction* other) const {
805  if (!WebRequestAction::Equals(other))
806    return false;
807  const WebRequestRemoveRequestHeaderAction* casted_other =
808      static_cast<const WebRequestRemoveRequestHeaderAction*>(other);
809  return name_ == casted_other->name_;
810}
811
812std::string WebRequestRemoveRequestHeaderAction::GetName() const {
813  return keys::kRemoveRequestHeaderType;
814}
815
816LinkedPtrEventResponseDelta
817WebRequestRemoveRequestHeaderAction::CreateDelta(
818    const WebRequestData& request_data,
819    const std::string& extension_id,
820    const base::Time& extension_install_time) const {
821  CHECK(request_data.stage & stages());
822  LinkedPtrEventResponseDelta result(
823      new helpers::EventResponseDelta(extension_id, extension_install_time));
824  result->deleted_request_headers.push_back(name_);
825  return result;
826}
827
828//
829// WebRequestAddResponseHeaderAction
830//
831
832WebRequestAddResponseHeaderAction::WebRequestAddResponseHeaderAction(
833    const std::string& name,
834    const std::string& value)
835    : WebRequestAction(ON_HEADERS_RECEIVED,
836                       ACTION_ADD_RESPONSE_HEADER,
837                       std::numeric_limits<int>::min(),
838                       STRATEGY_DEFAULT),
839      name_(name),
840      value_(value) {}
841
842WebRequestAddResponseHeaderAction::~WebRequestAddResponseHeaderAction() {}
843
844bool WebRequestAddResponseHeaderAction::Equals(
845    const WebRequestAction* other) const {
846  if (!WebRequestAction::Equals(other))
847    return false;
848  const WebRequestAddResponseHeaderAction* casted_other =
849      static_cast<const WebRequestAddResponseHeaderAction*>(other);
850  return name_ == casted_other->name_ && value_ == casted_other->value_;
851}
852
853std::string WebRequestAddResponseHeaderAction::GetName() const {
854  return keys::kAddResponseHeaderType;
855}
856
857LinkedPtrEventResponseDelta
858WebRequestAddResponseHeaderAction::CreateDelta(
859    const WebRequestData& request_data,
860    const std::string& extension_id,
861    const base::Time& extension_install_time) const {
862  CHECK(request_data.stage & stages());
863  const net::HttpResponseHeaders* headers =
864      request_data.original_response_headers;
865  if (!headers)
866    return LinkedPtrEventResponseDelta(NULL);
867
868  // Don't generate the header if it exists already.
869  if (headers->HasHeaderValue(name_, value_))
870    return LinkedPtrEventResponseDelta(NULL);
871
872  LinkedPtrEventResponseDelta result(
873      new helpers::EventResponseDelta(extension_id, extension_install_time));
874  result->added_response_headers.push_back(make_pair(name_, value_));
875  return result;
876}
877
878//
879// WebRequestRemoveResponseHeaderAction
880//
881
882WebRequestRemoveResponseHeaderAction::WebRequestRemoveResponseHeaderAction(
883    const std::string& name,
884    const std::string& value,
885    bool has_value)
886    : WebRequestAction(ON_HEADERS_RECEIVED,
887                       ACTION_REMOVE_RESPONSE_HEADER,
888                       std::numeric_limits<int>::min(),
889                       STRATEGY_DEFAULT),
890      name_(name),
891      value_(value),
892      has_value_(has_value) {}
893
894WebRequestRemoveResponseHeaderAction::~WebRequestRemoveResponseHeaderAction() {}
895
896bool WebRequestRemoveResponseHeaderAction::Equals(
897    const WebRequestAction* other) const {
898  if (!WebRequestAction::Equals(other))
899    return false;
900  const WebRequestRemoveResponseHeaderAction* casted_other =
901      static_cast<const WebRequestRemoveResponseHeaderAction*>(other);
902  return name_ == casted_other->name_ && value_ == casted_other->value_ &&
903         has_value_ == casted_other->has_value_;
904}
905
906std::string WebRequestRemoveResponseHeaderAction::GetName() const {
907  return keys::kRemoveResponseHeaderType;
908}
909
910LinkedPtrEventResponseDelta
911WebRequestRemoveResponseHeaderAction::CreateDelta(
912    const WebRequestData& request_data,
913    const std::string& extension_id,
914    const base::Time& extension_install_time) const {
915  CHECK(request_data.stage & stages());
916  const net::HttpResponseHeaders* headers =
917      request_data.original_response_headers;
918  if (!headers)
919    return LinkedPtrEventResponseDelta(NULL);
920
921  LinkedPtrEventResponseDelta result(
922      new helpers::EventResponseDelta(extension_id, extension_install_time));
923  void* iter = NULL;
924  std::string current_value;
925  while (headers->EnumerateHeader(&iter, name_, &current_value)) {
926    if (has_value_ &&
927           (current_value.size() != value_.size() ||
928            !std::equal(current_value.begin(), current_value.end(),
929                        value_.begin(),
930                        base::CaseInsensitiveCompare<char>()))) {
931      continue;
932    }
933    result->deleted_response_headers.push_back(make_pair(name_, current_value));
934  }
935  return result;
936}
937
938//
939// WebRequestIgnoreRulesAction
940//
941
942WebRequestIgnoreRulesAction::WebRequestIgnoreRulesAction(
943    int minimum_priority,
944    const std::string& ignore_tag)
945    : WebRequestAction(ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS |
946                           ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED,
947                       ACTION_IGNORE_RULES,
948                       minimum_priority,
949                       STRATEGY_NONE),
950      ignore_tag_(ignore_tag) {}
951
952WebRequestIgnoreRulesAction::~WebRequestIgnoreRulesAction() {}
953
954bool WebRequestIgnoreRulesAction::Equals(const WebRequestAction* other) const {
955  if (!WebRequestAction::Equals(other))
956    return false;
957  const WebRequestIgnoreRulesAction* casted_other =
958      static_cast<const WebRequestIgnoreRulesAction*>(other);
959  return minimum_priority() == casted_other->minimum_priority() &&
960         ignore_tag_ == casted_other->ignore_tag_;
961}
962
963std::string WebRequestIgnoreRulesAction::GetName() const {
964  return keys::kIgnoreRulesType;
965}
966
967LinkedPtrEventResponseDelta WebRequestIgnoreRulesAction::CreateDelta(
968    const WebRequestData& request_data,
969    const std::string& extension_id,
970    const base::Time& extension_install_time) const {
971  CHECK(request_data.stage & stages());
972  return LinkedPtrEventResponseDelta(NULL);
973}
974
975//
976// WebRequestRequestCookieAction
977//
978
979WebRequestRequestCookieAction::WebRequestRequestCookieAction(
980    linked_ptr<RequestCookieModification> request_cookie_modification)
981    : WebRequestAction(ON_BEFORE_SEND_HEADERS,
982                       ACTION_MODIFY_REQUEST_COOKIE,
983                       std::numeric_limits<int>::min(),
984                       STRATEGY_DEFAULT),
985      request_cookie_modification_(request_cookie_modification) {
986  CHECK(request_cookie_modification_.get());
987}
988
989WebRequestRequestCookieAction::~WebRequestRequestCookieAction() {}
990
991bool WebRequestRequestCookieAction::Equals(
992    const WebRequestAction* other) const {
993  if (!WebRequestAction::Equals(other))
994    return false;
995  const WebRequestRequestCookieAction* casted_other =
996      static_cast<const WebRequestRequestCookieAction*>(other);
997  return helpers::NullableEquals(
998      request_cookie_modification_.get(),
999      casted_other->request_cookie_modification_.get());
1000}
1001
1002std::string WebRequestRequestCookieAction::GetName() const {
1003  switch (request_cookie_modification_->type) {
1004    case helpers::ADD:
1005      return keys::kAddRequestCookieType;
1006    case helpers::EDIT:
1007      return keys::kEditRequestCookieType;
1008    case helpers::REMOVE:
1009      return keys::kRemoveRequestCookieType;
1010  }
1011  NOTREACHED();
1012  return "";
1013}
1014
1015LinkedPtrEventResponseDelta WebRequestRequestCookieAction::CreateDelta(
1016    const WebRequestData& request_data,
1017    const std::string& extension_id,
1018    const base::Time& extension_install_time) const {
1019  CHECK(request_data.stage & stages());
1020  LinkedPtrEventResponseDelta result(
1021      new extension_web_request_api_helpers::EventResponseDelta(
1022          extension_id, extension_install_time));
1023  result->request_cookie_modifications.push_back(
1024      request_cookie_modification_);
1025  return result;
1026}
1027
1028//
1029// WebRequestResponseCookieAction
1030//
1031
1032WebRequestResponseCookieAction::WebRequestResponseCookieAction(
1033    linked_ptr<ResponseCookieModification> response_cookie_modification)
1034    : WebRequestAction(ON_HEADERS_RECEIVED,
1035                       ACTION_MODIFY_RESPONSE_COOKIE,
1036                       std::numeric_limits<int>::min(),
1037                       STRATEGY_DEFAULT),
1038      response_cookie_modification_(response_cookie_modification) {
1039  CHECK(response_cookie_modification_.get());
1040}
1041
1042WebRequestResponseCookieAction::~WebRequestResponseCookieAction() {}
1043
1044bool WebRequestResponseCookieAction::Equals(
1045    const WebRequestAction* other) const {
1046  if (!WebRequestAction::Equals(other))
1047    return false;
1048  const WebRequestResponseCookieAction* casted_other =
1049      static_cast<const WebRequestResponseCookieAction*>(other);
1050  return helpers::NullableEquals(
1051      response_cookie_modification_.get(),
1052      casted_other->response_cookie_modification_.get());
1053}
1054
1055std::string WebRequestResponseCookieAction::GetName() const {
1056  switch (response_cookie_modification_->type) {
1057    case helpers::ADD:
1058      return keys::kAddResponseCookieType;
1059    case helpers::EDIT:
1060      return keys::kEditResponseCookieType;
1061    case helpers::REMOVE:
1062      return keys::kRemoveResponseCookieType;
1063  }
1064  NOTREACHED();
1065  return "";
1066}
1067
1068LinkedPtrEventResponseDelta WebRequestResponseCookieAction::CreateDelta(
1069    const WebRequestData& request_data,
1070    const std::string& extension_id,
1071    const base::Time& extension_install_time) const {
1072  CHECK(request_data.stage & stages());
1073  LinkedPtrEventResponseDelta result(
1074      new extension_web_request_api_helpers::EventResponseDelta(
1075          extension_id, extension_install_time));
1076  result->response_cookie_modifications.push_back(
1077      response_cookie_modification_);
1078  return result;
1079}
1080
1081//
1082// WebRequestSendMessageToExtensionAction
1083//
1084
1085WebRequestSendMessageToExtensionAction::WebRequestSendMessageToExtensionAction(
1086    const std::string& message)
1087    : WebRequestAction(ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS |
1088                           ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED,
1089                       ACTION_SEND_MESSAGE_TO_EXTENSION,
1090                       std::numeric_limits<int>::min(),
1091                       STRATEGY_HOST),
1092      message_(message) {}
1093
1094WebRequestSendMessageToExtensionAction::
1095~WebRequestSendMessageToExtensionAction() {}
1096
1097bool WebRequestSendMessageToExtensionAction::Equals(
1098    const WebRequestAction* other) const {
1099  if (!WebRequestAction::Equals(other))
1100    return false;
1101  const WebRequestSendMessageToExtensionAction* casted_other =
1102      static_cast<const WebRequestSendMessageToExtensionAction*>(other);
1103  return message_ == casted_other->message_;
1104}
1105
1106std::string WebRequestSendMessageToExtensionAction::GetName() const {
1107  return keys::kSendMessageToExtensionType;
1108}
1109
1110LinkedPtrEventResponseDelta WebRequestSendMessageToExtensionAction::CreateDelta(
1111    const WebRequestData& request_data,
1112    const std::string& extension_id,
1113    const base::Time& extension_install_time) const {
1114  CHECK(request_data.stage & stages());
1115  LinkedPtrEventResponseDelta result(
1116      new extension_web_request_api_helpers::EventResponseDelta(
1117          extension_id, extension_install_time));
1118  result->messages_to_extension.insert(message_);
1119  return result;
1120}
1121
1122}  // namespace extensions
1123