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

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: Latest master 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()
48 ->active_permissions()
49 ->ShouldWarnAllHosts();
50 }
51
52 } // namespace
53
33 ActiveScriptController::ActiveScriptController( 54 ActiveScriptController::ActiveScriptController(
34 content::WebContents* web_contents) 55 content::WebContents* web_contents)
35 : content::WebContentsObserver(web_contents), 56 : content::WebContentsObserver(web_contents),
36 enabled_(FeatureSwitch::scripts_require_action()->IsEnabled()) { 57 enabled_(FeatureSwitch::scripts_require_action()->IsEnabled()) {
37 CHECK(web_contents); 58 CHECK(web_contents);
38 } 59 }
39 60
40 ActiveScriptController::~ActiveScriptController() { 61 ActiveScriptController::~ActiveScriptController() {
41 LogUMA(); 62 LogUMA();
42 } 63 }
43 64
44 // static 65 // static
45 ActiveScriptController* ActiveScriptController::GetForWebContents( 66 ActiveScriptController* ActiveScriptController::GetForWebContents(
46 content::WebContents* web_contents) { 67 content::WebContents* web_contents) {
47 if (!web_contents) 68 if (!web_contents)
48 return NULL; 69 return NULL;
49 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); 70 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents);
50 if (!tab_helper) 71 if (!tab_helper)
51 return NULL; 72 return NULL;
52 LocationBarController* location_bar_controller = 73 LocationBarController* location_bar_controller =
53 tab_helper->location_bar_controller(); 74 tab_helper->location_bar_controller();
54 // This should never be NULL. 75 // This should never be NULL.
55 DCHECK(location_bar_controller); 76 DCHECK(location_bar_controller);
56 return location_bar_controller->active_script_controller(); 77 return location_bar_controller->active_script_controller();
57 } 78 }
58 79
59 bool ActiveScriptController::RequiresUserConsentForScriptInjection(
60 const Extension* extension) {
61 CHECK(extension);
62 if (!extension->permissions_data()->RequiresActionForScriptExecution(
63 extension,
64 SessionID::IdForTab(web_contents()),
65 web_contents()->GetVisibleURL()) ||
66 util::AllowedScriptingOnAllUrls(extension->id(),
67 web_contents()->GetBrowserContext())) {
68 return false;
69 }
70
71 // If the feature is not enabled, we automatically allow all extensions to
72 // run scripts.
73 if (!enabled_)
74 permitted_extensions_.insert(extension->id());
75
76 return permitted_extensions_.count(extension->id()) == 0;
77 }
78
79 void ActiveScriptController::RequestScriptInjection(
80 const Extension* extension,
81 const base::Closure& callback) {
82 CHECK(extension);
83 PendingRequestList& list = pending_requests_[extension->id()];
84 list.push_back(callback);
85
86 // If this was the first entry, notify the location bar that there's a new
87 // icon.
88 if (list.size() == 1u)
89 LocationBarController::NotifyChange(web_contents());
90 }
91
92 void ActiveScriptController::OnActiveTabPermissionGranted( 80 void ActiveScriptController::OnActiveTabPermissionGranted(
93 const Extension* extension) { 81 const Extension* extension) {
94 RunPendingForExtension(extension); 82 RunPendingForExtension(extension);
95 } 83 }
96 84
97 void ActiveScriptController::OnAdInjectionDetected( 85 void ActiveScriptController::OnAdInjectionDetected(
98 const std::set<std::string>& ad_injectors) { 86 const std::set<std::string>& ad_injectors) {
99 // We're only interested in data if there are ad injectors detected. 87 // We're only interested in data if there are ad injectors detected.
100 if (ad_injectors.empty()) 88 if (ad_injectors.empty())
101 return; 89 return;
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
152 permitted_extensions_.clear(); 140 permitted_extensions_.clear();
153 pending_requests_.clear(); 141 pending_requests_.clear();
154 } 142 }
155 143
156 void ActiveScriptController::OnExtensionUnloaded(const Extension* extension) { 144 void ActiveScriptController::OnExtensionUnloaded(const Extension* extension) {
157 PendingRequestMap::iterator iter = pending_requests_.find(extension->id()); 145 PendingRequestMap::iterator iter = pending_requests_.find(extension->id());
158 if (iter != pending_requests_.end()) 146 if (iter != pending_requests_.end())
159 pending_requests_.erase(iter); 147 pending_requests_.erase(iter);
160 } 148 }
161 149
150 PermissionsData::AccessType
151 ActiveScriptController::RequiresUserConsentForScriptInjection(
152 const Extension* extension,
153 UserScript::InjectionType type) {
154 CHECK(extension);
155
156 // If the feature is not enabled, we automatically allow all extensions to
157 // run scripts.
158 if (!enabled_)
159 permitted_extensions_.insert(extension->id());
160
161 // Allow the extension if it's been explicitly granted permission.
162 if (permitted_extensions_.count(extension->id()) > 0)
163 return PermissionsData::ACCESS_ALLOWED;
164
165 GURL url = web_contents()->GetVisibleURL();
166 int tab_id = SessionID::IdForTab(web_contents());
167 switch (type) {
168 case UserScript::CONTENT_SCRIPT:
169 return extension->permissions_data()->GetContentScriptAccess(
170 extension, url, url, tab_id, -1, NULL);
171 case UserScript::PROGRAMMATIC_SCRIPT:
172 return extension->permissions_data()->GetPageAccess(
173 extension, url, url, tab_id, -1, NULL);
174 }
175
176 NOTREACHED();
177 return PermissionsData::ACCESS_DENIED;
178 }
179
180 void ActiveScriptController::RequestScriptInjection(
181 const Extension* extension,
182 const base::Closure& callback) {
183 CHECK(extension);
184 PendingRequestList& list = pending_requests_[extension->id()];
185 list.push_back(callback);
186
187 // If this was the first entry, notify the location bar that there's a new
188 // icon.
189 if (list.size() == 1u)
190 LocationBarController::NotifyChange(web_contents());
191 }
192
162 void ActiveScriptController::RunPendingForExtension( 193 void ActiveScriptController::RunPendingForExtension(
163 const Extension* extension) { 194 const Extension* extension) {
164 DCHECK(extension); 195 DCHECK(extension);
165 PendingRequestMap::iterator iter =
166 pending_requests_.find(extension->id());
167 if (iter == pending_requests_.end())
168 return;
169 196
170 content::NavigationEntry* visible_entry = 197 content::NavigationEntry* visible_entry =
171 web_contents()->GetController().GetVisibleEntry(); 198 web_contents()->GetController().GetVisibleEntry();
172 // Refuse to run if there's no visible entry, because we have no idea of 199 // Refuse to run if there's no visible entry, because we have no idea of
173 // determining if it's the proper page. This should rarely, if ever, happen. 200 // determining if it's the proper page. This should rarely, if ever, happen.
174 if (!visible_entry) 201 if (!visible_entry)
175 return; 202 return;
176 203
177 // We add this to the list of permitted extensions and erase pending entries 204 // We add this to the list of permitted extensions and erase pending entries
178 // *before* running them to guard against the crazy case where running the 205 // *before* running them to guard against the crazy case where running the
179 // callbacks adds more entries. 206 // callbacks adds more entries.
180 permitted_extensions_.insert(extension->id()); 207 permitted_extensions_.insert(extension->id());
208
209 PendingRequestMap::iterator iter = pending_requests_.find(extension->id());
210 if (iter == pending_requests_.end())
211 return;
212
181 PendingRequestList requests; 213 PendingRequestList requests;
182 iter->second.swap(requests); 214 iter->second.swap(requests);
183 pending_requests_.erase(extension->id()); 215 pending_requests_.erase(extension->id());
184 216
185 // Clicking to run the extension counts as granting it permission to run on 217 // Clicking to run the extension counts as granting it permission to run on
186 // the given tab. 218 // the given tab.
187 // The extension may already have active tab at this point, but granting 219 // The extension may already have active tab at this point, but granting
188 // it twice is essentially a no-op. 220 // it twice is essentially a no-op.
189 TabHelper::FromWebContents(web_contents())-> 221 TabHelper::FromWebContents(web_contents())->
190 active_tab_permission_granter()->GrantIfRequested(extension); 222 active_tab_permission_granter()->GrantIfRequested(extension);
191 223
192 // Run all pending injections for the given extension. 224 // Run all pending injections for the given extension.
193 for (PendingRequestList::iterator request = requests.begin(); 225 for (PendingRequestList::iterator request = requests.begin();
194 request != requests.end(); 226 request != requests.end();
195 ++request) { 227 ++request) {
196 request->Run(); 228 request->Run();
197 } 229 }
198 230
199 // Inform the location bar that the action is now gone. 231 // Inform the location bar that the action is now gone.
200 LocationBarController::NotifyChange(web_contents()); 232 LocationBarController::NotifyChange(web_contents());
201 } 233 }
202 234
203 void ActiveScriptController::OnRequestScriptInjectionPermission( 235 void ActiveScriptController::OnRequestScriptInjectionPermission(
204 const std::string& extension_id, 236 const std::string& extension_id,
237 UserScript::InjectionType script_type,
205 int64 request_id) { 238 int64 request_id) {
206 if (!Extension::IdIsValid(extension_id)) { 239 if (!Extension::IdIsValid(extension_id)) {
207 NOTREACHED() << "'" << extension_id << "' is not a valid id."; 240 NOTREACHED() << "'" << extension_id << "' is not a valid id.";
208 return; 241 return;
209 } 242 }
210 243
211 const Extension* extension = 244 const Extension* extension =
212 ExtensionRegistry::Get(web_contents()->GetBrowserContext()) 245 ExtensionRegistry::Get(web_contents()->GetBrowserContext())
213 ->enabled_extensions().GetByID(extension_id); 246 ->enabled_extensions().GetByID(extension_id);
214 // We shouldn't allow extensions which are no longer enabled to run any 247 // We shouldn't allow extensions which are no longer enabled to run any
215 // scripts. Ignore the request. 248 // scripts. Ignore the request.
216 if (!extension) 249 if (!extension)
217 return; 250 return;
218 251
219 // If the request id is -1, that signals that the content script has already 252 // If the request id is -1, that signals that the content script has already
220 // ran (because this feature is not enabled). Add the extension to the list of 253 // ran (because this feature is not enabled). Add the extension to the list of
221 // permitted extensions (for metrics), and return immediately. 254 // permitted extensions (for metrics), and return immediately.
222 if (request_id == -1) { 255 if (request_id == -1) {
223 DCHECK(!enabled_); 256 if (ShouldRecordExtension(extension)) {
224 permitted_extensions_.insert(extension->id()); 257 DCHECK(!enabled_);
258 permitted_extensions_.insert(extension->id());
259 }
225 return; 260 return;
226 } 261 }
227 262
228 if (RequiresUserConsentForScriptInjection(extension)) { 263 switch (RequiresUserConsentForScriptInjection(extension, script_type)) {
229 // This base::Unretained() is safe, because the callback is only invoked by 264 case PermissionsData::ACCESS_ALLOWED:
230 // this object. 265 PermitScriptInjection(request_id);
231 RequestScriptInjection( 266 break;
232 extension, 267 case PermissionsData::ACCESS_WITHHELD:
233 base::Bind(&ActiveScriptController::PermitScriptInjection, 268 // This base::Unretained() is safe, because the callback is only invoked
234 base::Unretained(this), 269 // by this object.
235 request_id)); 270 RequestScriptInjection(
236 } else { 271 extension,
237 PermitScriptInjection(request_id); 272 base::Bind(&ActiveScriptController::PermitScriptInjection,
273 base::Unretained(this),
274 request_id));
275 break;
276 case PermissionsData::ACCESS_DENIED:
277 // We should usually only get a "deny access" if the page changed (as the
278 // renderer wouldn't have requested permission if the answer was always
279 // "no"). Just let the request fizzle and die.
280 break;
238 } 281 }
239 } 282 }
240 283
241 void ActiveScriptController::PermitScriptInjection(int64 request_id) { 284 void ActiveScriptController::PermitScriptInjection(int64 request_id) {
285 // This only sends the response to the renderer - the process of adding the
286 // extension to the list of |permitted_extensions_| is done elsewhere.
242 content::RenderViewHost* render_view_host = 287 content::RenderViewHost* render_view_host =
243 web_contents()->GetRenderViewHost(); 288 web_contents()->GetRenderViewHost();
244 if (render_view_host) { 289 if (render_view_host) {
245 render_view_host->Send(new ExtensionMsg_PermitScriptInjection( 290 render_view_host->Send(new ExtensionMsg_PermitScriptInjection(
246 render_view_host->GetRoutingID(), request_id)); 291 render_view_host->GetRoutingID(), request_id));
247 } 292 }
248 } 293 }
249 294
250 bool ActiveScriptController::OnMessageReceived(const IPC::Message& message) { 295 bool ActiveScriptController::OnMessageReceived(const IPC::Message& message) {
251 bool handled = true; 296 bool handled = true;
(...skipping 16 matching lines...) Expand all
268 UMA_HISTOGRAM_COUNTS_100( 313 UMA_HISTOGRAM_COUNTS_100(
269 "Extensions.ActiveScriptController.PermittedExtensions", 314 "Extensions.ActiveScriptController.PermittedExtensions",
270 permitted_extensions_.size()); 315 permitted_extensions_.size());
271 UMA_HISTOGRAM_COUNTS_100( 316 UMA_HISTOGRAM_COUNTS_100(
272 "Extensions.ActiveScriptController.DeniedExtensions", 317 "Extensions.ActiveScriptController.DeniedExtensions",
273 pending_requests_.size()); 318 pending_requests_.size());
274 } 319 }
275 } 320 }
276 321
277 } // namespace extensions 322 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698