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

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

Powered by Google App Engine
This is Rietveld 408576698