root/chrome/browser/extensions/api/declarative_webrequest/webrequest_action.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ParseRequestCookie
  2. ParseResponseCookieImpl
  3. ParseResponseCookie
  4. ParseFilterResponseCookie
  5. CallConstructorFactoryMethod
  6. CreateRedirectRequestAction
  7. CreateRedirectRequestByRegExAction
  8. CreateSetRequestHeaderAction
  9. CreateRemoveRequestHeaderAction
  10. CreateAddResponseHeaderAction
  11. CreateRemoveResponseHeaderAction
  12. CreateIgnoreRulesAction
  13. CreateRequestCookieAction
  14. CreateResponseCookieAction
  15. CreateSendMessageToExtensionAction
  16. Equals
  17. HasPermission
  18. Create
  19. Apply
  20. host_permissions_strategy_
  21. GetName
  22. CreateDelta
  23. redirect_url_
  24. Equals
  25. GetName
  26. CreateDelta
  27. WebRequestRedirectToTransparentImageAction
  28. WebRequestRedirectToTransparentImageAction
  29. GetName
  30. CreateDelta
  31. WebRequestRedirectToEmptyDocumentAction
  32. WebRequestRedirectToEmptyDocumentAction
  33. GetName
  34. CreateDelta
  35. to_pattern_
  36. PerlToRe2Style
  37. Equals
  38. GetName
  39. CreateDelta
  40. value_
  41. Equals
  42. GetName
  43. CreateDelta
  44. name_
  45. Equals
  46. GetName
  47. CreateDelta
  48. value_
  49. Equals
  50. GetName
  51. CreateDelta
  52. has_value_
  53. Equals
  54. GetName
  55. CreateDelta
  56. ignore_tag_
  57. Equals
  58. GetName
  59. CreateDelta
  60. request_cookie_modification_
  61. Equals
  62. GetName
  63. CreateDelta
  64. response_cookie_modification_
  65. Equals
  66. GetName
  67. CreateDelta
  68. message_
  69. WebRequestSendMessageToExtensionAction
  70. Equals
  71. GetName
  72. CreateDelta

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_action.h"

#include <limits>

#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/extensions/api/declarative/deduping_factory.h"
#include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h"
#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h"
#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h"
#include "chrome/browser/extensions/api/web_request/web_request_api_constants.h"
#include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
#include "chrome/browser/extensions/api/web_request/web_request_permissions.h"
#include "content/public/common/url_constants.h"
#include "extensions/browser/info_map.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/url_request/url_request.h"
#include "third_party/re2/re2/re2.h"

namespace extensions {

namespace helpers = extension_web_request_api_helpers;
namespace keys = declarative_webrequest_constants;

namespace {
// Error messages.
const char kIgnoreRulesRequiresParameterError[] =
    "IgnoreRules requires at least one parameter.";

const char kTransparentImageUrl[] = "data:image/png;base64,iVBORw0KGgoAAAANSUh"
    "EUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==";
const char kEmptyDocumentUrl[] = "data:text/html,";

#define INPUT_FORMAT_VALIDATE(test) do { \
    if (!(test)) { \
      *bad_message = true; \
      return scoped_refptr<const WebRequestAction>(NULL); \
    } \
  } while (0)

scoped_ptr<helpers::RequestCookie> ParseRequestCookie(
    const base::DictionaryValue* dict) {
  scoped_ptr<helpers::RequestCookie> result(new helpers::RequestCookie);
  std::string tmp;
  if (dict->GetString(keys::kNameKey, &tmp))
    result->name.reset(new std::string(tmp));
  if (dict->GetString(keys::kValueKey, &tmp))
    result->value.reset(new std::string(tmp));
  return result.Pass();
}

void ParseResponseCookieImpl(const base::DictionaryValue* dict,
                             helpers::ResponseCookie* cookie) {
  std::string string_tmp;
  int int_tmp = 0;
  bool bool_tmp = false;
  if (dict->GetString(keys::kNameKey, &string_tmp))
    cookie->name.reset(new std::string(string_tmp));
  if (dict->GetString(keys::kValueKey, &string_tmp))
    cookie->value.reset(new std::string(string_tmp));
  if (dict->GetString(keys::kExpiresKey, &string_tmp))
    cookie->expires.reset(new std::string(string_tmp));
  if (dict->GetInteger(keys::kMaxAgeKey, &int_tmp))
    cookie->max_age.reset(new int(int_tmp));
  if (dict->GetString(keys::kDomainKey, &string_tmp))
    cookie->domain.reset(new std::string(string_tmp));
  if (dict->GetString(keys::kPathKey, &string_tmp))
    cookie->path.reset(new std::string(string_tmp));
  if (dict->GetBoolean(keys::kSecureKey, &bool_tmp))
    cookie->secure.reset(new bool(bool_tmp));
  if (dict->GetBoolean(keys::kHttpOnlyKey, &bool_tmp))
    cookie->http_only.reset(new bool(bool_tmp));
}

scoped_ptr<helpers::ResponseCookie> ParseResponseCookie(
    const base::DictionaryValue* dict) {
  scoped_ptr<helpers::ResponseCookie> result(new helpers::ResponseCookie);
  ParseResponseCookieImpl(dict, result.get());
  return result.Pass();
}

scoped_ptr<helpers::FilterResponseCookie> ParseFilterResponseCookie(
    const base::DictionaryValue* dict) {
  scoped_ptr<helpers::FilterResponseCookie> result(
      new helpers::FilterResponseCookie);
  ParseResponseCookieImpl(dict, result.get());

  int int_tmp = 0;
  bool bool_tmp = false;
  if (dict->GetInteger(keys::kAgeUpperBoundKey, &int_tmp))
    result->age_upper_bound.reset(new int(int_tmp));
  if (dict->GetInteger(keys::kAgeLowerBoundKey, &int_tmp))
    result->age_lower_bound.reset(new int(int_tmp));
  if (dict->GetBoolean(keys::kSessionCookieKey, &bool_tmp))
    result->session_cookie.reset(new bool(bool_tmp));
  return result.Pass();
}

// Helper function for WebRequestActions that can be instantiated by just
// calling the constructor.
template <class T>
scoped_refptr<const WebRequestAction> CallConstructorFactoryMethod(
    const std::string& instance_type,
    const base::Value* value,
    std::string* error,
    bool* bad_message) {
  return scoped_refptr<const WebRequestAction>(new T);
}

scoped_refptr<const WebRequestAction> CreateRedirectRequestAction(
    const std::string& instance_type,
    const base::Value* value,
    std::string* error,
    bool* bad_message) {
  const base::DictionaryValue* dict = NULL;
  CHECK(value->GetAsDictionary(&dict));
  std::string redirect_url_string;
  INPUT_FORMAT_VALIDATE(
      dict->GetString(keys::kRedirectUrlKey, &redirect_url_string));
  GURL redirect_url(redirect_url_string);
  return scoped_refptr<const WebRequestAction>(
      new WebRequestRedirectAction(redirect_url));
}

scoped_refptr<const WebRequestAction> CreateRedirectRequestByRegExAction(
    const std::string& instance_type,
    const base::Value* value,
    std::string* error,
    bool* bad_message) {
  const base::DictionaryValue* dict = NULL;
  CHECK(value->GetAsDictionary(&dict));
  std::string from;
  std::string to;
  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kFromKey, &from));
  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kToKey, &to));

  to = WebRequestRedirectByRegExAction::PerlToRe2Style(to);

  RE2::Options options;
  options.set_case_sensitive(false);
  scoped_ptr<RE2> from_pattern(new RE2(from, options));

  if (!from_pattern->ok()) {
    *error = "Invalid pattern '" + from + "' -> '" + to + "'";
    return scoped_refptr<const WebRequestAction>(NULL);
  }
  return scoped_refptr<const WebRequestAction>(
      new WebRequestRedirectByRegExAction(from_pattern.Pass(), to));
}

