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