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(®ular_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