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

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

DEFINITIONS

This source file includes following definitions.
  1. CreateSetOfActions
  2. SetUp
  3. ActionWorksOnRequest
  4. CheckActionNeedsAllUrls
  5. TEST
  6. TEST
  7. TEST
  8. TEST_F
  9. TEST_F
  10. TEST_F
  11. TEST_F
  12. TEST_F
  13. TEST_F
  14. TEST_F
  15. TEST_F
  16. TEST_F
  17. TEST_F
  18. TEST_F
  19. TEST_F
  20. TEST_F
  21. TEST_F
  22. TEST_F
  23. TEST_F
  24. TEST_F
  25. TEST

// 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 "base/files/file_path.h"
#include "base/json/json_file_value_serializer.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/test/values_test_util.h"
#include "base/time/time.h"
#include "base/values.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_helpers.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/extension_test_util.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "extensions/browser/info_map.h"
#include "extensions/common/extension.h"
#include "net/base/request_priority.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using base::DictionaryValue;
using base::ListValue;
using extension_test_util::LoadManifestUnchecked;
using testing::HasSubstr;

namespace extensions {

namespace {

const char kUnknownActionType[] = "unknownType";

scoped_ptr<WebRequestActionSet> CreateSetOfActions(const char* json) {
  scoped_ptr<base::Value> parsed_value(base::test::ParseJson(json));
  const base::ListValue* parsed_list;
  CHECK(parsed_value->GetAsList(&parsed_list));

  WebRequestActionSet::AnyVector actions;
  for (base::ListValue::const_iterator it = parsed_list->begin();
       it != parsed_list->end();
       ++it) {
    const base::DictionaryValue* dict;
    CHECK((*it)->GetAsDictionary(&dict));
    actions.push_back(linked_ptr<base::Value>(dict->DeepCopy()));
  }

  std::string error;
  bool bad_message = false;

  scoped_ptr<WebRequestActionSet> action_set(
      WebRequestActionSet::Create(NULL, actions, &error, &bad_message));
  EXPECT_EQ("", error);
  EXPECT_FALSE(bad_message);
  CHECK(action_set);
  return action_set.Pass();
}

}  // namespace

namespace keys = declarative_webrequest_constants;

class WebRequestActionWithThreadsTest : public testing::Test {
 public:
  WebRequestActionWithThreadsTest()
      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}

 protected:
  virtual void SetUp() OVERRIDE;

  // Creates a URL request for URL |url_string|, and applies the actions from
  // |action_set| as if they were triggered by the extension with
  // |extension_id| during |stage|.
  bool ActionWorksOnRequest(const char* url_string,
                            const std::string& extension_id,
                            const WebRequestActionSet* action_set,
                            RequestStage stage);

  // Expects a JSON description of an |action| requiring <all_urls> host
  // permission, and checks that only an extensions with full host permissions
  // can execute that action at |stage|. Also checks that the action is not
  // executable for http://clients1.google.com.
  void CheckActionNeedsAllUrls(const char* action, RequestStage stage);

  net::TestURLRequestContext context_;

  // An extension with *.com host permissions and the DWR permission.
  scoped_refptr<Extension> extension_;
  // An extension with host permissions for all URLs and the DWR permission.
  scoped_refptr<Extension> extension_all_urls_;
  scoped_refptr<InfoMap> extension_info_map_;

 private:
  content::TestBrowserThreadBundle thread_bundle_;
};

void WebRequestActionWithThreadsTest::SetUp() {
  testing::Test::SetUp();

  std::string error;
  extension_ = LoadManifestUnchecked("permissions",
                                     "web_request_com_host_permissions.json",
                                     Manifest::INVALID_LOCATION,
                                     Extension::NO_FLAGS,
                                     "ext_id_1",
                                     &error);
  ASSERT_TRUE(extension_.get()) << error;
  extension_all_urls_ =
      LoadManifestUnchecked("permissions",
                            "web_request_all_host_permissions.json",
                            Manifest::INVALID_LOCATION,
                            Extension::NO_FLAGS,
                            "ext_id_2",
                            &error);
  ASSERT_TRUE(extension_all_urls_.get()) << error;
  extension_info_map_ = new InfoMap;
  ASSERT_TRUE(extension_info_map_.get());
  extension_info_map_->AddExtension(
      extension_.get(),
      base::Time::Now(),
      false /*incognito_enabled*/,
      false /*notifications_disabled*/);
  extension_info_map_->AddExtension(extension_all_urls_.get(),
                                    base::Time::Now(),
                                    false /*incognito_enabled*/,
                                    false /*notifications_disabled*/);
}

