| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "extensions/browser/api/declarative/rules_registry.h" |
| |
| // Here we test the TestRulesRegistry which is the simplest possible |
| // implementation of RulesRegistryWithCache as a proxy for |
| // RulesRegistryWithCache. |
| |
| #include "base/command_line.h" |
| #include "base/memory/ptr_util.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/test_extension_environment.h" |
| #include "chrome/browser/extensions/test_extension_system.h" |
| #include "chrome/common/extensions/extension_test_util.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/version_info/version_info.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "content/public/test/test_utils.h" |
| #include "extensions/browser/api/declarative/rules_cache_delegate.h" |
| #include "extensions/browser/api/declarative/rules_registry_service.h" |
| #include "extensions/browser/api/declarative/test_rules_registry.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/value_store/testing_value_store.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/features/feature_channel.h" |
| #include "extensions/common/manifest_constants.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using extension_test_util::LoadManifestUnchecked; |
| |
| namespace { |
| const char kRuleId[] = "rule"; |
| const char kRule2Id[] = "rule2"; |
| } |
| |
| namespace extensions { |
| const int kRulesRegistryID = RulesRegistryService::kDefaultRulesRegistryID; |
| |
| class RulesRegistryWithCacheTest : public testing::Test { |
| public: |
| RulesRegistryWithCacheTest() |
| : cache_delegate_(/*log_storage_init_delay=*/false), |
| registry_(new TestRulesRegistry(profile(), |
| /*event_name=*/"", |
| content::BrowserThread::UI, |
| &cache_delegate_, |
| kRulesRegistryID)) {} |
| |
| void SetUp() override { |
| // Note that env_.MakeExtension below also forces the creation of |
| // ExtensionService. |
| base::DictionaryValue manifest_extra; |
| std::string key; |
| CHECK(Extension::ProducePEM("test extension 1", &key)); |
| manifest_extra.SetString(manifest_keys::kPublicKey, key); |
| extension1_ = env_.MakeExtension(manifest_extra); |
| CHECK(extension1_.get()); |
| |
| // Different "key" values for the two extensions ensure a different ID. |
| CHECK(Extension::ProducePEM("test extension 2", &key)); |
| manifest_extra.SetString(manifest_keys::kPublicKey, key); |
| extension2_ = env_.MakeExtension(manifest_extra); |
| CHECK(extension2_.get()); |
| CHECK_NE(extension2_->id(), extension1_->id()); |
| } |
| |
| ~RulesRegistryWithCacheTest() override {} |
| |
| std::string AddRule(const std::string& extension_id, |
| const std::string& rule_id, |
| TestRulesRegistry* registry) { |
| std::vector<linked_ptr<api::events::Rule>> add_rules; |
| add_rules.push_back(make_linked_ptr(new api::events::Rule)); |
| add_rules[0]->id.reset(new std::string(rule_id)); |
| return registry->AddRules(extension_id, add_rules); |
| } |
| |
| std::string AddRule(const std::string& extension_id, |
| const std::string& rule_id) { |
| return AddRule(extension_id, rule_id, registry_.get()); |
| } |
| |
| std::string RemoveRule(const std::string& extension_id, |
| const std::string& rule_id) { |
| std::vector<std::string> remove_rules; |
| remove_rules.push_back(rule_id); |
| return registry_->RemoveRules(extension_id, remove_rules); |
| } |
| |
| int GetNumberOfRules(const std::string& extension_id, |
| TestRulesRegistry* registry) { |
| std::vector<linked_ptr<api::events::Rule>> get_rules; |
| registry->GetAllRules(extension_id, &get_rules); |
| return get_rules.size(); |
| } |
| |
| int GetNumberOfRules(const std::string& extension_id) { |
| return GetNumberOfRules(extension_id, registry_.get()); |
| } |
| |
| TestingProfile* profile() const { return env_.profile(); } |
| |
| protected: |
| TestExtensionEnvironment env_; |
| RulesCacheDelegate cache_delegate_; |
| scoped_refptr<TestRulesRegistry> registry_; |
| scoped_refptr<const Extension> extension1_; |
| scoped_refptr<const Extension> extension2_; |
| }; |
| |
| TEST_F(RulesRegistryWithCacheTest, AddRules) { |
| // Check that nothing happens if the concrete RulesRegistry refuses to insert |
| // the rules. |
| registry_->SetResult("Error"); |
| EXPECT_EQ("Error", AddRule(extension1_->id(), kRuleId)); |
| EXPECT_EQ(0, GetNumberOfRules(extension1_->id())); |
| registry_->SetResult(std::string()); |
| |
| // Check that rules can be inserted. |
| EXPECT_EQ("", AddRule(extension1_->id(), kRule2Id)); |
| EXPECT_EQ(1, GetNumberOfRules(extension1_->id())); |
| |
| // Check that rules cannot be inserted twice with the same kRuleId. |
| EXPECT_NE("", AddRule(extension1_->id(), kRuleId)); |
| EXPECT_EQ(1, GetNumberOfRules(extension1_->id())); |
| |
| // Check that different extensions may use the same kRuleId. |
| EXPECT_EQ("", AddRule(extension2_->id(), kRuleId)); |
| EXPECT_EQ(1, GetNumberOfRules(extension1_->id())); |
| EXPECT_EQ(1, GetNumberOfRules(extension2_->id())); |
| } |
| |
| TEST_F(RulesRegistryWithCacheTest, RemoveRules) { |
| // Prime registry. |
| EXPECT_EQ("", AddRule(extension1_->id(), kRuleId)); |
| EXPECT_EQ("", AddRule(extension2_->id(), kRuleId)); |
| EXPECT_EQ(1, GetNumberOfRules(extension1_->id())); |
| EXPECT_EQ(1, GetNumberOfRules(extension2_->id())); |
| |
| // Check that nothing happens if the concrete RuleRegistry refuses to remove |
| // the rules. |
| registry_->SetResult("Error"); |
| EXPECT_EQ("Error", RemoveRule(extension1_->id(), kRuleId)); |
| EXPECT_EQ(1, GetNumberOfRules(extension1_->id())); |
| registry_->SetResult(std::string()); |
| |
| // Check that nothing happens if a rule does not exist. |
| EXPECT_EQ("", RemoveRule(extension1_->id(), "unknown_rule")); |
| EXPECT_EQ(1, GetNumberOfRules(extension1_->id())); |
| |
| // Check that rules may be removed and only for the correct extension. |
| EXPECT_EQ("", RemoveRule(extension1_->id(), kRuleId)); |
| EXPECT_EQ(0, GetNumberOfRules(extension1_->id())); |
| EXPECT_EQ(1, GetNumberOfRules(extension2_->id())); |
| } |
| |
| TEST_F(RulesRegistryWithCacheTest, RemoveAllRules) { |
| // Prime registry. |
| EXPECT_EQ("", AddRule(extension1_->id(), kRuleId)); |
| EXPECT_EQ("", AddRule(extension1_->id(), kRule2Id)); |
| EXPECT_EQ("", AddRule(extension2_->id(), kRuleId)); |
| EXPECT_EQ(2, GetNumberOfRules(extension1_->id())); |
| EXPECT_EQ(1, GetNumberOfRules(extension2_->id())); |
| |
| // Check that nothing happens if the concrete RuleRegistry refuses to remove |
| // the rules. |
| registry_->SetResult("Error"); |
| EXPECT_EQ("Error", registry_->RemoveAllRules(extension1_->id())); |
| EXPECT_EQ(2, GetNumberOfRules(extension1_->id())); |
| registry_->SetResult(std::string()); |
| |
| // Check that rules may be removed and only for the correct extension. |
| EXPECT_EQ("", registry_->RemoveAllRules(extension1_->id())); |
| EXPECT_EQ(0, GetNumberOfRules(extension1_->id())); |
| EXPECT_EQ(1, GetNumberOfRules(extension2_->id())); |
| } |
| |
| TEST_F(RulesRegistryWithCacheTest, GetRules) { |
| // Prime registry. |
| EXPECT_EQ("", AddRule(extension1_->id(), kRuleId)); |
| EXPECT_EQ("", AddRule(extension1_->id(), kRule2Id)); |
| EXPECT_EQ("", AddRule(extension2_->id(), kRuleId)); |
| |
| // Check that we get the correct rule and unknown rules are ignored. |
| std::vector<std::string> rules_to_get; |
| rules_to_get.push_back(kRuleId); |
| rules_to_get.push_back("unknown_rule"); |
| std::vector<linked_ptr<api::events::Rule>> gotten_rules; |
| registry_->GetRules(extension1_->id(), rules_to_get, &gotten_rules); |
| ASSERT_EQ(1u, gotten_rules.size()); |
| ASSERT_TRUE(gotten_rules[0]->id.get()); |
| EXPECT_EQ(kRuleId, *(gotten_rules[0]->id)); |
| } |
| |
| TEST_F(RulesRegistryWithCacheTest, GetAllRules) { |
| // Prime registry. |
| EXPECT_EQ("", AddRule(extension1_->id(), kRuleId)); |
| EXPECT_EQ("", AddRule(extension1_->id(), kRule2Id)); |
| EXPECT_EQ("", AddRule(extension2_->id(), kRuleId)); |
| |
| // Check that we get the correct rules. |
| std::vector<linked_ptr<api::events::Rule>> gotten_rules; |
| registry_->GetAllRules(extension1_->id(), &gotten_rules); |
| EXPECT_EQ(2u, gotten_rules.size()); |
| ASSERT_TRUE(gotten_rules[0]->id.get()); |
| ASSERT_TRUE(gotten_rules[1]->id.get()); |
| EXPECT_TRUE((kRuleId == *(gotten_rules[0]->id) && |
| kRule2Id == *(gotten_rules[1]->id)) || |
| (kRuleId == *(gotten_rules[1]->id) && |
| kRule2Id == *(gotten_rules[0]->id)) ); |
| } |
| |
| TEST_F(RulesRegistryWithCacheTest, OnExtensionUninstalled) { |
| // Prime registry. |
| EXPECT_EQ("", AddRule(extension1_->id(), kRuleId)); |
| EXPECT_EQ("", AddRule(extension2_->id(), kRuleId)); |
| |
| // Check that the correct rules are removed. |
| registry_->OnExtensionUninstalled(extension1_.get()); |
| EXPECT_EQ(0, GetNumberOfRules(extension1_->id())); |
| EXPECT_EQ(1, GetNumberOfRules(extension2_->id())); |
| } |
| |
| TEST_F(RulesRegistryWithCacheTest, DeclarativeRulesStored) { |
| ExtensionPrefs* extension_prefs = env_.GetExtensionPrefs(); |
| |
| const std::string event_name("testEvent"); |
| const std::string rules_stored_key( |
| RulesCacheDelegate::GetRulesStoredKey( |
| event_name, profile()->IsOffTheRecord())); |
| std::unique_ptr<RulesCacheDelegate> cache_delegate( |
| new RulesCacheDelegate(false)); |
| scoped_refptr<RulesRegistry> registry( |
| new TestRulesRegistry(profile(), event_name, content::BrowserThread::UI, |
| cache_delegate.get(), kRulesRegistryID)); |
| |
| // 1. Test the handling of preferences. |
| // Default value is always true. |
| EXPECT_TRUE(cache_delegate->GetDeclarativeRulesStored(extension1_->id())); |
| |
| extension_prefs->UpdateExtensionPref(extension1_->id(), rules_stored_key, |
| base::MakeUnique<base::Value>(false)); |
| EXPECT_FALSE(cache_delegate->GetDeclarativeRulesStored(extension1_->id())); |
| |
| extension_prefs->UpdateExtensionPref(extension1_->id(), rules_stored_key, |
| base::MakeUnique<base::Value>(true)); |
| EXPECT_TRUE(cache_delegate->GetDeclarativeRulesStored(extension1_->id())); |
| |
| // 2. Test writing behavior. |
| std::unique_ptr<base::ListValue> value(new base::ListValue); |
| value->AppendBoolean(true); |
| cache_delegate->WriteToStorage(extension1_->id(), std::move(value)); |
| EXPECT_TRUE(cache_delegate->GetDeclarativeRulesStored(extension1_->id())); |
| content::RunAllBlockingPoolTasksUntilIdle(); |
| TestingValueStore* store = env_.GetExtensionSystem()->value_store(); |
| ASSERT_TRUE(store); |
| EXPECT_EQ(1, store->write_count()); |
| int write_count = store->write_count(); |
| |
| value.reset(new base::ListValue); |
| cache_delegate->WriteToStorage(extension1_->id(), std::move(value)); |
| EXPECT_FALSE(cache_delegate->GetDeclarativeRulesStored(extension1_->id())); |
| content::RunAllBlockingPoolTasksUntilIdle(); |
| // No rules currently, but previously there were, so we expect a write. |
| EXPECT_EQ(write_count + 1, store->write_count()); |
| write_count = store->write_count(); |
| |
| value.reset(new base::ListValue); |
| cache_delegate->WriteToStorage(extension1_->id(), std::move(value)); |
| EXPECT_FALSE(cache_delegate->GetDeclarativeRulesStored(extension1_->id())); |
| content::RunAllBlockingPoolTasksUntilIdle(); |
| EXPECT_EQ(write_count, store->write_count()); |
| |
| // 3. Test reading behavior. |
| int read_count = store->read_count(); |
| |
| cache_delegate->SetDeclarativeRulesStored(extension1_->id(), false); |
| cache_delegate->ReadFromStorage(extension1_->id()); |
| content::RunAllBlockingPoolTasksUntilIdle(); |
| EXPECT_EQ(read_count, store->read_count()); |
| read_count = store->read_count(); |
| |
| cache_delegate->SetDeclarativeRulesStored(extension1_->id(), true); |
| cache_delegate->ReadFromStorage(extension1_->id()); |
| content::RunAllBlockingPoolTasksUntilIdle(); |
| EXPECT_EQ(read_count + 1, store->read_count()); |
| } |
| |
| // Test that each registry has its own "are some rules stored" flag. |
| TEST_F(RulesRegistryWithCacheTest, RulesStoredFlagMultipleRegistries) { |
| ExtensionPrefs* extension_prefs = env_.GetExtensionPrefs(); |
| |
| const std::string event_name1("testEvent1"); |
| const std::string event_name2("testEvent2"); |
| const std::string rules_stored_key1( |
| RulesCacheDelegate::GetRulesStoredKey( |
| event_name1, profile()->IsOffTheRecord())); |
| const std::string rules_stored_key2( |
| RulesCacheDelegate::GetRulesStoredKey( |
| event_name2, profile()->IsOffTheRecord())); |
| std::unique_ptr<RulesCacheDelegate> cache_delegate1( |
| new RulesCacheDelegate(false)); |
| scoped_refptr<RulesRegistry> registry1( |
| new TestRulesRegistry(profile(), event_name1, content::BrowserThread::UI, |
| cache_delegate1.get(), kRulesRegistryID)); |
| |
| std::unique_ptr<RulesCacheDelegate> cache_delegate2( |
| new RulesCacheDelegate(false)); |
| scoped_refptr<RulesRegistry> registry2( |
| new TestRulesRegistry(profile(), event_name2, content::BrowserThread::UI, |
| cache_delegate2.get(), kRulesRegistryID)); |
| |
| // Checkt the correct default values. |
| EXPECT_TRUE(cache_delegate1->GetDeclarativeRulesStored(extension1_->id())); |
| EXPECT_TRUE(cache_delegate2->GetDeclarativeRulesStored(extension1_->id())); |
| |
| // Update the flag for the first registry. |
| extension_prefs->UpdateExtensionPref(extension1_->id(), rules_stored_key1, |
| base::MakeUnique<base::Value>(false)); |
| EXPECT_FALSE(cache_delegate1->GetDeclarativeRulesStored(extension1_->id())); |
| EXPECT_TRUE(cache_delegate2->GetDeclarativeRulesStored(extension1_->id())); |
| } |
| |
| TEST_F(RulesRegistryWithCacheTest, RulesPreservedAcrossRestart) { |
| // This test makes sure that rules are restored from the rule store |
| // on registry (in particular, browser) restart. |
| |
| // TODO(vabr): Once some API using declarative rules enters the stable |
| // channel, make sure to use that API here, and remove |channel|. |
| ScopedCurrentChannel channel(version_info::Channel::UNKNOWN); |
| |
| ExtensionService* extension_service = env_.GetExtensionService(); |
| |
| // 1. Add an extension, before rules registry gets created. |
| std::string error; |
| scoped_refptr<Extension> extension(LoadManifestUnchecked( |
| "permissions", "web_request_all_host_permissions.json", |
| Manifest::UNPACKED, Extension::NO_FLAGS, extension1_->id(), &error)); |
| ASSERT_TRUE(error.empty()); |
| extension_service->AddExtension(extension.get()); |
| EXPECT_TRUE(extensions::ExtensionRegistry::Get(env_.profile()) |
| ->enabled_extensions() |
| .Contains(extension->id())); |
| EXPECT_TRUE(extension->permissions_data()->HasAPIPermission( |
| APIPermission::kDeclarativeWebRequest)); |
| env_.GetExtensionSystem()->SetReady(); |
| |
| // 2. First run, adding a rule for the extension. |
| std::unique_ptr<RulesCacheDelegate> cache_delegate( |
| new RulesCacheDelegate(false)); |
| scoped_refptr<TestRulesRegistry> registry( |
| new TestRulesRegistry(profile(), "testEvent", content::BrowserThread::UI, |
| cache_delegate.get(), kRulesRegistryID)); |
| |
| AddRule(extension1_->id(), kRuleId, registry.get()); |
| |
| // Posted tasks store the added rule. |
| content::RunAllBlockingPoolTasksUntilIdle(); |
| EXPECT_EQ(1, GetNumberOfRules(extension1_->id(), registry.get())); |
| |
| // 3. Restart the TestRulesRegistry and see the rule still there. |
| cache_delegate.reset(new RulesCacheDelegate(false)); |
| registry = |
| new TestRulesRegistry(profile(), "testEvent", content::BrowserThread::UI, |
| cache_delegate.get(), kRulesRegistryID); |
| |
| // Posted tasks retrieve the stored rule. |
| content::RunAllBlockingPoolTasksUntilIdle(); |
| EXPECT_EQ(1, GetNumberOfRules(extension1_->id(), registry.get())); |
| } |
| |
| TEST_F(RulesRegistryWithCacheTest, ConcurrentStoringOfRules) { |
| // When an extension updates its rules, the new set of rules is stored to disk |
| // with some delay. While it is acceptable for a quick series of updates for a |
| // single extension to only write the last one, we should never forget to |
| // write a rules update for extension A, just because it is immediately |
| // followed by a rules update for extension B. |
| extensions::TestExtensionSystem* system = env_.GetExtensionSystem(); |
| |
| int write_count = 0; |
| EXPECT_EQ("", AddRule(extension1_->id(), kRuleId)); |
| EXPECT_EQ("", AddRule(extension2_->id(), kRule2Id)); |
| env_.GetExtensionSystem()->SetReady(); |
| content::RunAllBlockingPoolTasksUntilIdle(); |
| EXPECT_EQ(write_count + 2, system->value_store()->write_count()); |
| } |
| |
| } // namespace extensions |