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 3 // found in the LICENSE file. 4 5 #include "chrome/browser/extensions/active_script_controller.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/metrics/histogram.h" 11 #include "base/stl_util.h" 12 #include "chrome/browser/extensions/active_tab_permission_granter.h" 13 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" 14 #include "chrome/browser/extensions/extension_action.h" 15 #include "chrome/browser/extensions/extension_action_manager.h" 16 #include "chrome/browser/extensions/extension_util.h" 17 #include "chrome/browser/extensions/permissions_updater.h" 18 #include "chrome/browser/extensions/tab_helper.h" 19 #include "chrome/browser/profiles/profile.h" 20 #include "chrome/browser/sessions/session_tab_helper.h" 21 #include "chrome/common/extensions/api/extension_action/action_info.h" 22 #include "components/crx_file/id_util.h" 23 #include "content/public/browser/navigation_controller.h" 24 #include "content/public/browser/navigation_details.h" 25 #include "content/public/browser/navigation_entry.h" 26 #include "content/public/browser/render_view_host.h" 27 #include "content/public/browser/web_contents.h" 28 #include "extensions/browser/extension_registry.h" 29 #include "extensions/common/extension.h" 30 #include "extensions/common/extension_messages.h" 31 #include "extensions/common/extension_set.h" 32 #include "extensions/common/feature_switch.h" 33 #include "extensions/common/manifest.h" 34 #include "extensions/common/permissions/permission_set.h" 35 #include "extensions/common/permissions/permissions_data.h" 36 #include "ipc/ipc_message_macros.h" 37 38 namespace extensions { 39 40 namespace { 41 42 // Returns true if the extension should be regarded as a "permitted" extension 43 // for the case of metrics. We need this because we only actually withhold 44 // permissions if the switch is enabled, but want to record metrics in all 45 // cases. 46 // "ExtensionWouldHaveHadHostPermissionsWithheldIfSwitchWasOn()" would be 47 // more accurate, but too long. 48 bool ShouldRecordExtension(const Extension* extension) { 49 return extension->ShouldDisplayInExtensionSettings() && 50 !Manifest::IsPolicyLocation(extension->location()) && 51 !Manifest::IsComponentLocation(extension->location()) && 52 !PermissionsData::CanExecuteScriptEverywhere(extension) && 53 extension->permissions_data() 54 ->active_permissions() 55 ->ShouldWarnAllHosts(); 56 } 57 58 } // namespace 59 60 ActiveScriptController::ActiveScriptController( 61 content::WebContents* web_contents) 62 : content::WebContentsObserver(web_contents), 63 enabled_(FeatureSwitch::scripts_require_action()->IsEnabled()), 64 extension_registry_observer_(this) { 65 CHECK(web_contents); 66 extension_registry_observer_.Add( 67 ExtensionRegistry::Get(web_contents->GetBrowserContext())); 68 } 69 70 ActiveScriptController::~ActiveScriptController() { 71 LogUMA(); 72 } 73 74 // static 75 ActiveScriptController* ActiveScriptController::GetForWebContents( 76 content::WebContents* web_contents) { 77 if (!web_contents) 78 return NULL; 79 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); 80 return tab_helper ? tab_helper->active_script_controller() : NULL; 81 } 82 83 void ActiveScriptController::OnActiveTabPermissionGranted( 84 const Extension* extension) { 85 RunPendingForExtension(extension); 86 } 87 88 void ActiveScriptController::OnAdInjectionDetected( 89 const std::set<std::string>& ad_injectors) { 90 // We're only interested in data if there are ad injectors detected. 91 if (ad_injectors.empty()) 92 return; 93 94 size_t num_preventable_ad_injectors = 95 base::STLSetIntersection<std::set<std::string> >( 96 ad_injectors, permitted_extensions_).size(); 97 98 UMA_HISTOGRAM_COUNTS_100( 99 "Extensions.ActiveScriptController.PreventableAdInjectors", 100 num_preventable_ad_injectors); 101 UMA_HISTOGRAM_COUNTS_100( 102 "Extensions.ActiveScriptController.UnpreventableAdInjectors", 103 ad_injectors.size() - num_preventable_ad_injectors); 104 } 105 106 void ActiveScriptController::AlwaysRunOnVisibleOrigin( 107 const Extension* extension) { 108 const GURL& url = web_contents()->GetVisibleURL(); 109 URLPatternSet new_explicit_hosts; 110 URLPatternSet new_scriptable_hosts; 111 112 scoped_refptr<const PermissionSet> withheld_permissions = 113 extension->permissions_data()->withheld_permissions(); 114 if (withheld_permissions->explicit_hosts().MatchesURL(url)) { 115 new_explicit_hosts.AddOrigin(UserScript::ValidUserScriptSchemes(), 116 url.GetOrigin()); 117 } 118 if (withheld_permissions->scriptable_hosts().MatchesURL(url)) { 119 new_scriptable_hosts.AddOrigin(UserScript::ValidUserScriptSchemes(), 120 url.GetOrigin()); 121 } 122 123 scoped_refptr<PermissionSet> new_permissions = 124 new PermissionSet(APIPermissionSet(), 125 ManifestPermissionSet(), 126 new_explicit_hosts, 127 new_scriptable_hosts); 128 129 // Update permissions for the session. This adds |new_permissions| to active 130 // permissions and granted permissions. 131 // TODO(devlin): Make sure that the permission is removed from 132 // withheld_permissions if appropriate. 133 PermissionsUpdater(web_contents()->GetBrowserContext()) 134 .AddPermissions(extension, new_permissions.get()); 135 136 // Allow current tab to run injection. 137 OnClicked(extension); 138 } 139 140 void ActiveScriptController::OnClicked(const Extension* extension) { 141 DCHECK(ContainsKey(pending_requests_, extension->id())); 142 RunPendingForExtension(extension); 143 } 144 145 bool ActiveScriptController::WantsToRun(const Extension* extension) { 146 return enabled_ && pending_requests_.count(extension->id()) > 0; 147 } 148 149 PermissionsData::AccessType 150 ActiveScriptController::RequiresUserConsentForScriptInjection( 151 const Extension* extension, 152 UserScript::InjectionType type) { 153 CHECK(extension); 154 155 // If the feature is not enabled, we automatically allow all extensions to 156 // run scripts. 157 if (!enabled_) 158 permitted_extensions_.insert(extension->id()); 159 160 // Allow the extension if it's been explicitly granted permission. 161 if (permitted_extensions_.count(extension->id()) > 0) 162 return PermissionsData::ACCESS_ALLOWED; 163 164 GURL url = web_contents()->GetVisibleURL(); 165 int tab_id = SessionTabHelper::IdForTab(web_contents()); 166 switch (type) { 167 case UserScript::CONTENT_SCRIPT: 168 return extension->permissions_data()->GetContentScriptAccess( 169 extension, url, url, tab_id, -1, NULL); 170 case UserScript::PROGRAMMATIC_SCRIPT: 171 return extension->permissions_data()->GetPageAccess( 172 extension, url, url, tab_id, -1, NULL); 173 } 174 175 NOTREACHED(); 176 return PermissionsData::ACCESS_DENIED; 177 } 178 179 void ActiveScriptController::RequestScriptInjection( 180 const Extension* extension, 181 const base::Closure& callback) { 182 CHECK(extension); 183 PendingRequestList& list = pending_requests_[extension->id()]; 184 list.push_back(callback); 185 186 // If this was the first entry, notify the location bar that there's a new 187 // icon. 188 if (list.size() == 1u) { 189 ExtensionActionAPI::Get(web_contents()->GetBrowserContext())-> 190 NotifyPageActionsChanged(web_contents()); 191 } 192 } 193 194 void ActiveScriptController::RunPendingForExtension( 195 const Extension* extension) { 196 DCHECK(extension); 197 198 content::NavigationEntry* visible_entry = 199 web_contents()->GetController().GetVisibleEntry(); 200 // Refuse to run if there's no visible entry, because we have no idea of 201 // determining if it's the proper page. This should rarely, if ever, happen. 202 if (!visible_entry) 203 return; 204 205 // We add this to the list of permitted extensions and erase pending entries 206 // *before* running them to guard against the crazy case where running the 207 // callbacks adds more entries. 208 permitted_extensions_.insert(extension->id()); 209 210 PendingRequestMap::iterator iter = pending_requests_.find(extension->id()); 211 if (iter == pending_requests_.end()) 212 return; 213 214 PendingRequestList requests; 215 iter->second.swap(requests); 216 pending_requests_.erase(extension->id()); 217 218 // Clicking to run the extension counts as granting it permission to run on 219 // the given tab. 220 // The extension may already have active tab at this point, but granting 221 // it twice is essentially a no-op. 222 TabHelper::FromWebContents(web_contents())-> 223 active_tab_permission_granter()->GrantIfRequested(extension); 224 225 // Run all pending injections for the given extension. 226 for (PendingRequestList::iterator request = requests.begin(); 227 request != requests.end(); 228 ++request) { 229 request->Run(); 230 } 231 232 // Inform the location bar that the action is now gone. 233 ExtensionActionAPI::Get(web_contents()->GetBrowserContext())-> 234 NotifyPageActionsChanged(web_contents()); 235 } 236 237 void ActiveScriptController::OnRequestScriptInjectionPermission( 238 const std::string& extension_id, 239 UserScript::InjectionType script_type, 240 int64 request_id) { 241 if (!crx_file::id_util::IdIsValid(extension_id)) { 242 NOTREACHED() << "'" << extension_id << "' is not a valid id."; 243 return; 244 } 245 246 const Extension* extension = 247 ExtensionRegistry::Get(web_contents()->GetBrowserContext()) 248 ->enabled_extensions().GetByID(extension_id); 249 // We shouldn't allow extensions which are no longer enabled to run any 250 // scripts. Ignore the request. 251 if (!extension) 252 return; 253 254 // If the request id is -1, that signals that the content script has already 255 // ran (because this feature is not enabled). Add the extension to the list of 256 // permitted extensions (for metrics), and return immediately. 257 if (request_id == -1) { 258 if (ShouldRecordExtension(extension)) { 259 DCHECK(!enabled_); 260 permitted_extensions_.insert(extension->id()); 261 } 262 return; 263 } 264 265 switch (RequiresUserConsentForScriptInjection(extension, script_type)) { 266 case PermissionsData::ACCESS_ALLOWED: 267 PermitScriptInjection(request_id); 268 break; 269 case PermissionsData::ACCESS_WITHHELD: 270 // This base::Unretained() is safe, because the callback is only invoked 271 // by this object. 272 RequestScriptInjection( 273 extension, 274 base::Bind(&ActiveScriptController::PermitScriptInjection, 275 base::Unretained(this), 276 request_id)); 277 break; 278 case PermissionsData::ACCESS_DENIED: 279 // We should usually only get a "deny access" if the page changed (as the 280 // renderer wouldn't have requested permission if the answer was always 281 // "no"). Just let the request fizzle and die. 282 break; 283 } 284 } 285 286 void ActiveScriptController::PermitScriptInjection(int64 request_id) { 287 // This only sends the response to the renderer - the process of adding the 288 // extension to the list of |permitted_extensions_| is done elsewhere. 289 content::RenderViewHost* render_view_host = 290 web_contents()->GetRenderViewHost(); 291 if (render_view_host) { 292 render_view_host->Send(new ExtensionMsg_PermitScriptInjection( 293 render_view_host->GetRoutingID(), request_id)); 294 } 295 } 296 297 void ActiveScriptController::LogUMA() const { 298 UMA_HISTOGRAM_COUNTS_100( 299 "Extensions.ActiveScriptController.ShownActiveScriptsOnPage", 300 pending_requests_.size()); 301 302 // We only log the permitted extensions metric if the feature is enabled, 303 // because otherwise the data will be boring (100% allowed). 304 if (enabled_) { 305 UMA_HISTOGRAM_COUNTS_100( 306 "Extensions.ActiveScriptController.PermittedExtensions", 307 permitted_extensions_.size()); 308 UMA_HISTOGRAM_COUNTS_100( 309 "Extensions.ActiveScriptController.DeniedExtensions", 310 pending_requests_.size()); 311 } 312 } 313 314 bool ActiveScriptController::OnMessageReceived(const IPC::Message& message) { 315 bool handled = true; 316 IPC_BEGIN_MESSAGE_MAP(ActiveScriptController, message) 317 IPC_MESSAGE_HANDLER(ExtensionHostMsg_RequestScriptInjectionPermission, 318 OnRequestScriptInjectionPermission) 319 IPC_MESSAGE_UNHANDLED(handled = false) 320 IPC_END_MESSAGE_MAP() 321 return handled; 322 } 323 324 void ActiveScriptController::DidNavigateMainFrame( 325 const content::LoadCommittedDetails& details, 326 const content::FrameNavigateParams& params) { 327 if (details.is_in_page) 328 return; 329 330 LogUMA(); 331 permitted_extensions_.clear(); 332 pending_requests_.clear(); 333 } 334 335 void ActiveScriptController::OnExtensionUnloaded( 336 content::BrowserContext* browser_context, 337 const Extension* extension, 338 UnloadedExtensionInfo::Reason reason) { 339 PendingRequestMap::iterator iter = pending_requests_.find(extension->id()); 340 if (iter != pending_requests_.end()) { 341 pending_requests_.erase(iter); 342 ExtensionActionAPI::Get(web_contents()->GetBrowserContext())-> 343 NotifyPageActionsChanged(web_contents()); 344 } 345 } 346 347 } // namespace extensions 348