Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(400)

Side by Side Diff: chrome/browser/extensions/active_script_controller_unittest.cc

Issue 293003008: Make ActiveScriptController use Active Tab-style permissions (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Latest master for CQ Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/tab_helper.h"
11 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
12 #include "chrome/test/base/testing_profile.h"
13 #include "content/public/browser/navigation_controller.h"
14 #include "content/public/browser/navigation_entry.h"
15 #include "content/public/browser/web_contents.h"
16 #include "extensions/browser/extension_registry.h"
17 #include "extensions/common/extension.h"
18 #include "extensions/common/extension_builder.h"
19 #include "extensions/common/feature_switch.h"
20 #include "extensions/common/manifest.h"
21 #include "extensions/common/value_builder.h"
22
23 namespace extensions {
24
25 namespace {
26
27 const char kAllHostsPermission[] = "*://*/*";
28
29 } // namespace
30
31 // Unittests for the ActiveScriptController mostly test the internal logic
32 // of the controller itself (when to allow/deny extension script injection).
33 // Testing real injection is allowed/denied as expected (i.e., that the
34 // ActiveScriptController correctly interfaces in the system) is done in the
35 // ActiveScriptControllerBrowserTests.
36 class ActiveScriptControllerUnitTest : public ChromeRenderViewHostTestHarness {
37 protected:
38 ActiveScriptControllerUnitTest();
39 virtual ~ActiveScriptControllerUnitTest();
40
41 // Creates an extension with all hosts permission and adds it to the registry.
42 const Extension* AddExtension();
43
44 // Returns the current page id.
45 int GetPageId();
46
47 // Returns a closure to use as a script execution for a given extension.
48 base::Closure GetExecutionCallbackForExtension(
49 const std::string& extension_id);
50
51 // Returns the number of times a given extension has had a script execute.
52 size_t GetExecutionCountForExtension(const std::string& extension_id) const;
53
54 ActiveScriptController* controller() { return active_script_controller_; }
55
56 private:
57 // Increment the number of executions for the given |extension_id|.
58 void IncrementExecutionCount(const std::string& extension_id);
59
60 virtual void SetUp() OVERRIDE;
61
62 // Since ActiveScriptController's behavior is behind a flag, override the
63 // feature switch.
64 FeatureSwitch::ScopedOverride feature_override_;
65
66 // The associated ActiveScriptController.
67 ActiveScriptController* active_script_controller_;
68
69 // The map of observed executions, keyed by extension id.
70 std::map<std::string, int> extension_executions_;
71 };
72
73 ActiveScriptControllerUnitTest::ActiveScriptControllerUnitTest()
74 : feature_override_(FeatureSwitch::scripts_require_action(),
75 FeatureSwitch::OVERRIDE_ENABLED),
76 active_script_controller_(NULL) {
77 }
78
79 ActiveScriptControllerUnitTest::~ActiveScriptControllerUnitTest() {
80 }
81
82 const Extension* ActiveScriptControllerUnitTest::AddExtension() {
83 static const char kId[] = "all_hosts_extension";
84 scoped_refptr<const Extension> extension =
85 ExtensionBuilder()
86 .SetManifest(
87 DictionaryBuilder()
88 .Set("name", "all_hosts_extension")
89 .Set("description", "an extension")
90 .Set("manifest_version", 2)
91 .Set("version", "1.0.0")
92 .Set("permissions",
93 ListBuilder().Append(kAllHostsPermission)))
94 .SetLocation(Manifest::INTERNAL)
95 .SetID(kId)
96 .Build();
97
98 ExtensionRegistry::Get(profile())->AddEnabled(extension);
99 return extension;
100 }
101
102 int ActiveScriptControllerUnitTest::GetPageId() {
103 content::NavigationEntry* navigation_entry =
104 web_contents()->GetController().GetVisibleEntry();
105 DCHECK(navigation_entry); // This should never be NULL.
106 return navigation_entry->GetPageID();
107 }
108
109 base::Closure ActiveScriptControllerUnitTest::GetExecutionCallbackForExtension(
110 const std::string& extension_id) {
111 // We use base unretained here, but if this ever gets executed outside of
112 // this test's lifetime, we have a major problem anyway.
113 return base::Bind(&ActiveScriptControllerUnitTest::IncrementExecutionCount,
114 base::Unretained(this),
115 extension_id);
116 }
117
118 size_t ActiveScriptControllerUnitTest::GetExecutionCountForExtension(
119 const std::string& extension_id) const {
120 std::map<std::string, int>::const_iterator iter =
121 extension_executions_.find(extension_id);
122 if (iter != extension_executions_.end())
123 return iter->second;
124 return 0u;
125 }
126
127 void ActiveScriptControllerUnitTest::IncrementExecutionCount(
128 const std::string& extension_id) {
129 ++extension_executions_[extension_id];
130 }
131
132 void ActiveScriptControllerUnitTest::SetUp() {
133 ChromeRenderViewHostTestHarness::SetUp();
134
135 TabHelper::CreateForWebContents(web_contents());
136 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents());
137 // None of these should ever be NULL.
138 DCHECK(tab_helper);
139 DCHECK(tab_helper->location_bar_controller());
140 active_script_controller_ =
141 tab_helper->location_bar_controller()->active_script_controller();
142 DCHECK(active_script_controller_);
143 }
144
145 // Test that extensions with all_hosts require permission to execute, and, once
146 // that permission is granted, do execute.
147 TEST_F(ActiveScriptControllerUnitTest, RequestPermissionAndExecute) {
148 const Extension* extension = AddExtension();
149 ASSERT_TRUE(extension);
150
151 NavigateAndCommit(GURL("https://www.google.com"));
152
153 // Ensure that there aren't any executions pending.
154 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
155 ASSERT_FALSE(controller()->GetActionForExtension(extension));
156
157 // Since the extension requests all_hosts, we should require user consent.
158 EXPECT_TRUE(
159 controller()->RequiresUserConsentForScriptInjection(extension));
160
161 // Request an injection. There should be an action visible, but no executions.
162 controller()->RequestScriptInjection(
163 extension,
164 GetPageId(),
165 GetExecutionCallbackForExtension(extension->id()));
166 EXPECT_TRUE(controller()->GetActionForExtension(extension));
167 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
168
169 // Click to accept the extension executing.
170 controller()->OnClicked(extension);
171
172 // The extension should execute, and the action should go away.
173 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
174 EXPECT_FALSE(controller()->GetActionForExtension(extension));
175
176 // Since we already executed on the given page, we shouldn't need permission
177 // for a second time.
178 EXPECT_FALSE(
179 controller()->RequiresUserConsentForScriptInjection(extension));
180
181 // Reloading should clear those permissions, and we should again require user
182 // consent.
183 Reload();
184 EXPECT_TRUE(
185 controller()->RequiresUserConsentForScriptInjection(extension));
186
187 // Grant access.
188 controller()->RequestScriptInjection(
189 extension,
190 GetPageId(),
191 GetExecutionCallbackForExtension(extension->id()));
192 controller()->OnClicked(extension);
193 EXPECT_EQ(2u, GetExecutionCountForExtension(extension->id()));
194 EXPECT_FALSE(controller()->GetActionForExtension(extension));
195
196 // Navigating to another site should also clear the permissions.
197 NavigateAndCommit(GURL("https://www.foo.com"));
198 EXPECT_TRUE(
199 controller()->RequiresUserConsentForScriptInjection(extension));
200 }
201
202 // Test that injections that are not executed by the time the user navigates are
203 // ignored and never execute.
204 TEST_F(ActiveScriptControllerUnitTest, PendingInjectionsRemovedAtNavigation) {
205 const Extension* extension = AddExtension();
206 ASSERT_TRUE(extension);
207
208 NavigateAndCommit(GURL("https://www.google.com"));
209
210 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
211
212 // Request an injection. There should be an action visible, but no executions.
213 controller()->RequestScriptInjection(
214 extension,
215 GetPageId(),
216 GetExecutionCallbackForExtension(extension->id()));
217 EXPECT_TRUE(controller()->GetActionForExtension(extension));
218 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
219
220 // Navigate away. This should remove the pending injection, and we should not
221 // execute anything.
222 NavigateAndCommit(GURL("https://www.google.com"));
223 EXPECT_FALSE(controller()->GetActionForExtension(extension));
224 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
225
226 // Request and accept a new injection.
227 controller()->RequestScriptInjection(
228 extension,
229 GetPageId(),
230 GetExecutionCallbackForExtension(extension->id()));
231 controller()->OnClicked(extension);
232
233 // The extension should only have executed once, even though a grand total
234 // of two executions were requested.
235 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
236 EXPECT_FALSE(controller()->GetActionForExtension(extension));
237 }
238
239 // Test that queueing multiple pending injections, and then accepting, triggers
240 // them all.
241 TEST_F(ActiveScriptControllerUnitTest, MultiplePendingInjection) {
242 const Extension* extension = AddExtension();
243 ASSERT_TRUE(extension);
244 NavigateAndCommit(GURL("https://www.google.com"));
245
246 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
247
248 const size_t kNumInjections = 3u;
249 // Queue multiple pending injections.
250 for (size_t i = 0u; i < kNumInjections; ++i) {
251 controller()->RequestScriptInjection(
252 extension,
253 GetPageId(),
254 GetExecutionCallbackForExtension(extension->id()));
255 }
256 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
257
258 controller()->OnClicked(extension);
259
260 // All pending injections should have executed.
261 EXPECT_EQ(kNumInjections, GetExecutionCountForExtension(extension->id()));
262 EXPECT_FALSE(controller()->GetActionForExtension(extension));
263 }
264
265 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsUseActiveTabPermissions) {
266 const Extension* extension = AddExtension();
267 NavigateAndCommit(GURL("https://www.google.com"));
268
269 ActiveTabPermissionGranter* active_tab_permission_granter =
270 TabHelper::FromWebContents(web_contents())
271 ->active_tab_permission_granter();
272 ASSERT_TRUE(active_tab_permission_granter);
273 // Grant the extension active tab permissions. This normally happens, e.g.,
274 // if the user clicks on a browser action.
275 active_tab_permission_granter->GrantIfRequested(extension);
276
277 // Since we have active tab permissions, we shouldn't need user consent
278 // anymore.
279 EXPECT_FALSE(
280 controller()->RequiresUserConsentForScriptInjection(extension));
281
282 // TODO(rdevlin.cronin): We should also implement/test that granting active
283 // tab permissions automatically runs any pending injections.
284 }
285
286 } // namespace extensions
OLDNEW
« no previous file with comments | « chrome/browser/extensions/active_script_controller.cc ('k') | chrome/browser/extensions/active_tab_permission_granter.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698