Home | History | Annotate | Download | only in declarative_webrequest
      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 
     31 using content::ResourceRequestInfo;
     32 
     33 namespace extensions {
     34 
     35 namespace helpers = extension_web_request_api_helpers;
     36 namespace keys = declarative_webrequest_constants;
     37 
     38 namespace {
     39 // Error messages.
     40 const char kIgnoreRulesRequiresParameterError[] =
     41     "IgnoreRules requires at least one parameter.";
     42 
     43 const char kTransparentImageUrl[] = "data:image/png;base64,iVBORw0KGgoAAAANSUh"
     44     "EUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==";
     45 const 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 
     54 scoped_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 
     65 void 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 
     88 scoped_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 
     95 scoped_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.
    114 template <class T>
    115 scoped_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 
    123 scoped_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 
    138 scoped_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 
    164 scoped_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 
    188 scoped_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 
    205 scoped_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 
    229 scoped_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 
    253 scoped_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 
    280 scoped_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 
    326 scoped_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 
    372 scoped_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 
    385 struct 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 
    461 base::LazyInstance<WebRequestActionFactory>::Leaky
    462     g_web_request_action_factory = LAZY_INSTANCE_INITIALIZER;
    463 
    464 }  // namespace
    465 
    466 //
    467 // WebRequestAction
    468 //
    469 
    470 WebRequestAction::~WebRequestAction() {}
    471 
    472 bool WebRequestAction::Equals(const WebRequestAction* other) const {
    473   return type() == other->type();
    474 }
    475 
    476 bool 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
    516 scoped_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 
    536 void 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 
    557 WebRequestAction::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 
    570 WebRequestCancelAction::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 
    577 WebRequestCancelAction::~WebRequestCancelAction() {}
    578 
    579 std::string WebRequestCancelAction::GetName() const {
    580   return keys::kCancelRequestType;
    581 }
    582 
    583 LinkedPtrEventResponseDelta 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 
    598 WebRequestRedirectAction::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 
    605 WebRequestRedirectAction::~WebRequestRedirectAction() {}
    606 
    607 bool WebRequestRedirectAction::Equals(const WebRequestAction* other) const {
    608   return WebRequestAction::Equals(other) &&
    609          redirect_url_ ==
    610              static_cast<const WebRequestRedirectAction*>(other)->redirect_url_;
    611 }
    612 
    613 std::string WebRequestRedirectAction::GetName() const {
    614   return keys::kRedirectRequestType;
    615 }
    616 
    617 LinkedPtrEventResponseDelta 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 
    634 WebRequestRedirectToTransparentImageAction::
    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 
    641 WebRequestRedirectToTransparentImageAction::
    642 ~WebRequestRedirectToTransparentImageAction() {}
    643 
    644 std::string WebRequestRedirectToTransparentImageAction::GetName() const {
    645   return keys::kRedirectToTransparentImageType;
    646 }
    647 
    648 LinkedPtrEventResponseDelta
    649 WebRequestRedirectToTransparentImageAction::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 
    664 WebRequestRedirectToEmptyDocumentAction::
    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 
    671 WebRequestRedirectToEmptyDocumentAction::
    672 ~WebRequestRedirectToEmptyDocumentAction() {}
    673 
    674 std::string WebRequestRedirectToEmptyDocumentAction::GetName() const {
    675   return keys::kRedirectToEmptyDocumentType;
    676 }
    677 
    678 LinkedPtrEventResponseDelta
    679 WebRequestRedirectToEmptyDocumentAction::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 
    694 WebRequestRedirectByRegExAction::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 
    704 WebRequestRedirectByRegExAction::~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
    723 std::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 
    759 bool 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 
    769 std::string WebRequestRedirectByRegExAction::GetName() const {
    770   return keys::kRedirectByRegExType;
    771 }
    772 
    773 LinkedPtrEventResponseDelta 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 
    798 WebRequestSetRequestHeaderAction::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 
    808 WebRequestSetRequestHeaderAction::~WebRequestSetRequestHeaderAction() {}
    809 
    810 bool 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 
    819 std::string WebRequestSetRequestHeaderAction::GetName() const {
    820   return keys::kSetRequestHeaderType;
    821 }
    822 
    823 
    824 LinkedPtrEventResponseDelta
    825 WebRequestSetRequestHeaderAction::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 
    840 WebRequestRemoveRequestHeaderAction::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 
    848 WebRequestRemoveRequestHeaderAction::~WebRequestRemoveRequestHeaderAction() {}
    849 
    850 bool 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 
    859 std::string WebRequestRemoveRequestHeaderAction::GetName() const {
    860   return keys::kRemoveRequestHeaderType;
    861 }
    862 
    863 LinkedPtrEventResponseDelta
    864 WebRequestRemoveRequestHeaderAction::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 
    879 WebRequestAddResponseHeaderAction::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 
    889 WebRequestAddResponseHeaderAction::~WebRequestAddResponseHeaderAction() {}
    890 
    891 bool 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 
    900 std::string WebRequestAddResponseHeaderAction::GetName() const {
    901   return keys::kAddResponseHeaderType;
    902 }
    903 
    904 LinkedPtrEventResponseDelta
    905 WebRequestAddResponseHeaderAction::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 
    929 WebRequestRemoveResponseHeaderAction::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 
    941 WebRequestRemoveResponseHeaderAction::~WebRequestRemoveResponseHeaderAction() {}
    942 
    943 bool 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 
    953 std::string WebRequestRemoveResponseHeaderAction::GetName() const {
    954   return keys::kRemoveResponseHeaderType;
    955 }
    956 
    957 LinkedPtrEventResponseDelta
    958 WebRequestRemoveResponseHeaderAction::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 
    989 WebRequestIgnoreRulesAction::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 
    999 WebRequestIgnoreRulesAction::~WebRequestIgnoreRulesAction() {}
   1000 
   1001 bool 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 
   1010 std::string WebRequestIgnoreRulesAction::GetName() const {
   1011   return keys::kIgnoreRulesType;
   1012 }
   1013 
   1014 LinkedPtrEventResponseDelta 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 
   1026 WebRequestRequestCookieAction::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 
   1036 WebRequestRequestCookieAction::~WebRequestRequestCookieAction() {}
   1037 
   1038 bool 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 
   1049 std::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 
   1062 LinkedPtrEventResponseDelta 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 
   1079 WebRequestResponseCookieAction::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 
   1089 WebRequestResponseCookieAction::~WebRequestResponseCookieAction() {}
   1090 
   1091 bool 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 
   1102 std::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 
   1115 LinkedPtrEventResponseDelta 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 
   1132 WebRequestSendMessageToExtensionAction::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 
   1141 WebRequestSendMessageToExtensionAction::
   1142 ~WebRequestSendMessageToExtensionAction() {}
   1143 
   1144 bool 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 
   1153 std::string WebRequestSendMessageToExtensionAction::GetName() const {
   1154   return keys::kSendMessageToExtensionType;
   1155 }
   1156 
   1157 LinkedPtrEventResponseDelta 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