Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / active_script_controller_unittest.cc
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.
4
5 #include <map>
6
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"
26
27 namespace extensions {
28
29 namespace {
30
31 const char kAllHostsPermission[] = "*://*/*";
32
33 }  // namespace
34
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 {
41  protected:
42   ActiveScriptControllerUnitTest();
43   virtual ~ActiveScriptControllerUnitTest();
44
45   // Creates an extension with all hosts permission and adds it to the registry.
46   const Extension* AddExtension();
47
48   // Reloads |extension_| by removing it from the registry and recreating it.
49   const Extension* ReloadExtension();
50
51   // Returns true if the |extension| requires user consent before injecting
52   // a script.
53   bool RequiresUserConsent(const Extension* extension) const;
54
55   // Request an injection for the given |extension|.
56   void RequestInjection(const Extension* extension);
57
58   // Returns the number of times a given extension has had a script execute.
59   size_t GetExecutionCountForExtension(const std::string& extension_id) const;
60
61   ActiveScriptController* controller() const {
62     return active_script_controller_;
63   }
64
65  private:
66   // Returns a closure to use as a script execution for a given extension.
67   base::Closure GetExecutionCallbackForExtension(
68       const std::string& extension_id);
69
70   // Increment the number of executions for the given |extension_id|.
71   void IncrementExecutionCount(const std::string& extension_id);
72
73   virtual void SetUp() OVERRIDE;
74
75   // Since ActiveScriptController's behavior is behind a flag, override the
76   // feature switch.
77   FeatureSwitch::ScopedOverride feature_override_;
78
79   // The associated ActiveScriptController.
80   ActiveScriptController* active_script_controller_;
81
82   // The map of observed executions, keyed by extension id.
83   std::map<std::string, int> extension_executions_;
84
85   scoped_refptr<const Extension> extension_;
86 };
87
88 ActiveScriptControllerUnitTest::ActiveScriptControllerUnitTest()
89     : feature_override_(FeatureSwitch::scripts_require_action(),
90                         FeatureSwitch::OVERRIDE_ENABLED),
91       active_script_controller_(NULL) {
92 }
93
94 ActiveScriptControllerUnitTest::~ActiveScriptControllerUnitTest() {
95 }
96
97 const Extension* ActiveScriptControllerUnitTest::AddExtension() {
98   const std::string kId = id_util::GenerateId("all_hosts_extension");
99   extension_ = ExtensionBuilder()
100                    .SetManifest(
101                        DictionaryBuilder()
102                            .Set("name", "all_hosts_extension")
103                            .Set("description", "an extension")
104                            .Set("manifest_version", 2)
105                            .Set("version", "1.0.0")
106                            .Set("permissions",
107                                 ListBuilder().Append(kAllHostsPermission)))
108                    .SetLocation(Manifest::INTERNAL)
109                    .SetID(kId)
110                    .Build();
111
112   ExtensionRegistry::Get(profile())->AddEnabled(extension_);
113   PermissionsUpdater(profile()).InitializePermissions(extension_);
114   return extension_;
115 }
116
117 const Extension* ActiveScriptControllerUnitTest::ReloadExtension() {
118   ExtensionRegistry::Get(profile())->RemoveEnabled(extension_->id());
119   return AddExtension();
120 }
121
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;
130 }
131
132 void ActiveScriptControllerUnitTest::RequestInjection(
133     const Extension* extension) {
134   controller()->RequestScriptInjectionForTesting(
135       extension,
136       GetExecutionCallbackForExtension(extension->id()));
137 }
138
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())
144     return iter->second;
145   return 0u;
146 }
147
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),
154                     extension_id);
155 }
156
157 void ActiveScriptControllerUnitTest::IncrementExecutionCount(
158     const std::string& extension_id) {
159   ++extension_executions_[extension_id];
160 }
161
162 void ActiveScriptControllerUnitTest::SetUp() {
163   ChromeRenderViewHostTestHarness::SetUp();
164
165   TabHelper::CreateForWebContents(web_contents());
166   TabHelper* tab_helper = TabHelper::FromWebContents(web_contents());
167   // None of these should ever be NULL.
168   DCHECK(tab_helper);
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_);
173 }
174
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);
180
181   NavigateAndCommit(GURL("https://www.google.com"));
182
183   // Ensure that there aren't any executions pending.
184   ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
185   ASSERT_FALSE(controller()->GetActionForExtension(extension));
186
187   // Since the extension requests all_hosts, we should require user consent.
188   EXPECT_TRUE(RequiresUserConsent(extension));
189
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()));
194
195   // Click to accept the extension executing.
196   controller()->OnClicked(extension);
197
198   // The extension should execute, and the action should go away.
199   EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
200   EXPECT_FALSE(controller()->GetActionForExtension(extension));
201
202   // Since we already executed on the given page, we shouldn't need permission
203   // for a second time.
204   EXPECT_FALSE(RequiresUserConsent(extension));
205
206   // Reloading should clear those permissions, and we should again require user
207   // consent.
208   Reload();
209   EXPECT_TRUE(RequiresUserConsent(extension));
210
211   // Grant access.
212   RequestInjection(extension);
213   controller()->OnClicked(extension);
214   EXPECT_EQ(2u, GetExecutionCountForExtension(extension->id()));
215   EXPECT_FALSE(controller()->GetActionForExtension(extension));
216
217   // Navigating to another site should also clear the permissions.
218   NavigateAndCommit(GURL("https://www.foo.com"));
219   EXPECT_TRUE(RequiresUserConsent(extension));
220 }
221
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);
227
228   NavigateAndCommit(GURL("https://www.google.com"));
229
230   ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
231
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()));
236
237   // Reload. This should remove the pending injection, and we should not
238   // execute anything.
239   Reload();
240   EXPECT_FALSE(controller()->GetActionForExtension(extension));
241   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
242
243   // Request and accept a new injection.
244   RequestInjection(extension);
245   controller()->OnClicked(extension);
246
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));
251 }
252
253 // Test that queueing multiple pending injections, and then accepting, triggers
254 // them all.
255 TEST_F(ActiveScriptControllerUnitTest, MultiplePendingInjection) {
256   const Extension* extension = AddExtension();
257   ASSERT_TRUE(extension);
258   NavigateAndCommit(GURL("https://www.google.com"));
259
260   ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
261
262   const size_t kNumInjections = 3u;
263   // Queue multiple pending injections.
264   for (size_t i = 0u; i < kNumInjections; ++i)
265     RequestInjection(extension);
266
267   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
268
269   controller()->OnClicked(extension);
270
271   // All pending injections should have executed.
272   EXPECT_EQ(kNumInjections, GetExecutionCountForExtension(extension->id()));
273   EXPECT_FALSE(controller()->GetActionForExtension(extension));
274 }
275
276 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsUseActiveTabPermissions) {
277   const Extension* extension = AddExtension();
278   NavigateAndCommit(GURL("https://www.google.com"));
279
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);
287
288   // Since we have active tab permissions, we shouldn't need user consent
289   // anymore.
290   EXPECT_FALSE(RequiresUserConsent(extension));
291
292   // Also test that granting active tab runs any pending tasks.
293   Reload();
294   // Navigating should mean we need permission again.
295   EXPECT_TRUE(RequiresUserConsent(extension));
296
297   RequestInjection(extension);
298   EXPECT_TRUE(controller()->GetActionForExtension(extension));
299   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
300
301   // Grant active tab.
302   active_tab_permission_granter->GrantIfRequested(extension);
303
304   // The pending injections should have run since active tab permission was
305   // granted.
306   EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
307   EXPECT_FALSE(controller()->GetActionForExtension(extension));
308 }
309
310 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsCanHaveAllUrlsPref) {
311   const Extension* extension = AddExtension();
312   ASSERT_TRUE(extension);
313
314   NavigateAndCommit(GURL("https://www.google.com"));
315   EXPECT_TRUE(RequiresUserConsent(extension));
316
317   // Enable the extension on all urls.
318   util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), true);
319
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));
324
325   // Turning off the preference should have instant effect.
326   util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), false);
327   EXPECT_TRUE(RequiresUserConsent(extension));
328
329   // And should also persist across navigations and websites.
330   NavigateAndCommit(GURL("http://www.bar.com"));
331   EXPECT_TRUE(RequiresUserConsent(extension));
332 }
333
334 TEST_F(ActiveScriptControllerUnitTest, TestAlwaysRun) {
335   const Extension* extension = AddExtension();
336   ASSERT_TRUE(extension);
337
338   NavigateAndCommit(GURL("https://www.google.com/?gws_rd=ssl"));
339
340   // Ensure that there aren't any executions pending.
341   ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
342   ASSERT_FALSE(controller()->GetActionForExtension(extension));
343
344   // Since the extension requests all_hosts, we should require user consent.
345   EXPECT_TRUE(RequiresUserConsent(extension));
346
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()));
351
352   // Allow the extension to always run on this origin.
353   controller()->AlwaysRunOnVisibleOrigin(extension);
354
355   // The extension should execute, and the action should go away.
356   EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
357   EXPECT_FALSE(controller()->GetActionForExtension(extension));
358
359   // Since we already executed on the given page, we shouldn't need permission
360   // for a second time.
361   EXPECT_FALSE(RequiresUserConsent(extension));
362
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));
367
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));
371
372   // Reloading the extension should not clear any granted host permissions.
373   extension = ReloadExtension();
374   Reload();
375   EXPECT_FALSE(RequiresUserConsent(extension));
376
377   // Different host...
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));
389 }
390
391 }  // namespace extensions