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/extension_action.h"
14 #include "chrome/browser/extensions/extension_util.h"
15 #include "chrome/browser/extensions/location_bar_controller.h"
16 #include "chrome/browser/extensions/tab_helper.h"
17 #include "chrome/browser/sessions/session_id.h"
18 #include "chrome/common/extensions/api/extension_action/action_info.h"
19 #include "content/public/browser/navigation_controller.h"
20 #include "content/public/browser/navigation_entry.h"
21 #include "content/public/browser/render_view_host.h"
22 #include "content/public/browser/web_contents.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/extension_messages.h"
26 #include "extensions/common/extension_set.h"
27 #include "extensions/common/feature_switch.h"
28 #include "extensions/common/permissions/permissions_data.h"
29 #include "ipc/ipc_message_macros.h"
30
31 namespace extensions {
32
PendingRequest()33 ActiveScriptController::PendingRequest::PendingRequest() :
34 page_id(-1) {
35 }
36
PendingRequest(const base::Closure & closure,int page_id)37 ActiveScriptController::PendingRequest::PendingRequest(
38 const base::Closure& closure,
39 int page_id)
40 : closure(closure),
41 page_id(page_id) {
42 }
43
~PendingRequest()44 ActiveScriptController::PendingRequest::~PendingRequest() {
45 }
46
ActiveScriptController(content::WebContents * web_contents)47 ActiveScriptController::ActiveScriptController(
48 content::WebContents* web_contents)
49 : content::WebContentsObserver(web_contents),
50 enabled_(FeatureSwitch::scripts_require_action()->IsEnabled()) {
51 CHECK(web_contents);
52 }
53
~ActiveScriptController()54 ActiveScriptController::~ActiveScriptController() {
55 LogUMA();
56 }
57
58 // static
GetForWebContents(content::WebContents * web_contents)59 ActiveScriptController* ActiveScriptController::GetForWebContents(
60 content::WebContents* web_contents) {
61 if (!web_contents)
62 return NULL;
63 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents);
64 if (!tab_helper)
65 return NULL;
66 LocationBarController* location_bar_controller =
67 tab_helper->location_bar_controller();
68 // This should never be NULL.
69 DCHECK(location_bar_controller);
70 return location_bar_controller->active_script_controller();
71 }
72
RequiresUserConsentForScriptInjection(const Extension * extension)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
RequestScriptInjection(const Extension * extension,int page_id,const base::Closure & callback)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
OnActiveTabPermissionGranted(const Extension * extension)107 void ActiveScriptController::OnActiveTabPermissionGranted(
108 const Extension* extension) {
109 RunPendingForExtension(extension);
110 }
111
OnAdInjectionDetected(const std::set<std::string> & ad_injectors)112 void ActiveScriptController::OnAdInjectionDetected(
113 const std::set<std::string>& ad_injectors) {
114 // We're only interested in data if there are ad injectors detected.
115 if (ad_injectors.empty())
116 return;
117
118 size_t num_preventable_ad_injectors =
119 base::STLSetIntersection<std::set<std::string> >(
120 ad_injectors, permitted_extensions_).size();
121
122 UMA_HISTOGRAM_COUNTS_100(
123 "Extensions.ActiveScriptController.PreventableAdInjectors",
124 num_preventable_ad_injectors);
125 UMA_HISTOGRAM_COUNTS_100(
126 "Extensions.ActiveScriptController.UnpreventableAdInjectors",
127 ad_injectors.size() - num_preventable_ad_injectors);
128 }
129
GetActionForExtension(const Extension * extension)130 ExtensionAction* ActiveScriptController::GetActionForExtension(
131 const Extension* extension) {
132 if (!enabled_ || pending_requests_.count(extension->id()) == 0)
133 return NULL; // No action for this extension.
134
135 ActiveScriptMap::iterator existing =
136 active_script_actions_.find(extension->id());
137 if (existing != active_script_actions_.end())
138 return existing->second.get();
139
140 linked_ptr<ExtensionAction> action(new ExtensionAction(
141 extension->id(), ActionInfo::TYPE_PAGE, ActionInfo()));
142 action->SetTitle(ExtensionAction::kDefaultTabId, extension->name());
143 action->SetIsVisible(ExtensionAction::kDefaultTabId, true);
144
145 const ActionInfo* action_info = ActionInfo::GetPageActionInfo(extension);
146 if (!action_info)
147 action_info = ActionInfo::GetBrowserActionInfo(extension);
148
149 if (action_info && !action_info->default_icon.empty()) {
150 action->set_default_icon(
151 make_scoped_ptr(new ExtensionIconSet(action_info->default_icon)));
152 }
153
154 active_script_actions_[extension->id()] = action;
155 return action.get();
156 }
157
OnClicked(const Extension * extension)158 LocationBarController::Action ActiveScriptController::OnClicked(
159 const Extension* extension) {
160 DCHECK(ContainsKey(pending_requests_, extension->id()));
161 RunPendingForExtension(extension);
162 return LocationBarController::ACTION_NONE;
163 }
164
OnNavigated()165 void ActiveScriptController::OnNavigated() {
166 LogUMA();
167 permitted_extensions_.clear();
168 pending_requests_.clear();
169 }
170
OnExtensionUnloaded(const Extension * extension)171 void ActiveScriptController::OnExtensionUnloaded(const Extension* extension) {
172 PendingRequestMap::iterator iter = pending_requests_.find(extension->id());
173 if (iter != pending_requests_.end())
174 pending_requests_.erase(iter);
175 }
176
RunPendingForExtension(const Extension * extension)177 void ActiveScriptController::RunPendingForExtension(
178 const Extension* extension) {
179 DCHECK(extension);
180 PendingRequestMap::iterator iter =
181 pending_requests_.find(extension->id());
182 if (iter == pending_requests_.end())
183 return;
184
185 content::NavigationEntry* visible_entry =
186 web_contents()->GetController().GetVisibleEntry();
187 // 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.
189 if (!visible_entry)
190 return;
191
192 int page_id = visible_entry->GetPageID();
193
194 // 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
196 // callbacks adds more entries.
197 permitted_extensions_.insert(extension->id());
198 PendingRequestList requests;
199 iter->second.swap(requests);
200 pending_requests_.erase(extension->id());
201
202 // Clicking to run the extension counts as granting it permission to run on
203 // the given tab.
204 // The extension may already have active tab at this point, but granting
205 // it twice is essentially a no-op.
206 TabHelper::FromWebContents(web_contents())->
207 active_tab_permission_granter()->GrantIfRequested(extension);
208
209 // Run all pending injections for the given extension.
210 for (PendingRequestList::iterator request = requests.begin();
211 request != requests.end();
212 ++request) {
213 // Only run if it's on the proper page.
214 if (request->page_id == page_id)
215 request->closure.Run();
216 }
217
218 // Inform the location bar that the action is now gone.
219 LocationBarController::NotifyChange(web_contents());
220 }
221
OnRequestContentScriptPermission(const std::string & extension_id,int page_id,int request_id)222 void ActiveScriptController::OnRequestContentScriptPermission(
223 const std::string& extension_id,
224 int page_id,
225 int request_id) {
226 if (!Extension::IdIsValid(extension_id)) {
227 NOTREACHED() << "'" << extension_id << "' is not a valid id.";
228 return;
229 }
230
231 const Extension* extension =
232 ExtensionRegistry::Get(web_contents()->GetBrowserContext())
233 ->enabled_extensions().GetByID(extension_id);
234 // We shouldn't allow extensions which are no longer enabled to run any
235 // scripts. Ignore the request.
236 if (!extension)
237 return;
238
239 // 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
241 // permitted extensions (for metrics), and return immediately.
242 if (request_id == -1) {
243 DCHECK(!enabled_);
244 permitted_extensions_.insert(extension->id());
245 return;
246 }
247
248 if (RequiresUserConsentForScriptInjection(extension)) {
249 // This base::Unretained() is safe, because the callback is only invoked by
250 // this object.
251 RequestScriptInjection(
252 extension,
253 page_id,
254 base::Bind(&ActiveScriptController::GrantContentScriptPermission,
255 base::Unretained(this),
256 request_id));
257 } else {
258 GrantContentScriptPermission(request_id);
259 }
260 }
261
GrantContentScriptPermission(int request_id)262 void ActiveScriptController::GrantContentScriptPermission(int request_id) {
263 content::RenderViewHost* render_view_host =
264 web_contents()->GetRenderViewHost();
265 if (render_view_host) {
266 render_view_host->Send(new ExtensionMsg_GrantContentScriptPermission(
267 render_view_host->GetRoutingID(),
268 request_id));
269 }
270 }
271
OnMessageReceived(const IPC::Message & message)272 bool ActiveScriptController::OnMessageReceived(const IPC::Message& message) {
273 bool handled = true;
274 IPC_BEGIN_MESSAGE_MAP(ActiveScriptController, message)
275 IPC_MESSAGE_HANDLER(ExtensionHostMsg_RequestContentScriptPermission,
276 OnRequestContentScriptPermission)
277 IPC_MESSAGE_UNHANDLED(handled = false)
278 IPC_END_MESSAGE_MAP()
279 return handled;
280 }
281
LogUMA() const282 void ActiveScriptController::LogUMA() const {
283 UMA_HISTOGRAM_COUNTS_100(
284 "Extensions.ActiveScriptController.ShownActiveScriptsOnPage",
285 pending_requests_.size());
286
287 // We only log the permitted extensions metric if the feature is enabled,
288 // because otherwise the data will be boring (100% allowed).
289 if (enabled_) {
290 UMA_HISTOGRAM_COUNTS_100(
291 "Extensions.ActiveScriptController.PermittedExtensions",
292 permitted_extensions_.size());
293 UMA_HISTOGRAM_COUNTS_100(
294 "Extensions.ActiveScriptController.DeniedExtensions",
295 pending_requests_.size());
296 }
297 }
298
299 } // namespace extensions
300