Reland the ULONG -> SIZE_T change from 317177
[chromium-blink-merge.git] / extensions / browser / extension_function_dispatcher.cc
blobcbc08cca1a24c5afa3c0a51597a5bfc3c2dc00f1
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 "extensions/browser/extension_function_dispatcher.h"
7 #include "base/bind.h"
8 #include "base/json/json_string_value_serializer.h"
9 #include "base/lazy_instance.h"
10 #include "base/logging.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/metrics/sparse_histogram.h"
13 #include "base/process/process.h"
14 #include "base/profiler/scoped_profile.h"
15 #include "base/values.h"
16 #include "build/build_config.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_frame_host.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/user_metrics.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/browser/web_contents_observer.h"
24 #include "content/public/common/result_codes.h"
25 #include "extensions/browser/api_activity_monitor.h"
26 #include "extensions/browser/extension_function_registry.h"
27 #include "extensions/browser/extension_registry.h"
28 #include "extensions/browser/extension_system.h"
29 #include "extensions/browser/extensions_browser_client.h"
30 #include "extensions/browser/io_thread_extension_message_filter.h"
31 #include "extensions/browser/process_manager.h"
32 #include "extensions/browser/process_map.h"
33 #include "extensions/browser/quota_service.h"
34 #include "extensions/common/extension_api.h"
35 #include "extensions/common/extension_messages.h"
36 #include "extensions/common/extension_set.h"
37 #include "ipc/ipc_message.h"
38 #include "ipc/ipc_message_macros.h"
40 using content::BrowserThread;
41 using content::RenderViewHost;
43 namespace extensions {
44 namespace {
46 // Notifies the ApiActivityMonitor that an extension API function has been
47 // called. May be called from any thread.
48 void NotifyApiFunctionCalled(const std::string& extension_id,
49 const std::string& api_name,
50 scoped_ptr<base::ListValue> args,
51 content::BrowserContext* browser_context) {
52 // The ApiActivityMonitor can only be accessed from the main (UI) thread. If
53 // we're running on the wrong thread, re-dispatch from the main thread.
54 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
55 BrowserThread::PostTask(BrowserThread::UI,
56 FROM_HERE,
57 base::Bind(&NotifyApiFunctionCalled,
58 extension_id,
59 api_name,
60 base::Passed(&args),
61 browser_context));
62 return;
64 // The BrowserContext may become invalid after the task above is posted.
65 if (!ExtensionsBrowserClient::Get()->IsValidContext(browser_context))
66 return;
68 ApiActivityMonitor* monitor =
69 ExtensionsBrowserClient::Get()->GetApiActivityMonitor(browser_context);
70 if (monitor)
71 monitor->OnApiFunctionCalled(extension_id, api_name, args.Pass());
74 // Separate copy of ExtensionAPI used for IO thread extension functions. We need
75 // this because ExtensionAPI has mutable data. It should be possible to remove
76 // this once all the extension APIs are updated to the feature system.
77 struct Static {
78 Static() : api(ExtensionAPI::CreateWithDefaultConfiguration()) {}
79 scoped_ptr<ExtensionAPI> api;
81 base::LazyInstance<Static> g_global_io_data = LAZY_INSTANCE_INITIALIZER;
83 // Kills the specified process because it sends us a malformed message.
84 void KillBadMessageSender(base::ProcessHandle process) {
85 NOTREACHED();
86 content::RecordAction(base::UserMetricsAction("BadMessageTerminate_EFD"));
87 if (process)
88 base::KillProcess(process, content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
91 void CommonResponseCallback(IPC::Sender* ipc_sender,
92 int routing_id,
93 base::ProcessHandle peer_process,
94 int request_id,
95 ExtensionFunction::ResponseType type,
96 const base::ListValue& results,
97 const std::string& error) {
98 DCHECK(ipc_sender);
100 if (type == ExtensionFunction::BAD_MESSAGE) {
101 // The renderer has done validation before sending extension api requests.
102 // Therefore, we should never receive a request that is invalid in a way
103 // that JSON validation in the renderer should have caught. It could be an
104 // attacker trying to exploit the browser, so we crash the renderer instead.
105 LOG(ERROR) <<
106 "Terminating renderer because of malformed extension message.";
107 if (content::RenderProcessHost::run_renderer_in_process()) {
108 // In single process mode it is better if we don't suicide but just crash.
109 CHECK(false);
110 } else {
111 KillBadMessageSender(peer_process);
114 return;
117 ipc_sender->Send(new ExtensionMsg_Response(
118 routing_id, request_id, type == ExtensionFunction::SUCCEEDED, results,
119 error));
122 void IOThreadResponseCallback(
123 const base::WeakPtr<IOThreadExtensionMessageFilter>& ipc_sender,
124 int routing_id,
125 int request_id,
126 ExtensionFunction::ResponseType type,
127 const base::ListValue& results,
128 const std::string& error) {
129 if (!ipc_sender.get())
130 return;
132 CommonResponseCallback(ipc_sender.get(),
133 routing_id,
134 ipc_sender->PeerHandle(),
135 request_id,
136 type,
137 results,
138 error);
141 } // namespace
143 class ExtensionFunctionDispatcher::UIThreadResponseCallbackWrapper
144 : public content::WebContentsObserver {
145 public:
146 UIThreadResponseCallbackWrapper(
147 const base::WeakPtr<ExtensionFunctionDispatcher>& dispatcher,
148 RenderViewHost* render_view_host)
149 : content::WebContentsObserver(
150 content::WebContents::FromRenderViewHost(render_view_host)),
151 dispatcher_(dispatcher),
152 render_view_host_(render_view_host),
153 weak_ptr_factory_(this) {
156 ~UIThreadResponseCallbackWrapper() override {}
158 // content::WebContentsObserver overrides.
159 void RenderViewDeleted(RenderViewHost* render_view_host) override {
160 DCHECK_CURRENTLY_ON(BrowserThread::UI);
161 if (render_view_host != render_view_host_)
162 return;
164 if (dispatcher_.get()) {
165 dispatcher_->ui_thread_response_callback_wrappers_
166 .erase(render_view_host);
169 delete this;
172 ExtensionFunction::ResponseCallback CreateCallback(int request_id) {
173 return base::Bind(
174 &UIThreadResponseCallbackWrapper::OnExtensionFunctionCompleted,
175 weak_ptr_factory_.GetWeakPtr(),
176 request_id);
179 private:
180 void OnExtensionFunctionCompleted(int request_id,
181 ExtensionFunction::ResponseType type,
182 const base::ListValue& results,
183 const std::string& error) {
184 CommonResponseCallback(
185 render_view_host_, render_view_host_->GetRoutingID(),
186 render_view_host_->GetProcess()->GetHandle(), request_id, type,
187 results, error);
190 base::WeakPtr<ExtensionFunctionDispatcher> dispatcher_;
191 content::RenderViewHost* render_view_host_;
192 base::WeakPtrFactory<UIThreadResponseCallbackWrapper> weak_ptr_factory_;
194 DISALLOW_COPY_AND_ASSIGN(UIThreadResponseCallbackWrapper);
197 WindowController*
198 ExtensionFunctionDispatcher::Delegate::GetExtensionWindowController() const {
199 return NULL;
202 content::WebContents*
203 ExtensionFunctionDispatcher::Delegate::GetAssociatedWebContents() const {
204 return NULL;
207 content::WebContents*
208 ExtensionFunctionDispatcher::Delegate::GetVisibleWebContents() const {
209 return GetAssociatedWebContents();
212 void ExtensionFunctionDispatcher::GetAllFunctionNames(
213 std::vector<std::string>* names) {
214 ExtensionFunctionRegistry::GetInstance()->GetAllNames(names);
217 bool ExtensionFunctionDispatcher::OverrideFunction(
218 const std::string& name, ExtensionFunctionFactory factory) {
219 return ExtensionFunctionRegistry::GetInstance()->OverrideFunction(name,
220 factory);
223 // static
224 void ExtensionFunctionDispatcher::DispatchOnIOThread(
225 InfoMap* extension_info_map,
226 void* profile_id,
227 int render_process_id,
228 base::WeakPtr<IOThreadExtensionMessageFilter> ipc_sender,
229 int routing_id,
230 const ExtensionHostMsg_Request_Params& params) {
231 const Extension* extension =
232 extension_info_map->extensions().GetByID(params.extension_id);
234 ExtensionFunction::ResponseCallback callback(
235 base::Bind(&IOThreadResponseCallback, ipc_sender, routing_id,
236 params.request_id));
238 scoped_refptr<ExtensionFunction> function(
239 CreateExtensionFunction(params,
240 extension,
241 render_process_id,
242 extension_info_map->process_map(),
243 g_global_io_data.Get().api.get(),
244 profile_id,
245 callback));
246 if (!function.get())
247 return;
249 IOThreadExtensionFunction* function_io =
250 function->AsIOThreadExtensionFunction();
251 if (!function_io) {
252 NOTREACHED();
253 return;
255 function_io->set_ipc_sender(ipc_sender, routing_id);
256 function_io->set_extension_info_map(extension_info_map);
257 if (extension) {
258 function->set_include_incognito(
259 extension_info_map->IsIncognitoEnabled(extension->id()));
262 if (!CheckPermissions(function.get(), params, callback))
263 return;
265 if (!extension) {
266 // Skip all of the UMA, quota, event page, activity logging stuff if there
267 // isn't an extension, e.g. if the function call was from WebUI.
268 function->Run()->Execute();
269 return;
272 QuotaService* quota = extension_info_map->GetQuotaService();
273 std::string violation_error = quota->Assess(extension->id(),
274 function.get(),
275 &params.arguments,
276 base::TimeTicks::Now());
277 if (violation_error.empty()) {
278 scoped_ptr<base::ListValue> args(params.arguments.DeepCopy());
279 NotifyApiFunctionCalled(extension->id(),
280 params.name,
281 args.Pass(),
282 static_cast<content::BrowserContext*>(profile_id));
283 UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.FunctionCalls",
284 function->histogram_value());
285 tracked_objects::ScopedProfile scoped_profile(
286 FROM_HERE_WITH_EXPLICIT_FUNCTION(function->name()),
287 tracked_objects::ScopedProfile::ENABLED);
288 function->Run()->Execute();
289 } else {
290 function->OnQuotaExceeded(violation_error);
294 ExtensionFunctionDispatcher::ExtensionFunctionDispatcher(
295 content::BrowserContext* browser_context,
296 Delegate* delegate)
297 : browser_context_(browser_context),
298 delegate_(delegate) {
301 ExtensionFunctionDispatcher::~ExtensionFunctionDispatcher() {
304 void ExtensionFunctionDispatcher::Dispatch(
305 const ExtensionHostMsg_Request_Params& params,
306 RenderViewHost* render_view_host) {
307 UIThreadResponseCallbackWrapperMap::const_iterator
308 iter = ui_thread_response_callback_wrappers_.find(render_view_host);
309 UIThreadResponseCallbackWrapper* callback_wrapper = NULL;
310 if (iter == ui_thread_response_callback_wrappers_.end()) {
311 callback_wrapper = new UIThreadResponseCallbackWrapper(AsWeakPtr(),
312 render_view_host);
313 ui_thread_response_callback_wrappers_[render_view_host] = callback_wrapper;
314 } else {
315 callback_wrapper = iter->second;
318 DispatchWithCallbackInternal(
319 params, render_view_host, NULL,
320 callback_wrapper->CreateCallback(params.request_id));
323 void ExtensionFunctionDispatcher::DispatchWithCallbackInternal(
324 const ExtensionHostMsg_Request_Params& params,
325 RenderViewHost* render_view_host,
326 content::RenderFrameHost* render_frame_host,
327 const ExtensionFunction::ResponseCallback& callback) {
328 DCHECK(render_view_host || render_frame_host);
329 // TODO(yzshen): There is some shared logic between this method and
330 // DispatchOnIOThread(). It is nice to deduplicate.
331 ProcessMap* process_map = ProcessMap::Get(browser_context_);
332 if (!process_map)
333 return;
335 ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
336 const Extension* extension =
337 registry->enabled_extensions().GetByID(params.extension_id);
338 if (!extension) {
339 extension =
340 registry->enabled_extensions().GetHostedAppByURL(params.source_url);
343 int process_id = render_view_host ? render_view_host->GetProcess()->GetID() :
344 render_frame_host->GetProcess()->GetID();
345 scoped_refptr<ExtensionFunction> function(
346 CreateExtensionFunction(params,
347 extension,
348 process_id,
349 *process_map,
350 ExtensionAPI::GetSharedInstance(),
351 browser_context_,
352 callback));
353 if (!function.get())
354 return;
356 UIThreadExtensionFunction* function_ui =
357 function->AsUIThreadExtensionFunction();
358 if (!function_ui) {
359 NOTREACHED();
360 return;
362 if (render_view_host) {
363 function_ui->SetRenderViewHost(render_view_host);
364 } else {
365 function_ui->SetRenderFrameHost(render_frame_host);
367 function_ui->set_dispatcher(AsWeakPtr());
368 function_ui->set_browser_context(browser_context_);
369 if (extension &&
370 ExtensionsBrowserClient::Get()->CanExtensionCrossIncognito(
371 extension, browser_context_)) {
372 function->set_include_incognito(true);
375 if (!CheckPermissions(function.get(), params, callback))
376 return;
378 if (!extension) {
379 // Skip all of the UMA, quota, event page, activity logging stuff if there
380 // isn't an extension, e.g. if the function call was from WebUI.
381 function->Run()->Execute();
382 return;
385 // Fetch the ProcessManager before |this| is possibly invalidated.
386 ProcessManager* process_manager = ProcessManager::Get(browser_context_);
388 ExtensionSystem* extension_system = ExtensionSystem::Get(browser_context_);
389 QuotaService* quota = extension_system->quota_service();
390 std::string violation_error = quota->Assess(extension->id(),
391 function.get(),
392 &params.arguments,
393 base::TimeTicks::Now());
395 if (violation_error.empty()) {
396 scoped_ptr<base::ListValue> args(params.arguments.DeepCopy());
398 // See crbug.com/39178.
399 ExtensionsBrowserClient::Get()->PermitExternalProtocolHandler();
400 NotifyApiFunctionCalled(
401 extension->id(), params.name, args.Pass(), browser_context_);
402 UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.FunctionCalls",
403 function->histogram_value());
404 tracked_objects::ScopedProfile scoped_profile(
405 FROM_HERE_WITH_EXPLICIT_FUNCTION(function->name()),
406 tracked_objects::ScopedProfile::ENABLED);
407 function->Run()->Execute();
408 } else {
409 function->OnQuotaExceeded(violation_error);
412 // Note: do not access |this| after this point. We may have been deleted
413 // if function->Run() ended up closing the tab that owns us.
415 // Check if extension was uninstalled by management.uninstall.
416 if (!registry->enabled_extensions().GetByID(params.extension_id))
417 return;
419 // We only adjust the keepalive count for UIThreadExtensionFunction for
420 // now, largely for simplicity's sake. This is OK because currently, only
421 // the webRequest API uses IOThreadExtensionFunction, and that API is not
422 // compatible with lazy background pages.
423 process_manager->IncrementLazyKeepaliveCount(extension);
426 void ExtensionFunctionDispatcher::OnExtensionFunctionCompleted(
427 const Extension* extension) {
428 if (extension) {
429 ProcessManager::Get(browser_context_)
430 ->DecrementLazyKeepaliveCount(extension);
434 // static
435 bool ExtensionFunctionDispatcher::CheckPermissions(
436 ExtensionFunction* function,
437 const ExtensionHostMsg_Request_Params& params,
438 const ExtensionFunction::ResponseCallback& callback) {
439 if (!function->HasPermission()) {
440 LOG(ERROR) << "Permission denied for " << params.name;
441 SendAccessDenied(callback);
442 return false;
444 return true;
447 // static
448 ExtensionFunction* ExtensionFunctionDispatcher::CreateExtensionFunction(
449 const ExtensionHostMsg_Request_Params& params,
450 const Extension* extension,
451 int requesting_process_id,
452 const ProcessMap& process_map,
453 ExtensionAPI* api,
454 void* profile_id,
455 const ExtensionFunction::ResponseCallback& callback) {
456 ExtensionFunction* function =
457 ExtensionFunctionRegistry::GetInstance()->NewFunction(params.name);
458 if (!function) {
459 LOG(ERROR) << "Unknown Extension API - " << params.name;
460 SendAccessDenied(callback);
461 return NULL;
464 function->SetArgs(&params.arguments);
465 function->set_source_url(params.source_url);
466 function->set_request_id(params.request_id);
467 function->set_has_callback(params.has_callback);
468 function->set_user_gesture(params.user_gesture);
469 function->set_extension(extension);
470 function->set_profile_id(profile_id);
471 function->set_response_callback(callback);
472 function->set_source_tab_id(params.source_tab_id);
473 function->set_source_context_type(
474 process_map.GetMostLikelyContextType(extension, requesting_process_id));
476 return function;
479 // static
480 void ExtensionFunctionDispatcher::SendAccessDenied(
481 const ExtensionFunction::ResponseCallback& callback) {
482 base::ListValue empty_list;
483 callback.Run(ExtensionFunction::FAILED, empty_list,
484 "Access to extension API denied.");
487 } // namespace extensions