scoped_refptr<const WebRequestAction> CreateSetRequestHeaderAction(
    const std::string& instance_type,
    const base::Value* json_value,
    std::string* error,
    bool* bad_message) {
  const base::DictionaryValue* dict = NULL;
  CHECK(json_value->GetAsDictionary(&dict));
  std::string name;
  std::string value;
  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kNameKey, &name));
  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kValueKey, &value));
  if (!helpers::IsValidHeaderName(name)) {
    *error = extension_web_request_api_constants::kInvalidHeaderName;
    return scoped_refptr<const WebRequestAction>(NULL);
  }
  if (!helpers::IsValidHeaderValue(value)) {
    *error = ErrorUtils::FormatErrorMessage(
        extension_web_request_api_constants::kInvalidHeaderValue, name);
    return scoped_refptr<const WebRequestAction>(NULL);
  }
  return scoped_refptr<const WebRequestAction>(
      new WebRequestSetRequestHeaderAction(name, value));
}

scoped_refptr<const WebRequestAction> CreateRemoveRequestHeaderAction(
    const std::string& instance_type,
    const base::Value* value,
    std::string* error,
    bool* bad_message) {
  const base::DictionaryValue* dict = NULL;
  CHECK(value->GetAsDictionary(&dict));
  std::string name;
  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kNameKey, &name));
  if (!helpers::IsValidHeaderName(name)) {
    *error = extension_web_request_api_constants::kInvalidHeaderName;
    return scoped_refptr<const WebRequestAction>(NULL);
  }
  return scoped_refptr<const WebRequestAction>(
      new WebRequestRemoveRequestHeaderAction(name));
}

scoped_refptr<const WebRequestAction> CreateAddResponseHeaderAction(
    const std::string& instance_type,
    const base::Value* json_value,
    std::string* error,
    bool* bad_message) {
  const base::DictionaryValue* dict = NULL;
  CHECK(json_value->GetAsDictionary(&dict));
  std::string name;
  std::string value;
  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kNameKey, &name));
  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kValueKey, &value));
  if (!helpers::IsValidHeaderName(name)) {
    *error = extension_web_request_api_constants::kInvalidHeaderName;
    return scoped_refptr<const WebRequestAction>(NULL);
  }
  if (!helpers::IsValidHeaderValue(value)) {
    *error = ErrorUtils::FormatErrorMessage(
        extension_web_request_api_constants::kInvalidHeaderValue, name);
    return scoped_refptr<const WebRequestAction>(NULL);
  }
  return scoped_refptr<const WebRequestAction>(
      new WebRequestAddResponseHeaderAction(name, value));
}

