OLD | NEW |
---|---|
(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", ListBuilder().Append(kAllHostsPermission)) | |
93 .Build()) | |
not at google - send to devlin
2014/05/21 20:10:20
don't need the Build()
Devlin
2014/05/21 23:16:07
Done.
| |
94 .SetLocation(Manifest::INTERNAL) | |
95 .SetID(kId) | |
96 .Build(); | |
97 | |
98 ExtensionRegistry* registry = ExtensionRegistry::Get(profile()); | |
99 registry->AddEnabled(extension); | |
100 | |
101 // Make sure the extension is correctly added. If not, return NULL. | |
not at google - send to devlin
2014/05/21 20:10:20
how can this possibly fail..? ExtensionRegistry ha
Devlin
2014/05/21 23:16:07
Fair. :)
| |
102 if (!registry->enabled_extensions().GetByID(kId)) | |
103 return NULL; | |
104 | |
105 return extension; | |
106 } | |
107 | |
108 int ActiveScriptControllerUnitTest::GetPageId() { | |
109 content::NavigationEntry* navigation_entry = | |
110 web_contents()->GetController().GetVisibleEntry(); | |
111 DCHECK(navigation_entry); // This should never be NULL. | |
112 return navigation_entry->GetPageID(); | |
113 } | |
114 | |
115 base::Closure ActiveScriptControllerUnitTest::GetExecutionCallbackForExtension( | |
116 const std::string& extension_id) { | |
117 // We use base unretained here, but if this ever gets executed outside of | |
118 // this test's lifetime, we have a major problem anyway. | |
119 return base::Bind(&ActiveScriptControllerUnitTest::IncrementExecutionCount, | |
120 base::Unretained(this), | |
121 extension_id); | |
122 } | |
123 | |
124 size_t ActiveScriptControllerUnitTest::GetExecutionCountForExtension( | |
125 const std::string& extension_id) const { | |
126 std::map<std::string, int>::const_iterator iter = | |
127 extension_executions_.find(extension_id); | |
128 if (iter != extension_executions_.end()) | |
129 return iter->second; | |
130 return 0u; | |
131 } | |
132 | |
133 void ActiveScriptControllerUnitTest::IncrementExecutionCount( | |
134 const std::string& extension_id) { | |
135 std::map<std::string, int>::iterator iter = | |
136 extension_executions_.find(extension_id); | |
137 if (iter != extension_executions_.end()) | |
138 iter->second += 1; | |
139 else | |
140 extension_executions_[extension_id] = 1u; | |
not at google - send to devlin
2014/05/21 20:10:20
numbers are initialised to 0, so you just need "++
Devlin
2014/05/21 23:16:07
Done.
| |
141 } | |
142 | |
143 void ActiveScriptControllerUnitTest::SetUp() { | |
144 ChromeRenderViewHostTestHarness::SetUp(); | |
145 | |
146 TabHelper::CreateForWebContents(web_contents()); | |
147 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents()); | |
148 // None of these should ever be NULL. | |
149 DCHECK(tab_helper); | |
150 DCHECK(tab_helper->location_bar_controller()); | |
151 active_script_controller_ = | |
152 tab_helper->location_bar_controller()->active_script_controller(); | |
153 DCHECK(active_script_controller_); | |
154 } | |
155 | |
156 // Test that extensions with all_hosts require permission to execute, and, once | |
157 // that permission is granted, do execute. | |
158 TEST_F(ActiveScriptControllerUnitTest, RequestPermissionAndExecute) { | |
159 const Extension* extension = AddExtension(); | |
160 ASSERT_TRUE(extension); | |
161 | |
162 NavigateAndCommit(GURL("https://www.google.com")); | |
163 | |
164 // Ensure that there aren't any executions pending. | |
165 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id())); | |
166 ExtensionAction* action = controller()->GetActionForExtension(extension); | |
167 ASSERT_FALSE(action); | |
not at google - send to devlin
2014/05/21 20:10:20
holding onto |action| here looks odd. just ASSERT_
Devlin
2014/05/21 23:16:07
Yeah, noticed that after I had uploaded. Original
| |
168 | |
169 // Since the extension requests all_hosts, we should require user consent. | |
170 EXPECT_TRUE( | |
171 controller()->RequiresUserConsentForScriptInjection(extension)); | |
172 | |
173 // Request an injection. There should be an action visible, but no executions. | |
174 controller()->RequestScriptInjection( | |
175 extension, | |
176 GetPageId(), | |
177 GetExecutionCallbackForExtension(extension->id())); | |
178 action = controller()->GetActionForExtension(extension); | |
179 EXPECT_TRUE(action); | |
180 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id())); | |
181 | |
182 // Click to accept the extension executing. | |
183 controller()->OnClicked(extension); | |
184 | |
185 // The extension should execute, and the action should go away. | |
186 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id())); | |
187 action = controller()->GetActionForExtension(extension); | |
188 EXPECT_FALSE(action); | |
189 | |
190 // Since we already executed on the given page, we shouldn't need permission | |
191 // for a second time. | |
192 EXPECT_FALSE( | |
193 controller()->RequiresUserConsentForScriptInjection(extension)); | |
194 | |
195 // Navigating again should clear those permissions, and we should again | |
196 // require user consent. | |
197 NavigateAndCommit(GURL("https://www.google.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 ExtensionAction* action = controller()->GetActionForExtension(extension); | |
218 EXPECT_TRUE(action); | |
219 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id())); | |
220 | |
221 // Navigate away. This should remove the pending injection, and we should not | |
222 // execute anything. | |
223 NavigateAndCommit(GURL("https://www.google.com")); | |
not at google - send to devlin
2014/05/21 20:10:20
you could also Reload() if that's any easier.
wou
Devlin
2014/05/21 23:16:07
Done.
| |
224 action = controller()->GetActionForExtension(extension); | |
225 EXPECT_FALSE(action); | |
226 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id())); | |
227 | |
228 // Request and accept a new injection. | |
229 controller()->RequestScriptInjection( | |
230 extension, | |
231 GetPageId(), | |
232 GetExecutionCallbackForExtension(extension->id())); | |
233 controller()->OnClicked(extension); | |
234 | |
235 // The extension should only have executed once, even though a grand total | |
236 // of two executions were requested. | |
237 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id())); | |
238 action = controller()->GetActionForExtension(extension); | |
239 EXPECT_FALSE(action); | |
240 } | |
241 | |
242 // Test that queueing multiple pending injections, and then accepting, triggers | |
243 // them all. | |
244 TEST_F(ActiveScriptControllerUnitTest, MultiplePendingInjection) { | |
245 const Extension* extension = AddExtension(); | |
246 ASSERT_TRUE(extension); | |
247 NavigateAndCommit(GURL("https://www.google.com")); | |
248 | |
249 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id())); | |
250 | |
251 const size_t kNumInjections = 3u; | |
252 // Queue multiple pending injections. | |
253 for (size_t i = 0u; i < kNumInjections; ++i) { | |
254 controller()->RequestScriptInjection( | |
255 extension, | |
256 GetPageId(), | |
257 GetExecutionCallbackForExtension(extension->id())); | |
258 } | |
259 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id())); | |
260 | |
261 controller()->OnClicked(extension); | |
262 | |
263 // All pending injections should have executed. | |
264 EXPECT_EQ(3u, GetExecutionCountForExtension(extension->id())); | |
not at google - send to devlin
2014/05/21 20:10:20
kNumInjections?
Devlin
2014/05/21 23:16:07
heh, whoops. Done.
| |
265 ExtensionAction* action = controller()->GetActionForExtension(extension); | |
266 EXPECT_FALSE(action); | |
267 } | |
268 | |
269 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsUseActiveTabPermissions) { | |
270 const Extension* extension = AddExtension(); | |
271 NavigateAndCommit(GURL("https://www.google.com")); | |
272 | |
273 ActiveTabPermissionGranter* active_tab_permission_granter = | |
274 TabHelper::FromWebContents(web_contents()) | |
275 ->active_tab_permission_granter(); | |
276 ASSERT_TRUE(active_tab_permission_granter); | |
277 // Grant the extension active tab permissions. This normally happens, e.g., | |
278 // if the user clicks on a browser action. | |
279 active_tab_permission_granter->GrantIfRequested(extension); | |
280 | |
281 // Since we have active tab permissions, we shouldn't need user consent | |
282 // anymore. | |
283 EXPECT_FALSE( | |
284 controller()->RequiresUserConsentForScriptInjection(extension)); | |
285 | |
286 // TODO(rdevlin.cronin): We should also implement/test that granting active | |
287 // tab permissions automatically runs any pending injections. | |
288 } | |
289 | |
290 } // namespace extensions | |
OLD | NEW |