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