scoped_refptr<const WebRequestAction> CreateRemoveResponseHeaderAction(
    const std::string& instance_type,
    const base::Value* json_value,
    std::string* error,
    bool* bad_message) {
  const base::DictionaryValue* dict = NULL;
  CHECK(json_value->GetAsDictionary(&dict));
  std::string name;
  std::string value;
  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kNameKey, &name));
  bool has_value = dict->GetString(keys::kValueKey, &value);
  if (!helpers::IsValidHeaderName(name)) {
    *error = extension_web_request_api_constants::kInvalidHeaderName;
    return scoped_refptr<const WebRequestAction>(NULL);
  }
  if (has_value && !helpers::IsValidHeaderValue(value)) {
    *error = ErrorUtils::FormatErrorMessage(
        extension_web_request_api_constants::kInvalidHeaderValue, name);
    return scoped_refptr<const WebRequestAction>(NULL);
  }
  return scoped_refptr<const WebRequestAction>(
      new WebRequestRemoveResponseHeaderAction(name, value, has_value));
}

scoped_refptr<const WebRequestAction> CreateIgnoreRulesAction(
    const std::string& instance_type,
    const base::Value* value,
    std::string* error,
    bool* bad_message) {
  const base::DictionaryValue* dict = NULL;
  CHECK(value->GetAsDictionary(&dict));
  bool has_parameter = false;
  int minimum_priority = std::numeric_limits<int>::min();
  std::string ignore_tag;
  if (dict->HasKey(keys::kLowerPriorityThanKey)) {
    INPUT_FORMAT_VALIDATE(
        dict->GetInteger(keys::kLowerPriorityThanKey, &minimum_priority));
    has_parameter = true;
  }
  if (dict->HasKey(keys::kHasTagKey)) {
    INPUT_FORMAT_VALIDATE(dict->GetString(keys::kHasTagKey, &ignore_tag));
    has_parameter = true;
  }
  if (!has_parameter) {
    *error = kIgnoreRulesRequiresParameterError;
    return scoped_refptr<const WebRequestAction>(NULL);
  }
  return scoped_refptr<const WebRequestAction>(
      new WebRequestIgnoreRulesAction(minimum_priority, ignore_tag));
}

scoped_refptr<const WebRequestAction> CreateRequestCookieAction(
    const std::string& instance_type,
    const base::Value* value,
    std::string* error,
    bool* bad_message) {
  using extension_web_request_api_helpers::RequestCookieModification;

  const base::DictionaryValue* dict = NULL;
  CHECK(value->GetAsDictionary(&dict));

  linked_ptr<RequestCookieModification> modification(
      new RequestCookieModification);

  // Get modification type.
  if (instance_type == keys::kAddRequestCookieType)
    modification->type = helpers::ADD;
  else if (instance_type == keys::kEditRequestCookieType)
    modification->type = helpers::EDIT;
  else if (instance_type == keys::kRemoveRequestCookieType)
    modification->type = helpers::REMOVE;
  else
    INPUT_FORMAT_VALIDATE(false);

  // Get filter.
  if (modification->type == helpers::EDIT ||
      modification->type == helpers::REMOVE) {
    const base::DictionaryValue* filter = NULL;
    INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kFilterKey, &filter));
    modification->filter = ParseRequestCookie(filter);
  }

  // Get new value.
  if (modification->type == helpers::ADD) {
    const base::DictionaryValue* value = NULL;
    INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kCookieKey, &value));
    modification->modification = ParseRequestCookie(value);
  } else if (modification->type == helpers::EDIT) {
    const base::DictionaryValue* value = NULL;
    INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kModificationKey, &value));
    modification->modification = ParseRequestCookie(value);
  }

  return scoped_refptr<const WebRequestAction>(
      new WebRequestRequestCookieAction(modification));
}

scoped_refptr<const WebRequestAction> CreateResponseCookieAction(
    const std::string& instance_type,
    const base::Value* value,
    std::string* error,
    bool* bad_message) {
  using extension_web_request_api_helpers::ResponseCookieModification;

  const base::DictionaryValue* dict = NULL;
  CHECK(value->GetAsDictionary(&dict));

  linked_ptr<ResponseCookieModification> modification(
      new ResponseCookieModification);

  // Get modification type.
  if (instance_type == keys::kAddResponseCookieType)
    modification->type = helpers::ADD;
  else if (instance_type == keys::kEditResponseCookieType)
    modification->type = helpers::EDIT;
  else if (instance_type == keys::kRemoveResponseCookieType)
    modification->type = helpers::REMOVE;
  else
    INPUT_FORMAT_VALIDATE(false);

  // Get filter.
  if (modification->type == helpers::EDIT ||
      modification->type == helpers::REMOVE) {
    const base::DictionaryValue* filter = NULL;
    INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kFilterKey, &filter));
    modification->filter = ParseFilterResponseCookie(filter);
  }

  // Get new value.
  if (modification->type == helpers::ADD) {
    const base::DictionaryValue* value = NULL;
    INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kCookieKey, &value));
    modification->modification = ParseResponseCookie(value);
  } else if (modification->type == helpers::EDIT) {
    const base::DictionaryValue* value = NULL;
    INPUT_FORMAT_VALIDATE(dict->GetDictionary(keys::kModificationKey, &value));
    modification->modification = ParseResponseCookie(value);
  }

  return scoped_refptr<const WebRequestAction>(
      new WebRequestResponseCookieAction(modification));
}

scoped_refptr<const WebRequestAction> CreateSendMessageToExtensionAction(
    const std::string& name,
    const base::Value* value,
    std::string* error,
    bool* bad_message) {
  const base::DictionaryValue* dict = NULL;
  CHECK(value->GetAsDictionary(&dict));
  std::string message;
  INPUT_FORMAT_VALIDATE(dict->GetString(keys::kMessageKey, &message));
  return scoped_refptr<const WebRequestAction>(
      new WebRequestSendMessageToExtensionAction(message));
}