bool WebRequestActionWithThreadsTest::ActionWorksOnRequest(
    const char* url_string,
    const std::string& extension_id,
    const WebRequestActionSet* action_set,
    RequestStage stage) {
  net::TestURLRequest regular_request(
      GURL(url_string), net::DEFAULT_PRIORITY, NULL, &context_);
  std::list<LinkedPtrEventResponseDelta> deltas;
  scoped_refptr<net::HttpResponseHeaders> headers(
      new net::HttpResponseHeaders(""));
  WebRequestData request_data(&regular_request, stage, headers.get());
  std::set<std::string> ignored_tags;
  WebRequestAction::ApplyInfo apply_info = { extension_info_map_.get(),
                                             request_data,
                                             false /*crosses_incognito*/,
                                             &deltas, &ignored_tags };
  action_set->Apply(extension_id, base::Time(), &apply_info);
  return (1u == deltas.size() || 0u < ignored_tags.size());
}

void WebRequestActionWithThreadsTest::CheckActionNeedsAllUrls(
    const char* action,
    RequestStage stage) {
  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(action));

  // Although |extension_| has matching *.com host permission, |action|
  // is intentionally forbidden -- in Declarative WR, host permission
  // for less than all URLs are ignored (except in SendMessageToExtension).
  EXPECT_FALSE(ActionWorksOnRequest(
      "http://test.com", extension_->id(), action_set.get(), stage));
  // With the "<all_urls>" host permission they are allowed.
  EXPECT_TRUE(ActionWorksOnRequest(
      "http://test.com", extension_all_urls_->id(), action_set.get(), stage));

  // The protected URLs should not be touched at all.
  EXPECT_FALSE(ActionWorksOnRequest(
      "http://clients1.google.com", extension_->id(), action_set.get(), stage));
  EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com",
                                    extension_all_urls_->id(),
                                    action_set.get(),
                                    stage));
}

TEST(WebRequestActionTest, CreateAction) {
  std::string error;
  bool bad_message = false;
  scoped_refptr<const WebRequestAction> result;

  // Test wrong data type passed.
  error.clear();
  base::ListValue empty_list;
  result = WebRequestAction::Create(NULL, empty_list, &error, &bad_message);
  EXPECT_TRUE(bad_message);
  EXPECT_FALSE(result.get());

  // Test missing instanceType element.
  base::DictionaryValue input;
  error.clear();
  result = WebRequestAction::Create(NULL, input, &error, &bad_message);
  EXPECT_TRUE(bad_message);
  EXPECT_FALSE(result.get());

  // Test wrong instanceType element.
  input.SetString(keys::kInstanceTypeKey, kUnknownActionType);
  error.clear();
  result = WebRequestAction::Create(NULL, input, &error, &bad_message);
  EXPECT_NE("", error);
  EXPECT_FALSE(result.get());

  // Test success
  input.SetString(keys::kInstanceTypeKey, keys::kCancelRequestType);
  error.clear();
  result = WebRequestAction::Create(NULL, input, &error, &bad_message);
  EXPECT_EQ("", error);
  EXPECT_FALSE(bad_message);
  ASSERT_TRUE(result.get());
  EXPECT_EQ(WebRequestAction::ACTION_CANCEL_REQUEST, result->type());
}

TEST(WebRequestActionTest, CreateActionSet) {
  std::string error;
  bool bad_message = false;
  scoped_ptr<WebRequestActionSet> result;

  WebRequestActionSet::AnyVector input;

  // Test empty input.
  error.clear();
  result = WebRequestActionSet::Create(NULL, input, &error, &bad_message);
  EXPECT_TRUE(error.empty()) << error;
  EXPECT_FALSE(bad_message);
  ASSERT_TRUE(result.get());
  EXPECT_TRUE(result->actions().empty());
  EXPECT_EQ(std::numeric_limits<int>::min(), result->GetMinimumPriority());

  base::DictionaryValue correct_action;
  correct_action.SetString(keys::kInstanceTypeKey, keys::kIgnoreRulesType);
  correct_action.SetInteger(keys::kLowerPriorityThanKey, 10);
  base::DictionaryValue incorrect_action;
  incorrect_action.SetString(keys::kInstanceTypeKey, kUnknownActionType);

  // Test success.
  input.push_back(linked_ptr<base::Value>(correct_action.DeepCopy()));
  error.clear();
  result = WebRequestActionSet::Create(NULL, input, &error, &bad_message);
  EXPECT_TRUE(error.empty()) << error;
  EXPECT_FALSE(bad_message);
  ASSERT_TRUE(result.get());
  ASSERT_EQ(1u, result->actions().size());
  EXPECT_EQ(WebRequestAction::ACTION_IGNORE_RULES,
            result->actions()[0]->type());
  EXPECT_EQ(10, result->GetMinimumPriority());

  // Test failure.
  input.push_back(linked_ptr<base::Value>(incorrect_action.DeepCopy()));
  error.clear();
  result = WebRequestActionSet::Create(NULL, input, &error, &bad_message);
  EXPECT_NE("", error);
  EXPECT_FALSE(result.get());
}

