| 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::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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |