webrequest_rules_registry_unittest.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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_rules_registry.h"
6
7#include <string>
8#include <vector>
9
10#include "base/basictypes.h"
11#include "base/json/json_reader.h"
12#include "base/memory/linked_ptr.h"
13#include "base/message_loop.h"
14#include "base/stl_util.h"
15#include "base/test/values_test_util.h"
16#include "base/values.h"
17#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h"
18#include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
19#include "chrome/common/extensions/extension_test_util.h"
20#include "content/public/test/test_browser_thread.h"
21#include "extensions/common/matcher/url_matcher_constants.h"
22#include "net/url_request/url_request_test_util.h"
23#include "testing/gmock/include/gmock/gmock.h"
24#include "testing/gtest/include/gtest/gtest-message.h"
25#include "testing/gtest/include/gtest/gtest.h"
26
27namespace {
28const char kExtensionId[] = "ext1";
29const char kExtensionId2[] = "ext2";
30const char kRuleId1[] = "rule1";
31const char kRuleId2[] = "rule2";
32const char kRuleId3[] = "rule3";
33const char kRuleId4[] = "rule4";
34}  // namespace
35
36namespace extensions {
37
38using base::Value;
39using extension_test_util::LoadManifest;
40using extension_test_util::LoadManifestUnchecked;
41using testing::HasSubstr;
42
43namespace helpers = extension_web_request_api_helpers;
44namespace keys = declarative_webrequest_constants;
45namespace keys2 = url_matcher_constants;
46
47class TestWebRequestRulesRegistry : public WebRequestRulesRegistry {
48 public:
49  explicit TestWebRequestRulesRegistry(
50      scoped_refptr<ExtensionInfoMap> extension_info_map)
51      : WebRequestRulesRegistry(NULL /*profile*/, NULL /*ui_part*/),
52        num_clear_cache_calls_(0) {
53    SetExtensionInfoMapForTesting(extension_info_map);
54  }
55
56  // Returns how often the in-memory caches of the renderers were instructed
57  // to be cleared.
58  int num_clear_cache_calls() const { return num_clear_cache_calls_; }
59
60  // How many rules are there which have some conditions not triggered by URL
61  // matches.
62  size_t RulesWithoutTriggers() const {
63    return rules_with_untriggered_conditions_for_test().size();
64  }
65
66 protected:
67  virtual ~TestWebRequestRulesRegistry() {}
68
69  virtual void ClearCacheOnNavigation() OVERRIDE {
70    ++num_clear_cache_calls_;
71  }
72
73 private:
74  int num_clear_cache_calls_;
75};
76
77class WebRequestRulesRegistryTest : public testing::Test {
78 public:
79  WebRequestRulesRegistryTest()
80      : message_loop_(base::MessageLoop::TYPE_IO),
81        ui_(content::BrowserThread::UI, &message_loop_),
82        io_(content::BrowserThread::IO, &message_loop_) {}
83
84  virtual ~WebRequestRulesRegistryTest() {}
85
86  virtual void SetUp() OVERRIDE;
87
88  virtual void TearDown() OVERRIDE {
89    // Make sure that deletion traits of all registries are executed.
90    message_loop_.RunUntilIdle();
91  }
92
93  // Returns a rule that roughly matches http://*.example.com and
94  // https://www.example.com and cancels it
95  linked_ptr<RulesRegistry::Rule> CreateRule1() {
96    base::ListValue* scheme_http = new base::ListValue();
97    scheme_http->Append(Value::CreateStringValue("http"));
98    base::DictionaryValue* http_condition_dict = new base::DictionaryValue();
99    http_condition_dict->Set(keys2::kSchemesKey, scheme_http);
100    http_condition_dict->SetString(keys2::kHostSuffixKey, "example.com");
101    base::DictionaryValue http_condition_url_filter;
102    http_condition_url_filter.Set(keys::kUrlKey, http_condition_dict);
103    http_condition_url_filter.SetString(keys::kInstanceTypeKey,
104                                        keys::kRequestMatcherType);
105
106    base::ListValue* scheme_https = new base::ListValue();
107    scheme_http->Append(Value::CreateStringValue("https"));
108    base::DictionaryValue* https_condition_dict = new base::DictionaryValue();
109    https_condition_dict->Set(keys2::kSchemesKey, scheme_https);
110    https_condition_dict->SetString(keys2::kHostSuffixKey, "example.com");
111    https_condition_dict->SetString(keys2::kHostPrefixKey, "www");
112    base::DictionaryValue https_condition_url_filter;
113    https_condition_url_filter.Set(keys::kUrlKey, https_condition_dict);
114    https_condition_url_filter.SetString(keys::kInstanceTypeKey,
115                                         keys::kRequestMatcherType);
116
117    base::DictionaryValue action_dict;
118    action_dict.SetString(keys::kInstanceTypeKey, keys::kCancelRequestType);
119
120    linked_ptr<RulesRegistry::Rule> rule(new RulesRegistry::Rule);
121    rule->id.reset(new std::string(kRuleId1));
122    rule->priority.reset(new int(100));
123    rule->actions.push_back(linked_ptr<base::Value>(action_dict.DeepCopy()));
124    rule->conditions.push_back(
125        linked_ptr<base::Value>(http_condition_url_filter.DeepCopy()));
126    rule->conditions.push_back(
127        linked_ptr<base::Value>(https_condition_url_filter.DeepCopy()));
128    return rule;
129  }
130
131  // Returns a rule that matches anything and cancels it.
132  linked_ptr<RulesRegistry::Rule> CreateRule2() {
133    base::DictionaryValue condition_dict;
134    condition_dict.SetString(keys::kInstanceTypeKey, keys::kRequestMatcherType);
135
136    base::DictionaryValue action_dict;
137    action_dict.SetString(keys::kInstanceTypeKey, keys::kCancelRequestType);
138
139    linked_ptr<RulesRegistry::Rule> rule(new RulesRegistry::Rule);
140    rule->id.reset(new std::string(kRuleId2));
141    rule->priority.reset(new int(100));
142    rule->actions.push_back(linked_ptr<base::Value>(action_dict.DeepCopy()));
143    rule->conditions.push_back(
144        linked_ptr<base::Value>(condition_dict.DeepCopy()));
145    return rule;
146  }
147
148  linked_ptr<RulesRegistry::Rule> CreateRedirectRule(
149      const std::string& destination) {
150    base::DictionaryValue condition_dict;
151    condition_dict.SetString(keys::kInstanceTypeKey, keys::kRequestMatcherType);
152
153    base::DictionaryValue action_dict;
154    action_dict.SetString(keys::kInstanceTypeKey, keys::kRedirectRequestType);
155    action_dict.SetString(keys::kRedirectUrlKey, destination);
156
157    linked_ptr<RulesRegistry::Rule> rule(new RulesRegistry::Rule);
158    rule->id.reset(new std::string(kRuleId3));
159    rule->priority.reset(new int(100));
160    rule->actions.push_back(linked_ptr<base::Value>(action_dict.DeepCopy()));
161    rule->conditions.push_back(
162        linked_ptr<base::Value>(condition_dict.DeepCopy()));
163    return rule;
164  }
165
166  // Create a rule to ignore all other rules for a destination that
167  // contains index.html.
168  linked_ptr<RulesRegistry::Rule> CreateIgnoreRule() {
169    base::DictionaryValue condition_dict;
170    base::DictionaryValue* http_condition_dict = new base::DictionaryValue();
171    http_condition_dict->SetString(keys2::kPathContainsKey, "index.html");
172    condition_dict.SetString(keys::kInstanceTypeKey, keys::kRequestMatcherType);
173    condition_dict.Set(keys::kUrlKey, http_condition_dict);
174
175    base::DictionaryValue action_dict;
176    action_dict.SetString(keys::kInstanceTypeKey, keys::kIgnoreRulesType);
177    action_dict.SetInteger(keys::kLowerPriorityThanKey, 150);
178
179    linked_ptr<RulesRegistry::Rule> rule(new RulesRegistry::Rule);
180    rule->id.reset(new std::string(kRuleId4));
181    rule->priority.reset(new int(200));
182    rule->actions.push_back(linked_ptr<base::Value>(action_dict.DeepCopy()));
183    rule->conditions.push_back(
184        linked_ptr<base::Value>(condition_dict.DeepCopy()));
185    return rule;
186  }
187
188  // Create a condition with the attributes specified. An example value of
189  // |attributes| is: "\"resourceType\": [\"stylesheet\"], \n".
190  linked_ptr<base::Value> CreateCondition(const std::string& attributes) {
191    std::string json_description =
192        "{ \n"
193        "  \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n";
194    json_description += attributes;
195    json_description += "}";
196
197    return linked_ptr<base::Value>(
198        base::test::ParseJson(json_description).release());
199  }
200
201  // Create a rule with the ID |rule_id| and with conditions created from the
202  // |attributes| specified (one entry one condition). An example value of a
203  // string from |attributes| is: "\"resourceType\": [\"stylesheet\"], \n".
204  linked_ptr<RulesRegistry::Rule> CreateCancellingRule(
205      const char* rule_id,
206      const std::vector<const std::string*>& attributes) {
207    base::DictionaryValue action_dict;
208    action_dict.SetString(keys::kInstanceTypeKey, keys::kCancelRequestType);
209
210    linked_ptr<RulesRegistry::Rule> rule(new RulesRegistry::Rule);
211    rule->id.reset(new std::string(rule_id));
212    rule->priority.reset(new int(1));
213    rule->actions.push_back(linked_ptr<base::Value>(action_dict.DeepCopy()));
214    for (std::vector<const std::string*>::const_iterator it =
215             attributes.begin();
216         it != attributes.end(); ++it)
217      rule->conditions.push_back(CreateCondition(**it));
218    return rule;
219  }
220
221 protected:
222  base::MessageLoop message_loop_;
223  content::TestBrowserThread ui_;
224  content::TestBrowserThread io_;
225  // Two extensions with host permissions for all URLs and the DWR permission.
226  // Installation times will be so that |extension_| is older than
227  // |extension2_|.
228  scoped_refptr<Extension> extension_;
229  scoped_refptr<Extension> extension2_;
230  scoped_refptr<ExtensionInfoMap> extension_info_map_;
231};
232
233void WebRequestRulesRegistryTest::SetUp() {
234  testing::Test::SetUp();
235
236  std::string error;
237  extension_ = LoadManifestUnchecked("permissions",
238                                     "web_request_all_host_permissions.json",
239                                     Manifest::INVALID_LOCATION,
240                                     Extension::NO_FLAGS,
241                                     kExtensionId,
242                                     &error);
243  ASSERT_TRUE(extension_.get()) << error;
244  extension2_ = LoadManifestUnchecked("permissions",
245                                      "web_request_all_host_permissions.json",
246                                      Manifest::INVALID_LOCATION,
247                                      Extension::NO_FLAGS,
248                                      kExtensionId2,
249                                      &error);
250  ASSERT_TRUE(extension2_.get()) << error;
251  extension_info_map_ = new ExtensionInfoMap;
252  ASSERT_TRUE(extension_info_map_.get());
253  extension_info_map_->AddExtension(extension_.get(),
254                                    base::Time() + base::TimeDelta::FromDays(1),
255                                    false /*incognito_enabled*/);
256  extension_info_map_->AddExtension(extension2_.get(),
257                                    base::Time() + base::TimeDelta::FromDays(2),
258                                    false /*incognito_enabled*/);
259}
260
261
262TEST_F(WebRequestRulesRegistryTest, AddRulesImpl) {
263  scoped_refptr<TestWebRequestRulesRegistry> registry(
264      new TestWebRequestRulesRegistry(extension_info_map_));
265  std::string error;
266
267  std::vector<linked_ptr<RulesRegistry::Rule> > rules;
268  rules.push_back(CreateRule1());
269  rules.push_back(CreateRule2());
270
271  error = registry->AddRules(kExtensionId, rules);
272  EXPECT_EQ("", error);
273  EXPECT_EQ(1, registry->num_clear_cache_calls());
274
275  std::set<const WebRequestRule*> matches;
276
277  GURL http_url("http://www.example.com");
278  net::TestURLRequestContext context;
279  net::TestURLRequest http_request(http_url, NULL, &context, NULL);
280  WebRequestData request_data(&http_request, ON_BEFORE_REQUEST);
281  matches = registry->GetMatches(request_data);
282  EXPECT_EQ(2u, matches.size());
283
284  std::set<WebRequestRule::GlobalRuleId> matches_ids;
285  for (std::set<const WebRequestRule*>::const_iterator it = matches.begin();
286       it != matches.end(); ++it)
287    matches_ids.insert((*it)->id());
288  EXPECT_TRUE(ContainsKey(matches_ids, std::make_pair(kExtensionId, kRuleId1)));
289  EXPECT_TRUE(ContainsKey(matches_ids, std::make_pair(kExtensionId, kRuleId2)));
290
291  GURL foobar_url("http://www.foobar.com");
292  net::TestURLRequest foobar_request(foobar_url, NULL, &context, NULL);
293  request_data.request = &foobar_request;
294  matches = registry->GetMatches(request_data);
295  EXPECT_EQ(1u, matches.size());
296  WebRequestRule::GlobalRuleId expected_pair =
297      std::make_pair(kExtensionId, kRuleId2);
298  EXPECT_EQ(expected_pair, (*matches.begin())->id());
299}
300
301TEST_F(WebRequestRulesRegistryTest, RemoveRulesImpl) {
302  scoped_refptr<TestWebRequestRulesRegistry> registry(
303      new TestWebRequestRulesRegistry(extension_info_map_));
304  std::string error;
305
306  // Setup RulesRegistry to contain two rules.
307  std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add;
308  rules_to_add.push_back(CreateRule1());
309  rules_to_add.push_back(CreateRule2());
310  error = registry->AddRules(kExtensionId, rules_to_add);
311  EXPECT_EQ("", error);
312  EXPECT_EQ(1, registry->num_clear_cache_calls());
313
314  // Verify initial state.
315  std::vector<linked_ptr<RulesRegistry::Rule> > registered_rules;
316  registry->GetAllRules(kExtensionId, &registered_rules);
317  EXPECT_EQ(2u, registered_rules.size());
318  EXPECT_EQ(1u, registry->RulesWithoutTriggers());
319
320  // Remove first rule.
321  std::vector<std::string> rules_to_remove;
322  rules_to_remove.push_back(kRuleId1);
323  error = registry->RemoveRules(kExtensionId, rules_to_remove);
324  EXPECT_EQ("", error);
325  EXPECT_EQ(2, registry->num_clear_cache_calls());
326
327  // Verify that only one rule is left.
328  registered_rules.clear();
329  registry->GetAllRules(kExtensionId, &registered_rules);
330  EXPECT_EQ(1u, registered_rules.size());
331  EXPECT_EQ(1u, registry->RulesWithoutTriggers());
332
333  // Now rules_to_remove contains both rules, i.e. one that does not exist in
334  // the rules registry anymore. Effectively we only remove the second rule.
335  rules_to_remove.push_back(kRuleId2);
336  error = registry->RemoveRules(kExtensionId, rules_to_remove);
337  EXPECT_EQ("", error);
338  EXPECT_EQ(3, registry->num_clear_cache_calls());
339
340  // Verify that everything is gone.
341  registered_rules.clear();
342  registry->GetAllRules(kExtensionId, &registered_rules);
343  EXPECT_EQ(0u, registered_rules.size());
344  EXPECT_EQ(0u, registry->RulesWithoutTriggers());
345
346  EXPECT_TRUE(registry->IsEmpty());
347}
348
349TEST_F(WebRequestRulesRegistryTest, RemoveAllRulesImpl) {
350  scoped_refptr<TestWebRequestRulesRegistry> registry(
351      new TestWebRequestRulesRegistry(extension_info_map_));
352  std::string error;
353
354  // Setup RulesRegistry to contain two rules, one for each extension.
355  std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add(1);
356  rules_to_add[0] = CreateRule1();
357  error = registry->AddRules(kExtensionId, rules_to_add);
358  EXPECT_EQ("", error);
359  EXPECT_EQ(1, registry->num_clear_cache_calls());
360
361  rules_to_add[0] = CreateRule2();
362  error = registry->AddRules(kExtensionId2, rules_to_add);
363  EXPECT_EQ("", error);
364  EXPECT_EQ(2, registry->num_clear_cache_calls());
365
366  // Verify initial state.
367  std::vector<linked_ptr<RulesRegistry::Rule> > registered_rules;
368  registry->GetAllRules(kExtensionId, &registered_rules);
369  EXPECT_EQ(1u, registered_rules.size());
370  registered_rules.clear();
371  registry->GetAllRules(kExtensionId2, &registered_rules);
372  EXPECT_EQ(1u, registered_rules.size());
373
374  // Remove rule of first extension.
375  error = registry->RemoveAllRules(kExtensionId);
376  EXPECT_EQ("", error);
377  EXPECT_EQ(3, registry->num_clear_cache_calls());
378
379  // Verify that only the first rule is deleted.
380  registered_rules.clear();
381  registry->GetAllRules(kExtensionId, &registered_rules);
382  EXPECT_EQ(0u, registered_rules.size());
383  registered_rules.clear();
384  registry->GetAllRules(kExtensionId2, &registered_rules);
385  EXPECT_EQ(1u, registered_rules.size());
386
387  // Test removing rules if none exist.
388  error = registry->RemoveAllRules(kExtensionId);
389  EXPECT_EQ("", error);
390  EXPECT_EQ(4, registry->num_clear_cache_calls());
391
392  // Remove rule from second extension.
393  error = registry->RemoveAllRules(kExtensionId2);
394  EXPECT_EQ("", error);
395  EXPECT_EQ(5, registry->num_clear_cache_calls());
396
397  EXPECT_TRUE(registry->IsEmpty());
398}
399
400// Test precedences between extensions.
401TEST_F(WebRequestRulesRegistryTest, Precedences) {
402  scoped_refptr<WebRequestRulesRegistry> registry(
403      new TestWebRequestRulesRegistry(extension_info_map_));
404  std::string error;
405
406  std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add_1(1);
407  rules_to_add_1[0] = CreateRedirectRule("http://www.foo.com");
408  error = registry->AddRules(kExtensionId, rules_to_add_1);
409  EXPECT_EQ("", error);
410
411  std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add_2(1);
412  rules_to_add_2[0] = CreateRedirectRule("http://www.bar.com");
413  error = registry->AddRules(kExtensionId2, rules_to_add_2);
414  EXPECT_EQ("", error);
415
416  GURL url("http://www.google.com");
417  net::TestURLRequestContext context;
418  net::TestURLRequest request(url, NULL, &context, NULL);
419  WebRequestData request_data(&request, ON_BEFORE_REQUEST);
420  std::list<LinkedPtrEventResponseDelta> deltas =
421      registry->CreateDeltas(NULL, request_data, false);
422
423  // The second extension is installed later and will win for this reason
424  // in conflict resolution.
425  ASSERT_EQ(2u, deltas.size());
426  deltas.sort(&helpers::InDecreasingExtensionInstallationTimeOrder);
427
428  std::list<LinkedPtrEventResponseDelta>::iterator i = deltas.begin();
429  LinkedPtrEventResponseDelta winner = *i++;
430  LinkedPtrEventResponseDelta loser = *i;
431
432  EXPECT_EQ(kExtensionId2, winner->extension_id);
433  EXPECT_EQ(base::Time() + base::TimeDelta::FromDays(2),
434            winner->extension_install_time);
435  EXPECT_EQ(GURL("http://www.bar.com"), winner->new_url);
436
437  EXPECT_EQ(kExtensionId, loser->extension_id);
438  EXPECT_EQ(base::Time() + base::TimeDelta::FromDays(1),
439            loser->extension_install_time);
440  EXPECT_EQ(GURL("http://www.foo.com"), loser->new_url);
441}
442
443// Test priorities of rules within one extension.
444TEST_F(WebRequestRulesRegistryTest, Priorities) {
445  scoped_refptr<WebRequestRulesRegistry> registry(
446      new TestWebRequestRulesRegistry(extension_info_map_));
447  std::string error;
448
449  std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add_1(1);
450  rules_to_add_1[0] = CreateRedirectRule("http://www.foo.com");
451  error = registry->AddRules(kExtensionId, rules_to_add_1);
452  EXPECT_EQ("", error);
453
454  std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add_2(1);
455  rules_to_add_2[0] = CreateRedirectRule("http://www.bar.com");
456  error = registry->AddRules(kExtensionId2, rules_to_add_2);
457  EXPECT_EQ("", error);
458
459  std::vector<linked_ptr<RulesRegistry::Rule> > rules_to_add_3(1);
460  rules_to_add_3[0] = CreateIgnoreRule();
461  error = registry->AddRules(kExtensionId, rules_to_add_3);
462  EXPECT_EQ("", error);
463
464  GURL url("http://www.google.com/index.html");
465  net::TestURLRequestContext context;
466  net::TestURLRequest request(url, NULL, &context, NULL);
467  WebRequestData request_data(&request, ON_BEFORE_REQUEST);
468  std::list<LinkedPtrEventResponseDelta> deltas =
469      registry->CreateDeltas(NULL, request_data, false);
470
471  // The redirect by the first extension is ignored due to the ignore rule.
472  ASSERT_EQ(1u, deltas.size());
473  LinkedPtrEventResponseDelta effective_rule = *(deltas.begin());
474
475  EXPECT_EQ(kExtensionId2, effective_rule->extension_id);
476  EXPECT_EQ(base::Time() + base::TimeDelta::FromDays(2),
477            effective_rule->extension_install_time);
478  EXPECT_EQ(GURL("http://www.bar.com"), effective_rule->new_url);
479}
480
481// Test ignoring of rules by tag.
482TEST_F(WebRequestRulesRegistryTest, IgnoreRulesByTag) {
483  const char kRule1[] =
484      "{                                                                 \n"
485      "  \"id\": \"rule1\",                                              \n"
486      "  \"tags\": [\"non_matching_tag\", \"ignore_tag\"],               \n"
487      "  \"conditions\": [                                               \n"
488      "    {                                                             \n"
489      "      \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
490      "      \"url\": {\"hostSuffix\": \"foo.com\"}                      \n"
491      "    }                                                             \n"
492      "  ],                                                              \n"
493      "  \"actions\": [                                                  \n"
494      "    {                                                             \n"
495      "      \"instanceType\": \"declarativeWebRequest.RedirectRequest\",\n"
496      "      \"redirectUrl\": \"http://bar.com\"                         \n"
497      "    }                                                             \n"
498      "  ],                                                              \n"
499      "  \"priority\": 200                                               \n"
500      "}                                                                 ";
501
502  const char kRule2[] =
503      "{                                                                 \n"
504      "  \"id\": \"rule2\",                                              \n"
505      "  \"conditions\": [                                               \n"
506      "    {                                                             \n"
507      "      \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
508      "      \"url\": {\"pathPrefix\": \"/test\"}                        \n"
509      "    }                                                             \n"
510      "  ],                                                              \n"
511      "  \"actions\": [                                                  \n"
512      "    {                                                             \n"
513      "      \"instanceType\": \"declarativeWebRequest.IgnoreRules\",    \n"
514      "      \"hasTag\": \"ignore_tag\"                                  \n"
515      "    }                                                             \n"
516      "  ],                                                              \n"
517      "  \"priority\": 300                                               \n"
518      "}                                                                 ";
519
520  scoped_ptr<Value> value1(base::JSONReader::Read(kRule1));
521  ASSERT_TRUE(value1.get());
522  scoped_ptr<Value> value2(base::JSONReader::Read(kRule2));
523  ASSERT_TRUE(value2.get());
524
525  std::vector<linked_ptr<RulesRegistry::Rule> > rules;
526  rules.push_back(make_linked_ptr(new RulesRegistry::Rule));
527  rules.push_back(make_linked_ptr(new RulesRegistry::Rule));
528  ASSERT_TRUE(RulesRegistry::Rule::Populate(*value1, rules[0].get()));
529  ASSERT_TRUE(RulesRegistry::Rule::Populate(*value2, rules[1].get()));
530
531  scoped_refptr<WebRequestRulesRegistry> registry(
532      new TestWebRequestRulesRegistry(extension_info_map_));
533  std::string error = registry->AddRulesImpl(kExtensionId, rules);
534  EXPECT_EQ("", error);
535  EXPECT_FALSE(registry->IsEmpty());
536
537  GURL url("http://www.foo.com/test");
538  net::TestURLRequestContext context;
539  net::TestURLRequest request(url, NULL, &context, NULL);
540  WebRequestData request_data(&request, ON_BEFORE_REQUEST);
541  std::list<LinkedPtrEventResponseDelta> deltas =
542      registry->CreateDeltas(NULL, request_data, false);
543
544  // The redirect by the redirect rule is ignored due to the ignore rule.
545  std::set<const WebRequestRule*> matches = registry->GetMatches(request_data);
546  EXPECT_EQ(2u, matches.size());
547  ASSERT_EQ(0u, deltas.size());
548}
549
550// Test that rules failing IsFulfilled on their conditions are never returned by
551// GetMatches.
552TEST_F(WebRequestRulesRegistryTest, GetMatchesCheckFulfilled) {
553  scoped_refptr<TestWebRequestRulesRegistry> registry(
554      new TestWebRequestRulesRegistry(extension_info_map_));
555  const std::string kMatchingUrlAttribute(
556      "\"url\": { \"pathContains\": \"\" }, \n");
557  const std::string kNonMatchingNonUrlAttribute(
558      "\"resourceType\": [\"stylesheet\"], \n");
559  const std::string kBothAttributes(kMatchingUrlAttribute +
560                                    kNonMatchingNonUrlAttribute);
561  std::string error;
562  std::vector<const std::string*> attributes;
563  std::vector<linked_ptr<RulesRegistry::Rule> > rules;
564
565  // Rules 1 and 2 have one condition, neither of them should fire.
566  attributes.push_back(&kNonMatchingNonUrlAttribute);
567  rules.push_back(CreateCancellingRule(kRuleId1, attributes));
568
569  attributes.clear();
570  attributes.push_back(&kBothAttributes);
571  rules.push_back(CreateCancellingRule(kRuleId2, attributes));
572
573  // Rule 3 has two conditions, one with a matching URL attribute, and one
574  // with a non-matching non-URL attribute.
575  attributes.clear();
576  attributes.push_back(&kMatchingUrlAttribute);
577  attributes.push_back(&kNonMatchingNonUrlAttribute);
578  rules.push_back(CreateCancellingRule(kRuleId3, attributes));
579
580  error = registry->AddRules(kExtensionId, rules);
581  EXPECT_EQ("", error);
582  EXPECT_EQ(1, registry->num_clear_cache_calls());
583
584  std::set<const WebRequestRule*> matches;
585
586  GURL http_url("http://www.example.com");
587  net::TestURLRequestContext context;
588  net::TestURLRequest http_request(http_url, NULL, &context, NULL);
589  WebRequestData request_data(&http_request, ON_BEFORE_REQUEST);
590  matches = registry->GetMatches(request_data);
591  EXPECT_EQ(1u, matches.size());
592  WebRequestRule::GlobalRuleId expected_pair = std::make_pair(kExtensionId,
593                                                              kRuleId3);
594  EXPECT_EQ(expected_pair, (*matches.begin())->id());
595}
596
597// Test that the url and firstPartyForCookiesUrl attributes are evaluated
598// against corresponding URLs. Tested on requests where these URLs actually
599// differ.
600TEST_F(WebRequestRulesRegistryTest, GetMatchesDifferentUrls) {
601  scoped_refptr<TestWebRequestRulesRegistry> registry(
602      new TestWebRequestRulesRegistry(extension_info_map_));
603  const std::string kUrlAttribute(
604      "\"url\": { \"hostContains\": \"url\" }, \n");
605  const std::string kFirstPartyUrlAttribute(
606      "\"firstPartyForCookiesUrl\": { \"hostContains\": \"fpfc\" }, \n");
607  std::string error;
608  std::vector<const std::string*> attributes;
609  std::vector<linked_ptr<RulesRegistry::Rule> > rules;
610
611  // Rule 1 has one condition, with a url attribute
612  attributes.push_back(&kUrlAttribute);
613  rules.push_back(CreateCancellingRule(kRuleId1, attributes));
614
615  // Rule 2 has one condition, with a firstPartyForCookiesUrl attribute
616  attributes.clear();
617  attributes.push_back(&kFirstPartyUrlAttribute);
618  rules.push_back(CreateCancellingRule(kRuleId2, attributes));
619
620  error = registry->AddRules(kExtensionId, rules);
621  EXPECT_EQ("", error);
622  EXPECT_EQ(1, registry->num_clear_cache_calls());
623
624  std::set<const WebRequestRule*> matches;
625
626  const GURL urls[] = {
627    GURL("http://url.example.com"),  // matching
628    GURL("http://www.example.com")   // non-matching
629  };
630  const GURL firstPartyUrls[] = {
631    GURL("http://www.example.com"),  // non-matching
632    GURL("http://fpfc.example.com")  // matching
633  };
634  // Which rules should match in subsequent test iterations.
635  const char* matchingRuleIds[] = { kRuleId1, kRuleId2 };
636  COMPILE_ASSERT(arraysize(urls) == arraysize(firstPartyUrls),
637                 urls_and_firstPartyUrls_need_to_have_the_same_size);
638  COMPILE_ASSERT(arraysize(urls) == arraysize(matchingRuleIds),
639                 urls_and_matchingRuleIds_need_to_have_the_same_size);
640  net::TestURLRequestContext context;
641
642  for (size_t i = 0; i < arraysize(matchingRuleIds); ++i) {
643    // Construct the inputs.
644    net::TestURLRequest http_request(urls[i], NULL, &context, NULL);
645    WebRequestData request_data(&http_request, ON_BEFORE_REQUEST);
646    http_request.set_first_party_for_cookies(firstPartyUrls[i]);
647    // Now run both rules on the input.
648    matches = registry->GetMatches(request_data);
649    SCOPED_TRACE(testing::Message("i = ") << i << ", rule id = "
650                                          << matchingRuleIds[i]);
651    // Make sure that the right rule succeeded.
652    EXPECT_EQ(1u, matches.size());
653    EXPECT_EQ(WebRequestRule::GlobalRuleId(std::make_pair(kExtensionId,
654                                                          matchingRuleIds[i])),
655              (*matches.begin())->id());
656  }
657}
658
659TEST(WebRequestRulesRegistrySimpleTest, StageChecker) {
660  // The contentType condition can only be evaluated during ON_HEADERS_RECEIVED
661  // but the redirect action can only be executed during ON_BEFORE_REQUEST.
662  // Therefore, this is an inconsistent rule that needs to be flagged.
663  const char kRule[] =
664      "{                                                                 \n"
665      "  \"id\": \"rule1\",                                              \n"
666      "  \"conditions\": [                                               \n"
667      "    {                                                             \n"
668      "      \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
669      "      \"url\": {\"hostSuffix\": \"foo.com\"},                     \n"
670      "      \"contentType\": [\"image/jpeg\"]                           \n"
671      "    }                                                             \n"
672      "  ],                                                              \n"
673      "  \"actions\": [                                                  \n"
674      "    {                                                             \n"
675      "      \"instanceType\": \"declarativeWebRequest.RedirectRequest\",\n"
676      "      \"redirectUrl\": \"http://bar.com\"                         \n"
677      "    }                                                             \n"
678      "  ],                                                              \n"
679      "  \"priority\": 200                                               \n"
680      "}                                                                 ";
681
682  scoped_ptr<Value> value(base::JSONReader::Read(kRule));
683  ASSERT_TRUE(value);
684
685  RulesRegistry::Rule rule;
686  ASSERT_TRUE(RulesRegistry::Rule::Populate(*value, &rule));
687
688  std::string error;
689  URLMatcher matcher;
690  scoped_ptr<WebRequestConditionSet> conditions =
691      WebRequestConditionSet::Create(
692          matcher.condition_factory(), rule.conditions, &error);
693  ASSERT_TRUE(error.empty()) << error;
694  ASSERT_TRUE(conditions);
695
696  bool bad_message = false;
697  scoped_ptr<WebRequestActionSet> actions =
698      WebRequestActionSet::Create(rule.actions, &error, &bad_message);
699  ASSERT_TRUE(error.empty()) << error;
700  ASSERT_FALSE(bad_message);
701  ASSERT_TRUE(actions);
702
703  EXPECT_FALSE(WebRequestRulesRegistry::StageChecker(
704      conditions.get(), actions.get(), &error));
705  EXPECT_THAT(error, HasSubstr("no time in the request life-cycle"));
706  EXPECT_THAT(error, HasSubstr(actions->actions().back()->GetName()));
707}
708
709TEST(WebRequestRulesRegistrySimpleTest, HostPermissionsChecker) {
710  const char kAction[] =  // This action requires all URLs host permission.
711      "{                                                             \n"
712      "  \"instanceType\": \"declarativeWebRequest.RedirectRequest\",\n"
713      "  \"redirectUrl\": \"http://bar.com\"                         \n"
714      "}                                                             ";
715  scoped_ptr<Value> action_value(base::JSONReader::Read(kAction));
716  ASSERT_TRUE(action_value);
717
718  WebRequestActionSet::AnyVector actions;
719  actions.push_back(linked_ptr<base::Value>(action_value.release()));
720  ASSERT_TRUE(actions.back().get());
721
722  std::string error;
723  bool bad_message = false;
724  scoped_ptr<WebRequestActionSet> action_set(
725      WebRequestActionSet::Create(actions, &error, &bad_message));
726  ASSERT_TRUE(error.empty()) << error;
727  ASSERT_FALSE(bad_message);
728  ASSERT_TRUE(action_set);
729
730  scoped_refptr<Extension> extension_no_url(
731      LoadManifest("permissions", "web_request_no_host.json"));
732  scoped_refptr<Extension> extension_some_urls(
733      LoadManifest("permissions", "web_request_com_host_permissions.json"));
734  scoped_refptr<Extension> extension_all_urls(
735      LoadManifest("permissions", "web_request_all_host_permissions.json"));
736
737  EXPECT_TRUE(WebRequestRulesRegistry::HostPermissionsChecker(
738      extension_all_urls.get(), action_set.get(), &error));
739  EXPECT_TRUE(error.empty()) << error;
740
741  EXPECT_FALSE(WebRequestRulesRegistry::HostPermissionsChecker(
742      extension_some_urls.get(), action_set.get(), &error));
743  EXPECT_THAT(error, HasSubstr("permission for all"));
744  EXPECT_THAT(error, HasSubstr(action_set->actions().back()->GetName()));
745
746  EXPECT_FALSE(WebRequestRulesRegistry::HostPermissionsChecker(
747      extension_no_url.get(), action_set.get(), &error));
748  EXPECT_THAT(error, HasSubstr("permission for all"));
749  EXPECT_THAT(error, HasSubstr(action_set->actions().back()->GetName()));
750}
751
752TEST_F(WebRequestRulesRegistryTest, CheckOriginAndPathRegEx) {
753  const char kRule[] =
754      "{                                                                 \n"
755      "  \"id\": \"rule1\",                                              \n"
756      "  \"conditions\": [                                               \n"
757      "    {                                                             \n"
758      "      \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
759      "      \"url\": {\"originAndPathMatches\": \"fo+.com\"}            \n"
760      "    }                                                             \n"
761      "  ],                                                              \n"
762      "  \"actions\": [                                                  \n"
763      "    {                                                             \n"
764      "      \"instanceType\": \"declarativeWebRequest.RedirectRequest\",\n"
765      "      \"redirectUrl\": \"http://bar.com\"                         \n"
766      "    }                                                             \n"
767      "  ],                                                              \n"
768      "  \"priority\": 200                                               \n"
769      "}                                                                 ";
770
771  scoped_ptr<Value> value(base::JSONReader::Read(kRule));
772  ASSERT_TRUE(value.get());
773
774  std::vector<linked_ptr<RulesRegistry::Rule> > rules;
775  rules.push_back(make_linked_ptr(new RulesRegistry::Rule));
776  ASSERT_TRUE(RulesRegistry::Rule::Populate(*value, rules.back().get()));
777
778  scoped_refptr<WebRequestRulesRegistry> registry(
779      new TestWebRequestRulesRegistry(extension_info_map_));
780
781  URLMatcher matcher;
782  std::string error = registry->AddRulesImpl(kExtensionId, rules);
783  EXPECT_EQ("", error);
784
785  net::TestURLRequestContext context;
786  std::list<LinkedPtrEventResponseDelta> deltas;
787
788  // No match because match is in the query parameter.
789  GURL url1("http://bar.com/index.html?foo.com");
790  net::TestURLRequest request1(url1, NULL, &context, NULL);
791  WebRequestData request_data1(&request1, ON_BEFORE_REQUEST);
792  deltas = registry->CreateDeltas(NULL, request_data1, false);
793  EXPECT_EQ(0u, deltas.size());
794
795  // This is a correct match.
796  GURL url2("http://foo.com/index.html");
797  net::TestURLRequest request2(url2, NULL, &context, NULL);
798  WebRequestData request_data2(&request2, ON_BEFORE_REQUEST);
799  deltas = registry->CreateDeltas(NULL, request_data2, false);
800  EXPECT_EQ(1u, deltas.size());
801}
802
803}  // namespace extensions
804