struct WebRequestActionFactory {
  DedupingFactory<WebRequestAction> factory;

  WebRequestActionFactory() : factory(5) {
    factory.RegisterFactoryMethod(
        keys::kAddRequestCookieType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateRequestCookieAction);
    factory.RegisterFactoryMethod(
        keys::kAddResponseCookieType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateResponseCookieAction);
    factory.RegisterFactoryMethod(
        keys::kAddResponseHeaderType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateAddResponseHeaderAction);
    factory.RegisterFactoryMethod(
        keys::kCancelRequestType,
        DedupingFactory<WebRequestAction>::IS_NOT_PARAMETERIZED,
        &CallConstructorFactoryMethod<WebRequestCancelAction>);
    factory.RegisterFactoryMethod(
        keys::kEditRequestCookieType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateRequestCookieAction);
    factory.RegisterFactoryMethod(
        keys::kEditResponseCookieType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateResponseCookieAction);
    factory.RegisterFactoryMethod(
        keys::kRedirectByRegExType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateRedirectRequestByRegExAction);
    factory.RegisterFactoryMethod(
        keys::kRedirectRequestType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateRedirectRequestAction);
    factory.RegisterFactoryMethod(
        keys::kRedirectToTransparentImageType,
        DedupingFactory<WebRequestAction>::IS_NOT_PARAMETERIZED,
        &CallConstructorFactoryMethod<
            WebRequestRedirectToTransparentImageAction>);
    factory.RegisterFactoryMethod(
        keys::kRedirectToEmptyDocumentType,
        DedupingFactory<WebRequestAction>::IS_NOT_PARAMETERIZED,
        &CallConstructorFactoryMethod<WebRequestRedirectToEmptyDocumentAction>);
    factory.RegisterFactoryMethod(
        keys::kRemoveRequestCookieType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateRequestCookieAction);
    factory.RegisterFactoryMethod(
        keys::kRemoveResponseCookieType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateResponseCookieAction);
    factory.RegisterFactoryMethod(
        keys::kSetRequestHeaderType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateSetRequestHeaderAction);
    factory.RegisterFactoryMethod(
        keys::kRemoveRequestHeaderType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateRemoveRequestHeaderAction);
    factory.RegisterFactoryMethod(
        keys::kRemoveResponseHeaderType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateRemoveResponseHeaderAction);
    factory.RegisterFactoryMethod(
        keys::kIgnoreRulesType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateIgnoreRulesAction);
    factory.RegisterFactoryMethod(
        keys::kSendMessageToExtensionType,
        DedupingFactory<WebRequestAction>::IS_PARAMETERIZED,
        &CreateSendMessageToExtensionAction);
  }
};

base::LazyInstance<WebRequestActionFactory>::Leaky
    g_web_request_action_factory = LAZY_INSTANCE_INITIALIZER;

}  // namespace

//
// WebRequestAction
//

WebRequestAction::~WebRequestAction() {}

bool WebRequestAction::Equals(const WebRequestAction* other) const {
  return type() == other->type();
}

bool WebRequestAction::HasPermission(const InfoMap* extension_info_map,
                                     const std::string& extension_id,
                                     const net::URLRequest* request,
                                     bool crosses_incognito) const {
  if (WebRequestPermissions::HideRequest(extension_info_map, request))
    return false;

  // In unit tests we don't have an extension_info_map object here and skip host
  // permission checks.
  if (!extension_info_map)
    return true;

  WebRequestPermissions::HostPermissionsCheck permission_check =
      WebRequestPermissions::REQUIRE_ALL_URLS;
  switch (host_permissions_strategy()) {
    case STRATEGY_DEFAULT:  // Default value is already set.
      break;
    case STRATEGY_NONE:
      permission_check = WebRequestPermissions::DO_NOT_CHECK_HOST;
      break;
    case STRATEGY_HOST:
      permission_check = WebRequestPermissions::REQUIRE_HOST_PERMISSION;
      break;
  }
  return WebRequestPermissions::CanExtensionAccessURL(
      extension_info_map, extension_id, request->url(), crosses_incognito,
      permission_check);
}

// static
scoped_refptr<const WebRequestAction> WebRequestAction::Create(
    const Extension* extension,
    const base::Value& json_action,
    std::string* error,
    bool* bad_message) {
  *error = "";
  *bad_message = false;

  const base::DictionaryValue* action_dict = NULL;
  INPUT_FORMAT_VALIDATE(json_action.GetAsDictionary(&action_dict));

  std::string instance_type;
  INPUT_FORMAT_VALIDATE(
      action_dict->GetString(keys::kInstanceTypeKey, &instance_type));

  WebRequestActionFactory& factory = g_web_request_action_factory.Get();
  return factory.factory.Instantiate(
      instance_type, action_dict, error, bad_message);
}

void WebRequestAction::Apply(const std::string& extension_id,
                             base::Time extension_install_time,
                             ApplyInfo* apply_info) const {
  if (!HasPermission(apply_info->extension_info_map, extension_id,
                     apply_info->request_data.request,
                     apply_info->crosses_incognito))
    return;
  if (stages() & apply_info->request_data.stage) {
    LinkedPtrEventResponseDelta delta = CreateDelta(
        apply_info->request_data, extension_id, extension_install_time);
    if (delta.get())
      apply_info->deltas->push_back(delta);
    if (type() == WebRequestAction::ACTION_IGNORE_RULES) {
      const WebRequestIgnoreRulesAction* ignore_action =
          static_cast<const WebRequestIgnoreRulesAction*>(this);
      if (!ignore_action->ignore_tag().empty())
        apply_info->ignored_tags->insert(ignore_action->ignore_tag());
    }
  }
}

