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 enum RequiresConsent { |
| 32 REQUIRES_CONSENT, |
| 33 DOES_NOT_REQUIRE_CONSENT |
| 34 }; |
| 35 |
| 36 } // namespace |
| 37 |
21 class ActiveScriptControllerBrowserTest : public ExtensionBrowserTest { | 38 class ActiveScriptControllerBrowserTest : public ExtensionBrowserTest { |
22 public: | 39 public: |
23 ActiveScriptControllerBrowserTest() | 40 ActiveScriptControllerBrowserTest() |
24 : feature_override_(FeatureSwitch::scripts_require_action(), | 41 : feature_override_(FeatureSwitch::scripts_require_action(), |
25 FeatureSwitch::OVERRIDE_ENABLED) {} | 42 FeatureSwitch::OVERRIDE_ENABLED) {} |
26 private: | 43 private: |
27 FeatureSwitch::ScopedOverride feature_override_; | 44 FeatureSwitch::ScopedOverride feature_override_; |
28 }; | 45 }; |
29 | 46 |
30 class ActiveScriptTester { | 47 class ActiveScriptTester { |
31 public: | 48 public: |
32 ActiveScriptTester(const std::string& name, | 49 ActiveScriptTester(const std::string& name, |
33 const Extension* extension, | 50 const Extension* extension, |
34 bool expect_has_action); | 51 Browser* browser, |
| 52 RequiresConsent requires_consent, |
| 53 InjectionType type); |
35 ~ActiveScriptTester(); | 54 ~ActiveScriptTester(); |
36 | 55 |
37 testing::AssertionResult Verify(Browser* browser); | 56 testing::AssertionResult Verify(); |
38 | 57 |
39 private: | 58 private: |
| 59 // Returns the location bar controller, or NULL if one does not exist. |
| 60 LocationBarController* GetLocationBarController(); |
| 61 |
| 62 // Returns the active script controller, or NULL if one does not exist. |
| 63 ActiveScriptController* GetActiveScriptController(); |
| 64 |
| 65 // Get the ExtensionAction for this extension, or NULL if one does not exist. |
| 66 ExtensionAction* GetAction(); |
| 67 |
40 // The name of the extension, and also the message it sends. | 68 // The name of the extension, and also the message it sends. |
41 std::string name_; | 69 std::string name_; |
42 | 70 |
43 // The extension associated with this tester. | 71 // The extension associated with this tester. |
44 const Extension* extension_; | 72 const Extension* extension_; |
45 | 73 |
46 // Whether or not we expect the extension to have an action for script | 74 // The browser the tester is running in. |
47 // injection. | 75 Browser* browser_; |
48 bool expect_has_action_; | 76 |
| 77 // Whether or not the extension has permission to run the script without |
| 78 // asking the user. |
| 79 RequiresConsent requires_consent_; |
| 80 |
| 81 // The type of injection this tester uses. |
| 82 InjectionType type_; |
49 | 83 |
50 // All of these extensions should inject a script (either through content | 84 // All of these extensions should inject a script (either through content |
51 // scripts or through chrome.tabs.executeScript()) that sends a message with | 85 // scripts or through chrome.tabs.executeScript()) that sends a message with |
52 // their names (for verification). | 86 // the |kInjectSucceeded| message. |
53 // Be prepared to wait for each extension to inject the script. | 87 linked_ptr<ExtensionTestMessageListener> inject_attempt_listener_; |
54 linked_ptr<ExtensionTestMessageListener> listener_; | 88 |
| 89 // After trying to inject the script, extensions sending the script via |
| 90 // chrome.tabs.executeScript() send a |kInjectAttempted| message. |
| 91 linked_ptr<ExtensionTestMessageListener> inject_success_listener_; |
55 }; | 92 }; |
56 | 93 |
57 ActiveScriptTester::ActiveScriptTester(const std::string& name, | 94 ActiveScriptTester::ActiveScriptTester(const std::string& name, |
58 const Extension* extension, | 95 const Extension* extension, |
59 bool expect_has_action) | 96 Browser* browser, |
| 97 RequiresConsent requires_consent, |
| 98 InjectionType type) |
60 : name_(name), | 99 : name_(name), |
61 extension_(extension), | 100 extension_(extension), |
62 expect_has_action_(expect_has_action), | 101 browser_(browser), |
63 listener_( | 102 requires_consent_(requires_consent), |
64 new ExtensionTestMessageListener(name, false /* won't reply */)) { | 103 type_(type), |
| 104 inject_attempt_listener_( |
| 105 new ExtensionTestMessageListener(kInjectAttempted, |
| 106 false /* won't reply */)), |
| 107 inject_success_listener_( |
| 108 new ExtensionTestMessageListener(kInjectSucceeded, |
| 109 false /* won't reply */)) { |
| 110 inject_attempt_listener_->set_extension_id(extension->id()); |
| 111 inject_success_listener_->set_extension_id(extension->id()); |
65 } | 112 } |
66 | 113 |
67 ActiveScriptTester::~ActiveScriptTester() { | 114 ActiveScriptTester::~ActiveScriptTester() { |
68 } | 115 } |
69 | 116 |
70 testing::AssertionResult ActiveScriptTester::Verify(Browser* browser) { | 117 testing::AssertionResult ActiveScriptTester::Verify() { |
71 if (!extension_) | 118 if (!extension_) |
72 return testing::AssertionFailure() << "Could not load extension: " << name_; | 119 return testing::AssertionFailure() << "Could not load extension: " << name_; |
73 | 120 |
74 listener_->WaitUntilSatisfied(); | 121 // If it's not a content script, the Extension lets us know when it has |
75 content::WebContents* web_contents = | 122 // attempted to inject the script. |
76 browser ? browser->tab_strip_model()->GetActiveWebContents() : NULL; | 123 // This means there is a potential for a race condition with content scripts; |
77 | 124 // however, since they are all injected at document_start, this shouldn't be |
78 if (!web_contents) | 125 // a problem. If these tests start failing, though, that might be it. |
79 return testing::AssertionFailure() << "Could not find web contents."; | 126 if (type_ == EXECUTE_SCRIPT) |
80 | 127 inject_attempt_listener_->WaitUntilSatisfied(); |
81 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); | 128 |
82 if (!tab_helper) | 129 // Make sure all running tasks are complete. |
83 return testing::AssertionFailure() << "Could not find tab helper."; | 130 content::RunAllPendingInMessageLoop(); |
| 131 |
| 132 LocationBarController* location_bar_controller = GetLocationBarController(); |
| 133 if (!location_bar_controller) { |
| 134 return testing::AssertionFailure() |
| 135 << "Could not find location bar controller"; |
| 136 } |
84 | 137 |
85 ActiveScriptController* controller = | 138 ActiveScriptController* controller = |
86 tab_helper->location_bar_controller()->active_script_controller(); | 139 location_bar_controller->active_script_controller(); |
87 if (!controller) | 140 if (!controller) |
88 return testing::AssertionFailure() << "Could not find controller."; | 141 return testing::AssertionFailure() << "Could not find controller."; |
89 | 142 |
90 bool has_action = controller->GetActionForExtension(extension_) != NULL; | 143 ExtensionAction* action = GetAction(); |
91 if (expect_has_action_ != has_action) { | 144 bool has_action = action != NULL; |
| 145 |
| 146 // An extension should have an action displayed iff it requires user consent. |
| 147 if ((requires_consent_ == REQUIRES_CONSENT && !has_action) || |
| 148 (requires_consent_ == DOES_NOT_REQUIRE_CONSENT && has_action)) { |
92 return testing::AssertionFailure() | 149 return testing::AssertionFailure() |
93 << "Improper action status: expected " << expect_has_action_ | 150 << "Improper action status for " << name_ << ": expected " |
94 << ", found " << has_action; | 151 << (requires_consent_ == REQUIRES_CONSENT) << ", found " << has_action; |
| 152 } |
| 153 |
| 154 // If the extension has permission, we should be able to simply wait for it |
| 155 // to execute. |
| 156 if (requires_consent_ == DOES_NOT_REQUIRE_CONSENT) { |
| 157 inject_success_listener_->WaitUntilSatisfied(); |
| 158 return testing::AssertionSuccess(); |
| 159 } |
| 160 |
| 161 // Otherwise, we don't have permission, and have to grant it. Ensure the |
| 162 // script has *not* already executed. |
| 163 // Currently, it's okay for content scripts to execute, because we don't |
| 164 // block them. |
| 165 // TODO(rdevlin.cronin): Fix this. |
| 166 if (inject_success_listener_->was_satisfied() && type_ != CONTENT_SCRIPT) { |
| 167 return testing::AssertionFailure() << |
| 168 name_ << "'s script ran without permission."; |
| 169 } |
| 170 |
| 171 // If we reach this point, we should always have an action. |
| 172 DCHECK(action); |
| 173 |
| 174 // Grant permission by clicking on the extension action. |
| 175 location_bar_controller->OnClicked(action); |
| 176 |
| 177 // Now, the extension should be able to inject the script. |
| 178 inject_success_listener_->WaitUntilSatisfied(); |
| 179 |
| 180 // The Action should have disappeared. |
| 181 has_action = GetAction() != NULL; |
| 182 if (has_action) { |
| 183 return testing::AssertionFailure() |
| 184 << "Extension " << name_ << " has lingering action."; |
95 } | 185 } |
96 | 186 |
97 return testing::AssertionSuccess(); | 187 return testing::AssertionSuccess(); |
98 } | 188 } |
99 | 189 |
| 190 LocationBarController* ActiveScriptTester::GetLocationBarController() { |
| 191 content::WebContents* web_contents = |
| 192 browser_ ? browser_->tab_strip_model()->GetActiveWebContents() : NULL; |
| 193 |
| 194 if (!web_contents) |
| 195 return NULL; |
| 196 |
| 197 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); |
| 198 return tab_helper ? tab_helper->location_bar_controller() : NULL; |
| 199 } |
| 200 |
| 201 ActiveScriptController* ActiveScriptTester::GetActiveScriptController() { |
| 202 LocationBarController* location_bar_controller = GetLocationBarController(); |
| 203 return location_bar_controller ? |
| 204 location_bar_controller->active_script_controller() : NULL; |
| 205 } |
| 206 |
| 207 ExtensionAction* ActiveScriptTester::GetAction() { |
| 208 ActiveScriptController* controller = GetActiveScriptController(); |
| 209 return controller ? controller->GetActionForExtension(extension_) : NULL; |
| 210 } |
| 211 |
100 IN_PROC_BROWSER_TEST_F(ActiveScriptControllerBrowserTest, | 212 IN_PROC_BROWSER_TEST_F(ActiveScriptControllerBrowserTest, |
101 ActiveScriptsAreDisplayed) { | 213 ActiveScriptsAreDisplayed) { |
102 base::FilePath active_script_path = | 214 base::FilePath active_script_path = |
103 test_data_dir_.AppendASCII("active_script"); | 215 test_data_dir_.AppendASCII("active_script"); |
104 | 216 |
105 const char* kExtensionNames[] = { | 217 const char* kExtensionNames[] = { |
| 218 "inject_scripts_all_hosts", |
| 219 "inject_scripts_explicit_hosts", |
106 "content_scripts_all_hosts", | 220 "content_scripts_all_hosts", |
107 "inject_scripts_all_hosts", | |
108 "content_scripts_explicit_hosts" | 221 "content_scripts_explicit_hosts" |
109 }; | 222 }; |
110 | 223 |
111 // First, we load up three extensions: | 224 // First, we load up three extensions: |
| 225 // - An extension that injects scripts into all hosts, |
| 226 // - An extension that injects scripts into explicit hosts, |
112 // - An extension with a content script that runs on all hosts, | 227 // - 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. | 228 // - An extension with a content script that runs on explicit hosts. |
| 229 // The extensions that operate on explicit hosts have permission; the ones |
| 230 // that request all hosts require user consent. |
115 ActiveScriptTester testers[] = { | 231 ActiveScriptTester testers[] = { |
116 ActiveScriptTester( | 232 ActiveScriptTester( |
117 kExtensionNames[0], | 233 kExtensionNames[0], |
118 LoadExtension(active_script_path.AppendASCII(kExtensionNames[0])), | 234 LoadExtension(active_script_path.AppendASCII(kExtensionNames[0])), |
119 true /* expect action */), | 235 browser(), |
| 236 REQUIRES_CONSENT, |
| 237 EXECUTE_SCRIPT), |
120 ActiveScriptTester( | 238 ActiveScriptTester( |
121 kExtensionNames[1], | 239 kExtensionNames[1], |
122 LoadExtension(active_script_path.AppendASCII(kExtensionNames[1])), | 240 LoadExtension(active_script_path.AppendASCII(kExtensionNames[1])), |
123 true /* expect action */), | 241 browser(), |
| 242 DOES_NOT_REQUIRE_CONSENT, |
| 243 EXECUTE_SCRIPT), |
124 ActiveScriptTester( | 244 ActiveScriptTester( |
125 kExtensionNames[2], | 245 kExtensionNames[2], |
126 LoadExtension(active_script_path.AppendASCII(kExtensionNames[2])), | 246 LoadExtension(active_script_path.AppendASCII(kExtensionNames[2])), |
127 false /* expect action */) | 247 browser(), |
| 248 REQUIRES_CONSENT, |
| 249 CONTENT_SCRIPT), |
| 250 ActiveScriptTester( |
| 251 kExtensionNames[3], |
| 252 LoadExtension(active_script_path.AppendASCII(kExtensionNames[3])), |
| 253 browser(), |
| 254 DOES_NOT_REQUIRE_CONSENT, |
| 255 CONTENT_SCRIPT), |
128 }; | 256 }; |
129 | 257 |
130 // Navigate to an URL (which matches the explicit host specified in the | 258 // Navigate to an URL (which matches the explicit host specified in the |
131 // extension content_scripts_explicit_hosts). All three extensions should | 259 // extension content_scripts_explicit_hosts). All three extensions should |
132 // inject the script. | 260 // inject the script. |
133 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); | 261 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); |
134 ui_test_utils::NavigateToURL( | 262 ui_test_utils::NavigateToURL( |
135 browser(), embedded_test_server()->GetURL("/extensions/test_file.html")); | 263 browser(), embedded_test_server()->GetURL("/extensions/test_file.html")); |
136 | 264 |
137 for (size_t i = 0u; i < arraysize(testers); ++i) | 265 for (size_t i = 0u; i < arraysize(testers); ++i) |
138 ASSERT_TRUE(testers[i].Verify(browser())) << kExtensionNames[i]; | 266 EXPECT_TRUE(testers[i].Verify()) << kExtensionNames[i]; |
139 } | 267 } |
140 | 268 |
141 } // namespace extensions | 269 } // namespace extensions |
OLD | NEW |