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