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 "base/bind.h" 6 #include "base/bind_helpers.h" 7 #include "base/command_line.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/strings/stringprintf.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "base/test/thread_test_helper.h" 12 #include "chrome/browser/extensions/extension_apitest.h" 13 #include "chrome/browser/extensions/test_extension_dir.h" 14 #include "chrome/browser/ui/browser.h" 15 #include "chrome/browser/ui/tabs/tab_strip_model.h" 16 #include "chrome/test/base/ui_test_utils.h" 17 #include "content/public/browser/browser_thread.h" 18 #include "extensions/browser/api/declarative/rules_registry_service.h" 19 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h" 20 #include "extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h" 21 #include "extensions/browser/extension_prefs.h" 22 #include "extensions/common/extension.h" 23 #include "extensions/test/extension_test_message_listener.h" 24 25 using content::BrowserThread; 26 using extensions::Extension; 27 using extensions::ExtensionPrefs; 28 using extensions::RulesRegistry; 29 using extensions::RulesRegistryService; 30 using extensions::TestExtensionDir; 31 using extensions::WebRequestRulesRegistry; 32 33 namespace { 34 35 const char kArbitraryUrl[] = "http://www.example.com"; // Must be http://. 36 37 // The extension in "declarative/redirect_to_data" redirects every navigation to 38 // a page with title |kTestTitle|. 39 #define TEST_TITLE_STRING ":TEST:" 40 const char kTestTitle[] = TEST_TITLE_STRING; 41 42 // All methods and constands below containing "RedirectToData" in their names 43 // are parts of a test extension "Redirect to 'data:'". 44 std::string GetRedirectToDataManifestWithVersion(unsigned version) { 45 return base::StringPrintf( 46 "{\n" 47 " \"name\": \"Redirect to 'data:' (declarative apitest)\",\n" 48 " \"version\": \"%d\",\n" 49 " \"manifest_version\": 2,\n" 50 " \"description\": \"Redirects all requests to a fixed data: URI.\",\n" 51 " \"background\": {\n" 52 " \"scripts\": [\"background.js\"]\n" 53 " },\n" 54 " \"permissions\": [\n" 55 " \"declarativeWebRequest\", \"<all_urls>\"\n" 56 " ]\n" 57 "}\n", 58 version); 59 } 60 61 const char kRedirectToDataConstants[] = 62 "var redirectDataURI =\n" 63 " 'data:text/html;charset=utf-8,<html><head><title>' +\n" 64 " '" TEST_TITLE_STRING "' +\n" 65 " '<%2Ftitle><%2Fhtml>';\n"; 66 #undef TEST_TITLE_STRING 67 68 const char kRedirectToDataRules[] = 69 "var rules = [{\n" 70 " conditions: [\n" 71 " new chrome.declarativeWebRequest.RequestMatcher({\n" 72 " url: {schemes: ['http']}})\n" 73 " ],\n" 74 " actions: [\n" 75 " new chrome.declarativeWebRequest.RedirectRequest({\n" 76 " redirectUrl: redirectDataURI\n" 77 " })\n" 78 " ]\n" 79 "}];\n"; 80 81 const char kRedirectToDataInstallRules[] = 82 "function report(details) {\n" 83 " if (chrome.extension.lastError) {\n" 84 " chrome.test.log(chrome.extension.lastError.message);\n" 85 " } else {\n" 86 " chrome.test.sendMessage(\"ready\", function(reply) {})\n" 87 " }\n" 88 "}\n" 89 "\n" 90 "chrome.runtime.onInstalled.addListener(function(details) {\n" 91 " if (details.reason == 'install')\n" 92 " chrome.declarativeWebRequest.onRequest.addRules(rules, report);\n" 93 "});\n"; 94 95 const char kRedirectToDataNoRules[] = 96 "chrome.runtime.onInstalled.addListener(function(details) {\n" 97 " chrome.test.sendMessage(\"ready\", function(reply) {})\n" 98 "});\n"; 99 100 } // namespace 101 102 class DeclarativeApiTest : public ExtensionApiTest { 103 public: 104 std::string GetTitle() { 105 base::string16 title( 106 browser()->tab_strip_model()->GetActiveWebContents()->GetTitle()); 107 return base::UTF16ToUTF8(title); 108 } 109 110 // Reports the number of rules registered for the |extension_id| with the 111 // non-webview rules registry. 112 size_t NumberOfRegisteredRules(const std::string& extension_id) { 113 RulesRegistryService* rules_registry_service = 114 extensions::RulesRegistryService::Get(browser()->profile()); 115 scoped_refptr<RulesRegistry> rules_registry = 116 rules_registry_service->GetRulesRegistry( 117 RulesRegistry::WebViewKey(0, 0), 118 extensions::declarative_webrequest_constants::kOnRequest); 119 std::vector<linked_ptr<RulesRegistry::Rule> > rules; 120 BrowserThread::PostTask( 121 BrowserThread::IO, 122 FROM_HERE, 123 base::Bind( 124 &RulesRegistry::GetAllRules, rules_registry, extension_id, &rules)); 125 scoped_refptr<base::ThreadTestHelper> io_helper(new base::ThreadTestHelper( 126 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get())); 127 EXPECT_TRUE(io_helper->Run()); 128 return rules.size(); 129 } 130 }; 131 132 IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, DeclarativeApi) { 133 ASSERT_TRUE(RunExtensionTest("declarative/api")) << message_; 134 135 // Check that uninstalling the extension has removed all rules. 136 std::string extension_id = GetSingleLoadedExtension()->id(); 137 UninstallExtension(extension_id); 138 139 // UnloadExtension posts a task to the owner thread of the extension 140 // to process this unloading. The next task to retrive all rules 141 // is therefore processed after the UnloadExtension task has been executed. 142 EXPECT_EQ(0u, NumberOfRegisteredRules(extension_id)); 143 } 144 145 // PersistRules test first installs an extension, which registers some rules. 146 // Then after browser restart, it checks that the rules are still in effect. 147 IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, PRE_PersistRules) { 148 // Note that we cannot use an extension generated by *GetRedirectToData* 149 // helpers in a TestExtensionDir, because we need the extension to persist 150 // until the PersistRules test is run. 151 ASSERT_TRUE(RunExtensionTest("declarative/redirect_to_data")) << message_; 152 } 153 154 IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, PersistRules) { 155 ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl)); 156 EXPECT_EQ(kTestTitle, GetTitle()); 157 } 158 159 // Test that the rules are correctly persisted and (de)activated during 160 // changing the "installed" and "enabled" status of an extension. 161 IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, ExtensionLifetimeRulesHandling) { 162 TestExtensionDir ext_dir; 163 164 // 1. Install the extension. Rules should become active. 165 ext_dir.WriteManifest(GetRedirectToDataManifestWithVersion(1)); 166 ext_dir.WriteFile(FILE_PATH_LITERAL("background.js"), 167 base::StringPrintf("%s%s%s", 168 kRedirectToDataConstants, 169 kRedirectToDataRules, 170 kRedirectToDataInstallRules)); 171 ExtensionTestMessageListener ready("ready", /*will_reply=*/false); 172 const Extension* extension = InstallExtensionWithUIAutoConfirm( 173 ext_dir.Pack(), 1 /*+1 installed extension*/, browser()); 174 ASSERT_TRUE(extension); 175 std::string extension_id(extension->id()); 176 ASSERT_TRUE(ready.WaitUntilSatisfied()); 177 ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl)); 178 EXPECT_EQ(kTestTitle, GetTitle()); 179 EXPECT_EQ(1u, NumberOfRegisteredRules(extension_id)); 180 181 // 2. Disable the extension. Rules are no longer active, but are still 182 // registered. 183 DisableExtension(extension_id); 184 ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl)); 185 EXPECT_NE(kTestTitle, GetTitle()); 186 EXPECT_EQ(1u, NumberOfRegisteredRules(extension_id)); 187 188 // 3. Enable the extension again. Rules are active again. 189 EnableExtension(extension_id); 190 ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl)); 191 EXPECT_EQ(kTestTitle, GetTitle()); 192 EXPECT_EQ(1u, NumberOfRegisteredRules(extension_id)); 193 194 // 4. Bump the version and update, without the code to add the rules. Rules 195 // are still active, because the registry does not drop them unless the 196 // extension gets uninstalled. 197 ext_dir.WriteManifest(GetRedirectToDataManifestWithVersion(2)); 198 ext_dir.WriteFile( 199 FILE_PATH_LITERAL("background.js"), 200 base::StringPrintf( 201 "%s%s", kRedirectToDataConstants, kRedirectToDataNoRules)); 202 ExtensionTestMessageListener ready_after_update("ready", 203 /*will_reply=*/false); 204 EXPECT_TRUE(UpdateExtension( 205 extension_id, ext_dir.Pack(), 0 /*no new installed extension*/)); 206 ASSERT_TRUE(ready_after_update.WaitUntilSatisfied()); 207 ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl)); 208 EXPECT_EQ(kTestTitle, GetTitle()); 209 EXPECT_EQ(1u, NumberOfRegisteredRules(extension_id)); 210 211 // 5. Reload the extension. Rules remain active. 212 ReloadExtension(extension_id); 213 ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl)); 214 EXPECT_EQ(kTestTitle, GetTitle()); 215 EXPECT_EQ(1u, NumberOfRegisteredRules(extension_id)); 216 217 // 6. Uninstall the extension. Rules are gone. 218 UninstallExtension(extension_id); 219 ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl)); 220 EXPECT_NE(kTestTitle, GetTitle()); 221 EXPECT_EQ(0u, NumberOfRegisteredRules(extension_id)); 222 } 223 224 // When an extenion is uninstalled, the state store deletes all preferences 225 // stored for that extension. We need to make sure we don't store anything after 226 // that deletion occurs. 227 IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, NoTracesAfterUninstalling) { 228 TestExtensionDir ext_dir; 229 230 // 1. Install the extension. Verify that rules become active and some prefs 231 // are stored. 232 ext_dir.WriteManifest(GetRedirectToDataManifestWithVersion(1)); 233 ext_dir.WriteFile(FILE_PATH_LITERAL("background.js"), 234 base::StringPrintf("%s%s%s", 235 kRedirectToDataConstants, 236 kRedirectToDataRules, 237 kRedirectToDataInstallRules)); 238 ExtensionTestMessageListener ready("ready", /*will_reply=*/false); 239 const Extension* extension = InstallExtensionWithUIAutoConfirm( 240 ext_dir.Pack(), 1 /*+1 installed extension*/, browser()); 241 ASSERT_TRUE(extension); 242 std::string extension_id(extension->id()); 243 ASSERT_TRUE(ready.WaitUntilSatisfied()); 244 ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl)); 245 EXPECT_EQ(kTestTitle, GetTitle()); 246 EXPECT_EQ(1u, NumberOfRegisteredRules(extension_id)); 247 ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser()->profile()); 248 EXPECT_TRUE(extension_prefs->HasPrefForExtension(extension_id)); 249 250 // 2. Uninstall the extension. Rules are gone and preferences should be empty. 251 UninstallExtension(extension_id); 252 ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl)); 253 EXPECT_NE(kTestTitle, GetTitle()); 254 EXPECT_EQ(0u, NumberOfRegisteredRules(extension_id)); 255 EXPECT_FALSE(extension_prefs->HasPrefForExtension(extension_id)); 256 } 257