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