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

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

Issue 286003004: Block tabs.executeScript() from executing until user grants permission (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/files/file_path.h"
5 #include "base/macros.h" 6 #include "base/macros.h"
7 #include "base/strings/stringprintf.h"
6 #include "chrome/browser/extensions/active_script_controller.h" 8 #include "chrome/browser/extensions/active_script_controller.h"
7 #include "chrome/browser/extensions/extension_action.h" 9 #include "chrome/browser/extensions/extension_action.h"
8 #include "chrome/browser/extensions/extension_browsertest.h" 10 #include "chrome/browser/extensions/extension_browsertest.h"
9 #include "chrome/browser/extensions/extension_test_message_listener.h" 11 #include "chrome/browser/extensions/extension_test_message_listener.h"
10 #include "chrome/browser/extensions/location_bar_controller.h" 12 #include "chrome/browser/extensions/location_bar_controller.h"
11 #include "chrome/browser/extensions/tab_helper.h" 13 #include "chrome/browser/extensions/tab_helper.h"
14 #include "chrome/browser/extensions/test_extension_dir.h"
12 #include "chrome/browser/ui/browser.h" 15 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model.h" 16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
14 #include "chrome/test/base/ui_test_utils.h" 17 #include "chrome/test/base/ui_test_utils.h"
15 #include "extensions/common/feature_switch.h" 18 #include "extensions/common/feature_switch.h"
16 #include "net/test/embedded_test_server/embedded_test_server.h" 19 #include "net/test/embedded_test_server/embedded_test_server.h"
17 #include "testing/gtest/include/gtest/gtest.h" 20 #include "testing/gtest/include/gtest/gtest.h"
18 21
19 namespace extensions { 22 namespace extensions {
20 23
24 namespace {
25
26 const char kAllHostsScheme[] = "*://*/*";
27 const char kExplicitHostsScheme[] = "http://127.0.0.1/*";
28 const char kBackgroundScript[] =
29 "\"background\": {\"scripts\": [\"script.js\"]}";
30 const char kBackgroundScriptSource[] =
31 "var listener = function(tabId) {\n"
32 " chrome.tabs.onUpdated.removeListener(listener);\n"
33 " chrome.tabs.executeScript(tabId, {\n"
34 " code: \"chrome.test.sendMessage('inject succeeded');\"\n"
35 " });"
36 " chrome.test.sendMessage('inject attempted');\n"
37 "};\n"
38 "chrome.tabs.onUpdated.addListener(listener);";
39 const char kContentScriptSource[] =
40 "chrome.test.sendMessage('inject succeeded');";
41
42 const char kInjectAttempted[] = "inject attempted";
43 const char kInjectSucceeded[] = "inject succeeded";
44
45 enum InjectionType {
46 CONTENT_SCRIPT,
47 EXECUTE_SCRIPT
48 };
49
50 enum HostType {
51 ALL_HOSTS,
52 EXPLICIT_HOSTS
53 };
54
55 enum RequiresConsent {
56 REQUIRES_CONSENT,
57 DOES_NOT_REQUIRE_CONSENT
58 };
59
60 } // namespace
61
21 class ActiveScriptControllerBrowserTest : public ExtensionBrowserTest { 62 class ActiveScriptControllerBrowserTest : public ExtensionBrowserTest {
22 public: 63 public:
23 ActiveScriptControllerBrowserTest() 64 ActiveScriptControllerBrowserTest()
24 : feature_override_(FeatureSwitch::scripts_require_action(), 65 : feature_override_(FeatureSwitch::scripts_require_action(),
25 FeatureSwitch::OVERRIDE_ENABLED) {} 66 FeatureSwitch::OVERRIDE_ENABLED) {}
67
68 virtual void CleanUpOnMainThread() OVERRIDE;
69
70 const Extension* GetExtension(HostType host_type,
71 InjectionType injection_type);
72
26 private: 73 private:
27 FeatureSwitch::ScopedOverride feature_override_; 74 FeatureSwitch::ScopedOverride feature_override_;
28 }; 75 ScopedVector<TestExtensionDir> test_extension_dirs_;
76 std::vector<const Extension*> extensions_;
77 };
78
79 void ActiveScriptControllerBrowserTest::CleanUpOnMainThread() {
80 test_extension_dirs_.clear();
81 }
82
83 const Extension* ActiveScriptControllerBrowserTest::GetExtension(
not at google - send to devlin 2014/05/16 00:08:33 CreateExtension
Devlin 2014/05/16 16:13:46 GetOrCreateExtension(), probably, since we can ret
84 HostType host_type, InjectionType injection_type) {
85 std::string name =
86 base::StringPrintf(
87 "%s %s",
88 injection_type == CONTENT_SCRIPT ?
89 "content_script" : "execute_script",
90 host_type == ALL_HOSTS ? "all_hosts" : "explicit_hosts");
91
92 for (std::vector<const Extension*>::const_iterator iter = extensions_.begin();
93 iter != extensions_.end();
94 ++iter) {
95 if ((*iter)->name() == name)
96 return *iter;
97 }
98
99 const char* permission_scheme =
100 host_type == ALL_HOSTS ? kAllHostsScheme : kExplicitHostsScheme;
101
102 std::string permissions = base::StringPrintf(
103 "\"permissions\": [\"tabs\", \"%s\"]", permission_scheme);
104
105 std::string scripts;
106 std::string script_source;
107 if (injection_type == CONTENT_SCRIPT) {
108 scripts = base::StringPrintf(
109 "\"content_scripts\": ["
110 " {"
111 " \"matches\": [\"%s\"],"
112 " \"js\": [\"script.js\"],"
113 " \"run_at\": \"document_start\""
114 " }"
115 "]",
116 permission_scheme);
117 } else {
118 scripts = kBackgroundScript;
119 }
120
121 std::string manifest = base::StringPrintf(
122 "{"
123 " \"name\": \"%s\","
124 " \"version\": \"1.0\","
125 " \"manifest_version\": 2,"
126 " %s,"
127 " %s"
128 "}",
129 name.c_str(),
130 permissions.c_str(),
131 scripts.c_str());
132
133 scoped_ptr<TestExtensionDir> dir(new TestExtensionDir);
134 dir->WriteManifest(manifest);
135 dir->WriteFile(FILE_PATH_LITERAL("script.js"),
136 injection_type == CONTENT_SCRIPT ? kContentScriptSource :
137 kBackgroundScriptSource);
138
139 const Extension* extension = LoadExtension(dir->unpacked_path());
140 if (extension) {
141 test_extension_dirs_.push_back(dir.release());
142 extensions_.push_back(extension);
143 }
144
145 // If extension is NULL here, it will be caught later in the test.
146 return extension;
147 }
29 148
30 class ActiveScriptTester { 149 class ActiveScriptTester {
31 public: 150 public:
32 ActiveScriptTester(const std::string& name, 151 ActiveScriptTester(const std::string& name,
33 const Extension* extension, 152 const Extension* extension,
34 bool expect_has_action); 153 Browser* browser,
154 RequiresConsent requires_consent,
155 InjectionType type);
35 ~ActiveScriptTester(); 156 ~ActiveScriptTester();
36 157
37 testing::AssertionResult Verify(Browser* browser); 158 testing::AssertionResult Verify();
38 159
39 private: 160 private:
161 // Returns the location bar controller, or NULL if one does not exist.
162 LocationBarController* GetLocationBarController();
163
164 // Returns the active script controller, or NULL if one does not exist.
165 ActiveScriptController* GetActiveScriptController();
166
167 // Get the ExtensionAction for this extension, or NULL if one does not exist.
168 ExtensionAction* GetAction();
169
40 // The name of the extension, and also the message it sends. 170 // The name of the extension, and also the message it sends.
41 std::string name_; 171 std::string name_;
42 172
43 // The extension associated with this tester. 173 // The extension associated with this tester.
44 const Extension* extension_; 174 const Extension* extension_;
45 175
46 // Whether or not we expect the extension to have an action for script 176 // The browser the tester is running in.
47 // injection. 177 Browser* browser_;
48 bool expect_has_action_; 178
179 // Whether or not the extension has permission to run the script without
180 // asking the user.
181 RequiresConsent requires_consent_;
182
183 // The type of injection this tester uses.
184 InjectionType type_;
49 185
50 // All of these extensions should inject a script (either through content 186 // All of these extensions should inject a script (either through content
51 // scripts or through chrome.tabs.executeScript()) that sends a message with 187 // scripts or through chrome.tabs.executeScript()) that sends a message with
52 // their names (for verification). 188 // the |kInjectSucceeded| message.
53 // Be prepared to wait for each extension to inject the script. 189 linked_ptr<ExtensionTestMessageListener> inject_attempt_listener_;
54 linked_ptr<ExtensionTestMessageListener> listener_; 190
191 // After trying to inject the script, extensions sending the script via
192 // chrome.tabs.executeScript() send a |kInjectAttempted| message.
193 linked_ptr<ExtensionTestMessageListener> inject_success_listener_;
55 }; 194 };
56 195
57 ActiveScriptTester::ActiveScriptTester(const std::string& name, 196 ActiveScriptTester::ActiveScriptTester(const std::string& name,
58 const Extension* extension, 197 const Extension* extension,
59 bool expect_has_action) 198 Browser* browser,
199 RequiresConsent requires_consent,
200 InjectionType type)
60 : name_(name), 201 : name_(name),
61 extension_(extension), 202 extension_(extension),
62 expect_has_action_(expect_has_action), 203 browser_(browser),
63 listener_( 204 requires_consent_(requires_consent),
64 new ExtensionTestMessageListener(name, false /* won't reply */)) { 205 type_(type),
206 inject_attempt_listener_(
207 new ExtensionTestMessageListener(kInjectAttempted,
208 false /* won't reply */)),
209 inject_success_listener_(
210 new ExtensionTestMessageListener(kInjectSucceeded,
211 false /* won't reply */)) {
212 inject_attempt_listener_->set_extension_id(extension->id());
213 inject_success_listener_->set_extension_id(extension->id());
65 } 214 }
66 215
67 ActiveScriptTester::~ActiveScriptTester() { 216 ActiveScriptTester::~ActiveScriptTester() {
68 } 217 }
69 218
70 testing::AssertionResult ActiveScriptTester::Verify(Browser* browser) { 219 testing::AssertionResult ActiveScriptTester::Verify() {
71 if (!extension_) 220 if (!extension_)
72 return testing::AssertionFailure() << "Could not load extension: " << name_; 221 return testing::AssertionFailure() << "Could not load extension: " << name_;
73 222
74 listener_->WaitUntilSatisfied(); 223 // If it's not a content script, the Extension lets us know when it has
75 content::WebContents* web_contents = 224 // attempted to inject the script.
76 browser ? browser->tab_strip_model()->GetActiveWebContents() : NULL; 225 // This means there is a potential for a race condition with content scripts;
77 226 // however, since they are all injected at document_start, this shouldn't be
78 if (!web_contents) 227 // a problem. If these tests start failing, though, that might be it.
79 return testing::AssertionFailure() << "Could not find web contents."; 228 if (type_ == EXECUTE_SCRIPT)
80 229 inject_attempt_listener_->WaitUntilSatisfied();
81 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); 230
82 if (!tab_helper) 231 // Make sure all running tasks are complete.
83 return testing::AssertionFailure() << "Could not find tab helper."; 232 content::RunAllPendingInMessageLoop();
233
234 LocationBarController* location_bar_controller = GetLocationBarController();
235 if (!location_bar_controller) {
236 return testing::AssertionFailure()
237 << "Could not find location bar controller";
238 }
84 239
85 ActiveScriptController* controller = 240 ActiveScriptController* controller =
86 tab_helper->location_bar_controller()->active_script_controller(); 241 location_bar_controller->active_script_controller();
87 if (!controller) 242 if (!controller)
88 return testing::AssertionFailure() << "Could not find controller."; 243 return testing::AssertionFailure() << "Could not find controller.";
89 244
90 bool has_action = controller->GetActionForExtension(extension_) != NULL; 245 ExtensionAction* action = GetAction();
91 if (expect_has_action_ != has_action) { 246 bool has_action = action != NULL;
247
248 // An extension should have an action displayed iff it requires user consent.
249 if ((requires_consent_ == REQUIRES_CONSENT && !has_action) ||
250 (requires_consent_ == DOES_NOT_REQUIRE_CONSENT && has_action)) {
92 return testing::AssertionFailure() 251 return testing::AssertionFailure()
93 << "Improper action status: expected " << expect_has_action_ 252 << "Improper action status for " << name_ << ": expected "
94 << ", found " << has_action; 253 << (requires_consent_ == REQUIRES_CONSENT) << ", found " << has_action;
254 }
255
256 // If the extension has permission, we should be able to simply wait for it
257 // to execute.
258 if (requires_consent_ == DOES_NOT_REQUIRE_CONSENT) {
259 inject_success_listener_->WaitUntilSatisfied();
260 return testing::AssertionSuccess();
261 }
262
263 // Otherwise, we don't have permission, and have to grant it. Ensure the
264 // script has *not* already executed.
265 // Currently, it's okay for content scripts to execute, because we don't
266 // block them.
267 // TODO(rdevlin.cronin): Fix this.
268 if (inject_success_listener_->was_satisfied() && type_ != CONTENT_SCRIPT) {
269 return testing::AssertionFailure() <<
270 name_ << "'s script ran without permission.";
271 }
272
273 // If we reach this point, we should always have an action.
274 DCHECK(action);
275
276 // Grant permission by clicking on the extension action.
277 location_bar_controller->OnClicked(action);
278
279 // Now, the extension should be able to inject the script.
280 inject_success_listener_->WaitUntilSatisfied();
281
282 // The Action should have disappeared.
283 has_action = GetAction() != NULL;
284 if (has_action) {
285 return testing::AssertionFailure()
286 << "Extension " << name_ << " has lingering action.";
95 } 287 }
96 288
97 return testing::AssertionSuccess(); 289 return testing::AssertionSuccess();
98 } 290 }
99 291
292 LocationBarController* ActiveScriptTester::GetLocationBarController() {
293 content::WebContents* web_contents =
294 browser_ ? browser_->tab_strip_model()->GetActiveWebContents() : NULL;
295
296 if (!web_contents)
297 return NULL;
298
299 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents);
300 return tab_helper ? tab_helper->location_bar_controller() : NULL;
301 }
302
303 ActiveScriptController* ActiveScriptTester::GetActiveScriptController() {
304 LocationBarController* location_bar_controller = GetLocationBarController();
305 return location_bar_controller ?
306 location_bar_controller->active_script_controller() : NULL;
307 }
308
309 ExtensionAction* ActiveScriptTester::GetAction() {
310 ActiveScriptController* controller = GetActiveScriptController();
311 return controller ? controller->GetActionForExtension(extension_) : NULL;
312 }
313
100 IN_PROC_BROWSER_TEST_F(ActiveScriptControllerBrowserTest, 314 IN_PROC_BROWSER_TEST_F(ActiveScriptControllerBrowserTest,
101 ActiveScriptsAreDisplayed) { 315 ActiveScriptsAreDisplayed) {
102 base::FilePath active_script_path = 316 base::FilePath active_script_path =
103 test_data_dir_.AppendASCII("active_script"); 317 test_data_dir_.AppendASCII("active_script");
104 318
105 const char* kExtensionNames[] = { 319 const char* kExtensionNames[] = {
320 "inject_scripts_all_hosts",
321 "inject_scripts_explicit_hosts",
106 "content_scripts_all_hosts", 322 "content_scripts_all_hosts",
107 "inject_scripts_all_hosts",
108 "content_scripts_explicit_hosts" 323 "content_scripts_explicit_hosts"
109 }; 324 };
110 325
111 // First, we load up three extensions: 326 // First, we load up three extensions:
327 // - An extension that injects scripts into all hosts,
328 // - An extension that injects scripts into explicit hosts,
112 // - An extension with a content script that runs on all hosts, 329 // - An extension with a content script that runs on all hosts,
113 // - An extension that injects scripts into all hosts,
114 // - An extension with a content script that runs on explicit hosts. 330 // - An extension with a content script that runs on explicit hosts.
331 // The extensions that operate on explicit hosts have permission; the ones
332 // that request all hosts require user consent.
115 ActiveScriptTester testers[] = { 333 ActiveScriptTester testers[] = {
116 ActiveScriptTester( 334 ActiveScriptTester(
117 kExtensionNames[0], 335 kExtensionNames[0],
118 LoadExtension(active_script_path.AppendASCII(kExtensionNames[0])), 336 GetExtension(ALL_HOSTS, EXECUTE_SCRIPT),
119 true /* expect action */), 337 browser(),
338 REQUIRES_CONSENT,
339 EXECUTE_SCRIPT),
120 ActiveScriptTester( 340 ActiveScriptTester(
121 kExtensionNames[1], 341 kExtensionNames[1],
122 LoadExtension(active_script_path.AppendASCII(kExtensionNames[1])), 342 GetExtension(EXPLICIT_HOSTS, EXECUTE_SCRIPT),
123 true /* expect action */), 343 browser(),
344 DOES_NOT_REQUIRE_CONSENT,
345 EXECUTE_SCRIPT),
124 ActiveScriptTester( 346 ActiveScriptTester(
125 kExtensionNames[2], 347 kExtensionNames[2],
126 LoadExtension(active_script_path.AppendASCII(kExtensionNames[2])), 348 GetExtension(ALL_HOSTS, CONTENT_SCRIPT),
127 false /* expect action */) 349 browser(),
350 REQUIRES_CONSENT,
351 CONTENT_SCRIPT),
352 ActiveScriptTester(
353 kExtensionNames[3],
354 GetExtension(EXPLICIT_HOSTS, CONTENT_SCRIPT),
355 browser(),
356 DOES_NOT_REQUIRE_CONSENT,
357 CONTENT_SCRIPT),
not at google - send to devlin 2014/05/16 00:08:33 I'm not too worried about over-engineering this (y
128 }; 358 };
129 359
130 // Navigate to an URL (which matches the explicit host specified in the 360 // Navigate to an URL (which matches the explicit host specified in the
131 // extension content_scripts_explicit_hosts). All three extensions should 361 // extension content_scripts_explicit_hosts). All three extensions should
132 // inject the script. 362 // inject the script.
133 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 363 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
134 ui_test_utils::NavigateToURL( 364 ui_test_utils::NavigateToURL(
135 browser(), embedded_test_server()->GetURL("/extensions/test_file.html")); 365 browser(), embedded_test_server()->GetURL("/extensions/test_file.html"));
136 366
137 for (size_t i = 0u; i < arraysize(testers); ++i) 367 for (size_t i = 0u; i < arraysize(testers); ++i)
138 ASSERT_TRUE(testers[i].Verify(browser())) << kExtensionNames[i]; 368 EXPECT_TRUE(testers[i].Verify()) << kExtensionNames[i];
139 } 369 }
140 370
141 } // namespace extensions 371 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698