WebRequestAction::WebRequestAction(int stages,
                                   Type type,
                                   int minimum_priority,
                                   HostPermissionsStrategy strategy)
    : stages_(stages),
      type_(type),
      minimum_priority_(minimum_priority),
      host_permissions_strategy_(strategy) {}

//
// WebRequestCancelAction
//

WebRequestCancelAction::WebRequestCancelAction()
    : WebRequestAction(ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS |
                           ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED,
                       ACTION_CANCEL_REQUEST,
                       std::numeric_limits<int>::min(),
                       STRATEGY_NONE) {}

WebRequestCancelAction::~WebRequestCancelAction() {}

std::string WebRequestCancelAction::GetName() const {
  return keys::kCancelRequestType;
}

LinkedPtrEventResponseDelta WebRequestCancelAction::CreateDelta(
    const WebRequestData& request_data,
    const std::string& extension_id,
    const base::Time& extension_install_time) const {
  CHECK(request_data.stage & stages());
  LinkedPtrEventResponseDelta result(
      new helpers::EventResponseDelta(extension_id, extension_install_time));
  result->cancel = true;
  return result;
}

//
// WebRequestRedirectAction
//

WebRequestRedirectAction::WebRequestRedirectAction(const GURL& redirect_url)
    : WebRequestAction(ON_BEFORE_REQUEST | ON_HEADERS_RECEIVED,
                       ACTION_REDIRECT_REQUEST,
                       std::numeric_limits<int>::min(),
                       STRATEGY_DEFAULT),
      redirect_url_(redirect_url) {}

WebRequestRedirectAction::~WebRequestRedirectAction() {}

bool WebRequestRedirectAction::Equals(const WebRequestAction* other) const {
  return WebRequestAction::Equals(other) &&
         redirect_url_ ==
             static_cast<const WebRequestRedirectAction*>(other)->redirect_url_;
}

std::string WebRequestRedirectAction::GetName() const {
  return keys::kRedirectRequestType;
}

LinkedPtrEventResponseDelta WebRequestRedirectAction::CreateDelta(
    const WebRequestData& request_data,
    const std::string& extension_id,
    const base::Time& extension_install_time) const {
  CHECK(request_data.stage & stages());
  if (request_data.request->url() == redirect_url_)
    return LinkedPtrEventResponseDelta(NULL);
  LinkedPtrEventResponseDelta result(
      new helpers::EventResponseDelta(extension_id, extension_install_time));
  result->new_url = redirect_url_;
  return result;
}

//
// WebRequestRedirectToTransparentImageAction
//

WebRequestRedirectToTransparentImageAction::
    WebRequestRedirectToTransparentImageAction()
    : WebRequestAction(ON_BEFORE_REQUEST | ON_HEADERS_RECEIVED,
                       ACTION_REDIRECT_TO_TRANSPARENT_IMAGE,
                       std::numeric_limits<int>::min(),
                       STRATEGY_NONE) {}

WebRequestRedirectToTransparentImageAction::
~WebRequestRedirectToTransparentImageAction() {}

std::string WebRequestRedirectToTransparentImageAction::GetName() const {
  return keys::kRedirectToTransparentImageType;
}

LinkedPtrEventResponseDelta
WebRequestRedirectToTransparentImageAction::CreateDelta(
    const WebRequestData& request_data,
    const std::string& extension_id,
    const base::Time& extension_install_time) const {
  CHECK(request_data.stage & stages());
  LinkedPtrEventResponseDelta result(
      new helpers::EventResponseDelta(extension_id, extension_install_time));
  result->new_url = GURL(kTransparentImageUrl);
  return result;
}

//
// WebRequestRedirectToEmptyDocumentAction
//

WebRequestRedirectToEmptyDocumentAction::
    WebRequestRedirectToEmptyDocumentAction()
    : WebRequestAction(ON_BEFORE_REQUEST | ON_HEADERS_RECEIVED,
                       ACTION_REDIRECT_TO_EMPTY_DOCUMENT,
                       std::numeric_limits<int>::min(),
                       STRATEGY_NONE) {}

WebRequestRedirectToEmptyDocumentAction::
~WebRequestRedirectToEmptyDocumentAction() {}

std::string WebRequestRedirectToEmptyDocumentAction::GetName() const {
  return keys::kRedirectToEmptyDocumentType;
}

LinkedPtrEventResponseDelta
WebRequestRedirectToEmptyDocumentAction::CreateDelta(
    const WebRequestData& request_data,
    const std::string& extension_id,
    const base::Time& extension_install_time) const {
  CHECK(request_data.stage & stages());
  LinkedPtrEventResponseDelta result(
      new helpers::EventResponseDelta(extension_id, extension_install_time));
  result->new_url = GURL(kEmptyDocumentUrl);
  return result;
}

//
// WebRequestRedirectByRegExAction
//

WebRequestRedirectByRegExAction::WebRequestRedirectByRegExAction(
    scoped_ptr<RE2> from_pattern,
    const std::string& to_pattern)
    : WebRequestAction(ON_BEFORE_REQUEST | ON_HEADERS_RECEIVED,
                       ACTION_REDIRECT_BY_REGEX_DOCUMENT,
                       std::numeric_limits<int>::min(),
                       STRATEGY_DEFAULT),
      from_pattern_(from_pattern.Pass()),
      to_pattern_(to_pattern.data(), to_pattern.size()) {}

