ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / browser / extensions / active_script_controller.cc
blobc3e9956b60989bd3c9bc9fdb48690b16b187cbe0
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.
5 #include "chrome/browser/extensions/active_script_controller.h"
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/permissions_updater.h"
17 #include "chrome/browser/extensions/tab_helper.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/sessions/session_tab_helper.h"
20 #include "chrome/common/extensions/api/extension_action/action_info.h"
21 #include "components/crx_file/id_util.h"
22 #include "content/public/browser/navigation_controller.h"
23 #include "content/public/browser/navigation_details.h"
24 #include "content/public/browser/navigation_entry.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "content/public/browser/web_contents.h"
27 #include "extensions/browser/extension_registry.h"
28 #include "extensions/common/extension.h"
29 #include "extensions/common/extension_messages.h"
30 #include "extensions/common/extension_set.h"
31 #include "extensions/common/feature_switch.h"
32 #include "extensions/common/manifest.h"
33 #include "extensions/common/permissions/permission_set.h"
34 #include "extensions/common/permissions/permissions_data.h"
35 #include "ipc/ipc_message_macros.h"
37 namespace extensions {
39 ActiveScriptController::ActiveScriptController(
40 content::WebContents* web_contents)
41 : content::WebContentsObserver(web_contents),
42 num_page_requests_(0),
43 browser_context_(web_contents->GetBrowserContext()),
44 was_used_on_page_(false),
45 extension_registry_observer_(this) {
46 CHECK(web_contents);
47 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
50 ActiveScriptController::~ActiveScriptController() {
51 LogUMA();
54 // static
55 ActiveScriptController* ActiveScriptController::GetForWebContents(
56 content::WebContents* web_contents) {
57 if (!web_contents)
58 return NULL;
59 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents);
60 return tab_helper ? tab_helper->active_script_controller() : NULL;
63 void ActiveScriptController::OnActiveTabPermissionGranted(
64 const Extension* extension) {
65 RunPendingForExtension(extension);
68 void ActiveScriptController::OnAdInjectionDetected(
69 const std::set<std::string>& ad_injectors) {
70 // We're only interested in data if there are ad injectors detected.
71 if (ad_injectors.empty())
72 return;
74 size_t num_preventable_ad_injectors =
75 base::STLSetIntersection<std::set<std::string> >(
76 ad_injectors, permitted_extensions_).size();
78 UMA_HISTOGRAM_COUNTS_100(
79 "Extensions.ActiveScriptController.PreventableAdInjectors",
80 num_preventable_ad_injectors);
81 UMA_HISTOGRAM_COUNTS_100(
82 "Extensions.ActiveScriptController.UnpreventableAdInjectors",
83 ad_injectors.size() - num_preventable_ad_injectors);
86 void ActiveScriptController::AlwaysRunOnVisibleOrigin(
87 const Extension* extension) {
88 const GURL& url = web_contents()->GetVisibleURL();
89 URLPatternSet new_explicit_hosts;
90 URLPatternSet new_scriptable_hosts;
92 scoped_refptr<const PermissionSet> withheld_permissions =
93 extension->permissions_data()->withheld_permissions();
94 if (withheld_permissions->explicit_hosts().MatchesURL(url)) {
95 new_explicit_hosts.AddOrigin(UserScript::ValidUserScriptSchemes(),
96 url.GetOrigin());
98 if (withheld_permissions->scriptable_hosts().MatchesURL(url)) {
99 new_scriptable_hosts.AddOrigin(UserScript::ValidUserScriptSchemes(),
100 url.GetOrigin());
103 scoped_refptr<PermissionSet> new_permissions =
104 new PermissionSet(APIPermissionSet(),
105 ManifestPermissionSet(),
106 new_explicit_hosts,
107 new_scriptable_hosts);
109 // Update permissions for the session. This adds |new_permissions| to active
110 // permissions and granted permissions.
111 // TODO(devlin): Make sure that the permission is removed from
112 // withheld_permissions if appropriate.
113 PermissionsUpdater(browser_context_).AddPermissions(extension,
114 new_permissions.get());
116 // Allow current tab to run injection.
117 OnClicked(extension);
120 void ActiveScriptController::OnClicked(const Extension* extension) {
121 DCHECK(ContainsKey(pending_requests_, extension->id()));
122 RunPendingForExtension(extension);
125 bool ActiveScriptController::WantsToRun(const Extension* extension) {
126 return pending_requests_.count(extension->id()) > 0;
129 PermissionsData::AccessType
130 ActiveScriptController::RequiresUserConsentForScriptInjection(
131 const Extension* extension,
132 UserScript::InjectionType type) {
133 CHECK(extension);
135 // Allow the extension if it's been explicitly granted permission.
136 if (permitted_extensions_.count(extension->id()) > 0)
137 return PermissionsData::ACCESS_ALLOWED;
139 GURL url = web_contents()->GetVisibleURL();
140 int tab_id = SessionTabHelper::IdForTab(web_contents());
141 switch (type) {
142 case UserScript::CONTENT_SCRIPT:
143 return extension->permissions_data()->GetContentScriptAccess(
144 extension, url, url, tab_id, -1, NULL);
145 case UserScript::PROGRAMMATIC_SCRIPT:
146 return extension->permissions_data()->GetPageAccess(
147 extension, url, url, tab_id, -1, NULL);
150 NOTREACHED();
151 return PermissionsData::ACCESS_DENIED;
154 void ActiveScriptController::RequestScriptInjection(
155 const Extension* extension,
156 const base::Closure& callback) {
157 CHECK(extension);
158 PendingRequestList& list = pending_requests_[extension->id()];
159 list.push_back(callback);
161 // If this was the first entry, we need to notify that a new extension wants
162 // to run.
163 if (list.size() == 1u)
164 NotifyChange(extension);
166 was_used_on_page_ = true;
169 void ActiveScriptController::RunPendingForExtension(
170 const Extension* extension) {
171 DCHECK(extension);
173 content::NavigationEntry* visible_entry =
174 web_contents()->GetController().GetVisibleEntry();
175 // Refuse to run if there's no visible entry, because we have no idea of
176 // determining if it's the proper page. This should rarely, if ever, happen.
177 if (!visible_entry)
178 return;
180 // We add this to the list of permitted extensions and erase pending entries
181 // *before* running them to guard against the crazy case where running the
182 // callbacks adds more entries.
183 permitted_extensions_.insert(extension->id());
185 PendingRequestMap::iterator iter = pending_requests_.find(extension->id());
186 if (iter == pending_requests_.end())
187 return;
189 PendingRequestList requests;
190 iter->second.swap(requests);
191 pending_requests_.erase(extension->id());
193 // Clicking to run the extension counts as granting it permission to run on
194 // the given tab.
195 // The extension may already have active tab at this point, but granting
196 // it twice is essentially a no-op.
197 TabHelper::FromWebContents(web_contents())->
198 active_tab_permission_granter()->GrantIfRequested(extension);
200 // Run all pending injections for the given extension.
201 for (PendingRequestList::iterator request = requests.begin();
202 request != requests.end();
203 ++request) {
204 request->Run();
207 // The extension ran, so we need to update the ExtensionActionAPI that we no
208 // longer want to act.
209 NotifyChange(extension);
212 void ActiveScriptController::OnRequestScriptInjectionPermission(
213 const std::string& extension_id,
214 UserScript::InjectionType script_type,
215 int64 request_id) {
216 if (!crx_file::id_util::IdIsValid(extension_id)) {
217 NOTREACHED() << "'" << extension_id << "' is not a valid id.";
218 return;
221 const Extension* extension =
222 ExtensionRegistry::Get(browser_context_)
223 ->enabled_extensions().GetByID(extension_id);
224 // We shouldn't allow extensions which are no longer enabled to run any
225 // scripts. Ignore the request.
226 if (!extension)
227 return;
229 // If the request id is -1, that signals that the content script has already
230 // ran (because this feature is not enabled). Add the extension to the list of
231 // permitted extensions (for metrics), and return immediately.
232 if (request_id == -1) {
233 if (PermissionsData::ScriptsMayRequireActionForExtension(
234 extension,
235 extension->permissions_data()->active_permissions().get())) {
236 permitted_extensions_.insert(extension->id());
238 return;
241 ++num_page_requests_;
243 switch (RequiresUserConsentForScriptInjection(extension, script_type)) {
244 case PermissionsData::ACCESS_ALLOWED:
245 PermitScriptInjection(request_id);
246 break;
247 case PermissionsData::ACCESS_WITHHELD:
248 // This base::Unretained() is safe, because the callback is only invoked
249 // by this object.
250 RequestScriptInjection(
251 extension,
252 base::Bind(&ActiveScriptController::PermitScriptInjection,
253 base::Unretained(this),
254 request_id));
255 break;
256 case PermissionsData::ACCESS_DENIED:
257 // We should usually only get a "deny access" if the page changed (as the
258 // renderer wouldn't have requested permission if the answer was always
259 // "no"). Just let the request fizzle and die.
260 break;
264 void ActiveScriptController::PermitScriptInjection(int64 request_id) {
265 // This only sends the response to the renderer - the process of adding the
266 // extension to the list of |permitted_extensions_| is done elsewhere.
267 content::RenderViewHost* render_view_host =
268 web_contents()->GetRenderViewHost();
269 if (render_view_host) {
270 render_view_host->Send(new ExtensionMsg_PermitScriptInjection(
271 render_view_host->GetRoutingID(), request_id));
275 void ActiveScriptController::NotifyChange(const Extension* extension) {
276 ExtensionActionAPI* extension_action_api =
277 ExtensionActionAPI::Get(browser_context_);
278 ExtensionAction* extension_action =
279 ExtensionActionManager::Get(browser_context_)->
280 GetExtensionAction(*extension);
281 // If the extension has an action, we need to notify that it's updated.
282 if (extension_action) {
283 extension_action_api->NotifyChange(
284 extension_action, web_contents(), browser_context_);
287 // We also notify that page actions may have changed.
288 extension_action_api->NotifyPageActionsChanged(web_contents());
291 void ActiveScriptController::LogUMA() const {
292 // We only log the permitted extensions metric if the feature was used at all
293 // on the page, because otherwise the data will be boring.
294 if (was_used_on_page_) {
295 UMA_HISTOGRAM_COUNTS_100(
296 "Extensions.ActiveScriptController.PermittedExtensions",
297 permitted_extensions_.size());
298 UMA_HISTOGRAM_COUNTS_100(
299 "Extensions.ActiveScriptController.DeniedExtensions",
300 pending_requests_.size());
304 bool ActiveScriptController::OnMessageReceived(const IPC::Message& message) {
305 bool handled = true;
306 IPC_BEGIN_MESSAGE_MAP(ActiveScriptController, message)
307 IPC_MESSAGE_HANDLER(ExtensionHostMsg_RequestScriptInjectionPermission,
308 OnRequestScriptInjectionPermission)
309 IPC_MESSAGE_UNHANDLED(handled = false)
310 IPC_END_MESSAGE_MAP()
311 return handled;
314 void ActiveScriptController::DidNavigateMainFrame(
315 const content::LoadCommittedDetails& details,
316 const content::FrameNavigateParams& params) {
317 if (details.is_in_page)
318 return;
320 LogUMA();
321 num_page_requests_ = 0;
322 permitted_extensions_.clear();
323 pending_requests_.clear();
324 was_used_on_page_ = false;
327 void ActiveScriptController::OnExtensionUnloaded(
328 content::BrowserContext* browser_context,
329 const Extension* extension,
330 UnloadedExtensionInfo::Reason reason) {
331 PendingRequestMap::iterator iter = pending_requests_.find(extension->id());
332 if (iter != pending_requests_.end()) {
333 pending_requests_.erase(iter);
334 ExtensionActionAPI::Get(browser_context_)->
335 NotifyPageActionsChanged(web_contents());
339 } // namespace extensions