|  | // Copyright 2013 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" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/run_loop.h" | 
|  | #include "base/values.h" | 
|  | #include "content/public/test/test_browser_thread_bundle.h" | 
|  | #include "extensions/browser/api/declarative/rules_registry_service.h" | 
|  | #include "extensions/browser/api/declarative/test_rules_registry.h" | 
|  | #include "extensions/browser/api_test_utils.h" | 
|  | #include "extensions/common/extension.h" | 
|  | #include "extensions/common/extension_builder.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace { | 
|  | const char kExtensionId[] = "foobar"; | 
|  | const char kRuleId[] = "foo"; | 
|  | const int key = extensions::RulesRegistryService::kDefaultRulesRegistryID; | 
|  | }  // namespace | 
|  |  | 
|  | namespace extensions { | 
|  |  | 
|  | using api_test_utils::ParseDictionary; | 
|  |  | 
|  | TEST(RulesRegistryTest, FillOptionalIdentifiers) { | 
|  | content::TestBrowserThreadBundle test_browser_thread_bundle; | 
|  |  | 
|  | std::string error; | 
|  | scoped_refptr<RulesRegistry> registry = | 
|  | new TestRulesRegistry(content::BrowserThread::UI, "" /*event_name*/, key); | 
|  |  | 
|  | // Add rules and check that their identifiers are filled and unique. | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> add_rules; | 
|  | add_rules.push_back(make_linked_ptr(new api::events::Rule)); | 
|  | add_rules.push_back(make_linked_ptr(new api::events::Rule)); | 
|  | error = registry->AddRules(kExtensionId, add_rules); | 
|  | EXPECT_TRUE(error.empty()) << error; | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> get_rules; | 
|  | registry->GetAllRules(kExtensionId, &get_rules); | 
|  |  | 
|  | ASSERT_EQ(2u, get_rules.size()); | 
|  |  | 
|  | ASSERT_TRUE(get_rules[0]->id.get()); | 
|  | EXPECT_NE("", *get_rules[0]->id); | 
|  |  | 
|  | ASSERT_TRUE(get_rules[1]->id.get()); | 
|  | EXPECT_NE("", *get_rules[1]->id); | 
|  |  | 
|  | EXPECT_NE(*get_rules[0]->id, *get_rules[1]->id); | 
|  |  | 
|  | EXPECT_EQ(1u /*extensions*/ + 2u /*rules*/, | 
|  | registry->GetNumberOfUsedRuleIdentifiersForTesting()); | 
|  |  | 
|  | // Check that we cannot add a new rule with the same ID. | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> add_rules_2; | 
|  | add_rules_2.push_back(make_linked_ptr(new api::events::Rule)); | 
|  | add_rules_2[0]->id.reset(new std::string(*get_rules[0]->id)); | 
|  | error = registry->AddRules(kExtensionId, add_rules_2); | 
|  | EXPECT_FALSE(error.empty()); | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> get_rules_2; | 
|  | registry->GetAllRules(kExtensionId, &get_rules_2); | 
|  | ASSERT_EQ(2u, get_rules_2.size()); | 
|  | EXPECT_EQ(1u /*extensions*/ + 2u /*rules*/, | 
|  | registry->GetNumberOfUsedRuleIdentifiersForTesting()); | 
|  |  | 
|  | // Check that we can register the old rule IDs once they were unregistered. | 
|  |  | 
|  | std::vector<std::string> remove_rules_3; | 
|  | remove_rules_3.push_back(*get_rules[0]->id); | 
|  | error = registry->RemoveRules(kExtensionId, remove_rules_3); | 
|  | EXPECT_TRUE(error.empty()) << error; | 
|  |  | 
|  | EXPECT_EQ(1u /*extensions*/ + 1u /*rules*/, | 
|  | registry->GetNumberOfUsedRuleIdentifiersForTesting()); | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> get_rules_3a; | 
|  | registry->GetAllRules(kExtensionId, &get_rules_3a); | 
|  | ASSERT_EQ(1u, get_rules_3a.size()); | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> add_rules_3; | 
|  | add_rules_3.push_back(make_linked_ptr(new api::events::Rule)); | 
|  | add_rules_3[0]->id.reset(new std::string(*get_rules[0]->id)); | 
|  | error = registry->AddRules(kExtensionId, add_rules_3); | 
|  | EXPECT_TRUE(error.empty()) << error; | 
|  | EXPECT_EQ(1u /*extensions*/ + 2u /*rules*/, | 
|  | registry->GetNumberOfUsedRuleIdentifiersForTesting()); | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> get_rules_3b; | 
|  | registry->GetAllRules(kExtensionId, &get_rules_3b); | 
|  | ASSERT_EQ(2u, get_rules_3b.size()); | 
|  |  | 
|  | // Check that we can register a rule with an ID that is not modified. | 
|  |  | 
|  | error = registry->RemoveAllRules(kExtensionId); | 
|  | EXPECT_TRUE(error.empty()) << error; | 
|  | EXPECT_EQ(0u /*extensions*/ + 0u /*rules*/, | 
|  | registry->GetNumberOfUsedRuleIdentifiersForTesting()); | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> get_rules_4a; | 
|  | registry->GetAllRules(kExtensionId, &get_rules_4a); | 
|  | ASSERT_TRUE(get_rules_4a.empty()); | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> add_rules_4; | 
|  | add_rules_4.push_back(make_linked_ptr(new api::events::Rule)); | 
|  | add_rules_4[0]->id.reset(new std::string(kRuleId)); | 
|  | error = registry->AddRules(kExtensionId, add_rules_4); | 
|  | EXPECT_TRUE(error.empty()) << error; | 
|  |  | 
|  | EXPECT_EQ(1u /*extensions*/ + 1u /*rules*/, | 
|  | registry->GetNumberOfUsedRuleIdentifiersForTesting()); | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> get_rules_4b; | 
|  | registry->GetAllRules(kExtensionId, &get_rules_4b); | 
|  |  | 
|  | ASSERT_EQ(1u, get_rules_4b.size()); | 
|  |  | 
|  | ASSERT_TRUE(get_rules_4b[0]->id.get()); | 
|  | EXPECT_EQ(kRuleId, *get_rules_4b[0]->id); | 
|  |  | 
|  | // Create extension | 
|  | std::unique_ptr<base::DictionaryValue> manifest = ParseDictionary( | 
|  | "{" | 
|  | "  \"name\": \"Test\"," | 
|  | "  \"version\": \"1\"" | 
|  | "}"); | 
|  | scoped_refptr<Extension> extension = ExtensionBuilder() | 
|  | .SetManifest(std::move(manifest)) | 
|  | .SetID(kExtensionId) | 
|  | .Build(); | 
|  | registry->OnExtensionUninstalled(extension.get()); | 
|  | EXPECT_EQ(0u /*extensions*/ + 0u /*rules*/, | 
|  | registry->GetNumberOfUsedRuleIdentifiersForTesting()); | 
|  |  | 
|  | // Make sure that deletion traits of registry are executed. | 
|  | registry = NULL; | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | TEST(RulesRegistryTest, FillOptionalPriority) { | 
|  | content::TestBrowserThreadBundle test_browser_thread_bundle; | 
|  |  | 
|  | std::string error; | 
|  | scoped_refptr<RulesRegistry> registry = | 
|  | new TestRulesRegistry(content::BrowserThread::UI, "" /*event_name*/, key); | 
|  |  | 
|  | // Add rules and check that their priorities are filled if they are empty. | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> add_rules; | 
|  | add_rules.push_back(make_linked_ptr(new api::events::Rule)); | 
|  | add_rules[0]->priority.reset(new int(2)); | 
|  | add_rules.push_back(make_linked_ptr(new api::events::Rule)); | 
|  | error = registry->AddRules(kExtensionId, add_rules); | 
|  | EXPECT_TRUE(error.empty()) << error; | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> get_rules; | 
|  | registry->GetAllRules(kExtensionId, &get_rules); | 
|  |  | 
|  | ASSERT_EQ(2u, get_rules.size()); | 
|  |  | 
|  | ASSERT_TRUE(get_rules[0]->priority.get()); | 
|  | ASSERT_TRUE(get_rules[1]->priority.get()); | 
|  |  | 
|  | // Verify the precondition so that the following EXPECT_EQ statements work. | 
|  | EXPECT_GT(RulesRegistry::DEFAULT_PRIORITY, 2); | 
|  | EXPECT_EQ(2, std::min(*get_rules[0]->priority, *get_rules[1]->priority)); | 
|  | EXPECT_EQ(RulesRegistry::DEFAULT_PRIORITY, | 
|  | std::max(*get_rules[0]->priority, *get_rules[1]->priority)); | 
|  |  | 
|  | // Make sure that deletion traits of registry are executed. | 
|  | registry = NULL; | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | // Test verifies 2 rules defined in the manifest appear in the registry. | 
|  | TEST(RulesRegistryTest, TwoRulesInManifest) { | 
|  | content::TestBrowserThreadBundle test_browser_thread_bundle; | 
|  |  | 
|  | // Create extension | 
|  | std::unique_ptr<base::DictionaryValue> manifest = ParseDictionary( | 
|  | "{" | 
|  | "  \"name\": \"Test\"," | 
|  | "  \"version\": \"1\"," | 
|  | "  \"event_rules\": [" | 
|  | "    {" | 
|  | "      \"id\": \"000\"," | 
|  | "      \"priority\": 200," | 
|  | "      \"tags\": [\"tagged\"]," | 
|  | "      \"event\": \"declarativeContent.onPageChanged\"," | 
|  | "      \"actions\": [{" | 
|  | "        \"type\": \"declarativeContent.ShowPageAction\"" | 
|  | "      }]," | 
|  | "      \"conditions\" : [{" | 
|  | "        \"css\": [\"video\"]," | 
|  | "        \"type\" : \"declarativeContent.PageStateMatcher\"" | 
|  | "      }]" | 
|  | "    }," | 
|  | "    {" | 
|  | "      \"event\": \"declarativeContent.onPageChanged\"," | 
|  | "      \"actions\": [{" | 
|  | "        \"type\": \"declarativeContent.ShowPageAction\"" | 
|  | "      }]," | 
|  | "      \"conditions\" : [{" | 
|  | "        \"css\": [\"input[type='password']\"]," | 
|  | "        \"type\" : \"declarativeContent.PageStateMatcher\"" | 
|  | "      }]" | 
|  | "    }" | 
|  | "  ]" | 
|  | "}"); | 
|  | scoped_refptr<Extension> extension = ExtensionBuilder() | 
|  | .SetManifest(std::move(manifest)) | 
|  | .SetID(kExtensionId) | 
|  | .Build(); | 
|  |  | 
|  | scoped_refptr<RulesRegistry> registry = new TestRulesRegistry( | 
|  | content::BrowserThread::UI, "declarativeContent.onPageChanged", key); | 
|  | // Simulate what RulesRegistryService would do on extension load. | 
|  | registry->OnExtensionLoaded(extension.get()); | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> get_rules; | 
|  | registry->GetAllRules(kExtensionId, &get_rules); | 
|  |  | 
|  | ASSERT_EQ(2u, get_rules.size()); | 
|  | std::unique_ptr<base::DictionaryValue> expected_rule_0 = ParseDictionary( | 
|  | "{" | 
|  | "  \"id\": \"000\"," | 
|  | "  \"priority\": 200," | 
|  | "  \"tags\": [\"tagged\"]," | 
|  | "  \"actions\": [{" | 
|  | "    \"instanceType\": \"declarativeContent.ShowPageAction\"" | 
|  | "  }]," | 
|  | "  \"conditions\" : [{" | 
|  | "    \"css\": [\"video\"]," | 
|  | "    \"instanceType\" : \"declarativeContent.PageStateMatcher\"" | 
|  | "  }]" | 
|  | "}"); | 
|  | EXPECT_TRUE(expected_rule_0->Equals(get_rules[0]->ToValue().get())); | 
|  |  | 
|  | std::unique_ptr<base::DictionaryValue> expected_rule_1 = ParseDictionary( | 
|  | "{" | 
|  | "  \"id\": \"_0_\"," | 
|  | "  \"priority\": 100," | 
|  | "  \"actions\": [{" | 
|  | "    \"instanceType\": \"declarativeContent.ShowPageAction\"" | 
|  | "  }]," | 
|  | "  \"conditions\" : [{" | 
|  | "    \"css\": [\"input[type='password']\"]," | 
|  | "    \"instanceType\" : \"declarativeContent.PageStateMatcher\"" | 
|  | "  }]" | 
|  | "}"); | 
|  | EXPECT_TRUE(expected_rule_1->Equals(get_rules[1]->ToValue().get())); | 
|  | } | 
|  |  | 
|  | // Tests verifies that rules defined in the manifest cannot be deleted but | 
|  | // programmatically added rules still can be deleted. | 
|  | TEST(RulesRegistryTest, DeleteRuleInManifest) { | 
|  | content::TestBrowserThreadBundle test_browser_thread_bundle; | 
|  |  | 
|  | // Create extension | 
|  | std::unique_ptr<base::DictionaryValue> manifest = ParseDictionary( | 
|  | "{" | 
|  | "  \"name\": \"Test\"," | 
|  | "  \"version\": \"1\"," | 
|  | "  \"event_rules\": [{" | 
|  | "    \"id\":  \"manifest_rule_0\"," | 
|  | "    \"event\": \"declarativeContent.onPageChanged\"," | 
|  | "    \"actions\": [{" | 
|  | "      \"type\": \"declarativeContent.ShowPageAction\"" | 
|  | "    }]," | 
|  | "    \"conditions\" : [{" | 
|  | "      \"css\": [\"video\"]," | 
|  | "      \"type\" : \"declarativeContent.PageStateMatcher\"" | 
|  | "    }]" | 
|  | "  }]" | 
|  | "}"); | 
|  | scoped_refptr<Extension> extension = ExtensionBuilder() | 
|  | .SetManifest(std::move(manifest)) | 
|  | .SetID(kExtensionId) | 
|  | .Build(); | 
|  |  | 
|  | scoped_refptr<RulesRegistry> registry = new TestRulesRegistry( | 
|  | content::BrowserThread::UI, "declarativeContent.onPageChanged", key); | 
|  | // Simulate what RulesRegistryService would do on extension load. | 
|  | registry->OnExtensionLoaded(extension.get()); | 
|  | // Add some extra rules outside of the manifest. | 
|  | std::vector<linked_ptr<api::events::Rule>> add_rules; | 
|  | linked_ptr<api::events::Rule> rule_1 = make_linked_ptr(new api::events::Rule); | 
|  | rule_1->id.reset(new std::string("rule_1")); | 
|  | linked_ptr<api::events::Rule> rule_2 = make_linked_ptr(new api::events::Rule); | 
|  | rule_2->id.reset(new std::string("rule_2")); | 
|  | add_rules.push_back(rule_1); | 
|  | add_rules.push_back(rule_2); | 
|  | registry->AddRules(kExtensionId, add_rules); | 
|  |  | 
|  | std::vector<linked_ptr<api::events::Rule>> get_rules; | 
|  | registry->GetAllRules(kExtensionId, &get_rules); | 
|  | ASSERT_EQ(3u, get_rules.size()); | 
|  | EXPECT_EQ("manifest_rule_0", *(get_rules[0]->id)); | 
|  | EXPECT_EQ("rule_1", *(get_rules[1]->id)); | 
|  | EXPECT_EQ("rule_2", *(get_rules[2]->id)); | 
|  |  | 
|  | // Remove a rule from outside the manifest. | 
|  | std::vector<std::string> remove_ids; | 
|  | remove_ids.push_back("rule_1"); | 
|  | EXPECT_TRUE(registry->RemoveRules(kExtensionId, remove_ids).empty()); | 
|  | get_rules.clear(); | 
|  | registry->GetAllRules(kExtensionId, &get_rules); | 
|  | EXPECT_EQ(2u, get_rules.size()); | 
|  | EXPECT_EQ("manifest_rule_0", *(get_rules[0]->id)); | 
|  | EXPECT_EQ("rule_2", *(get_rules[1]->id)); | 
|  |  | 
|  | // Attempt to remove rule in manifest. | 
|  | remove_ids.clear(); | 
|  | remove_ids.push_back("manifest_rule_0"); | 
|  | EXPECT_FALSE(registry->RemoveRules(kExtensionId, remove_ids).empty()); | 
|  | get_rules.clear(); | 
|  | registry->GetAllRules(kExtensionId, &get_rules); | 
|  | ASSERT_EQ(2u, get_rules.size()); | 
|  | EXPECT_EQ("manifest_rule_0", *(get_rules[0]->id)); | 
|  | EXPECT_EQ("rule_2", *(get_rules[1]->id)); | 
|  |  | 
|  | // Remove all rules. | 
|  | registry->RemoveAllRules(kExtensionId); | 
|  | get_rules.clear(); | 
|  | registry->GetAllRules(kExtensionId, &get_rules); | 
|  | ASSERT_EQ(1u, get_rules.size()); | 
|  | EXPECT_EQ("manifest_rule_0", *(get_rules[0]->id)); | 
|  | } | 
|  |  | 
|  | }  // namespace extensions |