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
25using content::BrowserThread;
26using extensions::Extension;
27using extensions::ExtensionPrefs;
28using extensions::RulesRegistry;
29using extensions::RulesRegistryService;
30using extensions::TestExtensionDir;
31using extensions::WebRequestRulesRegistry;
32
33namespace {
34
35const 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:"
40const 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:'".
44std::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
61const 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
68const 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
81const 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
95const char kRedirectToDataNoRules[] =
96    "chrome.runtime.onInstalled.addListener(function(details) {\n"
97    "  chrome.test.sendMessage(\"ready\", function(reply) {})\n"
98    "});\n";
99
100}  // namespace
101
102class 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
132IN_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.
147IN_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
154IN_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.
161IN_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.
227IN_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