Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / extensions / active_script_controller_unittest.cc
blob497077d4bb9849d60685e4e77652653968bf922f
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.
5 #include <map>
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/api/extension_action/extension_action_api.h"
11 #include "chrome/browser/extensions/extension_sync_service_factory.h"
12 #include "chrome/browser/extensions/extension_util.h"
13 #include "chrome/browser/extensions/permissions_updater.h"
14 #include "chrome/browser/extensions/tab_helper.h"
15 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
16 #include "chrome/test/base/testing_profile.h"
17 #include "components/crx_file/id_util.h"
18 #include "content/public/browser/navigation_controller.h"
19 #include "content/public/browser/navigation_entry.h"
20 #include "content/public/browser/web_contents.h"
21 #include "extensions/browser/extension_registry.h"
22 #include "extensions/common/extension.h"
23 #include "extensions/common/extension_builder.h"
24 #include "extensions/common/feature_switch.h"
25 #include "extensions/common/manifest.h"
26 #include "extensions/common/user_script.h"
27 #include "extensions/common/value_builder.h"
29 namespace extensions {
31 namespace {
33 const char kAllHostsPermission[] = "*://*/*";
35 } // namespace
37 // Unittests for the ActiveScriptController mostly test the internal logic
38 // of the controller itself (when to allow/deny extension script injection).
39 // Testing real injection is allowed/denied as expected (i.e., that the
40 // ActiveScriptController correctly interfaces in the system) is done in the
41 // ActiveScriptControllerBrowserTests.
42 class ActiveScriptControllerUnitTest : public ChromeRenderViewHostTestHarness {
43 protected:
44 ActiveScriptControllerUnitTest();
45 ~ActiveScriptControllerUnitTest() override;
47 // Creates an extension with all hosts permission and adds it to the registry.
48 const Extension* AddExtension();
50 // Reloads |extension_| by removing it from the registry and recreating it.
51 const Extension* ReloadExtension();
53 // Returns true if the |extension| requires user consent before injecting
54 // a script.
55 bool RequiresUserConsent(const Extension* extension) const;
57 // Request an injection for the given |extension|.
58 void RequestInjection(const Extension* extension);
60 // Returns the number of times a given extension has had a script execute.
61 size_t GetExecutionCountForExtension(const std::string& extension_id) const;
63 ActiveScriptController* controller() const {
64 return active_script_controller_;
67 private:
68 // Returns a closure to use as a script execution for a given extension.
69 base::Closure GetExecutionCallbackForExtension(
70 const std::string& extension_id);
72 // Increment the number of executions for the given |extension_id|.
73 void IncrementExecutionCount(const std::string& extension_id);
75 void SetUp() override;
77 // Since ActiveScriptController's behavior is behind a flag, override the
78 // feature switch.
79 FeatureSwitch::ScopedOverride feature_override_;
81 // The associated ActiveScriptController.
82 ActiveScriptController* active_script_controller_;
84 // The map of observed executions, keyed by extension id.
85 std::map<std::string, int> extension_executions_;
87 scoped_refptr<const Extension> extension_;
90 ActiveScriptControllerUnitTest::ActiveScriptControllerUnitTest()
91 : feature_override_(FeatureSwitch::scripts_require_action(),
92 FeatureSwitch::OVERRIDE_ENABLED),
93 active_script_controller_(NULL) {
96 ActiveScriptControllerUnitTest::~ActiveScriptControllerUnitTest() {
99 const Extension* ActiveScriptControllerUnitTest::AddExtension() {
100 const std::string kId = crx_file::id_util::GenerateId("all_hosts_extension");
101 extension_ = ExtensionBuilder()
102 .SetManifest(
103 DictionaryBuilder()
104 .Set("name", "all_hosts_extension")
105 .Set("description", "an extension")
106 .Set("manifest_version", 2)
107 .Set("version", "1.0.0")
108 .Set("permissions",
109 ListBuilder().Append(kAllHostsPermission)))
110 .SetLocation(Manifest::INTERNAL)
111 .SetID(kId)
112 .Build();
114 ExtensionRegistry::Get(profile())->AddEnabled(extension_);
115 PermissionsUpdater(profile()).InitializePermissions(extension_.get());
116 return extension_.get();
119 const Extension* ActiveScriptControllerUnitTest::ReloadExtension() {
120 ExtensionRegistry::Get(profile())->RemoveEnabled(extension_->id());
121 return AddExtension();
124 bool ActiveScriptControllerUnitTest::RequiresUserConsent(
125 const Extension* extension) const {
126 PermissionsData::AccessType access_type =
127 controller()->RequiresUserConsentForScriptInjectionForTesting(
128 extension, UserScript::PROGRAMMATIC_SCRIPT);
129 // We should never downright refuse access in these tests.
130 DCHECK_NE(PermissionsData::ACCESS_DENIED, access_type);
131 return access_type == PermissionsData::ACCESS_WITHHELD;
134 void ActiveScriptControllerUnitTest::RequestInjection(
135 const Extension* extension) {
136 controller()->RequestScriptInjectionForTesting(
137 extension,
138 GetExecutionCallbackForExtension(extension->id()));
141 size_t ActiveScriptControllerUnitTest::GetExecutionCountForExtension(
142 const std::string& extension_id) const {
143 std::map<std::string, int>::const_iterator iter =
144 extension_executions_.find(extension_id);
145 if (iter != extension_executions_.end())
146 return iter->second;
147 return 0u;
150 base::Closure ActiveScriptControllerUnitTest::GetExecutionCallbackForExtension(
151 const std::string& extension_id) {
152 // We use base unretained here, but if this ever gets executed outside of
153 // this test's lifetime, we have a major problem anyway.
154 return base::Bind(&ActiveScriptControllerUnitTest::IncrementExecutionCount,
155 base::Unretained(this),
156 extension_id);
159 void ActiveScriptControllerUnitTest::IncrementExecutionCount(
160 const std::string& extension_id) {
161 ++extension_executions_[extension_id];
164 void ActiveScriptControllerUnitTest::SetUp() {
165 ChromeRenderViewHostTestHarness::SetUp();
167 // Skip syncing for testing purposes.
168 ExtensionSyncServiceFactory::GetInstance()->SetTestingFactory(profile(),
169 nullptr);
171 TabHelper::CreateForWebContents(web_contents());
172 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents());
173 // These should never be NULL.
174 DCHECK(tab_helper);
175 active_script_controller_ = tab_helper->active_script_controller();
176 DCHECK(active_script_controller_);
179 // Test that extensions with all_hosts require permission to execute, and, once
180 // that permission is granted, do execute.
181 TEST_F(ActiveScriptControllerUnitTest, RequestPermissionAndExecute) {
182 const Extension* extension = AddExtension();
183 ASSERT_TRUE(extension);
185 NavigateAndCommit(GURL("https://www.google.com"));
187 // Ensure that there aren't any executions pending.
188 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
189 ASSERT_FALSE(controller()->WantsToRun(extension));
191 ExtensionActionAPI* extension_action_api =
192 ExtensionActionAPI::Get(profile());
193 ASSERT_FALSE(extension_action_api->ExtensionWantsToRun(extension,
194 web_contents()));
196 // Since the extension requests all_hosts, we should require user consent.
197 EXPECT_TRUE(RequiresUserConsent(extension));
199 // Request an injection. The extension should want to run, but should not have
200 // executed.
201 RequestInjection(extension);
202 EXPECT_TRUE(controller()->WantsToRun(extension));
203 EXPECT_TRUE(extension_action_api->ExtensionWantsToRun(extension,
204 web_contents()));
205 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
207 // Click to accept the extension executing.
208 controller()->OnClicked(extension);
210 // The extension should execute, and the extension shouldn't want to run.
211 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
212 EXPECT_FALSE(controller()->WantsToRun(extension));
213 EXPECT_FALSE(extension_action_api->ExtensionWantsToRun(extension,
214 web_contents()));
216 // Since we already executed on the given page, we shouldn't need permission
217 // for a second time.
218 EXPECT_FALSE(RequiresUserConsent(extension));
220 // Reloading and same-origin navigations shouldn't clear those permissions,
221 // and we shouldn't require user constent again.
222 Reload();
223 EXPECT_FALSE(RequiresUserConsent(extension));
224 NavigateAndCommit(GURL("https://www.google.com/foo"));
225 EXPECT_FALSE(RequiresUserConsent(extension));
226 NavigateAndCommit(GURL("https://www.google.com/bar"));
227 EXPECT_FALSE(RequiresUserConsent(extension));
229 // Cross-origin navigations should clear permissions.
230 NavigateAndCommit(GURL("https://otherdomain.google.com"));
231 EXPECT_TRUE(RequiresUserConsent(extension));
233 // Grant access.
234 RequestInjection(extension);
235 controller()->OnClicked(extension);
236 EXPECT_EQ(2u, GetExecutionCountForExtension(extension->id()));
237 EXPECT_FALSE(controller()->WantsToRun(extension));
239 // Navigating to another site should also clear the permissions.
240 NavigateAndCommit(GURL("https://www.foo.com"));
241 EXPECT_TRUE(RequiresUserConsent(extension));
244 // Test that injections that are not executed by the time the user navigates are
245 // ignored and never execute.
246 TEST_F(ActiveScriptControllerUnitTest, PendingInjectionsRemovedAtNavigation) {
247 const Extension* extension = AddExtension();
248 ASSERT_TRUE(extension);
250 NavigateAndCommit(GURL("https://www.google.com"));
252 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
254 // Request an injection. The extension should want to run, but not execute.
255 RequestInjection(extension);
256 EXPECT_TRUE(controller()->WantsToRun(extension));
257 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
259 // Reload. This should remove the pending injection, and we should not
260 // execute anything.
261 Reload();
262 EXPECT_FALSE(controller()->WantsToRun(extension));
263 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
265 // Request and accept a new injection.
266 RequestInjection(extension);
267 controller()->OnClicked(extension);
269 // The extension should only have executed once, even though a grand total
270 // of two executions were requested.
271 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
272 EXPECT_FALSE(controller()->WantsToRun(extension));
275 // Test that queueing multiple pending injections, and then accepting, triggers
276 // them all.
277 TEST_F(ActiveScriptControllerUnitTest, MultiplePendingInjection) {
278 const Extension* extension = AddExtension();
279 ASSERT_TRUE(extension);
280 NavigateAndCommit(GURL("https://www.google.com"));
282 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
284 const size_t kNumInjections = 3u;
285 // Queue multiple pending injections.
286 for (size_t i = 0u; i < kNumInjections; ++i)
287 RequestInjection(extension);
289 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
291 controller()->OnClicked(extension);
293 // All pending injections should have executed.
294 EXPECT_EQ(kNumInjections, GetExecutionCountForExtension(extension->id()));
295 EXPECT_FALSE(controller()->WantsToRun(extension));
298 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsUseActiveTabPermissions) {
299 const Extension* extension = AddExtension();
300 NavigateAndCommit(GURL("https://www.google.com"));
302 ActiveTabPermissionGranter* active_tab_permission_granter =
303 TabHelper::FromWebContents(web_contents())
304 ->active_tab_permission_granter();
305 ASSERT_TRUE(active_tab_permission_granter);
306 // Grant the extension active tab permissions. This normally happens, e.g.,
307 // if the user clicks on a browser action.
308 active_tab_permission_granter->GrantIfRequested(extension);
310 // Since we have active tab permissions, we shouldn't need user consent
311 // anymore.
312 EXPECT_FALSE(RequiresUserConsent(extension));
314 // Reloading and other same-origin navigations maintain the permission to
315 // execute.
316 Reload();
317 EXPECT_FALSE(RequiresUserConsent(extension));
318 NavigateAndCommit(GURL("https://www.google.com/foo"));
319 EXPECT_FALSE(RequiresUserConsent(extension));
320 NavigateAndCommit(GURL("https://www.google.com/bar"));
321 EXPECT_FALSE(RequiresUserConsent(extension));
323 // Navigating to a different origin will require user consent again.
324 NavigateAndCommit(GURL("https://yahoo.com"));
325 EXPECT_TRUE(RequiresUserConsent(extension));
327 // Back to the original origin should also re-require constent.
328 NavigateAndCommit(GURL("https://www.google.com"));
329 EXPECT_TRUE(RequiresUserConsent(extension));
331 RequestInjection(extension);
332 EXPECT_TRUE(controller()->WantsToRun(extension));
333 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
335 // Grant active tab.
336 active_tab_permission_granter->GrantIfRequested(extension);
338 // The pending injections should have run since active tab permission was
339 // granted.
340 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
341 EXPECT_FALSE(controller()->WantsToRun(extension));
344 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsCanHaveAllUrlsPref) {
345 const Extension* extension = AddExtension();
346 ASSERT_TRUE(extension);
348 NavigateAndCommit(GURL("https://www.google.com"));
349 EXPECT_TRUE(RequiresUserConsent(extension));
351 // Enable the extension on all urls.
352 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), true);
354 EXPECT_FALSE(RequiresUserConsent(extension));
355 // This should carry across navigations, and websites.
356 NavigateAndCommit(GURL("http://www.foo.com"));
357 EXPECT_FALSE(RequiresUserConsent(extension));
359 // Turning off the preference should have instant effect.
360 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), false);
361 EXPECT_TRUE(RequiresUserConsent(extension));
363 // And should also persist across navigations and websites.
364 NavigateAndCommit(GURL("http://www.bar.com"));
365 EXPECT_TRUE(RequiresUserConsent(extension));
368 TEST_F(ActiveScriptControllerUnitTest, TestAlwaysRun) {
369 const Extension* extension = AddExtension();
370 ASSERT_TRUE(extension);
372 NavigateAndCommit(GURL("https://www.google.com/?gws_rd=ssl"));
374 // Ensure that there aren't any executions pending.
375 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
376 ASSERT_FALSE(controller()->WantsToRun(extension));
378 // Since the extension requests all_hosts, we should require user consent.
379 EXPECT_TRUE(RequiresUserConsent(extension));
381 // Request an injection. The extension should want to run, but not execute.
382 RequestInjection(extension);
383 EXPECT_TRUE(controller()->WantsToRun(extension));
384 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
386 // Allow the extension to always run on this origin.
387 controller()->AlwaysRunOnVisibleOrigin(extension);
389 // The extension should execute, and the extension shouldn't want to run.
390 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
391 EXPECT_FALSE(controller()->WantsToRun(extension));
393 // Since we already executed on the given page, we shouldn't need permission
394 // for a second time.
395 EXPECT_FALSE(RequiresUserConsent(extension));
397 // Navigating to another site that hasn't been granted a persisted permission
398 // should necessitate user consent.
399 NavigateAndCommit(GURL("https://www.foo.com/bar"));
400 EXPECT_TRUE(RequiresUserConsent(extension));
402 // We shouldn't need user permission upon returning to the original origin.
403 NavigateAndCommit(GURL("https://www.google.com/foo/bar"));
404 EXPECT_FALSE(RequiresUserConsent(extension));
406 // Reloading the extension should not clear any granted host permissions.
407 extension = ReloadExtension();
408 Reload();
409 EXPECT_FALSE(RequiresUserConsent(extension));
411 // Different host...
412 NavigateAndCommit(GURL("https://www.foo.com/bar"));
413 EXPECT_TRUE(RequiresUserConsent(extension));
414 // Different scheme...
415 NavigateAndCommit(GURL("http://www.google.com/foo/bar"));
416 EXPECT_TRUE(RequiresUserConsent(extension));
417 // Different subdomain...
418 NavigateAndCommit(GURL("https://en.google.com/foo/bar"));
419 EXPECT_TRUE(RequiresUserConsent(extension));
420 // Only the "always run" origin should be allowed to run without user consent.
421 NavigateAndCommit(GURL("https://www.google.com/foo/bar"));
422 EXPECT_FALSE(RequiresUserConsent(extension));
425 } // namespace extensions