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