WebRequestRedirectByRegExAction::~WebRequestRedirectByRegExAction() {}

// About the syntax of the two languages:
//
// ICU (Perl) states:
// $n The text of capture group n will be substituted for $n. n must be >= 0
//    and not greater than the number of capture groups. A $ not followed by a
//    digit has no special meaning, and will appear in the substitution text
//    as itself, a $.
// \  Treat the following character as a literal, suppressing any special
//    meaning. Backslash escaping in substitution text is only required for
//    '$' and '\', but may be used on any other character without bad effects.
//
// RE2, derived from RE2::Rewrite()
// \  May only be followed by a digit or another \. If followed by a single
//    digit, both characters represent the respective capture group. If followed
//    by another \, it is used as an escape sequence.

// static
std::string WebRequestRedirectByRegExAction::PerlToRe2Style(
    const std::string& perl) {
  std::string::const_iterator i = perl.begin();
  std::string result;
  while (i != perl.end()) {
    if (*i == '$') {
      ++i;
      if (i == perl.end()) {
        result += '$';
        return result;
      } else if (isdigit(*i)) {
        result += '\\';
        result += *i;
      } else {
        result += '$';
        result += *i;
      }
    } else if (*i == '\\') {
      ++i;
      if (i == perl.end()) {
        result += '\\';
      } else if (*i == '$') {
        result += '$';
      } else if (*i == '\\') {
        result += "\\\\";
      } else {
        result += *i;
      }
    } else {
      result += *i;
    }
    ++i;
  }
  return result;
}

bool WebRequestRedirectByRegExAction::Equals(
    const WebRequestAction* other) const {
  if (!WebRequestAction::Equals(other))
    return false;
  const WebRequestRedirectByRegExAction* casted_other =
      static_cast<const WebRequestRedirectByRegExAction*>(other);
  return from_pattern_->pattern() == casted_other->from_pattern_->pattern() &&
         to_pattern_ == casted_other->to_pattern_;
}

std::string WebRequestRedirectByRegExAction::GetName() const {
  return keys::kRedirectByRegExType;
}

LinkedPtrEventResponseDelta WebRequestRedirectByRegExAction::CreateDelta(
    const WebRequestData& request_data,
    const std::string& extension_id,
    const base::Time& extension_install_time) const {
  CHECK(request_data.stage & stages());
  CHECK(from_pattern_.get());

  const std::string& old_url = request_data.request->url().spec();
  std::string new_url = old_url;
  if (!RE2::Replace(&new_url, *from_pattern_, to_pattern_) ||
      new_url == old_url) {
    return LinkedPtrEventResponseDelta(NULL);
  }

  LinkedPtrEventResponseDelta result(
      new extension_web_request_api_helpers::EventResponseDelta(
          extension_id, extension_install_time));
  result->new_url = GURL(new_url);
  return result;
}

//
// WebRequestSetRequestHeaderAction
//

WebRequestSetRequestHeaderAction::WebRequestSetRequestHeaderAction(
    const std::string& name,
    const std::string& value)
    : WebRequestAction(ON_BEFORE_SEND_HEADERS,
                       ACTION_SET_REQUEST_HEADER,
                       std::numeric_limits<int>::min(),
                       STRATEGY_DEFAULT),
      name_(name),
      value_(value) {}

WebRequestSetRequestHeaderAction::~WebRequestSetRequestHeaderAction() {}

bool WebRequestSetRequestHeaderAction::Equals(
    const WebRequestAction* other) const {
  if (!WebRequestAction::Equals(other))
    return false;
  const WebRequestSetRequestHeaderAction* casted_other =
      static_cast<const WebRequestSetRequestHeaderAction*>(other);
  return name_ == casted_other->name_ && value_ == casted_other->value_;
}

std::string WebRequestSetRequestHeaderAction::GetName() const {
  return keys::kSetRequestHeaderType;
}


LinkedPtrEventResponseDelta
WebRequestSetRequestHeaderAction::CreateDelta(
    const WebRequestData& request_data,
    const std::string& extension_id,
    const base::Time& extension_install_time) const {
  CHECK(request_data.stage & stages());
  LinkedPtrEventResponseDelta result(
      new helpers::EventResponseDelta(extension_id, extension_install_time));
  result->modified_request_headers.SetHeader(name_, value_);
  return result;
}

//
// WebRequestRemoveRequestHeaderAction
//

WebRequestRemoveRequestHeaderAction::WebRequestRemoveRequestHeaderAction(
    const std::string& name)
    : WebRequestAction(ON_BEFORE_SEND_HEADERS,
                       ACTION_REMOVE_REQUEST_HEADER,
                       std::numeric_limits<int>::min(),
                       STRATEGY_DEFAULT),
      name_(name) {}

WebRequestRemoveRequestHeaderAction::~WebRequestRemoveRequestHeaderAction() {}

bool WebRequestRemoveRequestHeaderAction::Equals(
    const WebRequestAction* other) const {
  if (!WebRequestAction::Equals(other))
    return false;
  const WebRequestRemoveRequestHeaderAction* casted_other =
      static_cast<const WebRequestRemoveRequestHeaderAction*>(other);
  return name_ == casted_other->name_;
}

std::string WebRequestRemoveRequestHeaderAction::GetName() const {
  return keys::kRemoveRequestHeaderType;
}

