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/macros.h" | 5 #include "base/macros.h" |
| 6 #include "chrome/browser/extensions/active_script_controller.h" | 6 #include "chrome/browser/extensions/active_script_controller.h" |
| 7 #include "chrome/browser/extensions/extension_action.h" | 7 #include "chrome/browser/extensions/extension_action.h" |
| 8 #include "chrome/browser/extensions/extension_browsertest.h" | 8 #include "chrome/browser/extensions/extension_browsertest.h" |
| 9 #include "chrome/browser/extensions/extension_test_message_listener.h" | 9 #include "chrome/browser/extensions/extension_test_message_listener.h" |
| 10 #include "chrome/browser/extensions/location_bar_controller.h" | 10 #include "chrome/browser/extensions/location_bar_controller.h" |
| 11 #include "chrome/browser/extensions/tab_helper.h" | 11 #include "chrome/browser/extensions/tab_helper.h" |
| 12 #include "chrome/browser/ui/browser.h" | 12 #include "chrome/browser/ui/browser.h" |
| 13 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 13 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 14 #include "chrome/test/base/ui_test_utils.h" | 14 #include "chrome/test/base/ui_test_utils.h" |
| 15 #include "extensions/common/feature_switch.h" | 15 #include "extensions/common/feature_switch.h" |
| 16 #include "net/test/embedded_test_server/embedded_test_server.h" | 16 #include "net/test/embedded_test_server/embedded_test_server.h" |
| 17 #include "testing/gtest/include/gtest/gtest.h" | 17 #include "testing/gtest/include/gtest/gtest.h" |
| 18 | 18 |
| 19 namespace extensions { | 19 namespace extensions { |
| 20 | 20 |
| 21 namespace { | |
| 22 | |
| 23 const char kInjectAttempted[] = "inject attempted"; | |
| 24 const char kInjectSucceeded[] = "inject succeeded"; | |
| 25 | |
| 26 enum InjectionType { | |
| 27 CONTENT_SCRIPT, | |
| 28 EXECUTE_SCRIPT | |
| 29 }; | |
| 30 | |
| 31 } // namespace | |
| 32 | |
| 21 class ActiveScriptControllerBrowserTest : public ExtensionBrowserTest { | 33 class ActiveScriptControllerBrowserTest : public ExtensionBrowserTest { |
| 22 public: | 34 public: |
| 23 ActiveScriptControllerBrowserTest() | 35 ActiveScriptControllerBrowserTest() |
| 24 : feature_override_(FeatureSwitch::scripts_require_action(), | 36 : feature_override_(FeatureSwitch::scripts_require_action(), |
| 25 FeatureSwitch::OVERRIDE_ENABLED) {} | 37 FeatureSwitch::OVERRIDE_ENABLED) {} |
| 26 private: | 38 private: |
| 27 FeatureSwitch::ScopedOverride feature_override_; | 39 FeatureSwitch::ScopedOverride feature_override_; |
| 28 }; | 40 }; |
| 29 | 41 |
| 30 class ActiveScriptTester { | 42 class ActiveScriptTester { |
| 31 public: | 43 public: |
| 32 ActiveScriptTester(const std::string& name, | 44 ActiveScriptTester(const std::string& name, |
| 33 const Extension* extension, | 45 const Extension* extension, |
| 34 bool expect_has_action); | 46 bool requires_consent, |
| 47 InjectionType type); | |
| 35 ~ActiveScriptTester(); | 48 ~ActiveScriptTester(); |
| 36 | 49 |
| 37 testing::AssertionResult Verify(Browser* browser); | 50 testing::AssertionResult Verify(Browser* browser); |
| 38 | 51 |
| 39 private: | 52 private: |
| 40 // The name of the extension, and also the message it sends. | 53 // The name of the extension, and also the message it sends. |
| 41 std::string name_; | 54 std::string name_; |
| 42 | 55 |
| 43 // The extension associated with this tester. | 56 // The extension associated with this tester. |
| 44 const Extension* extension_; | 57 const Extension* extension_; |
| 45 | 58 |
| 46 // Whether or not we expect the extension to have an action for script | 59 // Whether or not the extension has permission to run the script without |
| 47 // injection. | 60 // asking the user. |
| 48 bool expect_has_action_; | 61 bool requires_consent_; |
| 62 | |
| 63 // The type of injection this tester uses. | |
| 64 InjectionType type_; | |
| 49 | 65 |
| 50 // All of these extensions should inject a script (either through content | 66 // All of these extensions should inject a script (either through content |
| 51 // scripts or through chrome.tabs.executeScript()) that sends a message with | 67 // scripts or through chrome.tabs.executeScript()) that sends a message with |
| 52 // their names (for verification). | 68 // the |kInjectSucceeded| message. |
| 53 // Be prepared to wait for each extension to inject the script. | 69 linked_ptr<ExtensionTestMessageListener> inject_attempt_listener_; |
| 54 linked_ptr<ExtensionTestMessageListener> listener_; | 70 |
| 71 // After trying to inject the script, extensions sending the script via | |
| 72 // chrome.tabs.executeScript() send a |kInjectAttempted| message. | |
| 73 linked_ptr<ExtensionTestMessageListener> inject_success_listener_; | |
| 55 }; | 74 }; |
| 56 | 75 |
| 57 ActiveScriptTester::ActiveScriptTester(const std::string& name, | 76 ActiveScriptTester::ActiveScriptTester(const std::string& name, |
| 58 const Extension* extension, | 77 const Extension* extension, |
| 59 bool expect_has_action) | 78 bool requires_consent, |
| 79 InjectionType type) | |
| 60 : name_(name), | 80 : name_(name), |
| 61 extension_(extension), | 81 extension_(extension), |
| 62 expect_has_action_(expect_has_action), | 82 requires_consent_(requires_consent), |
| 63 listener_( | 83 type_(type), |
| 64 new ExtensionTestMessageListener(name, false /* won't reply */)) { | 84 inject_attempt_listener_( |
| 85 new ExtensionTestMessageListener(kInjectAttempted, | |
| 86 false /* won't reply */)), | |
| 87 inject_success_listener_( | |
| 88 new ExtensionTestMessageListener(kInjectSucceeded, | |
| 89 false /* won't reply */)) { | |
| 90 inject_attempt_listener_->set_extension_id(extension->id()); | |
| 91 inject_success_listener_->set_extension_id(extension->id()); | |
| 65 } | 92 } |
| 66 | 93 |
| 67 ActiveScriptTester::~ActiveScriptTester() { | 94 ActiveScriptTester::~ActiveScriptTester() { |
| 68 } | 95 } |
| 69 | 96 |
| 70 testing::AssertionResult ActiveScriptTester::Verify(Browser* browser) { | 97 testing::AssertionResult ActiveScriptTester::Verify(Browser* browser) { |
| 71 if (!extension_) | 98 if (!extension_) |
| 72 return testing::AssertionFailure() << "Could not load extension: " << name_; | 99 return testing::AssertionFailure() << "Could not load extension: " << name_; |
| 73 | 100 |
| 74 listener_->WaitUntilSatisfied(); | 101 // If it's not a content script, the Extension lets us know when it has |
| 102 // attempted to inject the script. | |
| 103 // This means there is a potential for a race condition with content scripts; | |
| 104 // however, since they are all injected at document_start, this shouldn't be | |
|
not at google - send to devlin
2014/05/15 00:12:36
document_start will be an issue actually. if you r
Devlin
2014/05/15 17:45:59
I think this one deserves a bit more thought.
So,
not at google - send to devlin
2014/05/15 18:26:55
I agree, it requires thought, and it's complex eno
| |
| 105 // a problem. If these tests start failing, though, that might be it. | |
| 106 if (type_ == EXECUTE_SCRIPT) | |
| 107 inject_attempt_listener_->WaitUntilSatisfied(); | |
| 108 | |
| 109 // Make sure all running tasks are complete. | |
|
not at google - send to devlin
2014/05/15 00:12:36
what tasks? the active script controller ones?
Devlin
2014/05/15 17:45:59
Yeah. Just making sure that if anything is runnin
| |
| 110 content::RunAllPendingInMessageLoop(); | |
| 111 | |
| 75 content::WebContents* web_contents = | 112 content::WebContents* web_contents = |
| 76 browser ? browser->tab_strip_model()->GetActiveWebContents() : NULL; | 113 browser ? browser->tab_strip_model()->GetActiveWebContents() : NULL; |
| 77 | 114 |
| 78 if (!web_contents) | 115 if (!web_contents) |
| 79 return testing::AssertionFailure() << "Could not find web contents."; | 116 return testing::AssertionFailure() << "Could not find web contents."; |
| 80 | 117 |
| 81 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); | 118 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); |
| 82 if (!tab_helper) | 119 if (!tab_helper) |
| 83 return testing::AssertionFailure() << "Could not find tab helper."; | 120 return testing::AssertionFailure() << "Could not find tab helper."; |
| 84 | 121 |
| 122 LocationBarController* location_bar_controller = | |
| 123 tab_helper->location_bar_controller(); | |
| 124 if (!location_bar_controller) { | |
| 125 return testing::AssertionFailure() | |
| 126 << "Could not find location bar controller"; | |
| 127 } | |
| 128 | |
| 85 ActiveScriptController* controller = | 129 ActiveScriptController* controller = |
| 86 tab_helper->location_bar_controller()->active_script_controller(); | 130 tab_helper->location_bar_controller()->active_script_controller(); |
|
not at google - send to devlin
2014/05/15 00:12:36
s/tab_helper->location_bar_controller()/location_b
Devlin
2014/05/15 17:45:59
Whoops, done.
| |
| 87 if (!controller) | 131 if (!controller) |
| 88 return testing::AssertionFailure() << "Could not find controller."; | 132 return testing::AssertionFailure() << "Could not find controller."; |
| 89 | 133 |
| 90 bool has_action = controller->GetActionForExtension(extension_) != NULL; | 134 bool has_action = controller->GetActionForExtension(extension_) != NULL; |
| 91 if (expect_has_action_ != has_action) { | 135 |
| 136 // An extension should have an action displayed iff it requires user consent. | |
| 137 if (requires_consent_ != has_action) { | |
| 92 return testing::AssertionFailure() | 138 return testing::AssertionFailure() |
| 93 << "Improper action status: expected " << expect_has_action_ | 139 << "Improper action status for " << name_ << ": expected " |
| 94 << ", found " << has_action; | 140 << requires_consent_ << ", found " << has_action; |
| 141 } | |
| 142 | |
| 143 // If the extension has permission, we should be able to simply wait for it | |
| 144 // to execute. | |
| 145 if (!requires_consent_) { | |
| 146 inject_success_listener_->WaitUntilSatisfied(); | |
| 147 return testing::AssertionSuccess(); | |
| 148 } | |
| 149 | |
| 150 // Otherwise, we don't have permission, and have to grant it. Ensure the | |
| 151 // script has *not* already executed. | |
| 152 // Currently, it's okay for content scripts to execute, because we don't | |
| 153 // block them. | |
| 154 // TODO(rdevlin.cronin): Fix this. | |
| 155 if (inject_success_listener_->was_satisfied() && type_ != CONTENT_SCRIPT) { | |
| 156 return testing::AssertionFailure() << | |
| 157 name_ << "'s script ran without permission."; | |
| 158 } | |
| 159 | |
| 160 ExtensionAction* action = controller->GetActionForExtension(extension_); | |
| 161 // We've already verified this exists, so just a sanity check. | |
| 162 DCHECK(action); | |
|
not at google - send to devlin
2014/05/15 00:12:36
in theory the action could have disappeared, like
Devlin
2014/05/15 17:45:59
Actually... it can't.
After line 134 (when we che
| |
| 163 | |
| 164 // Grant permission by clicking on the extension action. | |
| 165 location_bar_controller->OnClicked(action); | |
| 166 | |
| 167 // Now, the extension should be able to inject the script. | |
| 168 inject_success_listener_->WaitUntilSatisfied(); | |
| 169 | |
| 170 // The Action should have disappeared. | |
| 171 has_action = controller->GetActionForExtension(extension_) != NULL; | |
|
not at google - send to devlin
2014/05/15 00:12:36
you do this a couple of times. how about a (privat
Devlin
2014/05/15 17:45:59
Done (though the end result is much longer).
| |
| 172 if (has_action) { | |
| 173 return testing::AssertionFailure() | |
| 174 << "Extension " << name_ << " has lingering action."; | |
| 95 } | 175 } |
| 96 | 176 |
| 97 return testing::AssertionSuccess(); | 177 return testing::AssertionSuccess(); |
| 98 } | 178 } |
| 99 | 179 |
| 100 IN_PROC_BROWSER_TEST_F(ActiveScriptControllerBrowserTest, | 180 IN_PROC_BROWSER_TEST_F(ActiveScriptControllerBrowserTest, |
| 101 ActiveScriptsAreDisplayed) { | 181 ActiveScriptsAreDisplayed) { |
| 102 base::FilePath active_script_path = | 182 base::FilePath active_script_path = |
| 103 test_data_dir_.AppendASCII("active_script"); | 183 test_data_dir_.AppendASCII("active_script"); |
| 104 | 184 |
| 105 const char* kExtensionNames[] = { | 185 const char* kExtensionNames[] = { |
| 186 "inject_scripts_all_hosts", | |
| 187 "inject_scripts_explicit_hosts", | |
| 106 "content_scripts_all_hosts", | 188 "content_scripts_all_hosts", |
| 107 "inject_scripts_all_hosts", | |
| 108 "content_scripts_explicit_hosts" | 189 "content_scripts_explicit_hosts" |
| 109 }; | 190 }; |
| 110 | 191 |
| 111 // First, we load up three extensions: | 192 // First, we load up three extensions: |
| 193 // - An extension that injects scripts into all hosts, | |
| 194 // - An extension that injects scripts into explicit hosts, | |
| 112 // - An extension with a content script that runs on all hosts, | 195 // - 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. | 196 // - An extension with a content script that runs on explicit hosts. |
| 197 // The extensions that operate on explicit hosts have permission; the ones | |
| 198 // that request all hosts require user consent. | |
| 115 ActiveScriptTester testers[] = { | 199 ActiveScriptTester testers[] = { |
| 116 ActiveScriptTester( | 200 ActiveScriptTester( |
| 117 kExtensionNames[0], | 201 kExtensionNames[0], |
| 118 LoadExtension(active_script_path.AppendASCII(kExtensionNames[0])), | 202 LoadExtension(active_script_path.AppendASCII(kExtensionNames[0])), |
| 119 true /* expect action */), | 203 true /* needs consent */, |
|
not at google - send to devlin
2014/05/15 00:12:36
how about making this an enum so you don't need to
Devlin
2014/05/15 17:45:59
The reason was because if statements like
if (re
| |
| 204 EXECUTE_SCRIPT), | |
| 120 ActiveScriptTester( | 205 ActiveScriptTester( |
| 121 kExtensionNames[1], | 206 kExtensionNames[1], |
| 122 LoadExtension(active_script_path.AppendASCII(kExtensionNames[1])), | 207 LoadExtension(active_script_path.AppendASCII(kExtensionNames[1])), |
| 123 true /* expect action */), | 208 false /* does not need consent */, |
| 209 EXECUTE_SCRIPT), | |
| 124 ActiveScriptTester( | 210 ActiveScriptTester( |
| 125 kExtensionNames[2], | 211 kExtensionNames[2], |
| 126 LoadExtension(active_script_path.AppendASCII(kExtensionNames[2])), | 212 LoadExtension(active_script_path.AppendASCII(kExtensionNames[2])), |
| 127 false /* expect action */) | 213 true /* needs consent */, |
| 214 CONTENT_SCRIPT), | |
| 215 ActiveScriptTester( | |
| 216 kExtensionNames[3], | |
| 217 LoadExtension(active_script_path.AppendASCII(kExtensionNames[3])), | |
| 218 false /* does not need consent */, | |
| 219 CONTENT_SCRIPT), | |
| 128 }; | 220 }; |
| 129 | 221 |
| 130 // Navigate to an URL (which matches the explicit host specified in the | 222 // Navigate to an URL (which matches the explicit host specified in the |
| 131 // extension content_scripts_explicit_hosts). All three extensions should | 223 // extension content_scripts_explicit_hosts). All three extensions should |
| 132 // inject the script. | 224 // inject the script. |
| 133 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); | 225 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); |
| 134 ui_test_utils::NavigateToURL( | 226 ui_test_utils::NavigateToURL( |
| 135 browser(), embedded_test_server()->GetURL("/extensions/test_file.html")); | 227 browser(), embedded_test_server()->GetURL("/extensions/test_file.html")); |
| 136 | 228 |
| 137 for (size_t i = 0u; i < arraysize(testers); ++i) | 229 for (size_t i = 0u; i < arraysize(testers); ++i) |
| 138 ASSERT_TRUE(testers[i].Verify(browser())) << kExtensionNames[i]; | 230 EXPECT_TRUE(testers[i].Verify(browser())) << kExtensionNames[i]; |
| 139 } | 231 } |
| 140 | 232 |
| 141 } // namespace extensions | 233 } // namespace extensions |
| OLD | NEW |