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