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()->active_permissions() | |
48 ->ShouldWarnAllHosts(); | |
not at google - send to devlin
2014/06/27 23:24:33
don't you need to check withheld permissions as we
Devlin
2014/06/30 17:06:09
No.
This method is the yak shave for being able t
| |
49 } | |
50 | |
51 } // namespace | |
52 | |
33 ActiveScriptController::PendingRequest::PendingRequest() : | 53 ActiveScriptController::PendingRequest::PendingRequest() : |
34 page_id(-1) { | 54 page_id(-1) { |
35 } | 55 } |
36 | 56 |
37 ActiveScriptController::PendingRequest::PendingRequest( | 57 ActiveScriptController::PendingRequest::PendingRequest( |
38 const base::Closure& closure, | 58 const base::Closure& closure, |
39 int page_id) | 59 int page_id) |
40 : closure(closure), | 60 : closure(closure), |
41 page_id(page_id) { | 61 page_id(page_id) { |
42 } | 62 } |
(...skipping 20 matching lines...) Expand all Loading... | |
63 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); | 83 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); |
64 if (!tab_helper) | 84 if (!tab_helper) |
65 return NULL; | 85 return NULL; |
66 LocationBarController* location_bar_controller = | 86 LocationBarController* location_bar_controller = |
67 tab_helper->location_bar_controller(); | 87 tab_helper->location_bar_controller(); |
68 // This should never be NULL. | 88 // This should never be NULL. |
69 DCHECK(location_bar_controller); | 89 DCHECK(location_bar_controller); |
70 return location_bar_controller->active_script_controller(); | 90 return location_bar_controller->active_script_controller(); |
71 } | 91 } |
72 | 92 |
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( | 93 void ActiveScriptController::OnActiveTabPermissionGranted( |
108 const Extension* extension) { | 94 const Extension* extension) { |
109 RunPendingForExtension(extension); | 95 RunPendingForExtension(extension); |
110 } | 96 } |
111 | 97 |
112 void ActiveScriptController::OnAdInjectionDetected( | 98 void ActiveScriptController::OnAdInjectionDetected( |
113 const std::set<std::string>& ad_injectors) { | 99 const std::set<std::string>& ad_injectors) { |
114 // We're only interested in data if there are ad injectors detected. | 100 // We're only interested in data if there are ad injectors detected. |
115 if (ad_injectors.empty()) | 101 if (ad_injectors.empty()) |
116 return; | 102 return; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
167 permitted_extensions_.clear(); | 153 permitted_extensions_.clear(); |
168 pending_requests_.clear(); | 154 pending_requests_.clear(); |
169 } | 155 } |
170 | 156 |
171 void ActiveScriptController::OnExtensionUnloaded(const Extension* extension) { | 157 void ActiveScriptController::OnExtensionUnloaded(const Extension* extension) { |
172 PendingRequestMap::iterator iter = pending_requests_.find(extension->id()); | 158 PendingRequestMap::iterator iter = pending_requests_.find(extension->id()); |
173 if (iter != pending_requests_.end()) | 159 if (iter != pending_requests_.end()) |
174 pending_requests_.erase(iter); | 160 pending_requests_.erase(iter); |
175 } | 161 } |
176 | 162 |
163 PermissionsData::AccessType | |
164 ActiveScriptController::RequiresUserConsentForScriptInjection( | |
165 const Extension* extension, | |
166 extension_misc::InjectedScriptType type) { | |
167 CHECK(extension); | |
168 | |
169 // If the feature is not enabled, we automatically allow all extensions to | |
170 // run scripts. | |
171 if (!enabled_) | |
172 permitted_extensions_.insert(extension->id()); | |
173 | |
174 // Allow the extension if it's been explicitly granted permission. | |
175 if (permitted_extensions_.count(extension->id()) > 0) | |
176 return PermissionsData::ALLOW_ACCESS; | |
177 | |
178 PermissionsData::AccessType access = PermissionsData::DENY_ACCESS; | |
179 GURL url = web_contents()->GetVisibleURL(); | |
180 int tab_id = SessionID::IdForTab(web_contents()); | |
181 switch (type) { | |
182 case extension_misc::CONTENT_SCRIPT: | |
183 access = extension->permissions_data() | |
184 ->CanRunContentScriptOnPageWithUserConsent( | |
185 extension, url, url, tab_id, -1, NULL); | |
186 break; | |
187 case extension_misc::PROGRAMMATIC_SCRIPT: | |
188 access = extension->permissions_data()->CanAccessPageWithUserConsent( | |
189 extension, url, url, tab_id, -1, NULL); | |
190 } | |
not at google - send to devlin
2014/06/27 23:24:33
nit: I'd prefer to phrase this more succinctly wit
not at google - send to devlin
2014/06/30 14:37:03
I mean "without declaring |access|"
Devlin
2014/06/30 17:06:09
Sure, why not.
| |
191 | |
192 return access; | |
193 } | |
194 | |
195 void ActiveScriptController::RequestScriptInjection( | |
196 const Extension* extension, | |
197 int page_id, | |
198 const base::Closure& callback) { | |
199 CHECK(extension); | |
200 PendingRequestList& list = pending_requests_[extension->id()]; | |
201 list.push_back(PendingRequest(callback, page_id)); | |
202 | |
203 // If this was the first entry, notify the location bar that there's a new | |
204 // icon. | |
205 if (list.size() == 1u) | |
206 LocationBarController::NotifyChange(web_contents()); | |
207 } | |
208 | |
177 void ActiveScriptController::RunPendingForExtension( | 209 void ActiveScriptController::RunPendingForExtension( |
178 const Extension* extension) { | 210 const Extension* extension) { |
179 DCHECK(extension); | 211 DCHECK(extension); |
180 PendingRequestMap::iterator iter = | |
181 pending_requests_.find(extension->id()); | |
182 if (iter == pending_requests_.end()) | |
183 return; | |
184 | 212 |
185 content::NavigationEntry* visible_entry = | 213 content::NavigationEntry* visible_entry = |
186 web_contents()->GetController().GetVisibleEntry(); | 214 web_contents()->GetController().GetVisibleEntry(); |
187 // Refuse to run if there's no visible entry, because we have no idea of | 215 // 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. | 216 // determining if it's the proper page. This should rarely, if ever, happen. |
189 if (!visible_entry) | 217 if (!visible_entry) |
190 return; | 218 return; |
191 | 219 |
192 int page_id = visible_entry->GetPageID(); | 220 int page_id = visible_entry->GetPageID(); |
193 | 221 |
194 // We add this to the list of permitted extensions and erase pending entries | 222 // 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 | 223 // *before* running them to guard against the crazy case where running the |
196 // callbacks adds more entries. | 224 // callbacks adds more entries. |
197 permitted_extensions_.insert(extension->id()); | 225 permitted_extensions_.insert(extension->id()); |
226 | |
227 PendingRequestMap::iterator iter = | |
228 pending_requests_.find(extension->id()); | |
229 if (iter == pending_requests_.end()) | |
230 return; | |
231 | |
198 PendingRequestList requests; | 232 PendingRequestList requests; |
199 iter->second.swap(requests); | 233 iter->second.swap(requests); |
200 pending_requests_.erase(extension->id()); | 234 pending_requests_.erase(extension->id()); |
201 | 235 |
202 // Clicking to run the extension counts as granting it permission to run on | 236 // Clicking to run the extension counts as granting it permission to run on |
203 // the given tab. | 237 // the given tab. |
204 // The extension may already have active tab at this point, but granting | 238 // The extension may already have active tab at this point, but granting |
205 // it twice is essentially a no-op. | 239 // it twice is essentially a no-op. |
206 TabHelper::FromWebContents(web_contents())-> | 240 TabHelper::FromWebContents(web_contents())-> |
207 active_tab_permission_granter()->GrantIfRequested(extension); | 241 active_tab_permission_granter()->GrantIfRequested(extension); |
208 | 242 |
209 // Run all pending injections for the given extension. | 243 // Run all pending injections for the given extension. |
210 for (PendingRequestList::iterator request = requests.begin(); | 244 for (PendingRequestList::iterator request = requests.begin(); |
211 request != requests.end(); | 245 request != requests.end(); |
212 ++request) { | 246 ++request) { |
213 // Only run if it's on the proper page. | 247 // Only run if it's on the proper page. |
214 if (request->page_id == page_id) | 248 if (request->page_id == page_id) |
215 request->closure.Run(); | 249 request->closure.Run(); |
216 } | 250 } |
217 | 251 |
218 // Inform the location bar that the action is now gone. | 252 // Inform the location bar that the action is now gone. |
219 LocationBarController::NotifyChange(web_contents()); | 253 LocationBarController::NotifyChange(web_contents()); |
220 } | 254 } |
221 | 255 |
222 void ActiveScriptController::OnRequestScriptInjectionPermission( | 256 void ActiveScriptController::OnRequestScriptInjectionPermission( |
223 const std::string& extension_id, | 257 const std::string& extension_id, |
258 extension_misc::InjectedScriptType script_type, | |
224 int page_id, | 259 int page_id, |
225 int request_id) { | 260 int request_id) { |
226 if (!Extension::IdIsValid(extension_id)) { | 261 if (!Extension::IdIsValid(extension_id)) { |
227 NOTREACHED() << "'" << extension_id << "' is not a valid id."; | 262 NOTREACHED() << "'" << extension_id << "' is not a valid id."; |
228 return; | 263 return; |
229 } | 264 } |
230 | 265 |
231 const Extension* extension = | 266 const Extension* extension = |
232 ExtensionRegistry::Get(web_contents()->GetBrowserContext()) | 267 ExtensionRegistry::Get(web_contents()->GetBrowserContext()) |
233 ->enabled_extensions().GetByID(extension_id); | 268 ->enabled_extensions().GetByID(extension_id); |
234 // We shouldn't allow extensions which are no longer enabled to run any | 269 // We shouldn't allow extensions which are no longer enabled to run any |
235 // scripts. Ignore the request. | 270 // scripts. Ignore the request. |
236 if (!extension) | 271 if (!extension) |
237 return; | 272 return; |
238 | 273 |
239 // If the request id is -1, that signals that the content script has already | 274 // 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 | 275 // ran (because this feature is not enabled). Add the extension to the list of |
241 // permitted extensions (for metrics), and return immediately. | 276 // permitted extensions (for metrics), and return immediately. |
242 if (request_id == -1) { | 277 if (request_id == -1) { |
243 DCHECK(!enabled_); | 278 if (ShouldRecordExtension(extension)) { |
244 permitted_extensions_.insert(extension->id()); | 279 DCHECK(!enabled_); |
280 permitted_extensions_.insert(extension->id()); | |
281 } | |
245 return; | 282 return; |
246 } | 283 } |
247 | 284 |
248 if (RequiresUserConsentForScriptInjection(extension)) { | 285 switch (RequiresUserConsentForScriptInjection(extension, script_type)) { |
249 // This base::Unretained() is safe, because the callback is only invoked by | 286 case PermissionsData::ALLOW_ACCESS: |
250 // this object. | 287 PermitScriptInjection(request_id); |
251 RequestScriptInjection( | 288 break; |
252 extension, | 289 case PermissionsData::REQUEST_ACCESS: |
253 page_id, | 290 // This base::Unretained() is safe, because the callback is only invoked |
254 base::Bind(&ActiveScriptController::PermitScriptInjection, | 291 // by this object. |
255 base::Unretained(this), | 292 RequestScriptInjection( |
256 request_id)); | 293 extension, |
257 } else { | 294 page_id, |
258 PermitScriptInjection(request_id); | 295 base::Bind(&ActiveScriptController::PermitScriptInjection, |
296 base::Unretained(this), | |
297 request_id)); | |
298 break; | |
299 case PermissionsData::DENY_ACCESS: | |
300 // We should usually only get a "deny access" if the page changed (as the | |
301 // renderer wouldn't have requested permission if the answer was always | |
302 // "no"). Just let the request fizzle and die. | |
303 break; | |
259 } | 304 } |
260 } | 305 } |
261 | 306 |
262 void ActiveScriptController::PermitScriptInjection(int request_id) { | 307 void ActiveScriptController::PermitScriptInjection(int request_id) { |
not at google - send to devlin
2014/06/30 14:37:03
the reason this works on the browser side is becau
Devlin
2014/06/30 17:06:09
Commented to clear it up.
| |
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 |