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 "chrome/browser/extensions/active_script_controller.h" | 5 #include "chrome/browser/extensions/active_script_controller.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
11 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
12 #include "chrome/browser/extensions/active_tab_permission_granter.h" | 12 #include "chrome/browser/extensions/active_tab_permission_granter.h" |
13 #include "chrome/browser/extensions/extension_action.h" | 13 #include "chrome/browser/extensions/extension_action.h" |
14 #include "chrome/browser/extensions/extension_util.h" | 14 #include "chrome/browser/extensions/extension_util.h" |
15 #include "chrome/browser/extensions/location_bar_controller.h" | 15 #include "chrome/browser/extensions/location_bar_controller.h" |
16 #include "chrome/browser/extensions/tab_helper.h" | 16 #include "chrome/browser/extensions/tab_helper.h" |
17 #include "chrome/browser/sessions/session_id.h" | 17 #include "chrome/browser/sessions/session_id.h" |
18 #include "chrome/common/extensions/api/extension_action/action_info.h" | 18 #include "chrome/common/extensions/api/extension_action/action_info.h" |
19 #include "content/public/browser/navigation_controller.h" | 19 #include "content/public/browser/navigation_controller.h" |
20 #include "content/public/browser/navigation_entry.h" | 20 #include "content/public/browser/navigation_entry.h" |
21 #include "content/public/browser/render_view_host.h" | 21 #include "content/public/browser/render_view_host.h" |
22 #include "content/public/browser/web_contents.h" | 22 #include "content/public/browser/web_contents.h" |
23 #include "extensions/browser/extension_registry.h" | 23 #include "extensions/browser/extension_registry.h" |
24 #include "extensions/common/extension.h" | 24 #include "extensions/common/extension.h" |
25 #include "extensions/common/extension_messages.h" | 25 #include "extensions/common/extension_messages.h" |
26 #include "extensions/common/extension_set.h" | 26 #include "extensions/common/extension_set.h" |
27 #include "extensions/common/feature_switch.h" | 27 #include "extensions/common/feature_switch.h" |
| 28 #include "extensions/common/manifest.h" |
28 #include "extensions/common/permissions/permissions_data.h" | 29 #include "extensions/common/permissions/permissions_data.h" |
29 #include "ipc/ipc_message_macros.h" | 30 #include "ipc/ipc_message_macros.h" |
30 | 31 |
31 namespace extensions { | 32 namespace extensions { |
32 | 33 |
| 34 namespace { |
| 35 |
| 36 // Returns true if the extension should be regarded as a "permitted" extension |
| 37 // for the case of metrics. We need this because we only actually withhold |
| 38 // permissions if the switch is enabled, but want to record metrics in all |
| 39 // cases. |
| 40 // "ExtensionWouldHaveHadHostPermissionsWithheldIfSwitchWasOn()" would be |
| 41 // more accurate, but too long. |
| 42 bool ShouldRecordExtension(const Extension* extension) { |
| 43 return extension->ShouldDisplayInExtensionSettings() && |
| 44 !Manifest::IsPolicyLocation(extension->location()) && |
| 45 !Manifest::IsComponentLocation(extension->location()) && |
| 46 !PermissionsData::CanExecuteScriptEverywhere(extension) && |
| 47 extension->permissions_data() |
| 48 ->active_permissions() |
| 49 ->ShouldWarnAllHosts(); |
| 50 } |
| 51 |
| 52 } // namespace |
| 53 |
33 ActiveScriptController::PendingRequest::PendingRequest() : | 54 ActiveScriptController::PendingRequest::PendingRequest() : |
34 page_id(-1) { | 55 page_id(-1) { |
35 } | 56 } |
36 | 57 |
37 ActiveScriptController::PendingRequest::PendingRequest( | 58 ActiveScriptController::PendingRequest::PendingRequest( |
38 const base::Closure& closure, | 59 const base::Closure& closure, |
39 int page_id) | 60 int page_id) |
40 : closure(closure), | 61 : closure(closure), |
41 page_id(page_id) { | 62 page_id(page_id) { |
42 } | 63 } |
(...skipping 20 matching lines...) Expand all Loading... |
63 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); | 84 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); |
64 if (!tab_helper) | 85 if (!tab_helper) |
65 return NULL; | 86 return NULL; |
66 LocationBarController* location_bar_controller = | 87 LocationBarController* location_bar_controller = |
67 tab_helper->location_bar_controller(); | 88 tab_helper->location_bar_controller(); |
68 // This should never be NULL. | 89 // This should never be NULL. |
69 DCHECK(location_bar_controller); | 90 DCHECK(location_bar_controller); |
70 return location_bar_controller->active_script_controller(); | 91 return location_bar_controller->active_script_controller(); |
71 } | 92 } |
72 | 93 |
73 bool ActiveScriptController::RequiresUserConsentForScriptInjection( | |
74 const Extension* extension) { | |
75 CHECK(extension); | |
76 if (!extension->permissions_data()->RequiresActionForScriptExecution( | |
77 extension, | |
78 SessionID::IdForTab(web_contents()), | |
79 web_contents()->GetVisibleURL()) || | |
80 util::AllowedScriptingOnAllUrls(extension->id(), | |
81 web_contents()->GetBrowserContext())) { | |
82 return false; | |
83 } | |
84 | |
85 // If the feature is not enabled, we automatically allow all extensions to | |
86 // run scripts. | |
87 if (!enabled_) | |
88 permitted_extensions_.insert(extension->id()); | |
89 | |
90 return permitted_extensions_.count(extension->id()) == 0; | |
91 } | |
92 | |
93 void ActiveScriptController::RequestScriptInjection( | |
94 const Extension* extension, | |
95 int page_id, | |
96 const base::Closure& callback) { | |
97 CHECK(extension); | |
98 PendingRequestList& list = pending_requests_[extension->id()]; | |
99 list.push_back(PendingRequest(callback, page_id)); | |
100 | |
101 // If this was the first entry, notify the location bar that there's a new | |
102 // icon. | |
103 if (list.size() == 1u) | |
104 LocationBarController::NotifyChange(web_contents()); | |
105 } | |
106 | |
107 void ActiveScriptController::OnActiveTabPermissionGranted( | 94 void ActiveScriptController::OnActiveTabPermissionGranted( |
108 const Extension* extension) { | 95 const Extension* extension) { |
109 RunPendingForExtension(extension); | 96 RunPendingForExtension(extension); |
110 } | 97 } |
111 | 98 |
112 void ActiveScriptController::OnAdInjectionDetected( | 99 void ActiveScriptController::OnAdInjectionDetected( |
113 const std::set<std::string>& ad_injectors) { | 100 const std::set<std::string>& ad_injectors) { |
114 // We're only interested in data if there are ad injectors detected. | 101 // We're only interested in data if there are ad injectors detected. |
115 if (ad_injectors.empty()) | 102 if (ad_injectors.empty()) |
116 return; | 103 return; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
167 permitted_extensions_.clear(); | 154 permitted_extensions_.clear(); |
168 pending_requests_.clear(); | 155 pending_requests_.clear(); |
169 } | 156 } |
170 | 157 |
171 void ActiveScriptController::OnExtensionUnloaded(const Extension* extension) { | 158 void ActiveScriptController::OnExtensionUnloaded(const Extension* extension) { |
172 PendingRequestMap::iterator iter = pending_requests_.find(extension->id()); | 159 PendingRequestMap::iterator iter = pending_requests_.find(extension->id()); |
173 if (iter != pending_requests_.end()) | 160 if (iter != pending_requests_.end()) |
174 pending_requests_.erase(iter); | 161 pending_requests_.erase(iter); |
175 } | 162 } |
176 | 163 |
| 164 PermissionsData::AccessType |
| 165 ActiveScriptController::RequiresUserConsentForScriptInjection( |
| 166 const Extension* extension, |
| 167 UserScript::InjectionType type) { |
| 168 CHECK(extension); |
| 169 |
| 170 // If the feature is not enabled, we automatically allow all extensions to |
| 171 // run scripts. |
| 172 if (!enabled_) |
| 173 permitted_extensions_.insert(extension->id()); |
| 174 |
| 175 // Allow the extension if it's been explicitly granted permission. |
| 176 if (permitted_extensions_.count(extension->id()) > 0) |
| 177 return PermissionsData::ACCESS_ALLOWED; |
| 178 |
| 179 GURL url = web_contents()->GetVisibleURL(); |
| 180 int tab_id = SessionID::IdForTab(web_contents()); |
| 181 switch (type) { |
| 182 case UserScript::CONTENT_SCRIPT: |
| 183 return extension->permissions_data()->GetContentScriptAccess( |
| 184 extension, url, url, tab_id, -1, NULL); |
| 185 case UserScript::PROGRAMMATIC_SCRIPT: |
| 186 return extension->permissions_data()->GetPageAccess( |
| 187 extension, url, url, tab_id, -1, NULL); |
| 188 } |
| 189 |
| 190 NOTREACHED(); |
| 191 return PermissionsData::ACCESS_DENIED; |
| 192 } |
| 193 |
| 194 void ActiveScriptController::RequestScriptInjection( |
| 195 const Extension* extension, |
| 196 int page_id, |
| 197 const base::Closure& callback) { |
| 198 CHECK(extension); |
| 199 PendingRequestList& list = pending_requests_[extension->id()]; |
| 200 list.push_back(PendingRequest(callback, page_id)); |
| 201 |
| 202 // If this was the first entry, notify the location bar that there's a new |
| 203 // icon. |
| 204 if (list.size() == 1u) |
| 205 LocationBarController::NotifyChange(web_contents()); |
| 206 } |
| 207 |
177 void ActiveScriptController::RunPendingForExtension( | 208 void ActiveScriptController::RunPendingForExtension( |
178 const Extension* extension) { | 209 const Extension* extension) { |
179 DCHECK(extension); | 210 DCHECK(extension); |
180 PendingRequestMap::iterator iter = | |
181 pending_requests_.find(extension->id()); | |
182 if (iter == pending_requests_.end()) | |
183 return; | |
184 | 211 |
185 content::NavigationEntry* visible_entry = | 212 content::NavigationEntry* visible_entry = |
186 web_contents()->GetController().GetVisibleEntry(); | 213 web_contents()->GetController().GetVisibleEntry(); |
187 // Refuse to run if there's no visible entry, because we have no idea of | 214 // Refuse to run if there's no visible entry, because we have no idea of |
188 // determining if it's the proper page. This should rarely, if ever, happen. | 215 // determining if it's the proper page. This should rarely, if ever, happen. |
189 if (!visible_entry) | 216 if (!visible_entry) |
190 return; | 217 return; |
191 | 218 |
192 int page_id = visible_entry->GetPageID(); | 219 int page_id = visible_entry->GetPageID(); |
193 | 220 |
194 // We add this to the list of permitted extensions and erase pending entries | 221 // We add this to the list of permitted extensions and erase pending entries |
195 // *before* running them to guard against the crazy case where running the | 222 // *before* running them to guard against the crazy case where running the |
196 // callbacks adds more entries. | 223 // callbacks adds more entries. |
197 permitted_extensions_.insert(extension->id()); | 224 permitted_extensions_.insert(extension->id()); |
| 225 |
| 226 PendingRequestMap::iterator iter = pending_requests_.find(extension->id()); |
| 227 if (iter == pending_requests_.end()) |
| 228 return; |
| 229 |
198 PendingRequestList requests; | 230 PendingRequestList requests; |
199 iter->second.swap(requests); | 231 iter->second.swap(requests); |
200 pending_requests_.erase(extension->id()); | 232 pending_requests_.erase(extension->id()); |
201 | 233 |
202 // Clicking to run the extension counts as granting it permission to run on | 234 // Clicking to run the extension counts as granting it permission to run on |
203 // the given tab. | 235 // the given tab. |
204 // The extension may already have active tab at this point, but granting | 236 // The extension may already have active tab at this point, but granting |
205 // it twice is essentially a no-op. | 237 // it twice is essentially a no-op. |
206 TabHelper::FromWebContents(web_contents())-> | 238 TabHelper::FromWebContents(web_contents())-> |
207 active_tab_permission_granter()->GrantIfRequested(extension); | 239 active_tab_permission_granter()->GrantIfRequested(extension); |
208 | 240 |
209 // Run all pending injections for the given extension. | 241 // Run all pending injections for the given extension. |
210 for (PendingRequestList::iterator request = requests.begin(); | 242 for (PendingRequestList::iterator request = requests.begin(); |
211 request != requests.end(); | 243 request != requests.end(); |
212 ++request) { | 244 ++request) { |
213 // Only run if it's on the proper page. | 245 // Only run if it's on the proper page. |
214 if (request->page_id == page_id) | 246 if (request->page_id == page_id) |
215 request->closure.Run(); | 247 request->closure.Run(); |
216 } | 248 } |
217 | 249 |
218 // Inform the location bar that the action is now gone. | 250 // Inform the location bar that the action is now gone. |
219 LocationBarController::NotifyChange(web_contents()); | 251 LocationBarController::NotifyChange(web_contents()); |
220 } | 252 } |
221 | 253 |
222 void ActiveScriptController::OnRequestScriptInjectionPermission( | 254 void ActiveScriptController::OnRequestScriptInjectionPermission( |
223 const std::string& extension_id, | 255 const std::string& extension_id, |
| 256 UserScript::InjectionType script_type, |
224 int page_id, | 257 int page_id, |
225 int request_id) { | 258 int request_id) { |
226 if (!Extension::IdIsValid(extension_id)) { | 259 if (!Extension::IdIsValid(extension_id)) { |
227 NOTREACHED() << "'" << extension_id << "' is not a valid id."; | 260 NOTREACHED() << "'" << extension_id << "' is not a valid id."; |
228 return; | 261 return; |
229 } | 262 } |
230 | 263 |
231 const Extension* extension = | 264 const Extension* extension = |
232 ExtensionRegistry::Get(web_contents()->GetBrowserContext()) | 265 ExtensionRegistry::Get(web_contents()->GetBrowserContext()) |
233 ->enabled_extensions().GetByID(extension_id); | 266 ->enabled_extensions().GetByID(extension_id); |
234 // We shouldn't allow extensions which are no longer enabled to run any | 267 // We shouldn't allow extensions which are no longer enabled to run any |
235 // scripts. Ignore the request. | 268 // scripts. Ignore the request. |
236 if (!extension) | 269 if (!extension) |
237 return; | 270 return; |
238 | 271 |
239 // If the request id is -1, that signals that the content script has already | 272 // If the request id is -1, that signals that the content script has already |
240 // ran (because this feature is not enabled). Add the extension to the list of | 273 // ran (because this feature is not enabled). Add the extension to the list of |
241 // permitted extensions (for metrics), and return immediately. | 274 // permitted extensions (for metrics), and return immediately. |
242 if (request_id == -1) { | 275 if (request_id == -1) { |
243 DCHECK(!enabled_); | 276 if (ShouldRecordExtension(extension)) { |
244 permitted_extensions_.insert(extension->id()); | 277 DCHECK(!enabled_); |
| 278 permitted_extensions_.insert(extension->id()); |
| 279 } |
245 return; | 280 return; |
246 } | 281 } |
247 | 282 |
248 if (RequiresUserConsentForScriptInjection(extension)) { | 283 switch (RequiresUserConsentForScriptInjection(extension, script_type)) { |
249 // This base::Unretained() is safe, because the callback is only invoked by | 284 case PermissionsData::ACCESS_ALLOWED: |
250 // this object. | 285 PermitScriptInjection(request_id); |
251 RequestScriptInjection( | 286 break; |
252 extension, | 287 case PermissionsData::ACCESS_WITHHELD: |
253 page_id, | 288 // This base::Unretained() is safe, because the callback is only invoked |
254 base::Bind(&ActiveScriptController::PermitScriptInjection, | 289 // by this object. |
255 base::Unretained(this), | 290 RequestScriptInjection( |
256 request_id)); | 291 extension, |
257 } else { | 292 page_id, |
258 PermitScriptInjection(request_id); | 293 base::Bind(&ActiveScriptController::PermitScriptInjection, |
| 294 base::Unretained(this), |
| 295 request_id)); |
| 296 break; |
| 297 case PermissionsData::ACCESS_DENIED: |
| 298 // We should usually only get a "deny access" if the page changed (as the |
| 299 // renderer wouldn't have requested permission if the answer was always |
| 300 // "no"). Just let the request fizzle and die. |
| 301 break; |
259 } | 302 } |
260 } | 303 } |
261 | 304 |
262 void ActiveScriptController::PermitScriptInjection(int request_id) { | 305 void ActiveScriptController::PermitScriptInjection(int request_id) { |
| 306 // This only sends the response to the renderer - the process of adding the |
| 307 // extension to the list of |permitted_extensions_| is done elsewhere. |
263 content::RenderViewHost* render_view_host = | 308 content::RenderViewHost* render_view_host = |
264 web_contents()->GetRenderViewHost(); | 309 web_contents()->GetRenderViewHost(); |
265 if (render_view_host) { | 310 if (render_view_host) { |
266 render_view_host->Send(new ExtensionMsg_PermitScriptInjection( | 311 render_view_host->Send(new ExtensionMsg_PermitScriptInjection( |
267 render_view_host->GetRoutingID(), request_id)); | 312 render_view_host->GetRoutingID(), request_id)); |
268 } | 313 } |
269 } | 314 } |
270 | 315 |
271 bool ActiveScriptController::OnMessageReceived(const IPC::Message& message) { | 316 bool ActiveScriptController::OnMessageReceived(const IPC::Message& message) { |
272 bool handled = true; | 317 bool handled = true; |
(...skipping 16 matching lines...) Expand all Loading... |
289 UMA_HISTOGRAM_COUNTS_100( | 334 UMA_HISTOGRAM_COUNTS_100( |
290 "Extensions.ActiveScriptController.PermittedExtensions", | 335 "Extensions.ActiveScriptController.PermittedExtensions", |
291 permitted_extensions_.size()); | 336 permitted_extensions_.size()); |
292 UMA_HISTOGRAM_COUNTS_100( | 337 UMA_HISTOGRAM_COUNTS_100( |
293 "Extensions.ActiveScriptController.DeniedExtensions", | 338 "Extensions.ActiveScriptController.DeniedExtensions", |
294 pending_requests_.size()); | 339 pending_requests_.size()); |
295 } | 340 } |
296 } | 341 } |
297 | 342 |
298 } // namespace extensions | 343 } // namespace extensions |
OLD | NEW |