Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1453)

Side by Side Diff: chrome/browser/extensions/active_script_controller.cc

Issue 348313003: Create withheld permissions (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Test fix Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698