Prevent chrome://net-internals/#export from flickering
[chromium-blink-merge.git] / chrome / browser / extensions / active_script_controller_unittest.cc
blob3f191ade29dd803ed39b749363749c9b8562a96b
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 // We skip syncing for testing purposes.
36 KeyedService* BuildSyncService(content::BrowserContext* context) {
37 return nullptr;
40 } // namespace
42 // Unittests for the ActiveScriptController mostly test the internal logic
43 // of the controller itself (when to allow/deny extension script injection).
44 // Testing real injection is allowed/denied as expected (i.e., that the
45 // ActiveScriptController correctly interfaces in the system) is done in the
46 // ActiveScriptControllerBrowserTests.
47 class ActiveScriptControllerUnitTest : public ChromeRenderViewHostTestHarness {
48 protected:
49 ActiveScriptControllerUnitTest();
50 ~ActiveScriptControllerUnitTest() override;
52 // Creates an extension with all hosts permission and adds it to the registry.
53 const Extension* AddExtension();
55 // Reloads |extension_| by removing it from the registry and recreating it.
56 const Extension* ReloadExtension();
58 // Returns true if the |extension| requires user consent before injecting
59 // a script.
60 bool RequiresUserConsent(const Extension* extension) const;
62 // Request an injection for the given |extension|.
63 void RequestInjection(const Extension* extension);
65 // Returns the number of times a given extension has had a script execute.
66 size_t GetExecutionCountForExtension(const std::string& extension_id) const;
68 ActiveScriptController* controller() const {
69 return active_script_controller_;
72 private:
73 // Returns a closure to use as a script execution for a given extension.
74 base::Closure GetExecutionCallbackForExtension(
75 const std::string& extension_id);
77 // Increment the number of executions for the given |extension_id|.
78 void IncrementExecutionCount(const std::string& extension_id);
80 void SetUp() override;
82 // Since ActiveScriptController's behavior is behind a flag, override the
83 // feature switch.
84 FeatureSwitch::ScopedOverride feature_override_;
86 // The associated ActiveScriptController.
87 ActiveScriptController* active_script_controller_;
89 // The map of observed executions, keyed by extension id.
90 std::map<std::string, int> extension_executions_;
92 scoped_refptr<const Extension> extension_;
95 ActiveScriptControllerUnitTest::ActiveScriptControllerUnitTest()
96 : feature_override_(FeatureSwitch::scripts_require_action(),
97 FeatureSwitch::OVERRIDE_ENABLED),
98 active_script_controller_(NULL) {
101 ActiveScriptControllerUnitTest::~ActiveScriptControllerUnitTest() {
104 const Extension* ActiveScriptControllerUnitTest::AddExtension() {
105 const std::string kId = crx_file::id_util::GenerateId("all_hosts_extension");
106 extension_ = ExtensionBuilder()
107 .SetManifest(
108 DictionaryBuilder()
109 .Set("name", "all_hosts_extension")
110 .Set("description", "an extension")
111 .Set("manifest_version", 2)
112 .Set("version", "1.0.0")
113 .Set("permissions",
114 ListBuilder().Append(kAllHostsPermission)))
115 .SetLocation(Manifest::INTERNAL)
116 .SetID(kId)
117 .Build();
119 ExtensionRegistry::Get(profile())->AddEnabled(extension_);
120 PermissionsUpdater(profile()).InitializePermissions(extension_.get());
121 return extension_.get();
124 const Extension* ActiveScriptControllerUnitTest::ReloadExtension() {
125 ExtensionRegistry::Get(profile())->RemoveEnabled(extension_->id());
126 return AddExtension();
129 bool ActiveScriptControllerUnitTest::RequiresUserConsent(
130 const Extension* extension) const {
131 PermissionsData::AccessType access_type =
132 controller()->RequiresUserConsentForScriptInjectionForTesting(
133 extension, UserScript::PROGRAMMATIC_SCRIPT);
134 // We should never downright refuse access in these tests.
135 DCHECK_NE(PermissionsData::ACCESS_DENIED, access_type);
136 return access_type == PermissionsData::ACCESS_WITHHELD;
139 void ActiveScriptControllerUnitTest::RequestInjection(
140 const Extension* extension) {
141 controller()->RequestScriptInjectionForTesting(
142 extension,
143 GetExecutionCallbackForExtension(extension->id()));
146 size_t ActiveScriptControllerUnitTest::GetExecutionCountForExtension(
147 const std::string& extension_id) const {
148 std::map<std::string, int>::const_iterator iter =
149 extension_executions_.find(extension_id);
150 if (iter != extension_executions_.end())
151 return iter->second;
152 return 0u;
155 base::Closure ActiveScriptControllerUnitTest::GetExecutionCallbackForExtension(
156 const std::string& extension_id) {
157 // We use base unretained here, but if this ever gets executed outside of
158 // this test's lifetime, we have a major problem anyway.
159 return base::Bind(&ActiveScriptControllerUnitTest::IncrementExecutionCount,
160 base::Unretained(this),
161 extension_id);
164 void ActiveScriptControllerUnitTest::IncrementExecutionCount(
165 const std::string& extension_id) {
166 ++extension_executions_[extension_id];
169 void ActiveScriptControllerUnitTest::SetUp() {
170 ChromeRenderViewHostTestHarness::SetUp();
172 ExtensionSyncServiceFactory::GetInstance()->SetTestingFactory(
173 profile(), &BuildSyncService);
175 TabHelper::CreateForWebContents(web_contents());
176 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents());
177 // These should never be NULL.
178 DCHECK(tab_helper);
179 active_script_controller_ = tab_helper->active_script_controller();
180 DCHECK(active_script_controller_);
183 // Test that extensions with all_hosts require permission to execute, and, once
184 // that permission is granted, do execute.
185 TEST_F(ActiveScriptControllerUnitTest, RequestPermissionAndExecute) {
186 const Extension* extension = AddExtension();
187 ASSERT_TRUE(extension);
189 NavigateAndCommit(GURL("https://www.google.com"));
191 // Ensure that there aren't any executions pending.
192 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
193 ASSERT_FALSE(controller()->WantsToRun(extension));
195 ExtensionActionAPI* extension_action_api =
196 ExtensionActionAPI::Get(profile());
197 ASSERT_FALSE(extension_action_api->ExtensionWantsToRun(extension,
198 web_contents()));
200 // Since the extension requests all_hosts, we should require user consent.
201 EXPECT_TRUE(RequiresUserConsent(extension));
203 // Request an injection. The extension should want to run, but should not have
204 // executed.
205 RequestInjection(extension);
206 EXPECT_TRUE(controller()->WantsToRun(extension));
207 EXPECT_TRUE(extension_action_api->ExtensionWantsToRun(extension,
208 web_contents()));
209 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
211 // Click to accept the extension executing.
212 controller()->OnClicked(extension);
214 // The extension should execute, and the extension shouldn't want to run.
215 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
216 EXPECT_FALSE(controller()->WantsToRun(extension));
217 EXPECT_FALSE(extension_action_api->ExtensionWantsToRun(extension,
218 web_contents()));
220 // Since we already executed on the given page, we shouldn't need permission
221 // for a second time.
222 EXPECT_FALSE(RequiresUserConsent(extension));
224 // Reloading and same-origin navigations shouldn't clear those permissions,
225 // and we shouldn't require user constent again.
226 Reload();
227 EXPECT_FALSE(RequiresUserConsent(extension));
228 NavigateAndCommit(GURL("https://www.google.com/foo"));
229 EXPECT_FALSE(RequiresUserConsent(extension));
230 NavigateAndCommit(GURL("https://www.google.com/bar"));
231 EXPECT_FALSE(RequiresUserConsent(extension));
233 // Cross-origin navigations should clear permissions.
234 NavigateAndCommit(GURL("https://otherdomain.google.com"));
235 EXPECT_TRUE(RequiresUserConsent(extension));
237 // Grant access.
238 RequestInjection(extension);
239 controller()->OnClicked(extension);
240 EXPECT_EQ(2u, GetExecutionCountForExtension(extension->id()));
241 EXPECT_FALSE(controller()->WantsToRun(extension));
243 // Navigating to another site should also clear the permissions.
244 NavigateAndCommit(GURL("https://www.foo.com"));
245 EXPECT_TRUE(RequiresUserConsent(extension));
248 // Test that injections that are not executed by the time the user navigates are
249 // ignored and never execute.
250 TEST_F(ActiveScriptControllerUnitTest, PendingInjectionsRemovedAtNavigation) {
251 const Extension* extension = AddExtension();
252 ASSERT_TRUE(extension);
254 NavigateAndCommit(GURL("https://www.google.com"));
256 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
258 // Request an injection. The extension should want to run, but not execute.
259 RequestInjection(extension);
260 EXPECT_TRUE(controller()->WantsToRun(extension));
261 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
263 // Reload. This should remove the pending injection, and we should not
264 // execute anything.
265 Reload();
266 EXPECT_FALSE(controller()->WantsToRun(extension));
267 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
269 // Request and accept a new injection.
270 RequestInjection(extension);
271 controller()->OnClicked(extension);
273 // The extension should only have executed once, even though a grand total
274 // of two executions were requested.
275 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
276 EXPECT_FALSE(controller()->WantsToRun(extension));
279 // Test that queueing multiple pending injections, and then accepting, triggers
280 // them all.
281 TEST_F(ActiveScriptControllerUnitTest, MultiplePendingInjection) {
282 const Extension* extension = AddExtension();
283 ASSERT_TRUE(extension);
284 NavigateAndCommit(GURL("https://www.google.com"));
286 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
288 const size_t kNumInjections = 3u;
289 // Queue multiple pending injections.
290 for (size_t i = 0u; i < kNumInjections; ++i)
291 RequestInjection(extension);
293 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
295 controller()->OnClicked(extension);
297 // All pending injections should have executed.
298 EXPECT_EQ(kNumInjections, GetExecutionCountForExtension(extension->id()));
299 EXPECT_FALSE(controller()->WantsToRun(extension));
302 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsUseActiveTabPermissions) {
303 const Extension* extension = AddExtension();
304 NavigateAndCommit(GURL("https://www.google.com"));
306 ActiveTabPermissionGranter* active_tab_permission_granter =
307 TabHelper::FromWebContents(web_contents())
308 ->active_tab_permission_granter();
309 ASSERT_TRUE(active_tab_permission_granter);
310 // Grant the extension active tab permissions. This normally happens, e.g.,
311 // if the user clicks on a browser action.
312 active_tab_permission_granter->GrantIfRequested(extension);
314 // Since we have active tab permissions, we shouldn't need user consent
315 // anymore.
316 EXPECT_FALSE(RequiresUserConsent(extension));
318 // Reloading and other same-origin navigations maintain the permission to
319 // execute.
320 Reload();
321 EXPECT_FALSE(RequiresUserConsent(extension));
322 NavigateAndCommit(GURL("https://www.google.com/foo"));
323 EXPECT_FALSE(RequiresUserConsent(extension));
324 NavigateAndCommit(GURL("https://www.google.com/bar"));
325 EXPECT_FALSE(RequiresUserConsent(extension));
327 // Navigating to a different origin will require user consent again.
328 NavigateAndCommit(GURL("https://yahoo.com"));
329 EXPECT_TRUE(RequiresUserConsent(extension));
331 // Back to the original origin should also re-require constent.
332 NavigateAndCommit(GURL("https://www.google.com"));
333 EXPECT_TRUE(RequiresUserConsent(extension));
335 RequestInjection(extension);
336 EXPECT_TRUE(controller()->WantsToRun(extension));
337 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
339 // Grant active tab.
340 active_tab_permission_granter->GrantIfRequested(extension);
342 // The pending injections should have run since active tab permission was
343 // granted.
344 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
345 EXPECT_FALSE(controller()->WantsToRun(extension));
348 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsCanHaveAllUrlsPref) {
349 const Extension* extension = AddExtension();
350 ASSERT_TRUE(extension);
352 NavigateAndCommit(GURL("https://www.google.com"));
353 EXPECT_TRUE(RequiresUserConsent(extension));
355 // Enable the extension on all urls.
356 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), true);
358 EXPECT_FALSE(RequiresUserConsent(extension));
359 // This should carry across navigations, and websites.
360 NavigateAndCommit(GURL("http://www.foo.com"));
361 EXPECT_FALSE(RequiresUserConsent(extension));
363 // Turning off the preference should have instant effect.
364 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), false);
365 EXPECT_TRUE(RequiresUserConsent(extension));
367 // And should also persist across navigations and websites.
368 NavigateAndCommit(GURL("http://www.bar.com"));
369 EXPECT_TRUE(RequiresUserConsent(extension));
372 TEST_F(ActiveScriptControllerUnitTest, TestAlwaysRun) {
373 const Extension* extension = AddExtension();
374 ASSERT_TRUE(extension);
376 NavigateAndCommit(GURL("https://www.google.com/?gws_rd=ssl"));
378 // Ensure that there aren't any executions pending.
379 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
380 ASSERT_FALSE(controller()->WantsToRun(extension));
382 // Since the extension requests all_hosts, we should require user consent.
383 EXPECT_TRUE(RequiresUserConsent(extension));
385 // Request an injection. The extension should want to run, but not execute.
386 RequestInjection(extension);
387 EXPECT_TRUE(controller()->WantsToRun(extension));
388 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
390 // Allow the extension to always run on this origin.
391 controller()->AlwaysRunOnVisibleOrigin(extension);
393 // The extension should execute, and the extension shouldn't want to run.
394 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
395 EXPECT_FALSE(controller()->WantsToRun(extension));
397 // Since we already executed on the given page, we shouldn't need permission
398 // for a second time.
399 EXPECT_FALSE(RequiresUserConsent(extension));
401 // Navigating to another site that hasn't been granted a persisted permission
402 // should necessitate user consent.
403 NavigateAndCommit(GURL("https://www.foo.com/bar"));
404 EXPECT_TRUE(RequiresUserConsent(extension));
406 // We shouldn't need user permission upon returning to the original origin.
407 NavigateAndCommit(GURL("https://www.google.com/foo/bar"));
408 EXPECT_FALSE(RequiresUserConsent(extension));
410 // Reloading the extension should not clear any granted host permissions.
411 extension = ReloadExtension();
412 Reload();
413 EXPECT_FALSE(RequiresUserConsent(extension));
415 // Different host...
416 NavigateAndCommit(GURL("https://www.foo.com/bar"));
417 EXPECT_TRUE(RequiresUserConsent(extension));
418 // Different scheme...
419 NavigateAndCommit(GURL("http://www.google.com/foo/bar"));
420 EXPECT_TRUE(RequiresUserConsent(extension));
421 // Different subdomain...
422 NavigateAndCommit(GURL("https://en.google.com/foo/bar"));
423 EXPECT_TRUE(RequiresUserConsent(extension));
424 // Only the "always run" origin should be allowed to run without user consent.
425 NavigateAndCommit(GURL("https://www.google.com/foo/bar"));
426 EXPECT_FALSE(RequiresUserConsent(extension));
429 } // namespace extensions