// Test capture group syntax conversions of WebRequestRedirectByRegExAction
TEST(WebRequestActionTest, PerlToRe2Style) {
#define CallPerlToRe2Style WebRequestRedirectByRegExAction::PerlToRe2Style
  // foo$1bar -> foo\1bar
  EXPECT_EQ("foo\\1bar", CallPerlToRe2Style("foo$1bar"));
  // foo\$1bar -> foo$1bar
  EXPECT_EQ("foo$1bar", CallPerlToRe2Style("foo\\$1bar"));
  // foo\\$1bar -> foo\\\1bar
  EXPECT_EQ("foo\\\\\\1bar", CallPerlToRe2Style("foo\\\\$1bar"));
  // foo\bar -> foobar
  EXPECT_EQ("foobar", CallPerlToRe2Style("foo\\bar"));
  // foo$bar -> foo$bar
  EXPECT_EQ("foo$bar", CallPerlToRe2Style("foo$bar"));
#undef CallPerlToRe2Style
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToRedirect) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.RedirectRequest\","
      " \"redirectUrl\": \"http://www.foobar.com\""
      "}]";
  CheckActionNeedsAllUrls(kAction, ON_BEFORE_REQUEST);
  CheckActionNeedsAllUrls(kAction, ON_HEADERS_RECEIVED);
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToRedirectByRegEx) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.RedirectByRegEx\","
      " \"from\": \".*\","
      " \"to\": \"http://www.foobar.com\""
      "}]";
  CheckActionNeedsAllUrls(kAction, ON_BEFORE_REQUEST);
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToSetRequestHeader) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.SetRequestHeader\","
      " \"name\": \"testname\","
      " \"value\": \"testvalue\""
      "}]";
  CheckActionNeedsAllUrls(kAction, ON_BEFORE_SEND_HEADERS);
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToRemoveRequestHeader) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.RemoveRequestHeader\","
      " \"name\": \"testname\""
      "}]";
  CheckActionNeedsAllUrls(kAction, ON_BEFORE_SEND_HEADERS);
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToAddResponseHeader) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.AddResponseHeader\","
      " \"name\": \"testname\","
      " \"value\": \"testvalue\""
      "}]";
  CheckActionNeedsAllUrls(kAction, ON_HEADERS_RECEIVED);
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToRemoveResponseHeader) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.RemoveResponseHeader\","
      " \"name\": \"testname\""
      "}]";
  CheckActionNeedsAllUrls(kAction, ON_HEADERS_RECEIVED);
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToSendMessageToExtension) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.SendMessageToExtension\","
      " \"message\": \"testtext\""
      "}]";
  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(kAction));

  // For sending messages, specific host permissions actually matter.
  EXPECT_TRUE(ActionWorksOnRequest("http://test.com",
                                   extension_->id(),
                                   action_set.get(),
                                   ON_BEFORE_REQUEST));
  // With the "<all_urls>" host permission they are allowed.
  EXPECT_TRUE(ActionWorksOnRequest("http://test.com",
                                   extension_all_urls_->id(),
                                   action_set.get(),
                                   ON_BEFORE_REQUEST));

  // The protected URLs should not be touched at all.
  EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com",
                                    extension_->id(),
                                    action_set.get(),
                                    ON_BEFORE_REQUEST));
  EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com",
                                    extension_all_urls_->id(),
                                    action_set.get(),
                                    ON_BEFORE_REQUEST));
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToAddRequestCookie) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.AddRequestCookie\","
      " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
      "}]";
  CheckActionNeedsAllUrls(kAction, ON_BEFORE_SEND_HEADERS);
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToAddResponseCookie) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.AddResponseCookie\","
      " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
      "}]";
  CheckActionNeedsAllUrls(kAction, ON_HEADERS_RECEIVED);
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToEditRequestCookie) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.EditRequestCookie\","
      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" },"
      " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }"
      "}]";
  CheckActionNeedsAllUrls(kAction, ON_BEFORE_SEND_HEADERS);
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToEditResponseCookie) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.EditResponseCookie\","
      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" },"
      " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }"
      "}]";
  CheckActionNeedsAllUrls(kAction, ON_HEADERS_RECEIVED);
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToRemoveRequestCookie) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.RemoveRequestCookie\","
      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
      "}]";
  CheckActionNeedsAllUrls(kAction, ON_BEFORE_SEND_HEADERS);
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToRemoveResponseCookie) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.RemoveResponseCookie\","
      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
      "}]";
  CheckActionNeedsAllUrls(kAction, ON_HEADERS_RECEIVED);
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToCancel) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.CancelRequest\""
      "}]";
  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(kAction));

  // Cancelling requests works without full host permissions.
  EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
                                   extension_->id(),
                                   action_set.get(),
                                   ON_BEFORE_REQUEST));
}