LinkedPtrEventResponseDelta
WebRequestRemoveRequestHeaderAction::CreateDelta(
    const WebRequestData& request_data,
    const std::string& extension_id,
    const base::Time& extension_install_time) const {
  CHECK(request_data.stage & stages());
  LinkedPtrEventResponseDelta result(
      new helpers::EventResponseDelta(extension_id, extension_install_time));
  result->deleted_request_headers.push_back(name_);
  return result;
}

//
// WebRequestAddResponseHeaderAction
//

WebRequestAddResponseHeaderAction::WebRequestAddResponseHeaderAction(
    const std::string& name,
    const std::string& value)
    : WebRequestAction(ON_HEADERS_RECEIVED,
                       ACTION_ADD_RESPONSE_HEADER,
                       std::numeric_limits<int>::min(),
                       STRATEGY_DEFAULT),
      name_(name),
      value_(value) {}

WebRequestAddResponseHeaderAction::~WebRequestAddResponseHeaderAction() {}

bool WebRequestAddResponseHeaderAction::Equals(
    const WebRequestAction* other) const {
  if (!WebRequestAction::Equals(other))
    return false;
  const WebRequestAddResponseHeaderAction* casted_other =
      static_cast<const WebRequestAddResponseHeaderAction*>(other);
  return name_ == casted_other->name_ && value_ == casted_other->value_;
}

std::string WebRequestAddResponseHeaderAction::GetName() const {
  return keys::kAddResponseHeaderType;
}

LinkedPtrEventResponseDelta
WebRequestAddResponseHeaderAction::CreateDelta(
    const WebRequestData& request_data,
    const std::string& extension_id,
    const base::Time& extension_install_time) const {
  CHECK(request_data.stage & stages());
  const net::HttpResponseHeaders* headers =
      request_data.original_response_headers;
  if (!headers)
    return LinkedPtrEventResponseDelta(NULL);

  // Don't generate the header if it exists already.
  if (headers->HasHeaderValue(name_, value_))
    return LinkedPtrEventResponseDelta(NULL);

  LinkedPtrEventResponseDelta result(
      new helpers::EventResponseDelta(extension_id, extension_install_time));
  result->added_response_headers.push_back(make_pair(name_, value_));
  return result;
}

//
// WebRequestRemoveResponseHeaderAction
//

WebRequestRemoveResponseHeaderAction::WebRequestRemoveResponseHeaderAction(
    const std::string& name,
    const std::string& value,
    bool has_value)
    : WebRequestAction(ON_HEADERS_RECEIVED,
                       ACTION_REMOVE_RESPONSE_HEADER,
                       std::numeric_limits<int>::min(),
                       STRATEGY_DEFAULT),
      name_(name),
      value_(value),
      has_value_(has_value) {}

WebRequestRemoveResponseHeaderAction::~WebRequestRemoveResponseHeaderAction() {}

bool WebRequestRemoveResponseHeaderAction::Equals(
    const WebRequestAction* other) const {
  if (!WebRequestAction::Equals(other))
    return false;
  const WebRequestRemoveResponseHeaderAction* casted_other =
      static_cast<const WebRequestRemoveResponseHeaderAction*>(other);
  return name_ == casted_other->name_ && value_ == casted_other->value_ &&
         has_value_ == casted_other->has_value_;
}

std::string WebRequestRemoveResponseHeaderAction::GetName() const {
  return keys::kRemoveResponseHeaderType;
}

LinkedPtrEventResponseDelta
WebRequestRemoveResponseHeaderAction::CreateDelta(
    const WebRequestData& request_data,
    const std::string& extension_id,
    const base::Time& extension_install_time) const {
  CHECK(request_data.stage & stages());
  const net::HttpResponseHeaders* headers =
      request_data.original_response_headers;
  if (!headers)
    return LinkedPtrEventResponseDelta(NULL);

  LinkedPtrEventResponseDelta result(
      new helpers::EventResponseDelta(extension_id, extension_install_time));
  void* iter = NULL;
  std::string current_value;
  while (headers->EnumerateHeader(&iter, name_, &current_value)) {
    if (has_value_ &&
           (current_value.size() != value_.size() ||
            !std::equal(current_value.begin(), current_value.end(),
                        value_.begin(),
                        base::CaseInsensitiveCompare<char>()))) {
      continue;
    }
    result->deleted_response_headers.push_back(make_pair(name_, current_value));
  }
  return result;
}

//
// WebRequestIgnoreRulesAction
//

WebRequestIgnoreRulesAction::WebRequestIgnoreRulesAction(
    int minimum_priority,
    const std::string& ignore_tag)
    : WebRequestAction(ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS |
                           ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED,
                       ACTION_IGNORE_RULES,
                       minimum_priority,
                       STRATEGY_NONE),
      ignore_tag_(ignore_tag) {}

WebRequestIgnoreRulesAction::~WebRequestIgnoreRulesAction() {}

bool WebRequestIgnoreRulesAction::Equals(const WebRequestAction* other) const {
  if (!WebRequestAction::Equals(other))
    return false;
  const WebRequestIgnoreRulesAction* casted_other =
      static_cast<const WebRequestIgnoreRulesAction*>(other);
  return minimum_priority() == casted_other->minimum_priority() &&
         ignore_tag_ == casted_other->ignore_tag_;
}

std::string WebRequestIgnoreRulesAction::GetName() const {
  return keys::kIgnoreRulesType;
}

