Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 |
| OLD | NEW |