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 "base/files/file_path.h"
8#include "base/json/json_file_value_serializer.h"
9#include "base/memory/ref_counted.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/message_loop/message_loop.h"
12#include "base/path_service.h"
13#include "base/test/values_test_util.h"
14#include "base/time/time.h"
15#include "base/values.h"
16#include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h"
17#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h"
18#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h"
19#include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
20#include "chrome/browser/extensions/extension_info_map.h"
21#include "chrome/common/chrome_paths.h"
22#include "chrome/common/extensions/extension.h"
23#include "chrome/common/extensions/extension_constants.h"
24#include "chrome/common/extensions/extension_test_util.h"
25#include "content/public/test/test_browser_thread_bundle.h"
26#include "net/http/http_response_headers.h"
27#include "net/url_request/url_request_test_util.h"
28#include "testing/gmock/include/gmock/gmock.h"
29#include "testing/gtest/include/gtest/gtest.h"
30
31using base::DictionaryValue;
32using base::ListValue;
33using extension_test_util::LoadManifestUnchecked;
34using testing::HasSubstr;
35
36namespace extensions {
37
38namespace {
39
40const char kUnknownActionType[] = "unknownType";
41
42scoped_ptr<WebRequestActionSet> CreateSetOfActions(const char* json) {
43  scoped_ptr<Value> parsed_value(base::test::ParseJson(json));
44  const ListValue* parsed_list;
45  CHECK(parsed_value->GetAsList(&parsed_list));
46
47  WebRequestActionSet::AnyVector actions;
48  for (ListValue::const_iterator it = parsed_list->begin();
49       it != parsed_list->end();
50       ++it) {
51    const DictionaryValue* dict;
52    CHECK((*it)->GetAsDictionary(&dict));
53    actions.push_back(linked_ptr<base::Value>(dict->DeepCopy()));
54  }
55
56  std::string error;
57  bool bad_message = false;
58
59  scoped_ptr<WebRequestActionSet> action_set(
60      WebRequestActionSet::Create(actions, &error, &bad_message));
61  EXPECT_EQ("", error);
62  EXPECT_FALSE(bad_message);
63  CHECK(action_set);
64  return action_set.Pass();
65}
66
67}  // namespace
68
69namespace keys = declarative_webrequest_constants;
70
71class WebRequestActionWithThreadsTest : public testing::Test {
72 public:
73  WebRequestActionWithThreadsTest()
74      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
75
76 protected:
77  virtual void SetUp() OVERRIDE;
78
79  // Creates a URL request for URL |url_string|, and applies the actions from
80  // |action_set| as if they were triggered by the extension with
81  // |extension_id| during |stage|.
82  bool ActionWorksOnRequest(const char* url_string,
83                            const std::string& extension_id,
84                            const WebRequestActionSet* action_set,
85                            RequestStage stage);
86
87  // Expects a JSON description of an |action| requiring <all_urls> host
88  // permission, and checks that only an extensions with full host permissions
89  // can execute that action at |stage|. Also checks that the action is not
90  // executable for http://clients1.google.com.
91  void CheckActionNeedsAllUrls(const char* action, RequestStage stage);
92
93  net::TestURLRequestContext context_;
94
95  // An extension with *.com host permissions and the DWR permission.
96  scoped_refptr<Extension> extension_;
97  // An extension with host permissions for all URLs and the DWR permission.
98  scoped_refptr<Extension> extension_all_urls_;
99  scoped_refptr<ExtensionInfoMap> extension_info_map_;
100
101 private:
102  content::TestBrowserThreadBundle thread_bundle_;
103};
104
105void WebRequestActionWithThreadsTest::SetUp() {
106  testing::Test::SetUp();
107
108  std::string error;
109  extension_ = LoadManifestUnchecked("permissions",
110                                     "web_request_com_host_permissions.json",
111                                     Manifest::INVALID_LOCATION,
112                                     Extension::NO_FLAGS,
113                                     "ext_id_1",
114                                     &error);
115  ASSERT_TRUE(extension_.get()) << error;
116  extension_all_urls_ =
117      LoadManifestUnchecked("permissions",
118                            "web_request_all_host_permissions.json",
119                            Manifest::INVALID_LOCATION,
120                            Extension::NO_FLAGS,
121                            "ext_id_2",
122                            &error);
123  ASSERT_TRUE(extension_all_urls_.get()) << error;
124  extension_info_map_ = new ExtensionInfoMap;
125  ASSERT_TRUE(extension_info_map_.get());
126  extension_info_map_->AddExtension(
127      extension_.get(), base::Time::Now(), false /*incognito_enabled*/);
128  extension_info_map_->AddExtension(extension_all_urls_.get(),
129                                    base::Time::Now(),
130                                    false /*incognito_enabled*/);
131}
132
133bool WebRequestActionWithThreadsTest::ActionWorksOnRequest(
134    const char* url_string,
135    const std::string& extension_id,
136    const WebRequestActionSet* action_set,
137    RequestStage stage) {
138  net::TestURLRequest regular_request(GURL(url_string), NULL, &context_, NULL);
139  std::list<LinkedPtrEventResponseDelta> deltas;
140  scoped_refptr<net::HttpResponseHeaders> headers(
141      new net::HttpResponseHeaders(""));
142  WebRequestData request_data(&regular_request, stage, headers.get());
143  std::set<std::string> ignored_tags;
144  WebRequestAction::ApplyInfo apply_info = { extension_info_map_.get(),
145                                             request_data,
146                                             false /*crosses_incognito*/,
147                                             &deltas, &ignored_tags };
148  action_set->Apply(extension_id, base::Time(), &apply_info);
149  return (1u == deltas.size() || 0u < ignored_tags.size());
150}
151
152void WebRequestActionWithThreadsTest::CheckActionNeedsAllUrls(
153    const char* action,
154    RequestStage stage) {
155  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(action));
156
157  // Although |extension_| has matching *.com host permission, |action|
158  // is intentionally forbidden -- in Declarative WR, host permission
159  // for less than all URLs are ignored (except in SendMessageToExtension).
160  EXPECT_FALSE(ActionWorksOnRequest(
161      "http://test.com", extension_->id(), action_set.get(), stage));
162  // With the "<all_urls>" host permission they are allowed.
163  EXPECT_TRUE(ActionWorksOnRequest(
164      "http://test.com", extension_all_urls_->id(), action_set.get(), stage));
165
166  // The protected URLs should not be touched at all.
167  EXPECT_FALSE(ActionWorksOnRequest(
168      "http://clients1.google.com", extension_->id(), action_set.get(), stage));
169  EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com",
170                                    extension_all_urls_->id(),
171                                    action_set.get(),
172                                    stage));
173}
174
175TEST(WebRequestActionTest, CreateAction) {
176  std::string error;
177  bool bad_message = false;
178  scoped_refptr<const WebRequestAction> result;
179
180  // Test wrong data type passed.
181  error.clear();
182  base::ListValue empty_list;
183  result = WebRequestAction::Create(empty_list, &error, &bad_message);
184  EXPECT_TRUE(bad_message);
185  EXPECT_FALSE(result.get());
186
187  // Test missing instanceType element.
188  base::DictionaryValue input;
189  error.clear();
190  result = WebRequestAction::Create(input, &error, &bad_message);
191  EXPECT_TRUE(bad_message);
192  EXPECT_FALSE(result.get());
193
194  // Test wrong instanceType element.
195  input.SetString(keys::kInstanceTypeKey, kUnknownActionType);
196  error.clear();
197  result = WebRequestAction::Create(input, &error, &bad_message);
198  EXPECT_NE("", error);
199  EXPECT_FALSE(result.get());
200
201  // Test success
202  input.SetString(keys::kInstanceTypeKey, keys::kCancelRequestType);
203  error.clear();
204  result = WebRequestAction::Create(input, &error, &bad_message);
205  EXPECT_EQ("", error);
206  EXPECT_FALSE(bad_message);
207  ASSERT_TRUE(result.get());
208  EXPECT_EQ(WebRequestAction::ACTION_CANCEL_REQUEST, result->type());
209}
210
211TEST(WebRequestActionTest, CreateActionSet) {
212  std::string error;
213  bool bad_message = false;
214  scoped_ptr<WebRequestActionSet> result;
215
216  WebRequestActionSet::AnyVector input;
217
218  // Test empty input.
219  error.clear();
220  result = WebRequestActionSet::Create(input, &error, &bad_message);
221  EXPECT_TRUE(error.empty()) << error;
222  EXPECT_FALSE(bad_message);
223  ASSERT_TRUE(result.get());
224  EXPECT_TRUE(result->actions().empty());
225  EXPECT_EQ(std::numeric_limits<int>::min(), result->GetMinimumPriority());
226
227  DictionaryValue correct_action;
228  correct_action.SetString(keys::kInstanceTypeKey, keys::kIgnoreRulesType);
229  correct_action.SetInteger(keys::kLowerPriorityThanKey, 10);
230  DictionaryValue incorrect_action;
231  incorrect_action.SetString(keys::kInstanceTypeKey, kUnknownActionType);
232
233  // Test success.
234  input.push_back(linked_ptr<base::Value>(correct_action.DeepCopy()));
235  error.clear();
236  result = WebRequestActionSet::Create(input, &error, &bad_message);
237  EXPECT_TRUE(error.empty()) << error;
238  EXPECT_FALSE(bad_message);
239  ASSERT_TRUE(result.get());
240  ASSERT_EQ(1u, result->actions().size());
241  EXPECT_EQ(WebRequestAction::ACTION_IGNORE_RULES,
242            result->actions()[0]->type());
243  EXPECT_EQ(10, result->GetMinimumPriority());
244
245  // Test failure.
246  input.push_back(linked_ptr<base::Value>(incorrect_action.DeepCopy()));
247  error.clear();
248  result = WebRequestActionSet::Create(input, &error, &bad_message);
249  EXPECT_NE("", error);
250  EXPECT_FALSE(result.get());
251}
252
253// Test capture group syntax conversions of WebRequestRedirectByRegExAction
254TEST(WebRequestActionTest, PerlToRe2Style) {
255#define CallPerlToRe2Style WebRequestRedirectByRegExAction::PerlToRe2Style
256  // foo$1bar -> foo\1bar
257  EXPECT_EQ("foo\\1bar", CallPerlToRe2Style("foo$1bar"));
258  // foo\$1bar -> foo$1bar
259  EXPECT_EQ("foo$1bar", CallPerlToRe2Style("foo\\$1bar"));
260  // foo\\$1bar -> foo\\\1bar
261  EXPECT_EQ("foo\\\\\\1bar", CallPerlToRe2Style("foo\\\\$1bar"));
262  // foo\bar -> foobar
263  EXPECT_EQ("foobar", CallPerlToRe2Style("foo\\bar"));
264  // foo$bar -> foo$bar
265  EXPECT_EQ("foo$bar", CallPerlToRe2Style("foo$bar"));
266#undef CallPerlToRe2Style
267}
268
269TEST_F(WebRequestActionWithThreadsTest, PermissionsToRedirect) {
270  const char kAction[] =
271      "[{"
272      " \"instanceType\": \"declarativeWebRequest.RedirectRequest\","
273      " \"redirectUrl\": \"http://www.foobar.com\""
274      "}]";
275  CheckActionNeedsAllUrls(kAction, ON_BEFORE_REQUEST);
276}
277
278TEST_F(WebRequestActionWithThreadsTest, PermissionsToRedirectByRegEx) {
279  const char kAction[] =
280      "[{"
281      " \"instanceType\": \"declarativeWebRequest.RedirectByRegEx\","
282      " \"from\": \".*\","
283      " \"to\": \"http://www.foobar.com\""
284      "}]";
285  CheckActionNeedsAllUrls(kAction, ON_BEFORE_REQUEST);
286}
287
288TEST_F(WebRequestActionWithThreadsTest, PermissionsToSetRequestHeader) {
289  const char kAction[] =
290      "[{"
291      " \"instanceType\": \"declarativeWebRequest.SetRequestHeader\","
292      " \"name\": \"testname\","
293      " \"value\": \"testvalue\""
294      "}]";
295  CheckActionNeedsAllUrls(kAction, ON_BEFORE_SEND_HEADERS);
296}
297
298TEST_F(WebRequestActionWithThreadsTest, PermissionsToRemoveRequestHeader) {
299  const char kAction[] =
300      "[{"
301      " \"instanceType\": \"declarativeWebRequest.RemoveRequestHeader\","
302      " \"name\": \"testname\""
303      "}]";
304  CheckActionNeedsAllUrls(kAction, ON_BEFORE_SEND_HEADERS);
305}
306
307TEST_F(WebRequestActionWithThreadsTest, PermissionsToAddResponseHeader) {
308  const char kAction[] =
309      "[{"
310      " \"instanceType\": \"declarativeWebRequest.AddResponseHeader\","
311      " \"name\": \"testname\","
312      " \"value\": \"testvalue\""
313      "}]";
314  CheckActionNeedsAllUrls(kAction, ON_HEADERS_RECEIVED);
315}
316
317TEST_F(WebRequestActionWithThreadsTest, PermissionsToRemoveResponseHeader) {
318  const char kAction[] =
319      "[{"
320      " \"instanceType\": \"declarativeWebRequest.RemoveResponseHeader\","
321      " \"name\": \"testname\""
322      "}]";
323  CheckActionNeedsAllUrls(kAction, ON_HEADERS_RECEIVED);
324}
325
326TEST_F(WebRequestActionWithThreadsTest, PermissionsToSendMessageToExtension) {
327  const char kAction[] =
328      "[{"
329      " \"instanceType\": \"declarativeWebRequest.SendMessageToExtension\","
330      " \"message\": \"testtext\""
331      "}]";
332  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(kAction));
333
334  // For sending messages, specific host permissions actually matter.
335  EXPECT_TRUE(ActionWorksOnRequest("http://test.com",
336                                   extension_->id(),
337                                   action_set.get(),
338                                   ON_BEFORE_REQUEST));
339  // With the "<all_urls>" host permission they are allowed.
340  EXPECT_TRUE(ActionWorksOnRequest("http://test.com",
341                                   extension_all_urls_->id(),
342                                   action_set.get(),
343                                   ON_BEFORE_REQUEST));
344
345  // The protected URLs should not be touched at all.
346  EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com",
347                                    extension_->id(),
348                                    action_set.get(),
349                                    ON_BEFORE_REQUEST));
350  EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com",
351                                    extension_all_urls_->id(),
352                                    action_set.get(),
353                                    ON_BEFORE_REQUEST));
354}
355
356TEST_F(WebRequestActionWithThreadsTest, PermissionsToAddRequestCookie) {
357  const char kAction[] =
358      "[{"
359      " \"instanceType\": \"declarativeWebRequest.AddRequestCookie\","
360      " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
361      "}]";
362  CheckActionNeedsAllUrls(kAction, ON_BEFORE_SEND_HEADERS);
363}
364
365TEST_F(WebRequestActionWithThreadsTest, PermissionsToAddResponseCookie) {
366  const char kAction[] =
367      "[{"
368      " \"instanceType\": \"declarativeWebRequest.AddResponseCookie\","
369      " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
370      "}]";
371  CheckActionNeedsAllUrls(kAction, ON_HEADERS_RECEIVED);
372}
373
374TEST_F(WebRequestActionWithThreadsTest, PermissionsToEditRequestCookie) {
375  const char kAction[] =
376      "[{"
377      " \"instanceType\": \"declarativeWebRequest.EditRequestCookie\","
378      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" },"
379      " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }"
380      "}]";
381  CheckActionNeedsAllUrls(kAction, ON_BEFORE_SEND_HEADERS);
382}
383
384TEST_F(WebRequestActionWithThreadsTest, PermissionsToEditResponseCookie) {
385  const char kAction[] =
386      "[{"
387      " \"instanceType\": \"declarativeWebRequest.EditResponseCookie\","
388      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" },"
389      " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }"
390      "}]";
391  CheckActionNeedsAllUrls(kAction, ON_HEADERS_RECEIVED);
392}
393
394TEST_F(WebRequestActionWithThreadsTest, PermissionsToRemoveRequestCookie) {
395  const char kAction[] =
396      "[{"
397      " \"instanceType\": \"declarativeWebRequest.RemoveRequestCookie\","
398      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
399      "}]";
400  CheckActionNeedsAllUrls(kAction, ON_BEFORE_SEND_HEADERS);
401}
402
403TEST_F(WebRequestActionWithThreadsTest, PermissionsToRemoveResponseCookie) {
404  const char kAction[] =
405      "[{"
406      " \"instanceType\": \"declarativeWebRequest.RemoveResponseCookie\","
407      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
408      "}]";
409  CheckActionNeedsAllUrls(kAction, ON_HEADERS_RECEIVED);
410}
411
412TEST_F(WebRequestActionWithThreadsTest, PermissionsToCancel) {
413  const char kAction[] =
414      "[{"
415      " \"instanceType\": \"declarativeWebRequest.CancelRequest\""
416      "}]";
417  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(kAction));
418
419  // Cancelling requests works without full host permissions.
420  EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
421                                   extension_->id(),
422                                   action_set.get(),
423                                   ON_BEFORE_REQUEST));
424}
425
426TEST_F(WebRequestActionWithThreadsTest,
427       PermissionsToRedirectToTransparentImage) {
428  const char kAction[] =
429      "[{"
430      " \"instanceType\": \"declarativeWebRequest.RedirectToTransparentImage\""
431      "}]";
432  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(kAction));
433
434  // Redirecting to transparent images works without full host permissions.
435  EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
436                                   extension_->id(),
437                                   action_set.get(),
438                                   ON_BEFORE_REQUEST));
439}
440
441TEST_F(WebRequestActionWithThreadsTest, PermissionsToRedirectToEmptyDocument) {
442  const char kAction[] =
443      "[{"
444      " \"instanceType\": \"declarativeWebRequest.RedirectToEmptyDocument\""
445      "}]";
446  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(kAction));
447
448  // Redirecting to the empty document works without full host permissions.
449  EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
450                                   extension_->id(),
451                                   action_set.get(),
452                                   ON_BEFORE_REQUEST));
453}
454
455TEST_F(WebRequestActionWithThreadsTest, PermissionsToIgnore) {
456  const char kAction[] =
457      "[{"
458      " \"instanceType\": \"declarativeWebRequest.IgnoreRules\","
459      " \"lowerPriorityThan\": 123,"
460      " \"hasTag\": \"some_tag\""
461      "}]";
462  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(kAction));
463
464  // Ignoring rules works without full host permissions.
465  EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
466                                   extension_->id(),
467                                   action_set.get(),
468                                   ON_BEFORE_REQUEST));
469}
470
471TEST(WebRequestActionTest, GetName) {
472  const char kActions[] =
473      "[{"
474      " \"instanceType\": \"declarativeWebRequest.RedirectRequest\","
475      " \"redirectUrl\": \"http://www.foobar.com\""
476      "},"
477      "{"
478      " \"instanceType\": \"declarativeWebRequest.RedirectByRegEx\","
479      " \"from\": \".*\","
480      " \"to\": \"http://www.foobar.com\""
481      "},"
482      "{"
483      " \"instanceType\": \"declarativeWebRequest.SetRequestHeader\","
484      " \"name\": \"testname\","
485      " \"value\": \"testvalue\""
486      "},"
487      "{"
488      " \"instanceType\": \"declarativeWebRequest.RemoveRequestHeader\","
489      " \"name\": \"testname\""
490      "},"
491      "{"
492      " \"instanceType\": \"declarativeWebRequest.AddResponseHeader\","
493      " \"name\": \"testname\","
494      " \"value\": \"testvalue\""
495      "},"
496      "{"
497      " \"instanceType\": \"declarativeWebRequest.RemoveResponseHeader\","
498      " \"name\": \"testname\""
499      "},"
500      "{"
501      " \"instanceType\": \"declarativeWebRequest.SendMessageToExtension\","
502      " \"message\": \"testtext\""
503      "},"
504      "{"
505      " \"instanceType\": \"declarativeWebRequest.AddRequestCookie\","
506      " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
507      "},"
508      "{"
509      " \"instanceType\": \"declarativeWebRequest.AddResponseCookie\","
510      " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
511      "},"
512      "{"
513      " \"instanceType\": \"declarativeWebRequest.EditRequestCookie\","
514      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" },"
515      " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }"
516      "},"
517      "{"
518      " \"instanceType\": \"declarativeWebRequest.EditResponseCookie\","
519      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" },"
520      " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }"
521      "},"
522      "{"
523      " \"instanceType\": \"declarativeWebRequest.RemoveRequestCookie\","
524      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
525      "},"
526      "{"
527      " \"instanceType\": \"declarativeWebRequest.RemoveResponseCookie\","
528      " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
529      "},"
530      "{"
531      " \"instanceType\": \"declarativeWebRequest.CancelRequest\""
532      "},"
533      "{"
534      " \"instanceType\": \"declarativeWebRequest.RedirectToTransparentImage\""
535      "},"
536      "{"
537      " \"instanceType\": \"declarativeWebRequest.RedirectToEmptyDocument\""
538      "},"
539      "{"
540      " \"instanceType\": \"declarativeWebRequest.IgnoreRules\","
541      " \"lowerPriorityThan\": 123,"
542      " \"hasTag\": \"some_tag\""
543      "}]";
544  const char* kExpectedNames[] = {
545    "declarativeWebRequest.RedirectRequest",
546    "declarativeWebRequest.RedirectByRegEx",
547    "declarativeWebRequest.SetRequestHeader",
548    "declarativeWebRequest.RemoveRequestHeader",
549    "declarativeWebRequest.AddResponseHeader",
550    "declarativeWebRequest.RemoveResponseHeader",
551    "declarativeWebRequest.SendMessageToExtension",
552    "declarativeWebRequest.AddRequestCookie",
553    "declarativeWebRequest.AddResponseCookie",
554    "declarativeWebRequest.EditRequestCookie",
555    "declarativeWebRequest.EditResponseCookie",
556    "declarativeWebRequest.RemoveRequestCookie",
557    "declarativeWebRequest.RemoveResponseCookie",
558    "declarativeWebRequest.CancelRequest",
559    "declarativeWebRequest.RedirectToTransparentImage",
560    "declarativeWebRequest.RedirectToEmptyDocument",
561    "declarativeWebRequest.IgnoreRules",
562  };
563  scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(kActions));
564  ASSERT_EQ(arraysize(kExpectedNames), action_set->actions().size());
565  size_t index = 0;
566  for (WebRequestActionSet::Actions::const_iterator it =
567           action_set->actions().begin();
568       it != action_set->actions().end();
569       ++it) {
570    EXPECT_EQ(kExpectedNames[index], (*it)->GetName());
571    ++index;
572  }
573}
574
575}  // namespace extensions
576