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.
5 #include "extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h"
10 #include "base/basictypes.h"
11 #include "base/json/json_reader.h"
12 #include "base/memory/linked_ptr.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/stl_util.h"
16 #include "base/test/values_test_util.h"
17 #include "base/values.h"
18 #include "chrome/common/extensions/extension_test_util.h"
19 #include "components/url_matcher/url_matcher_constants.h"
20 #include "content/public/test/test_browser_thread.h"
21 #include "extensions/browser/api/declarative/rules_registry_service.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 "net/base/request_priority.h"
25 #include "net/url_request/url_request.h"
26 #include "net/url_request/url_request_test_util.h"
27 #include "testing/gmock/include/gmock/gmock.h"
28 #include "testing/gtest/include/gtest/gtest-message.h"
29 #include "testing/gtest/include/gtest/gtest.h"
32 using extension_test_util::LoadManifest
;
33 using extension_test_util::LoadManifestUnchecked
;
34 using testing::HasSubstr
;
35 using url_matcher::URLMatcher
;
38 const char kExtensionId
[] = "ext1";
39 const char kExtensionId2
[] = "ext2";
40 const char kRuleId1
[] = "rule1";
41 const char kRuleId2
[] = "rule2";
42 const char kRuleId3
[] = "rule3";
43 const char kRuleId4
[] = "rule4";
46 namespace extensions
{
48 namespace helpers
= extension_web_request_api_helpers
;
49 namespace keys
= declarative_webrequest_constants
;
50 namespace keys2
= url_matcher::url_matcher_constants
;
52 class TestWebRequestRulesRegistry
: public WebRequestRulesRegistry
{
54 explicit TestWebRequestRulesRegistry(
55 scoped_refptr
<InfoMap
> extension_info_map
)
56 : WebRequestRulesRegistry(NULL
/*profile*/,
57 NULL
/* cache_delegate */,
58 RulesRegistryService::kDefaultRulesRegistryID
),
59 num_clear_cache_calls_(0) {
60 SetExtensionInfoMapForTesting(extension_info_map
);
63 // Returns how often the in-memory caches of the renderers were instructed
65 int num_clear_cache_calls() const { return num_clear_cache_calls_
; }
67 // How many rules are there which have some conditions not triggered by URL
69 size_t RulesWithoutTriggers() const {
70 return rules_with_untriggered_conditions_for_test().size();
74 ~TestWebRequestRulesRegistry() override
{}
76 void ClearCacheOnNavigation() override
{ ++num_clear_cache_calls_
; }
79 int num_clear_cache_calls_
;
82 class WebRequestRulesRegistryTest
: public testing::Test
{
84 WebRequestRulesRegistryTest()
85 : ui_(content::BrowserThread::UI
, &message_loop_
),
86 io_(content::BrowserThread::IO
, &message_loop_
) {}
88 ~WebRequestRulesRegistryTest() override
{}
90 void SetUp() override
;
92 void TearDown() override
{
93 // Make sure that deletion traits of all registries are executed.
94 message_loop_
.RunUntilIdle();
97 // Returns a rule that roughly matches http://*.example.com and
98 // https://www.example.com and cancels it
99 linked_ptr
<api::events::Rule
> CreateRule1() {
100 base::ListValue
* scheme_http
= new base::ListValue();
101 scheme_http
->Append(new base::StringValue("http"));
102 base::DictionaryValue
* http_condition_dict
= new base::DictionaryValue();
103 http_condition_dict
->Set(keys2::kSchemesKey
, scheme_http
);
104 http_condition_dict
->SetString(keys2::kHostSuffixKey
, "example.com");
105 base::DictionaryValue http_condition_url_filter
;
106 http_condition_url_filter
.Set(keys::kUrlKey
, http_condition_dict
);
107 http_condition_url_filter
.SetString(keys::kInstanceTypeKey
,
108 keys::kRequestMatcherType
);
110 base::ListValue
* scheme_https
= new base::ListValue();
111 scheme_http
->Append(new base::StringValue("https"));
112 base::DictionaryValue
* https_condition_dict
= new base::DictionaryValue();
113 https_condition_dict
->Set(keys2::kSchemesKey
, scheme_https
);
114 https_condition_dict
->SetString(keys2::kHostSuffixKey
, "example.com");
115 https_condition_dict
->SetString(keys2::kHostPrefixKey
, "www");
116 base::DictionaryValue https_condition_url_filter
;
117 https_condition_url_filter
.Set(keys::kUrlKey
, https_condition_dict
);
118 https_condition_url_filter
.SetString(keys::kInstanceTypeKey
,
119 keys::kRequestMatcherType
);
121 base::DictionaryValue action_dict
;
122 action_dict
.SetString(keys::kInstanceTypeKey
, keys::kCancelRequestType
);
124 linked_ptr
<api::events::Rule
> rule(new api::events::Rule
);
125 rule
->id
.reset(new std::string(kRuleId1
));
126 rule
->priority
.reset(new int(100));
127 rule
->actions
.push_back(linked_ptr
<base::Value
>(action_dict
.DeepCopy()));
128 rule
->conditions
.push_back(
129 linked_ptr
<base::Value
>(http_condition_url_filter
.DeepCopy()));
130 rule
->conditions
.push_back(
131 linked_ptr
<base::Value
>(https_condition_url_filter
.DeepCopy()));
135 // Returns a rule that matches anything and cancels it.
136 linked_ptr
<api::events::Rule
> CreateRule2() {
137 base::DictionaryValue condition_dict
;
138 condition_dict
.SetString(keys::kInstanceTypeKey
, keys::kRequestMatcherType
);
140 base::DictionaryValue action_dict
;
141 action_dict
.SetString(keys::kInstanceTypeKey
, keys::kCancelRequestType
);
143 linked_ptr
<api::events::Rule
> rule(new api::events::Rule
);
144 rule
->id
.reset(new std::string(kRuleId2
));
145 rule
->priority
.reset(new int(100));
146 rule
->actions
.push_back(linked_ptr
<base::Value
>(action_dict
.DeepCopy()));
147 rule
->conditions
.push_back(
148 linked_ptr
<base::Value
>(condition_dict
.DeepCopy()));
152 linked_ptr
<api::events::Rule
> CreateRedirectRule(
153 const std::string
& destination
) {
154 base::DictionaryValue condition_dict
;
155 condition_dict
.SetString(keys::kInstanceTypeKey
, keys::kRequestMatcherType
);
157 base::DictionaryValue action_dict
;
158 action_dict
.SetString(keys::kInstanceTypeKey
, keys::kRedirectRequestType
);
159 action_dict
.SetString(keys::kRedirectUrlKey
, destination
);
161 linked_ptr
<api::events::Rule
> rule(new api::events::Rule
);
162 rule
->id
.reset(new std::string(kRuleId3
));
163 rule
->priority
.reset(new int(100));
164 rule
->actions
.push_back(linked_ptr
<base::Value
>(action_dict
.DeepCopy()));
165 rule
->conditions
.push_back(
166 linked_ptr
<base::Value
>(condition_dict
.DeepCopy()));
170 // Create a rule to ignore all other rules for a destination that
171 // contains index.html.
172 linked_ptr
<api::events::Rule
> CreateIgnoreRule() {
173 base::DictionaryValue condition_dict
;
174 base::DictionaryValue
* http_condition_dict
= new base::DictionaryValue();
175 http_condition_dict
->SetString(keys2::kPathContainsKey
, "index.html");
176 condition_dict
.SetString(keys::kInstanceTypeKey
, keys::kRequestMatcherType
);
177 condition_dict
.Set(keys::kUrlKey
, http_condition_dict
);
179 base::DictionaryValue action_dict
;
180 action_dict
.SetString(keys::kInstanceTypeKey
, keys::kIgnoreRulesType
);
181 action_dict
.SetInteger(keys::kLowerPriorityThanKey
, 150);
183 linked_ptr
<api::events::Rule
> rule(new api::events::Rule
);
184 rule
->id
.reset(new std::string(kRuleId4
));
185 rule
->priority
.reset(new int(200));
186 rule
->actions
.push_back(linked_ptr
<base::Value
>(action_dict
.DeepCopy()));
187 rule
->conditions
.push_back(
188 linked_ptr
<base::Value
>(condition_dict
.DeepCopy()));
192 // Create a condition with the attributes specified. An example value of
193 // |attributes| is: "\"resourceType\": [\"stylesheet\"], \n".
194 linked_ptr
<base::Value
> CreateCondition(const std::string
& attributes
) {
195 std::string json_description
=
197 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n";
198 json_description
+= attributes
;
199 json_description
+= "}";
201 return linked_ptr
<base::Value
>(
202 base::test::ParseJson(json_description
).release());
205 // Create a rule with the ID |rule_id| and with conditions created from the
206 // |attributes| specified (one entry one condition). An example value of a
207 // string from |attributes| is: "\"resourceType\": [\"stylesheet\"], \n".
208 linked_ptr
<api::events::Rule
> CreateCancellingRule(
210 const std::vector
<const std::string
*>& attributes
) {
211 base::DictionaryValue action_dict
;
212 action_dict
.SetString(keys::kInstanceTypeKey
, keys::kCancelRequestType
);
214 linked_ptr
<api::events::Rule
> rule(new api::events::Rule
);
215 rule
->id
.reset(new std::string(rule_id
));
216 rule
->priority
.reset(new int(1));
217 rule
->actions
.push_back(linked_ptr
<base::Value
>(action_dict
.DeepCopy()));
218 for (std::vector
<const std::string
*>::const_iterator it
=
220 it
!= attributes
.end(); ++it
)
221 rule
->conditions
.push_back(CreateCondition(**it
));
226 base::MessageLoopForIO message_loop_
;
227 content::TestBrowserThread ui_
;
228 content::TestBrowserThread io_
;
229 // Two extensions with host permissions for all URLs and the DWR permission.
230 // Installation times will be so that |extension_| is older than
232 scoped_refptr
<Extension
> extension_
;
233 scoped_refptr
<Extension
> extension2_
;
234 scoped_refptr
<InfoMap
> extension_info_map_
;
237 void WebRequestRulesRegistryTest::SetUp() {
238 testing::Test::SetUp();
241 extension_
= LoadManifestUnchecked("permissions",
242 "web_request_all_host_permissions.json",
243 Manifest::INVALID_LOCATION
,
247 ASSERT_TRUE(extension_
.get()) << error
;
248 extension2_
= LoadManifestUnchecked("permissions",
249 "web_request_all_host_permissions.json",
250 Manifest::INVALID_LOCATION
,
254 ASSERT_TRUE(extension2_
.get()) << error
;
255 extension_info_map_
= new InfoMap
;
256 ASSERT_TRUE(extension_info_map_
.get());
257 extension_info_map_
->AddExtension(extension_
.get(),
258 base::Time() + base::TimeDelta::FromDays(1),
259 false /*incognito_enabled*/,
260 false /*notifications_disabled*/);
261 extension_info_map_
->AddExtension(extension2_
.get(),
262 base::Time() + base::TimeDelta::FromDays(2),
263 false /*incognito_enabled*/,
264 false /*notifications_disabled*/);
268 TEST_F(WebRequestRulesRegistryTest
, AddRulesImpl
) {
269 scoped_refptr
<TestWebRequestRulesRegistry
> registry(
270 new TestWebRequestRulesRegistry(extension_info_map_
));
273 std::vector
<linked_ptr
<api::events::Rule
>> rules
;
274 rules
.push_back(CreateRule1());
275 rules
.push_back(CreateRule2());
277 error
= registry
->AddRules(kExtensionId
, rules
);
278 EXPECT_EQ("", error
);
279 EXPECT_EQ(1, registry
->num_clear_cache_calls());
281 std::set
<const WebRequestRule
*> matches
;
283 GURL
http_url("http://www.example.com");
284 net::TestURLRequestContext context
;
285 scoped_ptr
<net::URLRequest
> http_request(context
.CreateRequest(
286 http_url
, net::DEFAULT_PRIORITY
, NULL
));
287 WebRequestData
request_data(http_request
.get(), ON_BEFORE_REQUEST
);
288 matches
= registry
->GetMatches(request_data
);
289 EXPECT_EQ(2u, matches
.size());
291 std::set
<WebRequestRule::GlobalRuleId
> matches_ids
;
292 for (std::set
<const WebRequestRule
*>::const_iterator it
= matches
.begin();
293 it
!= matches
.end(); ++it
)
294 matches_ids
.insert((*it
)->id());
295 EXPECT_TRUE(ContainsKey(matches_ids
, std::make_pair(kExtensionId
, kRuleId1
)));
296 EXPECT_TRUE(ContainsKey(matches_ids
, std::make_pair(kExtensionId
, kRuleId2
)));
298 GURL
foobar_url("http://www.foobar.com");
299 scoped_ptr
<net::URLRequest
> foobar_request(context
.CreateRequest(
300 foobar_url
, net::DEFAULT_PRIORITY
, NULL
));
301 request_data
.request
= foobar_request
.get();
302 matches
= registry
->GetMatches(request_data
);
303 EXPECT_EQ(1u, matches
.size());
304 WebRequestRule::GlobalRuleId expected_pair
=
305 std::make_pair(kExtensionId
, kRuleId2
);
306 EXPECT_EQ(expected_pair
, (*matches
.begin())->id());
309 TEST_F(WebRequestRulesRegistryTest
, RemoveRulesImpl
) {
310 scoped_refptr
<TestWebRequestRulesRegistry
> registry(
311 new TestWebRequestRulesRegistry(extension_info_map_
));
314 // Setup RulesRegistry to contain two rules.
315 std::vector
<linked_ptr
<api::events::Rule
>> rules_to_add
;
316 rules_to_add
.push_back(CreateRule1());
317 rules_to_add
.push_back(CreateRule2());
318 error
= registry
->AddRules(kExtensionId
, rules_to_add
);
319 EXPECT_EQ("", error
);
320 EXPECT_EQ(1, registry
->num_clear_cache_calls());
322 // Verify initial state.
323 std::vector
<linked_ptr
<api::events::Rule
>> registered_rules
;
324 registry
->GetAllRules(kExtensionId
, ®istered_rules
);
325 EXPECT_EQ(2u, registered_rules
.size());
326 EXPECT_EQ(1u, registry
->RulesWithoutTriggers());
328 // Remove first rule.
329 std::vector
<std::string
> rules_to_remove
;
330 rules_to_remove
.push_back(kRuleId1
);
331 error
= registry
->RemoveRules(kExtensionId
, rules_to_remove
);
332 EXPECT_EQ("", error
);
333 EXPECT_EQ(2, registry
->num_clear_cache_calls());
335 // Verify that only one rule is left.
336 registered_rules
.clear();
337 registry
->GetAllRules(kExtensionId
, ®istered_rules
);
338 EXPECT_EQ(1u, registered_rules
.size());
339 EXPECT_EQ(1u, registry
->RulesWithoutTriggers());
341 // Now rules_to_remove contains both rules, i.e. one that does not exist in
342 // the rules registry anymore. Effectively we only remove the second rule.
343 rules_to_remove
.push_back(kRuleId2
);
344 error
= registry
->RemoveRules(kExtensionId
, rules_to_remove
);
345 EXPECT_EQ("", error
);
346 EXPECT_EQ(3, registry
->num_clear_cache_calls());
348 // Verify that everything is gone.
349 registered_rules
.clear();
350 registry
->GetAllRules(kExtensionId
, ®istered_rules
);
351 EXPECT_EQ(0u, registered_rules
.size());
352 EXPECT_EQ(0u, registry
->RulesWithoutTriggers());
354 EXPECT_TRUE(registry
->IsEmpty());
357 TEST_F(WebRequestRulesRegistryTest
, RemoveAllRulesImpl
) {
358 scoped_refptr
<TestWebRequestRulesRegistry
> registry(
359 new TestWebRequestRulesRegistry(extension_info_map_
));
362 // Setup RulesRegistry to contain two rules, one for each extension.
363 std::vector
<linked_ptr
<api::events::Rule
>> rules_to_add(1);
364 rules_to_add
[0] = CreateRule1();
365 error
= registry
->AddRules(kExtensionId
, rules_to_add
);
366 EXPECT_EQ("", error
);
367 EXPECT_EQ(1, registry
->num_clear_cache_calls());
369 rules_to_add
[0] = CreateRule2();
370 error
= registry
->AddRules(kExtensionId2
, rules_to_add
);
371 EXPECT_EQ("", error
);
372 EXPECT_EQ(2, registry
->num_clear_cache_calls());
374 // Verify initial state.
375 std::vector
<linked_ptr
<api::events::Rule
>> registered_rules
;
376 registry
->GetAllRules(kExtensionId
, ®istered_rules
);
377 EXPECT_EQ(1u, registered_rules
.size());
378 registered_rules
.clear();
379 registry
->GetAllRules(kExtensionId2
, ®istered_rules
);
380 EXPECT_EQ(1u, registered_rules
.size());
382 // Remove rule of first extension.
383 error
= registry
->RemoveAllRules(kExtensionId
);
384 EXPECT_EQ("", error
);
385 EXPECT_EQ(3, registry
->num_clear_cache_calls());
387 // Verify that only the first rule is deleted.
388 registered_rules
.clear();
389 registry
->GetAllRules(kExtensionId
, ®istered_rules
);
390 EXPECT_EQ(0u, registered_rules
.size());
391 registered_rules
.clear();
392 registry
->GetAllRules(kExtensionId2
, ®istered_rules
);
393 EXPECT_EQ(1u, registered_rules
.size());
395 // Test removing rules if none exist.
396 error
= registry
->RemoveAllRules(kExtensionId
);
397 EXPECT_EQ("", error
);
398 EXPECT_EQ(4, registry
->num_clear_cache_calls());
400 // Remove rule from second extension.
401 error
= registry
->RemoveAllRules(kExtensionId2
);
402 EXPECT_EQ("", error
);
403 EXPECT_EQ(5, registry
->num_clear_cache_calls());
405 EXPECT_TRUE(registry
->IsEmpty());
408 // Test precedences between extensions.
409 TEST_F(WebRequestRulesRegistryTest
, Precedences
) {
410 scoped_refptr
<WebRequestRulesRegistry
> registry(
411 new TestWebRequestRulesRegistry(extension_info_map_
));
414 std::vector
<linked_ptr
<api::events::Rule
>> rules_to_add_1(1);
415 rules_to_add_1
[0] = CreateRedirectRule("http://www.foo.com");
416 error
= registry
->AddRules(kExtensionId
, rules_to_add_1
);
417 EXPECT_EQ("", error
);
419 std::vector
<linked_ptr
<api::events::Rule
>> rules_to_add_2(1);
420 rules_to_add_2
[0] = CreateRedirectRule("http://www.bar.com");
421 error
= registry
->AddRules(kExtensionId2
, rules_to_add_2
);
422 EXPECT_EQ("", error
);
424 GURL
url("http://www.google.com");
425 net::TestURLRequestContext context
;
426 scoped_ptr
<net::URLRequest
> request(context
.CreateRequest(
427 url
, net::DEFAULT_PRIORITY
, NULL
));
428 WebRequestData
request_data(request
.get(), ON_BEFORE_REQUEST
);
429 std::list
<LinkedPtrEventResponseDelta
> deltas
=
430 registry
->CreateDeltas(NULL
, request_data
, false);
432 // The second extension is installed later and will win for this reason
433 // in conflict resolution.
434 ASSERT_EQ(2u, deltas
.size());
435 deltas
.sort(&helpers::InDecreasingExtensionInstallationTimeOrder
);
437 std::list
<LinkedPtrEventResponseDelta
>::iterator i
= deltas
.begin();
438 LinkedPtrEventResponseDelta winner
= *i
++;
439 LinkedPtrEventResponseDelta loser
= *i
;
441 EXPECT_EQ(kExtensionId2
, winner
->extension_id
);
442 EXPECT_EQ(base::Time() + base::TimeDelta::FromDays(2),
443 winner
->extension_install_time
);
444 EXPECT_EQ(GURL("http://www.bar.com"), winner
->new_url
);
446 EXPECT_EQ(kExtensionId
, loser
->extension_id
);
447 EXPECT_EQ(base::Time() + base::TimeDelta::FromDays(1),
448 loser
->extension_install_time
);
449 EXPECT_EQ(GURL("http://www.foo.com"), loser
->new_url
);
452 // Test priorities of rules within one extension.
453 TEST_F(WebRequestRulesRegistryTest
, Priorities
) {
454 scoped_refptr
<WebRequestRulesRegistry
> registry(
455 new TestWebRequestRulesRegistry(extension_info_map_
));
458 std::vector
<linked_ptr
<api::events::Rule
>> rules_to_add_1(1);
459 rules_to_add_1
[0] = CreateRedirectRule("http://www.foo.com");
460 error
= registry
->AddRules(kExtensionId
, rules_to_add_1
);
461 EXPECT_EQ("", error
);
463 std::vector
<linked_ptr
<api::events::Rule
>> rules_to_add_2(1);
464 rules_to_add_2
[0] = CreateRedirectRule("http://www.bar.com");
465 error
= registry
->AddRules(kExtensionId2
, rules_to_add_2
);
466 EXPECT_EQ("", error
);
468 std::vector
<linked_ptr
<api::events::Rule
>> rules_to_add_3(1);
469 rules_to_add_3
[0] = CreateIgnoreRule();
470 error
= registry
->AddRules(kExtensionId
, rules_to_add_3
);
471 EXPECT_EQ("", error
);
473 GURL
url("http://www.google.com/index.html");
474 net::TestURLRequestContext context
;
475 scoped_ptr
<net::URLRequest
> request(context
.CreateRequest(
476 url
, net::DEFAULT_PRIORITY
, NULL
));
477 WebRequestData
request_data(request
.get(), ON_BEFORE_REQUEST
);
478 std::list
<LinkedPtrEventResponseDelta
> deltas
=
479 registry
->CreateDeltas(NULL
, request_data
, false);
481 // The redirect by the first extension is ignored due to the ignore rule.
482 ASSERT_EQ(1u, deltas
.size());
483 LinkedPtrEventResponseDelta effective_rule
= *(deltas
.begin());
485 EXPECT_EQ(kExtensionId2
, effective_rule
->extension_id
);
486 EXPECT_EQ(base::Time() + base::TimeDelta::FromDays(2),
487 effective_rule
->extension_install_time
);
488 EXPECT_EQ(GURL("http://www.bar.com"), effective_rule
->new_url
);
491 // Test ignoring of rules by tag.
492 TEST_F(WebRequestRulesRegistryTest
, IgnoreRulesByTag
) {
493 const char kRule1
[] =
495 " \"id\": \"rule1\", \n"
496 " \"tags\": [\"non_matching_tag\", \"ignore_tag\"], \n"
497 " \"conditions\": [ \n"
499 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
500 " \"url\": {\"hostSuffix\": \"foo.com\"} \n"
505 " \"instanceType\": \"declarativeWebRequest.RedirectRequest\",\n"
506 " \"redirectUrl\": \"http://bar.com\" \n"
509 " \"priority\": 200 \n"
512 const char kRule2
[] =
514 " \"id\": \"rule2\", \n"
515 " \"conditions\": [ \n"
517 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
518 " \"url\": {\"pathPrefix\": \"/test\"} \n"
523 " \"instanceType\": \"declarativeWebRequest.IgnoreRules\", \n"
524 " \"hasTag\": \"ignore_tag\" \n"
527 " \"priority\": 300 \n"
530 scoped_ptr
<base::Value
> value1
= base::JSONReader::Read(kRule1
);
531 ASSERT_TRUE(value1
.get());
532 scoped_ptr
<base::Value
> value2
= base::JSONReader::Read(kRule2
);
533 ASSERT_TRUE(value2
.get());
535 std::vector
<linked_ptr
<api::events::Rule
>> rules
;
536 rules
.push_back(make_linked_ptr(new api::events::Rule
));
537 rules
.push_back(make_linked_ptr(new api::events::Rule
));
538 ASSERT_TRUE(api::events::Rule::Populate(*value1
, rules
[0].get()));
539 ASSERT_TRUE(api::events::Rule::Populate(*value2
, rules
[1].get()));
541 scoped_refptr
<WebRequestRulesRegistry
> registry(
542 new TestWebRequestRulesRegistry(extension_info_map_
));
543 std::string error
= registry
->AddRulesImpl(kExtensionId
, rules
);
544 EXPECT_EQ("", error
);
545 EXPECT_FALSE(registry
->IsEmpty());
547 GURL
url("http://www.foo.com/test");
548 net::TestURLRequestContext context
;
549 scoped_ptr
<net::URLRequest
> request(context
.CreateRequest(
550 url
, net::DEFAULT_PRIORITY
, NULL
));
551 WebRequestData
request_data(request
.get(), ON_BEFORE_REQUEST
);
552 std::list
<LinkedPtrEventResponseDelta
> deltas
=
553 registry
->CreateDeltas(NULL
, request_data
, false);
555 // The redirect by the redirect rule is ignored due to the ignore rule.
556 std::set
<const WebRequestRule
*> matches
= registry
->GetMatches(request_data
);
557 EXPECT_EQ(2u, matches
.size());
558 ASSERT_EQ(0u, deltas
.size());
561 // Test that rules failing IsFulfilled on their conditions are never returned by
563 TEST_F(WebRequestRulesRegistryTest
, GetMatchesCheckFulfilled
) {
564 scoped_refptr
<TestWebRequestRulesRegistry
> registry(
565 new TestWebRequestRulesRegistry(extension_info_map_
));
566 const std::string
kMatchingUrlAttribute(
567 "\"url\": { \"pathContains\": \"\" }, \n");
568 const std::string
kNonMatchingNonUrlAttribute(
569 "\"resourceType\": [\"stylesheet\"], \n");
570 const std::string
kBothAttributes(kMatchingUrlAttribute
+
571 kNonMatchingNonUrlAttribute
);
573 std::vector
<const std::string
*> attributes
;
574 std::vector
<linked_ptr
<api::events::Rule
>> rules
;
576 // Rules 1 and 2 have one condition, neither of them should fire.
577 attributes
.push_back(&kNonMatchingNonUrlAttribute
);
578 rules
.push_back(CreateCancellingRule(kRuleId1
, attributes
));
581 attributes
.push_back(&kBothAttributes
);
582 rules
.push_back(CreateCancellingRule(kRuleId2
, attributes
));
584 // Rule 3 has two conditions, one with a matching URL attribute, and one
585 // with a non-matching non-URL attribute.
587 attributes
.push_back(&kMatchingUrlAttribute
);
588 attributes
.push_back(&kNonMatchingNonUrlAttribute
);
589 rules
.push_back(CreateCancellingRule(kRuleId3
, attributes
));
591 error
= registry
->AddRules(kExtensionId
, rules
);
592 EXPECT_EQ("", error
);
593 EXPECT_EQ(1, registry
->num_clear_cache_calls());
595 std::set
<const WebRequestRule
*> matches
;
597 GURL
http_url("http://www.example.com");
598 net::TestURLRequestContext context
;
599 scoped_ptr
<net::URLRequest
> http_request(context
.CreateRequest(
600 http_url
, net::DEFAULT_PRIORITY
, NULL
));
601 WebRequestData
request_data(http_request
.get(), ON_BEFORE_REQUEST
);
602 matches
= registry
->GetMatches(request_data
);
603 EXPECT_EQ(1u, matches
.size());
604 WebRequestRule::GlobalRuleId expected_pair
= std::make_pair(kExtensionId
,
606 EXPECT_EQ(expected_pair
, (*matches
.begin())->id());
609 // Test that the url and firstPartyForCookiesUrl attributes are evaluated
610 // against corresponding URLs. Tested on requests where these URLs actually
612 TEST_F(WebRequestRulesRegistryTest
, GetMatchesDifferentUrls
) {
613 scoped_refptr
<TestWebRequestRulesRegistry
> registry(
614 new TestWebRequestRulesRegistry(extension_info_map_
));
615 const std::string
kUrlAttribute(
616 "\"url\": { \"hostContains\": \"url\" }, \n");
617 const std::string
kFirstPartyUrlAttribute(
618 "\"firstPartyForCookiesUrl\": { \"hostContains\": \"fpfc\" }, \n");
620 std::vector
<const std::string
*> attributes
;
621 std::vector
<linked_ptr
<api::events::Rule
>> rules
;
623 // Rule 1 has one condition, with a url attribute
624 attributes
.push_back(&kUrlAttribute
);
625 rules
.push_back(CreateCancellingRule(kRuleId1
, attributes
));
627 // Rule 2 has one condition, with a firstPartyForCookiesUrl attribute
629 attributes
.push_back(&kFirstPartyUrlAttribute
);
630 rules
.push_back(CreateCancellingRule(kRuleId2
, attributes
));
632 error
= registry
->AddRules(kExtensionId
, rules
);
633 EXPECT_EQ("", error
);
634 EXPECT_EQ(1, registry
->num_clear_cache_calls());
636 std::set
<const WebRequestRule
*> matches
;
638 const GURL urls
[] = {
639 GURL("http://url.example.com"), // matching
640 GURL("http://www.example.com") // non-matching
642 const GURL firstPartyUrls
[] = {
643 GURL("http://www.example.com"), // non-matching
644 GURL("http://fpfc.example.com") // matching
646 // Which rules should match in subsequent test iterations.
647 const char* const matchingRuleIds
[] = { kRuleId1
, kRuleId2
};
648 static_assert(arraysize(urls
) == arraysize(firstPartyUrls
),
649 "urls and firstPartyUrls must have the same number "
651 static_assert(arraysize(urls
) == arraysize(matchingRuleIds
),
652 "urls and matchingRuleIds must have the same number "
654 net::TestURLRequestContext context
;
656 for (size_t i
= 0; i
< arraysize(matchingRuleIds
); ++i
) {
657 // Construct the inputs.
658 scoped_ptr
<net::URLRequest
> http_request(context
.CreateRequest(
659 urls
[i
], net::DEFAULT_PRIORITY
, NULL
));
660 WebRequestData
request_data(http_request
.get(), ON_BEFORE_REQUEST
);
661 http_request
->set_first_party_for_cookies(firstPartyUrls
[i
]);
662 // Now run both rules on the input.
663 matches
= registry
->GetMatches(request_data
);
664 SCOPED_TRACE(testing::Message("i = ") << i
<< ", rule id = "
665 << matchingRuleIds
[i
]);
666 // Make sure that the right rule succeeded.
667 EXPECT_EQ(1u, matches
.size());
668 EXPECT_EQ(WebRequestRule::GlobalRuleId(std::make_pair(kExtensionId
,
669 matchingRuleIds
[i
])),
670 (*matches
.begin())->id());
674 TEST(WebRequestRulesRegistrySimpleTest
, StageChecker
) {
675 // The contentType condition can only be evaluated during ON_HEADERS_RECEIVED
676 // but the SetRequestHeader action can only be executed during
677 // ON_BEFORE_SEND_HEADERS.
678 // Therefore, this is an inconsistent rule that needs to be flagged.
681 " \"id\": \"rule1\", \n"
682 " \"conditions\": [ \n"
684 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
685 " \"url\": {\"hostSuffix\": \"foo.com\"}, \n"
686 " \"contentType\": [\"image/jpeg\"] \n"
691 " \"instanceType\": \"declarativeWebRequest.SetRequestHeader\",\n"
692 " \"name\": \"Content-Type\", \n"
693 " \"value\": \"text/plain\" \n"
696 " \"priority\": 200 \n"
699 scoped_ptr
<base::Value
> value
= base::JSONReader::Read(kRule
);
702 api::events::Rule rule
;
703 ASSERT_TRUE(api::events::Rule::Populate(*value
, &rule
));
707 scoped_ptr
<WebRequestConditionSet
> conditions
=
708 WebRequestConditionSet::Create(
709 NULL
, matcher
.condition_factory(), rule
.conditions
, &error
);
710 ASSERT_TRUE(error
.empty()) << error
;
711 ASSERT_TRUE(conditions
);
713 bool bad_message
= false;
714 scoped_ptr
<WebRequestActionSet
> actions
=
715 WebRequestActionSet::Create(
716 NULL
, NULL
, rule
.actions
, &error
, &bad_message
);
717 ASSERT_TRUE(error
.empty()) << error
;
718 ASSERT_FALSE(bad_message
);
719 ASSERT_TRUE(actions
);
721 EXPECT_FALSE(WebRequestRulesRegistry::StageChecker(
722 conditions
.get(), actions
.get(), &error
));
723 EXPECT_THAT(error
, HasSubstr("no time in the request life-cycle"));
724 EXPECT_THAT(error
, HasSubstr(actions
->actions().back()->GetName()));
727 TEST(WebRequestRulesRegistrySimpleTest
, HostPermissionsChecker
) {
728 const char kAction
[] = // This action requires all URLs host permission.
730 " \"instanceType\": \"declarativeWebRequest.RedirectRequest\",\n"
731 " \"redirectUrl\": \"http://bar.com\" \n"
733 scoped_ptr
<base::Value
> action_value
= base::JSONReader::Read(kAction
);
734 ASSERT_TRUE(action_value
);
736 WebRequestActionSet::Values actions
;
737 actions
.push_back(linked_ptr
<base::Value
>(action_value
.release()));
738 ASSERT_TRUE(actions
.back().get());
741 bool bad_message
= false;
742 scoped_ptr
<WebRequestActionSet
> action_set(
743 WebRequestActionSet::Create(NULL
, NULL
, actions
, &error
, &bad_message
));
744 ASSERT_TRUE(error
.empty()) << error
;
745 ASSERT_FALSE(bad_message
);
746 ASSERT_TRUE(action_set
);
748 scoped_refptr
<Extension
> extension_no_url(
749 LoadManifest("permissions", "web_request_no_host.json"));
750 scoped_refptr
<Extension
> extension_some_urls(
751 LoadManifest("permissions", "web_request_com_host_permissions.json"));
752 scoped_refptr
<Extension
> extension_all_urls(
753 LoadManifest("permissions", "web_request_all_host_permissions.json"));
755 EXPECT_TRUE(WebRequestRulesRegistry::HostPermissionsChecker(
756 extension_all_urls
.get(), action_set
.get(), &error
));
757 EXPECT_TRUE(error
.empty()) << error
;
759 EXPECT_FALSE(WebRequestRulesRegistry::HostPermissionsChecker(
760 extension_some_urls
.get(), action_set
.get(), &error
));
761 EXPECT_THAT(error
, HasSubstr("permission for all"));
762 EXPECT_THAT(error
, HasSubstr(action_set
->actions().back()->GetName()));
764 EXPECT_FALSE(WebRequestRulesRegistry::HostPermissionsChecker(
765 extension_no_url
.get(), action_set
.get(), &error
));
766 EXPECT_THAT(error
, HasSubstr("permission for all"));
767 EXPECT_THAT(error
, HasSubstr(action_set
->actions().back()->GetName()));
770 TEST_F(WebRequestRulesRegistryTest
, CheckOriginAndPathRegEx
) {
773 " \"id\": \"rule1\", \n"
774 " \"conditions\": [ \n"
776 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
777 " \"url\": {\"originAndPathMatches\": \"fo+.com\"} \n"
782 " \"instanceType\": \"declarativeWebRequest.RedirectRequest\",\n"
783 " \"redirectUrl\": \"http://bar.com\" \n"
786 " \"priority\": 200 \n"
789 scoped_ptr
<base::Value
> value
= base::JSONReader::Read(kRule
);
790 ASSERT_TRUE(value
.get());
792 std::vector
<linked_ptr
<api::events::Rule
>> rules
;
793 rules
.push_back(make_linked_ptr(new api::events::Rule
));
794 ASSERT_TRUE(api::events::Rule::Populate(*value
, rules
.back().get()));
796 scoped_refptr
<WebRequestRulesRegistry
> registry(
797 new TestWebRequestRulesRegistry(extension_info_map_
));
800 std::string error
= registry
->AddRulesImpl(kExtensionId
, rules
);
801 EXPECT_EQ("", error
);
803 net::TestURLRequestContext context
;
804 std::list
<LinkedPtrEventResponseDelta
> deltas
;
806 // No match because match is in the query parameter.
807 GURL
url1("http://bar.com/index.html?foo.com");
808 scoped_ptr
<net::URLRequest
> request1(context
.CreateRequest(
809 url1
, net::DEFAULT_PRIORITY
, NULL
));
810 WebRequestData
request_data1(request1
.get(), ON_BEFORE_REQUEST
);
811 deltas
= registry
->CreateDeltas(NULL
, request_data1
, false);
812 EXPECT_EQ(0u, deltas
.size());
814 // This is a correct match.
815 GURL
url2("http://foo.com/index.html");
816 scoped_ptr
<net::URLRequest
> request2(context
.CreateRequest(
817 url2
, net::DEFAULT_PRIORITY
, NULL
));
818 WebRequestData
request_data2(request2
.get(), ON_BEFORE_REQUEST
);
819 deltas
= registry
->CreateDeltas(NULL
, request_data2
, false);
820 EXPECT_EQ(1u, deltas
.size());
823 } // namespace extensions