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.
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"
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
;
35 const char kArbitraryUrl
[] = "http://www.example.com"; // Must be http://.
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
;
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(
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"
54 " \"permissions\": [\n"
55 " \"declarativeWebRequest\", \"<all_urls>\"\n"
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
68 const char kRedirectToDataRules
[] =
71 " new chrome.declarativeWebRequest.RequestMatcher({\n"
72 " url: {schemes: ['http']}})\n"
75 " new chrome.declarativeWebRequest.RedirectRequest({\n"
76 " redirectUrl: redirectDataURI\n"
81 const char kRedirectToDataInstallRules
[] =
82 "function report(details) {\n"
83 " if (chrome.extension.lastError) {\n"
84 " chrome.test.log(chrome.extension.lastError.message);\n"
86 " chrome.test.sendMessage(\"ready\", function(reply) {})\n"
90 "chrome.runtime.onInstalled.addListener(function(details) {\n"
91 " if (details.reason == 'install')\n"
92 " chrome.declarativeWebRequest.onRequest.addRules(rules, report);\n"
95 const char kRedirectToDataNoRules
[] =
96 "chrome.runtime.onInstalled.addListener(function(details) {\n"
97 " chrome.test.sendMessage(\"ready\", function(reply) {})\n"
102 class DeclarativeApiTest
: public ExtensionApiTest
{
104 std::string
GetTitle() {
105 base::string16
title(
106 browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
107 return base::UTF16ToUTF8(title
);
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 RulesRegistryService::kDefaultRulesRegistryID
,
118 extensions::declarative_webrequest_constants::kOnRequest
);
119 std::vector
<linked_ptr
<RulesRegistry::Rule
> > rules
;
120 BrowserThread::PostTask(
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());
132 IN_PROC_BROWSER_TEST_F(DeclarativeApiTest
, DeclarativeApi
) {
133 ASSERT_TRUE(RunExtensionTest("declarative/api")) << message_
;
135 // Check that uninstalling the extension has removed all rules.
136 std::string extension_id
= GetSingleLoadedExtension()->id();
137 UninstallExtension(extension_id
);
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
));
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_
;
154 IN_PROC_BROWSER_TEST_F(DeclarativeApiTest
, PersistRules
) {
155 ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl
));
156 EXPECT_EQ(kTestTitle
, GetTitle());
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
;
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
));
181 // 2. Disable the extension. Rules are no longer active, but are still
183 DisableExtension(extension_id
);
184 ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl
));
185 EXPECT_NE(kTestTitle
, GetTitle());
186 EXPECT_EQ(1u, NumberOfRegisteredRules(extension_id
));
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
));
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));
199 FILE_PATH_LITERAL("background.js"),
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
));
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
));
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
));
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
;
230 // 1. Install the extension. Verify that rules become active and some prefs
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
));
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
));