a2a95739d12fdd90e521509aae4ca85dcdd0bde6
[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 "components/crx_file/id_util.h"
16 #include "content/public/browser/navigation_controller.h"
17 #include "content/public/browser/navigation_entry.h"
18 #include "content/public/browser/web_contents.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/common/extension.h"
21 #include "extensions/common/extension_builder.h"
22 #include "extensions/common/feature_switch.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 = crx_file::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_.get());
114   return extension_.get();
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   // These should never be NULL.
168   DCHECK(tab_helper);
169   active_script_controller_ = tab_helper->active_script_controller();
170   DCHECK(active_script_controller_);
171 }
172
173 // Test that extensions with all_hosts require permission to execute, and, once
174 // that permission is granted, do execute.
175 TEST_F(ActiveScriptControllerUnitTest, RequestPermissionAndExecute) {
176   const Extension* extension = AddExtension();
177   ASSERT_TRUE(extension);
178
179   NavigateAndCommit(GURL("https://www.google.com"));
180
181   // Ensure that there aren't any executions pending.
182   ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
183   ASSERT_FALSE(controller()->WantsToRun(extension));
184
185   // Since the extension requests all_hosts, we should require user consent.
186   EXPECT_TRUE(RequiresUserConsent(extension));
187
188   // Request an injection. The extension should want to run, but should not have
189   // executed.
190   RequestInjection(extension);
191   EXPECT_TRUE(controller()->WantsToRun(extension));
192   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
193
194   // Click to accept the extension executing.
195   controller()->OnClicked(extension);
196
197   // The extension should execute, and the extension shouldn't want to run.
198   EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
199   EXPECT_FALSE(controller()->WantsToRun(extension));
200
201   // Since we already executed on the given page, we shouldn't need permission
202   // for a second time.
203   EXPECT_FALSE(RequiresUserConsent(extension));
204
205   // Reloading and same-origin navigations shouldn't clear those permissions,
206   // and we shouldn't require user constent again.
207   Reload();
208   EXPECT_FALSE(RequiresUserConsent(extension));
209   NavigateAndCommit(GURL("https://www.google.com/foo"));
210   EXPECT_FALSE(RequiresUserConsent(extension));
211   NavigateAndCommit(GURL("https://www.google.com/bar"));
212   EXPECT_FALSE(RequiresUserConsent(extension));
213
214   // Cross-origin navigations should clear permissions.
215   NavigateAndCommit(GURL("https://otherdomain.google.com"));
216   EXPECT_TRUE(RequiresUserConsent(extension));
217
218   // Grant access.
219   RequestInjection(extension);
220   controller()->OnClicked(extension);
221   EXPECT_EQ(2u, GetExecutionCountForExtension(extension->id()));
222   EXPECT_FALSE(controller()->WantsToRun(extension));
223
224   // Navigating to another site should also clear the permissions.
225   NavigateAndCommit(GURL("https://www.foo.com"));
226   EXPECT_TRUE(RequiresUserConsent(extension));
227 }
228
229 // Test that injections that are not executed by the time the user navigates are
230 // ignored and never execute.
231 TEST_F(ActiveScriptControllerUnitTest, PendingInjectionsRemovedAtNavigation) {
232   const Extension* extension = AddExtension();
233   ASSERT_TRUE(extension);
234
235   NavigateAndCommit(GURL("https://www.google.com"));
236
237   ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
238
239   // Request an injection. The extension should want to run, but not execute.
240   RequestInjection(extension);
241   EXPECT_TRUE(controller()->WantsToRun(extension));
242   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
243
244   // Reload. This should remove the pending injection, and we should not
245   // execute anything.
246   Reload();
247   EXPECT_FALSE(controller()->WantsToRun(extension));
248   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
249
250   // Request and accept a new injection.
251   RequestInjection(extension);
252   controller()->OnClicked(extension);
253
254   // The extension should only have executed once, even though a grand total
255   // of two executions were requested.
256   EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
257   EXPECT_FALSE(controller()->WantsToRun(extension));
258 }
259
260 // Test that queueing multiple pending injections, and then accepting, triggers
261 // them all.
262 TEST_F(ActiveScriptControllerUnitTest, MultiplePendingInjection) {
263   const Extension* extension = AddExtension();
264   ASSERT_TRUE(extension);
265   NavigateAndCommit(GURL("https://www.google.com"));
266
267   ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
268
269   const size_t kNumInjections = 3u;
270   // Queue multiple pending injections.
271   for (size_t i = 0u; i < kNumInjections; ++i)
272     RequestInjection(extension);
273
274   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
275
276   controller()->OnClicked(extension);
277
278   // All pending injections should have executed.
279   EXPECT_EQ(kNumInjections, GetExecutionCountForExtension(extension->id()));
280   EXPECT_FALSE(controller()->WantsToRun(extension));
281 }
282
283 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsUseActiveTabPermissions) {
284   const Extension* extension = AddExtension();
285   NavigateAndCommit(GURL("https://www.google.com"));
286
287   ActiveTabPermissionGranter* active_tab_permission_granter =
288       TabHelper::FromWebContents(web_contents())
289           ->active_tab_permission_granter();
290   ASSERT_TRUE(active_tab_permission_granter);
291   // Grant the extension active tab permissions. This normally happens, e.g.,
292   // if the user clicks on a browser action.
293   active_tab_permission_granter->GrantIfRequested(extension);
294
295   // Since we have active tab permissions, we shouldn't need user consent
296   // anymore.
297   EXPECT_FALSE(RequiresUserConsent(extension));
298
299   // Reloading and other same-origin navigations maintain the permission to
300   // execute.
301   Reload();
302   EXPECT_FALSE(RequiresUserConsent(extension));
303   NavigateAndCommit(GURL("https://www.google.com/foo"));
304   EXPECT_FALSE(RequiresUserConsent(extension));
305   NavigateAndCommit(GURL("https://www.google.com/bar"));
306   EXPECT_FALSE(RequiresUserConsent(extension));
307
308   // Navigating to a different origin will require user consent again.
309   NavigateAndCommit(GURL("https://yahoo.com"));
310   EXPECT_TRUE(RequiresUserConsent(extension));
311
312   // Back to the original origin should also re-require constent.
313   NavigateAndCommit(GURL("https://www.google.com"));
314   EXPECT_TRUE(RequiresUserConsent(extension));
315
316   RequestInjection(extension);
317   EXPECT_TRUE(controller()->WantsToRun(extension));
318   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
319
320   // Grant active tab.
321   active_tab_permission_granter->GrantIfRequested(extension);
322
323   // The pending injections should have run since active tab permission was
324   // granted.
325   EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
326   EXPECT_FALSE(controller()->WantsToRun(extension));
327 }
328
329 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsCanHaveAllUrlsPref) {
330   const Extension* extension = AddExtension();
331   ASSERT_TRUE(extension);
332
333   NavigateAndCommit(GURL("https://www.google.com"));
334   EXPECT_TRUE(RequiresUserConsent(extension));
335
336   // Enable the extension on all urls.
337   util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), true);
338
339   EXPECT_FALSE(RequiresUserConsent(extension));
340   // This should carry across navigations, and websites.
341   NavigateAndCommit(GURL("http://www.foo.com"));
342   EXPECT_FALSE(RequiresUserConsent(extension));
343
344   // Turning off the preference should have instant effect.
345   util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), false);
346   EXPECT_TRUE(RequiresUserConsent(extension));
347
348   // And should also persist across navigations and websites.
349   NavigateAndCommit(GURL("http://www.bar.com"));
350   EXPECT_TRUE(RequiresUserConsent(extension));
351 }
352
353 TEST_F(ActiveScriptControllerUnitTest, TestAlwaysRun) {
354   const Extension* extension = AddExtension();
355   ASSERT_TRUE(extension);
356
357   NavigateAndCommit(GURL("https://www.google.com/?gws_rd=ssl"));
358
359   // Ensure that there aren't any executions pending.
360   ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
361   ASSERT_FALSE(controller()->WantsToRun(extension));
362
363   // Since the extension requests all_hosts, we should require user consent.
364   EXPECT_TRUE(RequiresUserConsent(extension));
365
366   // Request an injection. The extension should want to run, but not execute.
367   RequestInjection(extension);
368   EXPECT_TRUE(controller()->WantsToRun(extension));
369   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
370
371   // Allow the extension to always run on this origin.
372   controller()->AlwaysRunOnVisibleOrigin(extension);
373
374   // The extension should execute, and the extension shouldn't want to run.
375   EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
376   EXPECT_FALSE(controller()->WantsToRun(extension));
377
378   // Since we already executed on the given page, we shouldn't need permission
379   // for a second time.
380   EXPECT_FALSE(RequiresUserConsent(extension));
381
382   // Navigating to another site that hasn't been granted a persisted permission
383   // should necessitate user consent.
384   NavigateAndCommit(GURL("https://www.foo.com/bar"));
385   EXPECT_TRUE(RequiresUserConsent(extension));
386
387   // We shouldn't need user permission upon returning to the original origin.
388   NavigateAndCommit(GURL("https://www.google.com/foo/bar"));
389   EXPECT_FALSE(RequiresUserConsent(extension));
390
391   // Reloading the extension should not clear any granted host permissions.
392   extension = ReloadExtension();
393   Reload();
394   EXPECT_FALSE(RequiresUserConsent(extension));
395
396   // Different host...
397   NavigateAndCommit(GURL("https://www.foo.com/bar"));
398   EXPECT_TRUE(RequiresUserConsent(extension));
399   // Different scheme...
400   NavigateAndCommit(GURL("http://www.google.com/foo/bar"));
401   EXPECT_TRUE(RequiresUserConsent(extension));
402   // Different subdomain...
403   NavigateAndCommit(GURL("https://en.google.com/foo/bar"));
404   EXPECT_TRUE(RequiresUserConsent(extension));
405   // Only the "always run" origin should be allowed to run without user consent.
406   NavigateAndCommit(GURL("https://www.google.com/foo/bar"));
407   EXPECT_FALSE(RequiresUserConsent(extension));
408 }
409
410 }  // namespace extensions