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