Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / extensions / browser / extension_function_dispatcher.cc
blob475d2604aacc11b412868c214de4b01c68e157f8
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/histogram_macros.h"
13 #include "base/metrics/sparse_histogram.h"
14 #include "base/process/process.h"
15 #include "base/profiler/scoped_profile.h"
16 #include "base/values.h"
17 #include "build/build_config.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/render_frame_host.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "content/public/browser/render_view_host.h"
22 #include "content/public/browser/user_metrics.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/browser/web_contents_observer.h"
25 #include "content/public/common/result_codes.h"
26 #include "extensions/browser/api_activity_monitor.h"
27 #include "extensions/browser/extension_function_registry.h"
28 #include "extensions/browser/extension_registry.h"
29 #include "extensions/browser/extension_system.h"
30 #include "extensions/browser/extensions_browser_client.h"
31 #include "extensions/browser/io_thread_extension_message_filter.h"
32 #include "extensions/browser/process_manager.h"
33 #include "extensions/browser/process_map.h"
34 #include "extensions/browser/quota_service.h"
35 #include "extensions/common/extension_api.h"
36 #include "extensions/common/extension_messages.h"
37 #include "extensions/common/extension_set.h"
38 #include "ipc/ipc_message.h"
39 #include "ipc/ipc_message_macros.h"
41 using content::BrowserThread;
42 using content::RenderViewHost;
44 namespace extensions {
45 namespace {
47 // Notifies the ApiActivityMonitor that an extension API function has been
48 // called. May be called from any thread.
49 void NotifyApiFunctionCalled(const std::string& extension_id,
50 const std::string& api_name,
51 scoped_ptr<base::ListValue> args,
52 content::BrowserContext* browser_context) {
53 // The ApiActivityMonitor can only be accessed from the main (UI) thread. If
54 // we're running on the wrong thread, re-dispatch from the main thread.
55 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
56 BrowserThread::PostTask(BrowserThread::UI,
57 FROM_HERE,
58 base::Bind(&NotifyApiFunctionCalled,
59 extension_id,
60 api_name,
61 base::Passed(&args),
62 browser_context));
63 return;
65 // The BrowserContext may become invalid after the task above is posted.
66 if (!ExtensionsBrowserClient::Get()->IsValidContext(browser_context))
67 return;
69 ApiActivityMonitor* monitor =
70 ExtensionsBrowserClient::Get()->GetApiActivityMonitor(browser_context);
71 if (monitor)
72 monitor->OnApiFunctionCalled(extension_id, api_name, args.Pass());
75 // Separate copy of ExtensionAPI used for IO thread extension functions. We need
76 // this because ExtensionAPI has mutable data. It should be possible to remove
77 // this once all the extension APIs are updated to the feature system.
78 struct Static {
79 Static() : api(ExtensionAPI::CreateWithDefaultConfiguration()) {}
80 scoped_ptr<ExtensionAPI> api;
82 base::LazyInstance<Static> g_global_io_data = LAZY_INSTANCE_INITIALIZER;
84 // Kills the specified process because it sends us a malformed message.
85 // Track the specific function's |histogram_value|, as this may indicate a bug
86 // in that API's implementation on the renderer.
87 void KillBadMessageSender(const base::Process& process,
88 functions::HistogramValue histogram_value) {
89 NOTREACHED();
90 content::RecordAction(base::UserMetricsAction("BadMessageTerminate_EFD"));
91 UMA_HISTOGRAM_ENUMERATION("Extensions.BadMessageFunctionName",
92 histogram_value, functions::ENUM_BOUNDARY);
93 if (process.IsValid())
94 process.Terminate(content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
97 void CommonResponseCallback(IPC::Sender* ipc_sender,
98 int routing_id,
99 const base::Process& peer_process,
100 int request_id,
101 ExtensionFunction::ResponseType type,
102 const base::ListValue& results,
103 const std::string& error,
104 functions::HistogramValue histogram_value) {
105 DCHECK(ipc_sender);
107 if (type == ExtensionFunction::BAD_MESSAGE) {
108 // The renderer has done validation before sending extension api requests.
109 // Therefore, we should never receive a request that is invalid in a way
110 // that JSON validation in the renderer should have caught. It could be an
111 // attacker trying to exploit the browser, so we crash the renderer instead.
112 LOG(ERROR) <<
113 "Terminating renderer because of malformed extension message.";
114 if (content::RenderProcessHost::run_renderer_in_process()) {
115 // In single process mode it is better if we don't suicide but just crash.
116 CHECK(false);
117 } else {
118 KillBadMessageSender(peer_process, histogram_value);
120 return;
123 ipc_sender->Send(new ExtensionMsg_Response(
124 routing_id, request_id, type == ExtensionFunction::SUCCEEDED, results,
125 error));
128 void IOThreadResponseCallback(
129 const base::WeakPtr<IOThreadExtensionMessageFilter>& ipc_sender,
130 int routing_id,
131 int request_id,
132 ExtensionFunction::ResponseType type,
133 const base::ListValue& results,
134 const std::string& error,
135 functions::HistogramValue histogram_value) {
136 if (!ipc_sender.get())
137 return;
139 base::Process peer_process =
140 base::Process::DeprecatedGetProcessFromHandle(ipc_sender->PeerHandle());
141 CommonResponseCallback(ipc_sender.get(), routing_id, peer_process, request_id,
142 type, results, error, histogram_value);
145 } // namespace
147 class ExtensionFunctionDispatcher::UIThreadResponseCallbackWrapper
148 : public content::WebContentsObserver {
149 public:
150 UIThreadResponseCallbackWrapper(
151 const base::WeakPtr<ExtensionFunctionDispatcher>& dispatcher,
152 content::RenderFrameHost* render_frame_host)
153 : content::WebContentsObserver(
154 content::WebContents::FromRenderFrameHost(render_frame_host)),
155 dispatcher_(dispatcher),
156 render_frame_host_(render_frame_host),
157 weak_ptr_factory_(this) {
160 ~UIThreadResponseCallbackWrapper() override {}
162 // content::WebContentsObserver overrides.
163 void RenderFrameDeleted(
164 content::RenderFrameHost* render_frame_host) override {
165 DCHECK_CURRENTLY_ON(BrowserThread::UI);
166 if (render_frame_host != render_frame_host_)
167 return;
169 if (dispatcher_.get()) {
170 dispatcher_->ui_thread_response_callback_wrappers_
171 .erase(render_frame_host);
174 delete this;
177 ExtensionFunction::ResponseCallback CreateCallback(int request_id) {
178 return base::Bind(
179 &UIThreadResponseCallbackWrapper::OnExtensionFunctionCompleted,
180 weak_ptr_factory_.GetWeakPtr(),
181 request_id);
184 private:
185 void OnExtensionFunctionCompleted(int request_id,
186 ExtensionFunction::ResponseType type,
187 const base::ListValue& results,
188 const std::string& error,
189 functions::HistogramValue histogram_value) {
190 base::Process process =
191 content::RenderProcessHost::run_renderer_in_process()
192 ? base::Process::Current()
193 : base::Process::DeprecatedGetProcessFromHandle(
194 render_frame_host_->GetProcess()->GetHandle());
195 CommonResponseCallback(render_frame_host_,
196 render_frame_host_->GetRoutingID(),
197 process, request_id, type, results, error,
198 histogram_value);
201 base::WeakPtr<ExtensionFunctionDispatcher> dispatcher_;
202 content::RenderFrameHost* render_frame_host_;
203 base::WeakPtrFactory<UIThreadResponseCallbackWrapper> weak_ptr_factory_;
205 DISALLOW_COPY_AND_ASSIGN(UIThreadResponseCallbackWrapper);
208 WindowController*
209 ExtensionFunctionDispatcher::Delegate::GetExtensionWindowController() const {
210 return nullptr;
213 content::WebContents*
214 ExtensionFunctionDispatcher::Delegate::GetAssociatedWebContents() const {
215 return nullptr;
218 content::WebContents*
219 ExtensionFunctionDispatcher::Delegate::GetVisibleWebContents() const {
220 return GetAssociatedWebContents();
223 void ExtensionFunctionDispatcher::GetAllFunctionNames(
224 std::vector<std::string>* names) {
225 ExtensionFunctionRegistry::GetInstance()->GetAllNames(names);
228 bool ExtensionFunctionDispatcher::OverrideFunction(
229 const std::string& name, ExtensionFunctionFactory factory) {
230 return ExtensionFunctionRegistry::GetInstance()->OverrideFunction(name,
231 factory);
234 // static
235 void ExtensionFunctionDispatcher::DispatchOnIOThread(
236 InfoMap* extension_info_map,
237 void* profile_id,
238 int render_process_id,
239 base::WeakPtr<IOThreadExtensionMessageFilter> ipc_sender,
240 int routing_id,
241 const ExtensionHostMsg_Request_Params& params) {
242 const Extension* extension =
243 extension_info_map->extensions().GetByID(params.extension_id);
245 ExtensionFunction::ResponseCallback callback(
246 base::Bind(&IOThreadResponseCallback, ipc_sender, routing_id,
247 params.request_id));
249 scoped_refptr<ExtensionFunction> function(
250 CreateExtensionFunction(params,
251 extension,
252 render_process_id,
253 extension_info_map->process_map(),
254 g_global_io_data.Get().api.get(),
255 profile_id,
256 callback));
257 if (!function.get())
258 return;
260 IOThreadExtensionFunction* function_io =
261 function->AsIOThreadExtensionFunction();
262 if (!function_io) {
263 NOTREACHED();
264 return;
266 function_io->set_ipc_sender(ipc_sender, routing_id);
267 function_io->set_extension_info_map(extension_info_map);
268 if (extension) {
269 function->set_include_incognito(
270 extension_info_map->IsIncognitoEnabled(extension->id()));
273 if (!CheckPermissions(function.get(), params, callback))
274 return;
276 if (!extension) {
277 // Skip all of the UMA, quota, event page, activity logging stuff if there
278 // isn't an extension, e.g. if the function call was from WebUI.
279 function->Run()->Execute();
280 return;
283 QuotaService* quota = extension_info_map->GetQuotaService();
284 std::string violation_error = quota->Assess(extension->id(),
285 function.get(),
286 &params.arguments,
287 base::TimeTicks::Now());
288 if (violation_error.empty()) {
289 scoped_ptr<base::ListValue> args(params.arguments.DeepCopy());
290 NotifyApiFunctionCalled(extension->id(),
291 params.name,
292 args.Pass(),
293 static_cast<content::BrowserContext*>(profile_id));
294 UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.FunctionCalls",
295 function->histogram_value());
296 tracked_objects::ScopedProfile scoped_profile(
297 FROM_HERE_WITH_EXPLICIT_FUNCTION(function->name()),
298 tracked_objects::ScopedProfile::ENABLED);
299 function->Run()->Execute();
300 } else {
301 function->OnQuotaExceeded(violation_error);
305 ExtensionFunctionDispatcher::ExtensionFunctionDispatcher(
306 content::BrowserContext* browser_context)
307 : browser_context_(browser_context) {
310 ExtensionFunctionDispatcher::~ExtensionFunctionDispatcher() {
313 void ExtensionFunctionDispatcher::Dispatch(
314 const ExtensionHostMsg_Request_Params& params,
315 content::RenderFrameHost* render_frame_host) {
316 UIThreadResponseCallbackWrapperMap::const_iterator
317 iter = ui_thread_response_callback_wrappers_.find(render_frame_host);
318 UIThreadResponseCallbackWrapper* callback_wrapper = nullptr;
319 if (iter == ui_thread_response_callback_wrappers_.end()) {
320 callback_wrapper = new UIThreadResponseCallbackWrapper(AsWeakPtr(),
321 render_frame_host);
322 ui_thread_response_callback_wrappers_[render_frame_host] = callback_wrapper;
323 } else {
324 callback_wrapper = iter->second;
327 DispatchWithCallbackInternal(
328 params, render_frame_host,
329 callback_wrapper->CreateCallback(params.request_id));
332 void ExtensionFunctionDispatcher::DispatchWithCallbackInternal(
333 const ExtensionHostMsg_Request_Params& params,
334 content::RenderFrameHost* render_frame_host,
335 const ExtensionFunction::ResponseCallback& callback) {
336 DCHECK(render_frame_host);
337 // TODO(yzshen): There is some shared logic between this method and
338 // DispatchOnIOThread(). It is nice to deduplicate.
339 ProcessMap* process_map = ProcessMap::Get(browser_context_);
340 if (!process_map)
341 return;
343 ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
344 const Extension* extension =
345 registry->enabled_extensions().GetByID(params.extension_id);
346 if (!extension) {
347 extension =
348 registry->enabled_extensions().GetHostedAppByURL(params.source_url);
351 int process_id = render_frame_host->GetProcess()->GetID();
352 scoped_refptr<ExtensionFunction> function(
353 CreateExtensionFunction(params,
354 extension,
355 process_id,
356 *process_map,
357 ExtensionAPI::GetSharedInstance(),
358 browser_context_,
359 callback));
360 if (!function.get())
361 return;
363 UIThreadExtensionFunction* function_ui =
364 function->AsUIThreadExtensionFunction();
365 if (!function_ui) {
366 NOTREACHED();
367 return;
369 function_ui->SetRenderFrameHost(render_frame_host);
370 function_ui->set_dispatcher(AsWeakPtr());
371 function_ui->set_browser_context(browser_context_);
372 if (extension &&
373 ExtensionsBrowserClient::Get()->CanExtensionCrossIncognito(
374 extension, browser_context_)) {
375 function->set_include_incognito(true);
378 if (!CheckPermissions(function.get(), params, callback))
379 return;
381 if (!extension) {
382 // Skip all of the UMA, quota, event page, activity logging stuff if there
383 // isn't an extension, e.g. if the function call was from WebUI.
384 function->Run()->Execute();
385 return;
388 // Fetch the ProcessManager before |this| is possibly invalidated.
389 ProcessManager* process_manager = ProcessManager::Get(browser_context_);
391 ExtensionSystem* extension_system = ExtensionSystem::Get(browser_context_);
392 QuotaService* quota = extension_system->quota_service();
393 std::string violation_error = quota->Assess(extension->id(),
394 function.get(),
395 &params.arguments,
396 base::TimeTicks::Now());
398 if (violation_error.empty()) {
399 scoped_ptr<base::ListValue> args(params.arguments.DeepCopy());
401 // See crbug.com/39178.
402 ExtensionsBrowserClient::Get()->PermitExternalProtocolHandler();
403 NotifyApiFunctionCalled(
404 extension->id(), params.name, args.Pass(), browser_context_);
405 UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.FunctionCalls",
406 function->histogram_value());
407 tracked_objects::ScopedProfile scoped_profile(
408 FROM_HERE_WITH_EXPLICIT_FUNCTION(function->name()),
409 tracked_objects::ScopedProfile::ENABLED);
410 function->Run()->Execute();
411 } else {
412 function->OnQuotaExceeded(violation_error);
415 // Note: do not access |this| after this point. We may have been deleted
416 // if function->Run() ended up closing the tab that owns us.
418 // Check if extension was uninstalled by management.uninstall.
419 if (!registry->enabled_extensions().GetByID(params.extension_id))
420 return;
422 // We only adjust the keepalive count for UIThreadExtensionFunction for
423 // now, largely for simplicity's sake. This is OK because currently, only
424 // the webRequest API uses IOThreadExtensionFunction, and that API is not
425 // compatible with lazy background pages.
426 process_manager->IncrementLazyKeepaliveCount(extension);
429 void ExtensionFunctionDispatcher::OnExtensionFunctionCompleted(
430 const Extension* extension) {
431 if (extension) {
432 ProcessManager::Get(browser_context_)
433 ->DecrementLazyKeepaliveCount(extension);
437 WindowController*
438 ExtensionFunctionDispatcher::GetExtensionWindowController() const {
439 return delegate_ ? delegate_->GetExtensionWindowController() : nullptr;
442 content::WebContents*
443 ExtensionFunctionDispatcher::GetAssociatedWebContents() const {
444 return delegate_ ? delegate_->GetAssociatedWebContents() : nullptr;
447 content::WebContents*
448 ExtensionFunctionDispatcher::GetVisibleWebContents() const {
449 return delegate_ ? delegate_->GetVisibleWebContents() :
450 GetAssociatedWebContents();
453 // static
454 bool ExtensionFunctionDispatcher::CheckPermissions(
455 ExtensionFunction* function,
456 const ExtensionHostMsg_Request_Params& params,
457 const ExtensionFunction::ResponseCallback& callback) {
458 if (!function->HasPermission()) {
459 LOG(ERROR) << "Permission denied for " << params.name;
460 SendAccessDenied(callback, function->histogram_value());
461 return false;
463 return true;
466 // static
467 ExtensionFunction* ExtensionFunctionDispatcher::CreateExtensionFunction(
468 const ExtensionHostMsg_Request_Params& params,
469 const Extension* extension,
470 int requesting_process_id,
471 const ProcessMap& process_map,
472 ExtensionAPI* api,
473 void* profile_id,
474 const ExtensionFunction::ResponseCallback& callback) {
475 ExtensionFunction* function =
476 ExtensionFunctionRegistry::GetInstance()->NewFunction(params.name);
477 if (!function) {
478 LOG(ERROR) << "Unknown Extension API - " << params.name;
479 SendAccessDenied(callback, function->histogram_value());
480 return NULL;
483 function->SetArgs(&params.arguments);
484 function->set_source_url(params.source_url);
485 function->set_request_id(params.request_id);
486 function->set_has_callback(params.has_callback);
487 function->set_user_gesture(params.user_gesture);
488 function->set_extension(extension);
489 function->set_profile_id(profile_id);
490 function->set_response_callback(callback);
491 function->set_source_tab_id(params.source_tab_id);
492 function->set_source_context_type(
493 process_map.GetMostLikelyContextType(extension, requesting_process_id));
494 function->set_source_process_id(requesting_process_id);
496 return function;
499 // static
500 void ExtensionFunctionDispatcher::SendAccessDenied(
501 const ExtensionFunction::ResponseCallback& callback,
502 functions::HistogramValue histogram_value) {
503 base::ListValue empty_list;
504 callback.Run(ExtensionFunction::FAILED, empty_list,
505 "Access to extension API denied.", histogram_value);
508 } // namespace extensions