LinkedPtrEventResponseDelta WebRequestIgnoreRulesAction::CreateDelta(
    const WebRequestData& request_data,
    const std::string& extension_id,
    const base::Time& extension_install_time) const {
  CHECK(request_data.stage & stages());
  return LinkedPtrEventResponseDelta(NULL);
}

//
// WebRequestRequestCookieAction
//

WebRequestRequestCookieAction::WebRequestRequestCookieAction(
    linked_ptr<RequestCookieModification> request_cookie_modification)
    : WebRequestAction(ON_BEFORE_SEND_HEADERS,
                       ACTION_MODIFY_REQUEST_COOKIE,
                       std::numeric_limits<int>::min(),
                       STRATEGY_DEFAULT),
      request_cookie_modification_(request_cookie_modification) {
  CHECK(request_cookie_modification_.get());
}

WebRequestRequestCookieAction::~WebRequestRequestCookieAction() {}

bool WebRequestRequestCookieAction::Equals(
    const WebRequestAction* other) const {
  if (!WebRequestAction::Equals(other))
    return false;
  const WebRequestRequestCookieAction* casted_other =
      static_cast<const WebRequestRequestCookieAction*>(other);
  return helpers::NullableEquals(
      request_cookie_modification_.get(),
      casted_other->request_cookie_modification_.get());
}

std::string WebRequestRequestCookieAction::GetName() const {
  switch (request_cookie_modification_->type) {
    case helpers::ADD:
      return keys::kAddRequestCookieType;
    case helpers::EDIT:
      return keys::kEditRequestCookieType;
    case helpers::REMOVE:
      return keys::kRemoveRequestCookieType;
  }
  NOTREACHED();
  return "";
}

LinkedPtrEventResponseDelta WebRequestRequestCookieAction::CreateDelta(
    const WebRequestData& request_data,
    const std::string& extension_id,
    const base::Time& extension_install_time) const {
  CHECK(request_data.stage & stages());
  LinkedPtrEventResponseDelta result(
      new extension_web_request_api_helpers::EventResponseDelta(
          extension_id, extension_install_time));
  result->request_cookie_modifications.push_back(
      request_cookie_modification_);
  return result;
}

//
// WebRequestResponseCookieAction
//

WebRequestResponseCookieAction::WebRequestResponseCookieAction(
    linked_ptr<ResponseCookieModification> response_cookie_modification)
    : WebRequestAction(ON_HEADERS_RECEIVED,
                       ACTION_MODIFY_RESPONSE_COOKIE,
                       std::numeric_limits<int>::min(),
                       STRATEGY_DEFAULT),
      response_cookie_modification_(response_cookie_modification) {
  CHECK(response_cookie_modification_.get());
}

WebRequestResponseCookieAction::~WebRequestResponseCookieAction() {}

bool WebRequestResponseCookieAction::Equals(
    const WebRequestAction* other) const {
  if (!WebRequestAction::Equals(other))
    return false;
  const WebRequestResponseCookieAction* casted_other =
      static_cast<const WebRequestResponseCookieAction*>(other);
  return helpers::NullableEquals(
      response_cookie_modification_.get(),
      casted_other->response_cookie_modification_.get());
}

std::string WebRequestResponseCookieAction::GetName() const {
  switch (response_cookie_modification_->type) {
    case helpers::ADD:
      return keys::kAddResponseCookieType;
    case helpers::EDIT:
      return keys::kEditResponseCookieType;
    case helpers::REMOVE:
      return keys::kRemoveResponseCookieType;
  }
  NOTREACHED();
  return "";
}

LinkedPtrEventResponseDelta WebRequestResponseCookieAction::CreateDelta(
    const WebRequestData& request_data,
    const std::string& extension_id,
    const base::Time& extension_install_time) const {
  CHECK(request_data.stage & stages());
  LinkedPtrEventResponseDelta result(
      new extension_web_request_api_helpers::EventResponseDelta(
          extension_id, extension_install_time));
  result->response_cookie_modifications.push_back(
      response_cookie_modification_);
  return result;
}

//
// WebRequestSendMessageToExtensionAction
//

WebRequestSendMessageToExtensionAction::WebRequestSendMessageToExtensionAction(
    const std::string& message)
    : WebRequestAction(ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS |
                           ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED,
                       ACTION_SEND_MESSAGE_TO_EXTENSION,
                       std::numeric_limits<int>::min(),
                       STRATEGY_HOST),
      message_(message) {}

WebRequestSendMessageToExtensionAction::
~WebRequestSendMessageToExtensionAction() {}

bool WebRequestSendMessageToExtensionAction::Equals(
    const WebRequestAction* other) const {
  if (!WebRequestAction::Equals(other))
    return false;
  const WebRequestSendMessageToExtensionAction* casted_other =
      static_cast<const WebRequestSendMessageToExtensionAction*>(other);
  return message_ == casted_other->message_;
}

std::string WebRequestSendMessageToExtensionAction::GetName() const {
  return keys::kSendMessageToExtensionType;
}

LinkedPtrEventResponseDelta WebRequestSendMessageToExtensionAction::CreateDelta(
    const WebRequestData& request_data,
    const std::string& extension_id,
    const base::Time& extension_install_time) const {
  CHECK(request_data.stage & stages());
  LinkedPtrEventResponseDelta result(
      new extension_web_request_api_helpers::EventResponseDelta(
          extension_id, extension_install_time));
  result->messages_to_extension.insert(message_);
  return result;
}

}  // namespace extensions

/* [<][>][^][v][top][bottom][index][help] */