TEST_F(WebRequestActionWithThreadsTest,
       PermissionsToRedirectToTransparentImage) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.RedirectToTransparentImage\""
      "}]";
  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(kAction));

  // Redirecting to transparent images works without full host permissions.
  EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
                                   extension_->id(),
                                   action_set.get(),
                                   ON_BEFORE_REQUEST));
  EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
                                   extension_->id(),
                                   action_set.get(),
                                   ON_HEADERS_RECEIVED));
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToRedirectToEmptyDocument) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.RedirectToEmptyDocument\""
      "}]";
  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(kAction));

  // Redirecting to the empty document works without full host permissions.
  EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
                                   extension_->id(),
                                   action_set.get(),
                                   ON_BEFORE_REQUEST));
  EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
                                   extension_->id(),
                                   action_set.get(),
                                   ON_HEADERS_RECEIVED));
}

TEST_F(WebRequestActionWithThreadsTest, PermissionsToIgnore) {
  const char kAction[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.IgnoreRules\","
      " \"lowerPriorityThan\": 123,"
      " \"hasTag\": \"some_tag\""
      "}]";
  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(kAction));

  // Ignoring rules works without full host permissions.
  EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
                                   extension_->id(),
                                   action_set.get(),
                                   ON_BEFORE_REQUEST));
}

TEST(WebRequestActionTest, GetName) {
  const char kActions[] =
      "[{"
      " \"instanceType\": \"declarativeWebRequest.RedirectRequest\","
      " \"redirectUrl\": \"http://www.foobar.com\""
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.RedirectByRegEx\","
      " \"from\": \".*\","
      " \"to\": \"http://www.foobar.com\""
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.SetRequestHeader\","
      " \"name\": \"testname\","
      " \"value\": \"testvalue\""
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.RemoveRequestHeader\","
      " \"name\": \"testname\""
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.AddResponseHeader\","
      " \"name\": \"testname\","
      " \"value\": \"testvalue\""
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.RemoveResponseHeader\","
      " \"name\": \"testname\""
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.SendMessageToExtension\","
      " \"message\": \"testtext\""
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.AddRequestCookie\","
      " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.AddResponseCookie\","
      " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.EditRequestCookie\","
      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" },"
      " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }"
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.EditResponseCookie\","
      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" },"
      " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }"
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.RemoveRequestCookie\","
      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.RemoveResponseCookie\","
      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.CancelRequest\""
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.RedirectToTransparentImage\""
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.RedirectToEmptyDocument\""
      "},"
      "{"
      " \"instanceType\": \"declarativeWebRequest.IgnoreRules\","
      " \"lowerPriorityThan\": 123,"
      " \"hasTag\": \"some_tag\""
      "}]";
  const char* kExpectedNames[] = {
    "declarativeWebRequest.RedirectRequest",
    "declarativeWebRequest.RedirectByRegEx",
    "declarativeWebRequest.SetRequestHeader",
    "declarativeWebRequest.RemoveRequestHeader",
    "declarativeWebRequest.AddResponseHeader",
    "declarativeWebRequest.RemoveResponseHeader",
    "declarativeWebRequest.SendMessageToExtension",
    "declarativeWebRequest.AddRequestCookie",
    "declarativeWebRequest.AddResponseCookie",
    "declarativeWebRequest.EditRequestCookie",
    "declarativeWebRequest.EditResponseCookie",
    "declarativeWebRequest.RemoveRequestCookie",
    "declarativeWebRequest.RemoveResponseCookie",
    "declarativeWebRequest.CancelRequest",
    "declarativeWebRequest.RedirectToTransparentImage",
    "declarativeWebRequest.RedirectToEmptyDocument",
    "declarativeWebRequest.IgnoreRules",
  };
  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(kActions));
  ASSERT_EQ(arraysize(kExpectedNames), action_set->actions().size());
  size_t index = 0;
  for (WebRequestActionSet::Actions::const_iterator it =
           action_set->actions().begin();
       it != action_set->actions().end();
       ++it) {
    EXPECT_EQ(kExpectedNames[index], (*it)->GetName());
    ++index;
  }
}

}  // namespace extensions

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