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 "chrome/browser/extensions/extension_info_map.h"
21#include "chrome/common/extensions/extension.h"
22#include "content/public/common/url_constants.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 ExtensionInfoMap* 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 base::Value& json_action,
471    std::string* error,
472    bool* bad_message) {
473  *error = "";
474  *bad_message = false;
475
476  const base::DictionaryValue* action_dict = NULL;
477  INPUT_FORMAT_VALIDATE(json_action.GetAsDictionary(&action_dict));
478
479  std::string instance_type;
480  INPUT_FORMAT_VALIDATE(
481      action_dict->GetString(keys::kInstanceTypeKey, &instance_type));
482
483  WebRequestActionFactory& factory = g_web_request_action_factory.Get();
484  return factory.factory.Instantiate(
485      instance_type, action_dict, error, bad_message);
486}
487
488void WebRequestAction::Apply(const std::string& extension_id,
489                             base::Time extension_install_time,
490                             ApplyInfo* apply_info) const {
491  if (!HasPermission(apply_info->extension_info_map, extension_id,
492                     apply_info->request_data.request,
493                     apply_info->crosses_incognito))
494    return;
495  if (stages() & apply_info->request_data.stage) {
496    LinkedPtrEventResponseDelta delta = CreateDelta(
497        apply_info->request_data, extension_id, extension_install_time);
498    if (delta.get())
499      apply_info->deltas->push_back(delta);
500    if (type() == WebRequestAction::ACTION_IGNORE_RULES) {
501      const WebRequestIgnoreRulesAction* ignore_action =
502          static_cast<const WebRequestIgnoreRulesAction*>(this);
503      if (!ignore_action->ignore_tag().empty())
504        apply_info->ignored_tags->insert(ignore_action->ignore_tag());
505    }
506  }
507}
508
509WebRequestAction::WebRequestAction(int stages,
510                                   Type type,
511                                   int minimum_priority,
512                                   HostPermissionsStrategy strategy)
513    : stages_(stages),
514      type_(type),
515      minimum_priority_(minimum_priority),
516      host_permissions_strategy_(strategy) {}
517
518//
519// WebRequestCancelAction
520//
521
522WebRequestCancelAction::WebRequestCancelAction()
523    : WebRequestAction(ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS |
524                           ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED,
525                       ACTION_CANCEL_REQUEST,
526                       std::numeric_limits<int>::min(),
527                       STRATEGY_NONE) {}
528
529WebRequestCancelAction::~WebRequestCancelAction() {}
530
531std::string WebRequestCancelAction::GetName() const {
532  return keys::kCancelRequestType;
533}
534
535LinkedPtrEventResponseDelta WebRequestCancelAction::CreateDelta(
536    const WebRequestData& request_data,
537    const std::string& extension_id,
538    const base::Time& extension_install_time) const {
539  CHECK(request_data.stage & stages());
540  LinkedPtrEventResponseDelta result(
541      new helpers::EventResponseDelta(extension_id, extension_install_time));
542  result->cancel = true;
543  return result;
544}
545
546//
547// WebRequestRedirectAction
548//
549
550WebRequestRedirectAction::WebRequestRedirectAction(const GURL& redirect_url)
551    : WebRequestAction(ON_BEFORE_REQUEST,
552                       ACTION_REDIRECT_REQUEST,
553                       std::numeric_limits<int>::min(),
554                       STRATEGY_DEFAULT),
555      redirect_url_(redirect_url) {}
556
557WebRequestRedirectAction::~WebRequestRedirectAction() {}
558
559bool WebRequestRedirectAction::Equals(const WebRequestAction* other) const {
560  return WebRequestAction::Equals(other) &&
561         redirect_url_ ==
562             static_cast<const WebRequestRedirectAction*>(other)->redirect_url_;
563}
564
565std::string WebRequestRedirectAction::GetName() const {
566  return keys::kRedirectRequestType;
567}
568
569LinkedPtrEventResponseDelta WebRequestRedirectAction::CreateDelta(
570    const WebRequestData& request_data,
571    const std::string& extension_id,
572    const base::Time& extension_install_time) const {
573  CHECK(request_data.stage & stages());
574  if (request_data.request->url() == redirect_url_)
575    return LinkedPtrEventResponseDelta(NULL);
576  LinkedPtrEventResponseDelta result(
577      new helpers::EventResponseDelta(extension_id, extension_install_time));
578  result->new_url = redirect_url_;
579  return result;
580}
581
582//
583// WebRequestRedirectToTransparentImageAction
584//
585
586WebRequestRedirectToTransparentImageAction::
587    WebRequestRedirectToTransparentImageAction()
588    : WebRequestAction(ON_BEFORE_REQUEST,
589                       ACTION_REDIRECT_TO_TRANSPARENT_IMAGE,
590                       std::numeric_limits<int>::min(),
591                       STRATEGY_NONE) {}
592
593WebRequestRedirectToTransparentImageAction::
594~WebRequestRedirectToTransparentImageAction() {}
595
596std::string WebRequestRedirectToTransparentImageAction::GetName() const {
597  return keys::kRedirectToTransparentImageType;
598}
599
600LinkedPtrEventResponseDelta
601WebRequestRedirectToTransparentImageAction::CreateDelta(
602    const WebRequestData& request_data,
603    const std::string& extension_id,
604    const base::Time& extension_install_time) const {
605  CHECK(request_data.stage & stages());
606  LinkedPtrEventResponseDelta result(
607      new helpers::EventResponseDelta(extension_id, extension_install_time));
608  result->new_url = GURL(kTransparentImageUrl);
609  return result;
610}
611
612//
613// WebRequestRedirectToEmptyDocumentAction
614//
615
616WebRequestRedirectToEmptyDocumentAction::
617    WebRequestRedirectToEmptyDocumentAction()
618    : WebRequestAction(ON_BEFORE_REQUEST,
619                       ACTION_REDIRECT_TO_EMPTY_DOCUMENT,
620                       std::numeric_limits<int>::min(),
621                       STRATEGY_NONE) {}
622
623WebRequestRedirectToEmptyDocumentAction::
624~WebRequestRedirectToEmptyDocumentAction() {}
625
626std::string WebRequestRedirectToEmptyDocumentAction::GetName() const {
627  return keys::kRedirectToEmptyDocumentType;
628}
629
630LinkedPtrEventResponseDelta
631WebRequestRedirectToEmptyDocumentAction::CreateDelta(
632    const WebRequestData& request_data,
633    const std::string& extension_id,
634    const base::Time& extension_install_time) const {
635  CHECK(request_data.stage & stages());
636  LinkedPtrEventResponseDelta result(
637      new helpers::EventResponseDelta(extension_id, extension_install_time));
638  result->new_url = GURL(kEmptyDocumentUrl);
639  return result;
640}
641
642//
643// WebRequestRedirectByRegExAction
644//
645
646WebRequestRedirectByRegExAction::WebRequestRedirectByRegExAction(
647    scoped_ptr<RE2> from_pattern,
648    const std::string& to_pattern)
649    : WebRequestAction(ON_BEFORE_REQUEST,
650                       ACTION_REDIRECT_BY_REGEX_DOCUMENT,
651                       std::numeric_limits<int>::min(),
652                       STRATEGY_DEFAULT),
653      from_pattern_(from_pattern.Pass()),
654      to_pattern_(to_pattern.data(), to_pattern.size()) {}
655
656WebRequestRedirectByRegExAction::~WebRequestRedirectByRegExAction() {}
657
658// About the syntax of the two languages:
659//
660// ICU (Perl) states:
661// $n The text of capture group n will be substituted for $n. n must be >= 0
662//    and not greater than the number of capture groups. A $ not followed by a
663//    digit has no special meaning, and will appear in the substitution text
664//    as itself, a $.
665// \  Treat the following character as a literal, suppressing any special
666//    meaning. Backslash escaping in substitution text is only required for
667//    '$' and '\', but may be used on any other character without bad effects.
668//
669// RE2, derived from RE2::Rewrite()
670// \  May only be followed by a digit or another \. If followed by a single
671//    digit, both characters represent the respective capture group. If followed
672//    by another \, it is used as an escape sequence.
673
674// static
675std::string WebRequestRedirectByRegExAction::PerlToRe2Style(
676    const std::string& perl) {
677  std::string::const_iterator i = perl.begin();
678  std::string result;
679  while (i != perl.end()) {
680    if (*i == '$') {
681      ++i;
682      if (i == perl.end()) {
683        result += '$';
684        return result;
685      } else if (isdigit(*i)) {
686        result += '\\';
687        result += *i;
688      } else {
689        result += '$';
690        result += *i;
691      }
692    } else if (*i == '\\') {
693      ++i;
694      if (i == perl.end()) {
695        result += '\\';
696      } else if (*i == '$') {
697        result += '$';
698      } else if (*i == '\\') {
699        result += "\\\\";
700      } else {
701        result += *i;
702      }
703    } else {
704      result += *i;
705    }
706    ++i;
707  }
708  return result;
709}
710
711bool WebRequestRedirectByRegExAction::Equals(
712    const WebRequestAction* other) const {
713  if (!WebRequestAction::Equals(other))
714    return false;
715  const WebRequestRedirectByRegExAction* casted_other =
716      static_cast<const WebRequestRedirectByRegExAction*>(other);
717  return from_pattern_->pattern() == casted_other->from_pattern_->pattern() &&
718         to_pattern_ == casted_other->to_pattern_;
719}
720
721std::string WebRequestRedirectByRegExAction::GetName() const {
722  return keys::kRedirectByRegExType;
723}
724
725LinkedPtrEventResponseDelta WebRequestRedirectByRegExAction::CreateDelta(
726    const WebRequestData& request_data,
727    const std::string& extension_id,
728    const base::Time& extension_install_time) const {
729  CHECK(request_data.stage & stages());
730  CHECK(from_pattern_.get());
731
732  const std::string& old_url = request_data.request->url().spec();
733  std::string new_url = old_url;
734  if (!RE2::Replace(&new_url, *from_pattern_, to_pattern_) ||
735      new_url == old_url) {
736    return LinkedPtrEventResponseDelta(NULL);
737  }
738
739  LinkedPtrEventResponseDelta result(
740      new extension_web_request_api_helpers::EventResponseDelta(
741          extension_id, extension_install_time));
742  result->new_url = GURL(new_url);
743  return result;
744}
745
746//
747// WebRequestSetRequestHeaderAction
748//
749
750WebRequestSetRequestHeaderAction::WebRequestSetRequestHeaderAction(
751    const std::string& name,
752    const std::string& value)
753    : WebRequestAction(ON_BEFORE_SEND_HEADERS,
754                       ACTION_SET_REQUEST_HEADER,
755                       std::numeric_limits<int>::min(),
756                       STRATEGY_DEFAULT),
757      name_(name),
758      value_(value) {}
759
760WebRequestSetRequestHeaderAction::~WebRequestSetRequestHeaderAction() {}
761
762bool WebRequestSetRequestHeaderAction::Equals(
763    const WebRequestAction* other) const {
764  if (!WebRequestAction::Equals(other))
765    return false;
766  const WebRequestSetRequestHeaderAction* casted_other =
767      static_cast<const WebRequestSetRequestHeaderAction*>(other);
768  return name_ == casted_other->name_ && value_ == casted_other->value_;
769}
770
771std::string WebRequestSetRequestHeaderAction::GetName() const {
772  return keys::kSetRequestHeaderType;
773}
774
775
776LinkedPtrEventResponseDelta
777WebRequestSetRequestHeaderAction::CreateDelta(
778    const WebRequestData& request_data,
779    const std::string& extension_id,
780    const base::Time& extension_install_time) const {
781  CHECK(request_data.stage & stages());
782  LinkedPtrEventResponseDelta result(
783      new helpers::EventResponseDelta(extension_id, extension_install_time));
784  result->modified_request_headers.SetHeader(name_, value_);
785  return result;
786}
787
788//
789// WebRequestRemoveRequestHeaderAction
790//
791
792WebRequestRemoveRequestHeaderAction::WebRequestRemoveRequestHeaderAction(
793    const std::string& name)
794    : WebRequestAction(ON_BEFORE_SEND_HEADERS,
795                       ACTION_REMOVE_REQUEST_HEADER,
796                       std::numeric_limits<int>::min(),
797                       STRATEGY_DEFAULT),
798      name_(name) {}
799
800WebRequestRemoveRequestHeaderAction::~WebRequestRemoveRequestHeaderAction() {}
801
802bool WebRequestRemoveRequestHeaderAction::Equals(
803    const WebRequestAction* other) const {
804  if (!WebRequestAction::Equals(other))
805    return false;
806  const WebRequestRemoveRequestHeaderAction* casted_other =
807      static_cast<const WebRequestRemoveRequestHeaderAction*>(other);
808  return name_ == casted_other->name_;
809}
810
811std::string WebRequestRemoveRequestHeaderAction::GetName() const {
812  return keys::kRemoveRequestHeaderType;
813}
814
815LinkedPtrEventResponseDelta
816WebRequestRemoveRequestHeaderAction::CreateDelta(
817    const WebRequestData& request_data,
818    const std::string& extension_id,
819    const base::Time& extension_install_time) const {
820  CHECK(request_data.stage & stages());
821  LinkedPtrEventResponseDelta result(
822      new helpers::EventResponseDelta(extension_id, extension_install_time));
823  result->deleted_request_headers.push_back(name_);
824  return result;
825}
826
827//
828// WebRequestAddResponseHeaderAction
829//
830
831WebRequestAddResponseHeaderAction::WebRequestAddResponseHeaderAction(
832    const std::string& name,
833    const std::string& value)
834    : WebRequestAction(ON_HEADERS_RECEIVED,
835                       ACTION_ADD_RESPONSE_HEADER,
836                       std::numeric_limits<int>::min(),
837                       STRATEGY_DEFAULT),
838      name_(name),
839      value_(value) {}
840
841WebRequestAddResponseHeaderAction::~WebRequestAddResponseHeaderAction() {}
842
843bool WebRequestAddResponseHeaderAction::Equals(
844    const WebRequestAction* other) const {
845  if (!WebRequestAction::Equals(other))
846    return false;
847  const WebRequestAddResponseHeaderAction* casted_other =
848      static_cast<const WebRequestAddResponseHeaderAction*>(other);
849  return name_ == casted_other->name_ && value_ == casted_other->value_;
850}
851
852std::string WebRequestAddResponseHeaderAction::GetName() const {
853  return keys::kAddResponseHeaderType;
854}
855
856LinkedPtrEventResponseDelta
857WebRequestAddResponseHeaderAction::CreateDelta(
858    const WebRequestData& request_data,
859    const std::string& extension_id,
860    const base::Time& extension_install_time) const {
861  CHECK(request_data.stage & stages());
862  const net::HttpResponseHeaders* headers =
863      request_data.original_response_headers;
864  if (!headers)
865    return LinkedPtrEventResponseDelta(NULL);
866
867  // Don't generate the header if it exists already.
868  if (headers->HasHeaderValue(name_, value_))
869    return LinkedPtrEventResponseDelta(NULL);
870
871  LinkedPtrEventResponseDelta result(
872      new helpers::EventResponseDelta(extension_id, extension_install_time));
873  result->added_response_headers.push_back(make_pair(name_, value_));
874  return result;
875}
876
877//
878// WebRequestRemoveResponseHeaderAction
879//
880
881WebRequestRemoveResponseHeaderAction::WebRequestRemoveResponseHeaderAction(
882    const std::string& name,
883    const std::string& value,
884    bool has_value)
885    : WebRequestAction(ON_HEADERS_RECEIVED,
886                       ACTION_REMOVE_RESPONSE_HEADER,
887                       std::numeric_limits<int>::min(),
888                       STRATEGY_DEFAULT),
889      name_(name),
890      value_(value),
891      has_value_(has_value) {}
892
893WebRequestRemoveResponseHeaderAction::~WebRequestRemoveResponseHeaderAction() {}
894
895bool WebRequestRemoveResponseHeaderAction::Equals(
896    const WebRequestAction* other) const {
897  if (!WebRequestAction::Equals(other))
898    return false;
899  const WebRequestRemoveResponseHeaderAction* casted_other =
900      static_cast<const WebRequestRemoveResponseHeaderAction*>(other);
901  return name_ == casted_other->name_ && value_ == casted_other->value_ &&
902         has_value_ == casted_other->has_value_;
903}
904
905std::string WebRequestRemoveResponseHeaderAction::GetName() const {
906  return keys::kRemoveResponseHeaderType;
907}
908
909LinkedPtrEventResponseDelta
910WebRequestRemoveResponseHeaderAction::CreateDelta(
911    const WebRequestData& request_data,
912    const std::string& extension_id,
913    const base::Time& extension_install_time) const {
914  CHECK(request_data.stage & stages());
915  const net::HttpResponseHeaders* headers =
916      request_data.original_response_headers;
917  if (!headers)
918    return LinkedPtrEventResponseDelta(NULL);
919
920  LinkedPtrEventResponseDelta result(
921      new helpers::EventResponseDelta(extension_id, extension_install_time));
922  void* iter = NULL;
923  std::string current_value;
924  while (headers->EnumerateHeader(&iter, name_, &current_value)) {
925    if (has_value_ &&
926           (current_value.size() != value_.size() ||
927            !std::equal(current_value.begin(), current_value.end(),
928                        value_.begin(),
929                        base::CaseInsensitiveCompare<char>()))) {
930      continue;
931    }
932    result->deleted_response_headers.push_back(make_pair(name_, current_value));
933  }
934  return result;
935}
936
937//
938// WebRequestIgnoreRulesAction
939//
940
941WebRequestIgnoreRulesAction::WebRequestIgnoreRulesAction(
942    int minimum_priority,
943    const std::string& ignore_tag)
944    : WebRequestAction(ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS |
945                           ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED,
946                       ACTION_IGNORE_RULES,
947                       minimum_priority,
948                       STRATEGY_NONE),
949      ignore_tag_(ignore_tag) {}
950
951WebRequestIgnoreRulesAction::~WebRequestIgnoreRulesAction() {}
952
953bool WebRequestIgnoreRulesAction::Equals(const WebRequestAction* other) const {
954  if (!WebRequestAction::Equals(other))
955    return false;
956  const WebRequestIgnoreRulesAction* casted_other =
957      static_cast<const WebRequestIgnoreRulesAction*>(other);
958  return minimum_priority() == casted_other->minimum_priority() &&
959         ignore_tag_ == casted_other->ignore_tag_;
960}
961
962std::string WebRequestIgnoreRulesAction::GetName() const {
963  return keys::kIgnoreRulesType;
964}
965
966LinkedPtrEventResponseDelta WebRequestIgnoreRulesAction::CreateDelta(
967    const WebRequestData& request_data,
968    const std::string& extension_id,
969    const base::Time& extension_install_time) const {
970  CHECK(request_data.stage & stages());
971  return LinkedPtrEventResponseDelta(NULL);
972}
973
974//
975// WebRequestRequestCookieAction
976//
977
978WebRequestRequestCookieAction::WebRequestRequestCookieAction(
979    linked_ptr<RequestCookieModification> request_cookie_modification)
980    : WebRequestAction(ON_BEFORE_SEND_HEADERS,
981                       ACTION_MODIFY_REQUEST_COOKIE,
982                       std::numeric_limits<int>::min(),
983                       STRATEGY_DEFAULT),
984      request_cookie_modification_(request_cookie_modification) {
985  CHECK(request_cookie_modification_.get());
986}
987
988WebRequestRequestCookieAction::~WebRequestRequestCookieAction() {}
989
990bool WebRequestRequestCookieAction::Equals(
991    const WebRequestAction* other) const {
992  if (!WebRequestAction::Equals(other))
993    return false;
994  const WebRequestRequestCookieAction* casted_other =
995      static_cast<const WebRequestRequestCookieAction*>(other);
996  return helpers::NullableEquals(
997      request_cookie_modification_.get(),
998      casted_other->request_cookie_modification_.get());
999}
1000
1001std::string WebRequestRequestCookieAction::GetName() const {
1002  switch (request_cookie_modification_->type) {
1003    case helpers::ADD:
1004      return keys::kAddRequestCookieType;
1005    case helpers::EDIT:
1006      return keys::kEditRequestCookieType;
1007    case helpers::REMOVE:
1008      return keys::kRemoveRequestCookieType;
1009  }
1010  NOTREACHED();
1011  return "";
1012}
1013
1014LinkedPtrEventResponseDelta WebRequestRequestCookieAction::CreateDelta(
1015    const WebRequestData& request_data,
1016    const std::string& extension_id,
1017    const base::Time& extension_install_time) const {
1018  CHECK(request_data.stage & stages());
1019  LinkedPtrEventResponseDelta result(
1020      new extension_web_request_api_helpers::EventResponseDelta(
1021          extension_id, extension_install_time));
1022  result->request_cookie_modifications.push_back(
1023      request_cookie_modification_);
1024  return result;
1025}
1026
1027//
1028// WebRequestResponseCookieAction
1029//
1030
1031WebRequestResponseCookieAction::WebRequestResponseCookieAction(
1032    linked_ptr<ResponseCookieModification> response_cookie_modification)
1033    : WebRequestAction(ON_HEADERS_RECEIVED,
1034                       ACTION_MODIFY_RESPONSE_COOKIE,
1035                       std::numeric_limits<int>::min(),
1036                       STRATEGY_DEFAULT),
1037      response_cookie_modification_(response_cookie_modification) {
1038  CHECK(response_cookie_modification_.get());
1039}
1040
1041WebRequestResponseCookieAction::~WebRequestResponseCookieAction() {}
1042
1043bool WebRequestResponseCookieAction::Equals(
1044    const WebRequestAction* other) const {
1045  if (!WebRequestAction::Equals(other))
1046    return false;
1047  const WebRequestResponseCookieAction* casted_other =
1048      static_cast<const WebRequestResponseCookieAction*>(other);
1049  return helpers::NullableEquals(
1050      response_cookie_modification_.get(),
1051      casted_other->response_cookie_modification_.get());
1052}
1053
1054std::string WebRequestResponseCookieAction::GetName() const {
1055  switch (response_cookie_modification_->type) {
1056    case helpers::ADD:
1057      return keys::kAddResponseCookieType;
1058    case helpers::EDIT:
1059      return keys::kEditResponseCookieType;
1060    case helpers::REMOVE:
1061      return keys::kRemoveResponseCookieType;
1062  }
1063  NOTREACHED();
1064  return "";
1065}
1066
1067LinkedPtrEventResponseDelta WebRequestResponseCookieAction::CreateDelta(
1068    const WebRequestData& request_data,
1069    const std::string& extension_id,
1070    const base::Time& extension_install_time) const {
1071  CHECK(request_data.stage & stages());
1072  LinkedPtrEventResponseDelta result(
1073      new extension_web_request_api_helpers::EventResponseDelta(
1074          extension_id, extension_install_time));
1075  result->response_cookie_modifications.push_back(
1076      response_cookie_modification_);
1077  return result;
1078}
1079
1080//
1081// WebRequestSendMessageToExtensionAction
1082//
1083
1084WebRequestSendMessageToExtensionAction::WebRequestSendMessageToExtensionAction(
1085    const std::string& message)
1086    : WebRequestAction(ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS |
1087                           ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED,
1088                       ACTION_SEND_MESSAGE_TO_EXTENSION,
1089                       std::numeric_limits<int>::min(),
1090                       STRATEGY_HOST),
1091      message_(message) {}
1092
1093WebRequestSendMessageToExtensionAction::
1094~WebRequestSendMessageToExtensionAction() {}
1095
1096bool WebRequestSendMessageToExtensionAction::Equals(
1097    const WebRequestAction* other) const {
1098  if (!WebRequestAction::Equals(other))
1099    return false;
1100  const WebRequestSendMessageToExtensionAction* casted_other =
1101      static_cast<const WebRequestSendMessageToExtensionAction*>(other);
1102  return message_ == casted_other->message_;
1103}
1104
1105std::string WebRequestSendMessageToExtensionAction::GetName() const {
1106  return keys::kSendMessageToExtensionType;
1107}
1108
1109LinkedPtrEventResponseDelta WebRequestSendMessageToExtensionAction::CreateDelta(
1110    const WebRequestData& request_data,
1111    const std::string& extension_id,
1112    const base::Time& extension_install_time) const {
1113  CHECK(request_data.stage & stages());
1114  LinkedPtrEventResponseDelta result(
1115      new extension_web_request_api_helpers::EventResponseDelta(
1116          extension_id, extension_install_time));
1117  result->messages_to_extension.insert(message_);
1118  return result;
1119}
1120
1121}  // namespace extensions
1122