1 // Copyright 2014 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.
7 #include "base/values.h"
8 #include "chrome/browser/extensions/active_script_controller.h"
9 #include "chrome/browser/extensions/active_tab_permission_granter.h"
10 #include "chrome/browser/extensions/extension_util.h"
11 #include "chrome/browser/extensions/permissions_updater.h"
12 #include "chrome/browser/extensions/tab_helper.h"
13 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
14 #include "chrome/test/base/testing_profile.h"
15 #include "content/public/browser/navigation_controller.h"
16 #include "content/public/browser/navigation_entry.h"
17 #include "content/public/browser/web_contents.h"
18 #include "extensions/browser/extension_registry.h"
19 #include "extensions/common/extension.h"
20 #include "extensions/common/extension_builder.h"
21 #include "extensions/common/feature_switch.h"
22 #include "extensions/common/id_util.h"
23 #include "extensions/common/manifest.h"
24 #include "extensions/common/user_script.h"
25 #include "extensions/common/value_builder.h"
27 namespace extensions {
31 const char kAllHostsPermission[] = "*://*/*";
35 // Unittests for the ActiveScriptController mostly test the internal logic
36 // of the controller itself (when to allow/deny extension script injection).
37 // Testing real injection is allowed/denied as expected (i.e., that the
38 // ActiveScriptController correctly interfaces in the system) is done in the
39 // ActiveScriptControllerBrowserTests.
40 class ActiveScriptControllerUnitTest : public ChromeRenderViewHostTestHarness {
42 ActiveScriptControllerUnitTest();
43 virtual ~ActiveScriptControllerUnitTest();
45 // Creates an extension with all hosts permission and adds it to the registry.
46 const Extension* AddExtension();
48 // Reloads |extension_| by removing it from the registry and recreating it.
49 const Extension* ReloadExtension();
51 // Returns true if the |extension| requires user consent before injecting
53 bool RequiresUserConsent(const Extension* extension) const;
55 // Request an injection for the given |extension|.
56 void RequestInjection(const Extension* extension);
58 // Returns the number of times a given extension has had a script execute.
59 size_t GetExecutionCountForExtension(const std::string& extension_id) const;
61 ActiveScriptController* controller() const {
62 return active_script_controller_;
66 // Returns a closure to use as a script execution for a given extension.
67 base::Closure GetExecutionCallbackForExtension(
68 const std::string& extension_id);
70 // Increment the number of executions for the given |extension_id|.
71 void IncrementExecutionCount(const std::string& extension_id);
73 virtual void SetUp() OVERRIDE;
75 // Since ActiveScriptController's behavior is behind a flag, override the
77 FeatureSwitch::ScopedOverride feature_override_;
79 // The associated ActiveScriptController.
80 ActiveScriptController* active_script_controller_;
82 // The map of observed executions, keyed by extension id.
83 std::map<std::string, int> extension_executions_;
85 scoped_refptr<const Extension> extension_;
88 ActiveScriptControllerUnitTest::ActiveScriptControllerUnitTest()
89 : feature_override_(FeatureSwitch::scripts_require_action(),
90 FeatureSwitch::OVERRIDE_ENABLED),
91 active_script_controller_(NULL) {
94 ActiveScriptControllerUnitTest::~ActiveScriptControllerUnitTest() {
97 const Extension* ActiveScriptControllerUnitTest::AddExtension() {
98 const std::string kId = id_util::GenerateId("all_hosts_extension");
99 extension_ = ExtensionBuilder()
102 .Set("name", "all_hosts_extension")
103 .Set("description", "an extension")
104 .Set("manifest_version", 2)
105 .Set("version", "1.0.0")
107 ListBuilder().Append(kAllHostsPermission)))
108 .SetLocation(Manifest::INTERNAL)
112 ExtensionRegistry::Get(profile())->AddEnabled(extension_);
113 PermissionsUpdater(profile()).InitializePermissions(extension_);
117 const Extension* ActiveScriptControllerUnitTest::ReloadExtension() {
118 ExtensionRegistry::Get(profile())->RemoveEnabled(extension_->id());
119 return AddExtension();
122 bool ActiveScriptControllerUnitTest::RequiresUserConsent(
123 const Extension* extension) const {
124 PermissionsData::AccessType access_type =
125 controller()->RequiresUserConsentForScriptInjectionForTesting(
126 extension, UserScript::PROGRAMMATIC_SCRIPT);
127 // We should never downright refuse access in these tests.
128 DCHECK_NE(PermissionsData::ACCESS_DENIED, access_type);
129 return access_type == PermissionsData::ACCESS_WITHHELD;
132 void ActiveScriptControllerUnitTest::RequestInjection(
133 const Extension* extension) {
134 controller()->RequestScriptInjectionForTesting(
136 GetExecutionCallbackForExtension(extension->id()));
139 size_t ActiveScriptControllerUnitTest::GetExecutionCountForExtension(
140 const std::string& extension_id) const {
141 std::map<std::string, int>::const_iterator iter =
142 extension_executions_.find(extension_id);
143 if (iter != extension_executions_.end())
148 base::Closure ActiveScriptControllerUnitTest::GetExecutionCallbackForExtension(
149 const std::string& extension_id) {
150 // We use base unretained here, but if this ever gets executed outside of
151 // this test's lifetime, we have a major problem anyway.
152 return base::Bind(&ActiveScriptControllerUnitTest::IncrementExecutionCount,
153 base::Unretained(this),
157 void ActiveScriptControllerUnitTest::IncrementExecutionCount(
158 const std::string& extension_id) {
159 ++extension_executions_[extension_id];
162 void ActiveScriptControllerUnitTest::SetUp() {
163 ChromeRenderViewHostTestHarness::SetUp();
165 TabHelper::CreateForWebContents(web_contents());
166 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents());
167 // None of these should ever be NULL.
169 DCHECK(tab_helper->location_bar_controller());
170 active_script_controller_ =
171 tab_helper->location_bar_controller()->active_script_controller();
172 DCHECK(active_script_controller_);
175 // Test that extensions with all_hosts require permission to execute, and, once
176 // that permission is granted, do execute.
177 TEST_F(ActiveScriptControllerUnitTest, RequestPermissionAndExecute) {
178 const Extension* extension = AddExtension();
179 ASSERT_TRUE(extension);
181 NavigateAndCommit(GURL("https://www.google.com"));
183 // Ensure that there aren't any executions pending.
184 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
185 ASSERT_FALSE(controller()->GetActionForExtension(extension));
187 // Since the extension requests all_hosts, we should require user consent.
188 EXPECT_TRUE(RequiresUserConsent(extension));
190 // Request an injection. There should be an action visible, but no executions.
191 RequestInjection(extension);
192 EXPECT_TRUE(controller()->GetActionForExtension(extension));
193 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
195 // Click to accept the extension executing.
196 controller()->OnClicked(extension);
198 // The extension should execute, and the action should go away.
199 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
200 EXPECT_FALSE(controller()->GetActionForExtension(extension));
202 // Since we already executed on the given page, we shouldn't need permission
203 // for a second time.
204 EXPECT_FALSE(RequiresUserConsent(extension));
206 // Reloading should clear those permissions, and we should again require user
209 EXPECT_TRUE(RequiresUserConsent(extension));
212 RequestInjection(extension);
213 controller()->OnClicked(extension);
214 EXPECT_EQ(2u, GetExecutionCountForExtension(extension->id()));
215 EXPECT_FALSE(controller()->GetActionForExtension(extension));
217 // Navigating to another site should also clear the permissions.
218 NavigateAndCommit(GURL("https://www.foo.com"));
219 EXPECT_TRUE(RequiresUserConsent(extension));
222 // Test that injections that are not executed by the time the user navigates are
223 // ignored and never execute.
224 TEST_F(ActiveScriptControllerUnitTest, PendingInjectionsRemovedAtNavigation) {
225 const Extension* extension = AddExtension();
226 ASSERT_TRUE(extension);
228 NavigateAndCommit(GURL("https://www.google.com"));
230 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
232 // Request an injection. There should be an action visible, but no executions.
233 RequestInjection(extension);
234 EXPECT_TRUE(controller()->GetActionForExtension(extension));
235 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
237 // Reload. This should remove the pending injection, and we should not
240 EXPECT_FALSE(controller()->GetActionForExtension(extension));
241 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
243 // Request and accept a new injection.
244 RequestInjection(extension);
245 controller()->OnClicked(extension);
247 // The extension should only have executed once, even though a grand total
248 // of two executions were requested.
249 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
250 EXPECT_FALSE(controller()->GetActionForExtension(extension));
253 // Test that queueing multiple pending injections, and then accepting, triggers
255 TEST_F(ActiveScriptControllerUnitTest, MultiplePendingInjection) {
256 const Extension* extension = AddExtension();
257 ASSERT_TRUE(extension);
258 NavigateAndCommit(GURL("https://www.google.com"));
260 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
262 const size_t kNumInjections = 3u;
263 // Queue multiple pending injections.
264 for (size_t i = 0u; i < kNumInjections; ++i)
265 RequestInjection(extension);
267 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
269 controller()->OnClicked(extension);
271 // All pending injections should have executed.
272 EXPECT_EQ(kNumInjections, GetExecutionCountForExtension(extension->id()));
273 EXPECT_FALSE(controller()->GetActionForExtension(extension));
276 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsUseActiveTabPermissions) {
277 const Extension* extension = AddExtension();
278 NavigateAndCommit(GURL("https://www.google.com"));
280 ActiveTabPermissionGranter* active_tab_permission_granter =
281 TabHelper::FromWebContents(web_contents())
282 ->active_tab_permission_granter();
283 ASSERT_TRUE(active_tab_permission_granter);
284 // Grant the extension active tab permissions. This normally happens, e.g.,
285 // if the user clicks on a browser action.
286 active_tab_permission_granter->GrantIfRequested(extension);
288 // Since we have active tab permissions, we shouldn't need user consent
290 EXPECT_FALSE(RequiresUserConsent(extension));
292 // Also test that granting active tab runs any pending tasks.
294 // Navigating should mean we need permission again.
295 EXPECT_TRUE(RequiresUserConsent(extension));
297 RequestInjection(extension);
298 EXPECT_TRUE(controller()->GetActionForExtension(extension));
299 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
302 active_tab_permission_granter->GrantIfRequested(extension);
304 // The pending injections should have run since active tab permission was
306 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
307 EXPECT_FALSE(controller()->GetActionForExtension(extension));
310 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsCanHaveAllUrlsPref) {
311 const Extension* extension = AddExtension();
312 ASSERT_TRUE(extension);
314 NavigateAndCommit(GURL("https://www.google.com"));
315 EXPECT_TRUE(RequiresUserConsent(extension));
317 // Enable the extension on all urls.
318 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), true);
320 EXPECT_FALSE(RequiresUserConsent(extension));
321 // This should carry across navigations, and websites.
322 NavigateAndCommit(GURL("http://www.foo.com"));
323 EXPECT_FALSE(RequiresUserConsent(extension));
325 // Turning off the preference should have instant effect.
326 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), false);
327 EXPECT_TRUE(RequiresUserConsent(extension));
329 // And should also persist across navigations and websites.
330 NavigateAndCommit(GURL("http://www.bar.com"));
331 EXPECT_TRUE(RequiresUserConsent(extension));
334 TEST_F(ActiveScriptControllerUnitTest, TestAlwaysRun) {
335 const Extension* extension = AddExtension();
336 ASSERT_TRUE(extension);
338 NavigateAndCommit(GURL("https://www.google.com/?gws_rd=ssl"));
340 // Ensure that there aren't any executions pending.
341 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
342 ASSERT_FALSE(controller()->GetActionForExtension(extension));
344 // Since the extension requests all_hosts, we should require user consent.
345 EXPECT_TRUE(RequiresUserConsent(extension));
347 // Request an injection. There should be an action visible, but no executions.
348 RequestInjection(extension);
349 EXPECT_TRUE(controller()->GetActionForExtension(extension));
350 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
352 // Allow the extension to always run on this origin.
353 controller()->AlwaysRunOnVisibleOrigin(extension);
355 // The extension should execute, and the action should go away.
356 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
357 EXPECT_FALSE(controller()->GetActionForExtension(extension));
359 // Since we already executed on the given page, we shouldn't need permission
360 // for a second time.
361 EXPECT_FALSE(RequiresUserConsent(extension));
363 // Navigating to another site that hasn't been granted a persisted permission
364 // should necessitate user consent.
365 NavigateAndCommit(GURL("https://www.foo.com/bar"));
366 EXPECT_TRUE(RequiresUserConsent(extension));
368 // We shouldn't need user permission upon returning to the original origin.
369 NavigateAndCommit(GURL("https://www.google.com/foo/bar"));
370 EXPECT_FALSE(RequiresUserConsent(extension));
372 // Reloading the extension should not clear any granted host permissions.
373 extension = ReloadExtension();
375 EXPECT_FALSE(RequiresUserConsent(extension));
378 NavigateAndCommit(GURL("https://www.foo.com/bar"));
379 EXPECT_TRUE(RequiresUserConsent(extension));
380 // Different scheme...
381 NavigateAndCommit(GURL("http://www.google.com/foo/bar"));
382 EXPECT_TRUE(RequiresUserConsent(extension));
383 // Different subdomain...
384 NavigateAndCommit(GURL("https://en.google.com/foo/bar"));
385 EXPECT_TRUE(RequiresUserConsent(extension));
386 // Only the "always run" origin should be allowed to run without user consent.
387 NavigateAndCommit(GURL("https://www.google.com/foo/bar"));
388 EXPECT_FALSE(RequiresUserConsent(extension));
391 } // namespace extensions