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 "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 
     28 using base::Value;
     29 using extension_test_util::LoadManifest;
     30 using extension_test_util::LoadManifestUnchecked;
     31 using testing::HasSubstr;
     32 using url_matcher::URLMatcher;
     33 
     34 namespace {
     35 const char kExtensionId[] = "ext1";
     36 const char kExtensionId2[] = "ext2";
     37 const char kRuleId1[] = "rule1";
     38 const char kRuleId2[] = "rule2";
     39 const char kRuleId3[] = "rule3";
     40 const char kRuleId4[] = "rule4";
     41 }  // namespace
     42 
     43 namespace extensions {
     44 
     45 namespace helpers = extension_web_request_api_helpers;
     46 namespace keys = declarative_webrequest_constants;
     47 namespace keys2 = url_matcher::url_matcher_constants;
     48 
     49 class 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 
     81 class 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 
    236 void 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 
    267 TEST_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 
    308 TEST_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 
    356 TEST_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.
    408 TEST_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.
    451 TEST_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.
    489 TEST_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.
    559 TEST_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.
    608 TEST_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 
    668 TEST(WebRequestRulesRegistrySimpleTest, StageChecker) {
    669   // The contentType condition can only be evaluated during ON_HEADERS_RECEIVED
    670   // but the SetRequestHeader action can only be executed during
    671   // ON_BEFORE_SEND_HEADERS.
    672   // Therefore, this is an inconsistent rule that needs to be flagged.
    673   const char kRule[] =
    674       "{                                                                  \n"
    675       "  \"id\": \"rule1\",                                               \n"
    676       "  \"conditions\": [                                                \n"
    677       "    {                                                              \n"
    678       "      \"instanceType\": \"declarativeWebRequest.RequestMatcher\",  \n"
    679       "      \"url\": {\"hostSuffix\": \"foo.com\"},                      \n"
    680       "      \"contentType\": [\"image/jpeg\"]                            \n"
    681       "    }                                                              \n"
    682       "  ],                                                               \n"
    683       "  \"actions\": [                                                   \n"
    684       "    {                                                              \n"
    685       "      \"instanceType\": \"declarativeWebRequest.SetRequestHeader\",\n"
    686       "      \"name\": \"Content-Type\",                                  \n"
    687       "      \"value\": \"text/plain\"                                    \n"
    688       "    }                                                              \n"
    689       "  ],                                                               \n"
    690       "  \"priority\": 200                                                \n"
    691       "}                                                                  ";
    692 
    693   scoped_ptr<base::Value> value(base::JSONReader::Read(kRule));
    694   ASSERT_TRUE(value);
    695 
    696   RulesRegistry::Rule rule;
    697   ASSERT_TRUE(RulesRegistry::Rule::Populate(*value, &rule));
    698 
    699   std::string error;
    700   URLMatcher matcher;
    701   scoped_ptr<WebRequestConditionSet> conditions =
    702       WebRequestConditionSet::Create(
    703           NULL, matcher.condition_factory(), rule.conditions, &error);
    704   ASSERT_TRUE(error.empty()) << error;
    705   ASSERT_TRUE(conditions);
    706 
    707   bool bad_message = false;
    708   scoped_ptr<WebRequestActionSet> actions =
    709       WebRequestActionSet::Create(NULL, rule.actions, &error, &bad_message);
    710   ASSERT_TRUE(error.empty()) << error;
    711   ASSERT_FALSE(bad_message);
    712   ASSERT_TRUE(actions);
    713 
    714   EXPECT_FALSE(WebRequestRulesRegistry::StageChecker(
    715       conditions.get(), actions.get(), &error));
    716   EXPECT_THAT(error, HasSubstr("no time in the request life-cycle"));
    717   EXPECT_THAT(error, HasSubstr(actions->actions().back()->GetName()));
    718 }
    719 
    720 TEST(WebRequestRulesRegistrySimpleTest, HostPermissionsChecker) {
    721   const char kAction[] =  // This action requires all URLs host permission.
    722       "{                                                             \n"
    723       "  \"instanceType\": \"declarativeWebRequest.RedirectRequest\",\n"
    724       "  \"redirectUrl\": \"http://bar.com\"                         \n"
    725       "}                                                             ";
    726   scoped_ptr<base::Value> action_value(base::JSONReader::Read(kAction));
    727   ASSERT_TRUE(action_value);
    728 
    729   WebRequestActionSet::AnyVector actions;
    730   actions.push_back(linked_ptr<base::Value>(action_value.release()));
    731   ASSERT_TRUE(actions.back().get());
    732 
    733   std::string error;
    734   bool bad_message = false;
    735   scoped_ptr<WebRequestActionSet> action_set(
    736       WebRequestActionSet::Create(NULL, actions, &error, &bad_message));
    737   ASSERT_TRUE(error.empty()) << error;
    738   ASSERT_FALSE(bad_message);
    739   ASSERT_TRUE(action_set);
    740 
    741   scoped_refptr<Extension> extension_no_url(
    742       LoadManifest("permissions", "web_request_no_host.json"));
    743   scoped_refptr<Extension> extension_some_urls(
    744       LoadManifest("permissions", "web_request_com_host_permissions.json"));
    745   scoped_refptr<Extension> extension_all_urls(
    746       LoadManifest("permissions", "web_request_all_host_permissions.json"));
    747 
    748   EXPECT_TRUE(WebRequestRulesRegistry::HostPermissionsChecker(
    749       extension_all_urls.get(), action_set.get(), &error));
    750   EXPECT_TRUE(error.empty()) << error;
    751 
    752   EXPECT_FALSE(WebRequestRulesRegistry::HostPermissionsChecker(
    753       extension_some_urls.get(), action_set.get(), &error));
    754   EXPECT_THAT(error, HasSubstr("permission for all"));
    755   EXPECT_THAT(error, HasSubstr(action_set->actions().back()->GetName()));
    756 
    757   EXPECT_FALSE(WebRequestRulesRegistry::HostPermissionsChecker(
    758       extension_no_url.get(), action_set.get(), &error));
    759   EXPECT_THAT(error, HasSubstr("permission for all"));
    760   EXPECT_THAT(error, HasSubstr(action_set->actions().back()->GetName()));
    761 }
    762 
    763 TEST_F(WebRequestRulesRegistryTest, CheckOriginAndPathRegEx) {
    764   const char kRule[] =
    765       "{                                                                 \n"
    766       "  \"id\": \"rule1\",                                              \n"
    767       "  \"conditions\": [                                               \n"
    768       "    {                                                             \n"
    769       "      \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n"
    770       "      \"url\": {\"originAndPathMatches\": \"fo+.com\"}            \n"
    771       "    }                                                             \n"
    772       "  ],                                                              \n"
    773       "  \"actions\": [                                                  \n"
    774       "    {                                                             \n"
    775       "      \"instanceType\": \"declarativeWebRequest.RedirectRequest\",\n"
    776       "      \"redirectUrl\": \"http://bar.com\"                         \n"
    777       "    }                                                             \n"
    778       "  ],                                                              \n"
    779       "  \"priority\": 200                                               \n"
    780       "}                                                                 ";
    781 
    782   scoped_ptr<base::Value> value(base::JSONReader::Read(kRule));
    783   ASSERT_TRUE(value.get());
    784 
    785   std::vector<linked_ptr<RulesRegistry::Rule> > rules;
    786   rules.push_back(make_linked_ptr(new RulesRegistry::Rule));
    787   ASSERT_TRUE(RulesRegistry::Rule::Populate(*value, rules.back().get()));
    788 
    789   scoped_refptr<WebRequestRulesRegistry> registry(
    790       new TestWebRequestRulesRegistry(extension_info_map_));
    791 
    792   URLMatcher matcher;
    793   std::string error = registry->AddRulesImpl(kExtensionId, rules);
    794   EXPECT_EQ("", error);
    795 
    796   net::TestURLRequestContext context;
    797   std::list<LinkedPtrEventResponseDelta> deltas;
    798 
    799   // No match because match is in the query parameter.
    800   GURL url1("http://bar.com/index.html?foo.com");
    801   net::TestURLRequest request1(url1, net::DEFAULT_PRIORITY, NULL, &context);
    802   WebRequestData request_data1(&request1, ON_BEFORE_REQUEST);
    803   deltas = registry->CreateDeltas(NULL, request_data1, false);
    804   EXPECT_EQ(0u, deltas.size());
    805 
    806   // This is a correct match.
    807   GURL url2("http://foo.com/index.html");
    808   net::TestURLRequest request2(url2, net::DEFAULT_PRIORITY, NULL, &context);
    809   WebRequestData request_data2(&request2, ON_BEFORE_REQUEST);
    810   deltas = registry->CreateDeltas(NULL, request_data2, false);
    811   EXPECT_EQ(1u, deltas.size());
    812 }
    813 
    814 }  // namespace extensions
    815