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"
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
{
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
,
58 base::Bind(&NotifyApiFunctionCalled
,
65 // The BrowserContext may become invalid after the task above is posted.
66 if (!ExtensionsBrowserClient::Get()->IsValidContext(browser_context
))
69 ApiActivityMonitor
* monitor
=
70 ExtensionsBrowserClient::Get()->GetApiActivityMonitor(browser_context
);
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.
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
) {
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
,
99 const base::Process
& peer_process
,
101 ExtensionFunction::ResponseType type
,
102 const base::ListValue
& results
,
103 const std::string
& error
,
104 functions::HistogramValue histogram_value
) {
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.
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.
118 KillBadMessageSender(peer_process
, histogram_value
);
123 ipc_sender
->Send(new ExtensionMsg_Response(
124 routing_id
, request_id
, type
== ExtensionFunction::SUCCEEDED
, results
,
128 void IOThreadResponseCallback(
129 const base::WeakPtr
<IOThreadExtensionMessageFilter
>& ipc_sender
,
132 ExtensionFunction::ResponseType type
,
133 const base::ListValue
& results
,
134 const std::string
& error
,
135 functions::HistogramValue histogram_value
) {
136 if (!ipc_sender
.get())
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
);
147 class ExtensionFunctionDispatcher::UIThreadResponseCallbackWrapper
148 : public content::WebContentsObserver
{
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_
)
169 if (dispatcher_
.get()) {
170 dispatcher_
->ui_thread_response_callback_wrappers_
171 .erase(render_frame_host
);
177 ExtensionFunction::ResponseCallback
CreateCallback(int request_id
) {
179 &UIThreadResponseCallbackWrapper::OnExtensionFunctionCompleted
,
180 weak_ptr_factory_
.GetWeakPtr(),
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
,
201 base::WeakPtr
<ExtensionFunctionDispatcher
> dispatcher_
;
202 content::RenderFrameHost
* render_frame_host_
;
203 base::WeakPtrFactory
<UIThreadResponseCallbackWrapper
> weak_ptr_factory_
;
205 DISALLOW_COPY_AND_ASSIGN(UIThreadResponseCallbackWrapper
);
209 ExtensionFunctionDispatcher::Delegate::GetExtensionWindowController() const {
213 content::WebContents
*
214 ExtensionFunctionDispatcher::Delegate::GetAssociatedWebContents() const {
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
,
235 void ExtensionFunctionDispatcher::DispatchOnIOThread(
236 InfoMap
* extension_info_map
,
238 int render_process_id
,
239 base::WeakPtr
<IOThreadExtensionMessageFilter
> ipc_sender
,
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
,
249 scoped_refptr
<ExtensionFunction
> function(
250 CreateExtensionFunction(params
,
253 extension_info_map
->process_map(),
254 g_global_io_data
.Get().api
.get(),
260 IOThreadExtensionFunction
* function_io
=
261 function
->AsIOThreadExtensionFunction();
266 function_io
->set_ipc_sender(ipc_sender
, routing_id
);
267 function_io
->set_extension_info_map(extension_info_map
);
269 function
->set_include_incognito(
270 extension_info_map
->IsIncognitoEnabled(extension
->id()));
273 if (!CheckPermissions(function
.get(), params
, callback
))
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();
283 QuotaService
* quota
= extension_info_map
->GetQuotaService();
284 std::string violation_error
= quota
->Assess(extension
->id(),
287 base::TimeTicks::Now());
288 if (violation_error
.empty()) {
289 scoped_ptr
<base::ListValue
> args(params
.arguments
.DeepCopy());
290 NotifyApiFunctionCalled(extension
->id(),
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();
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(),
322 ui_thread_response_callback_wrappers_
[render_frame_host
] = callback_wrapper
;
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_
);
343 ExtensionRegistry
* registry
= ExtensionRegistry::Get(browser_context_
);
344 const Extension
* extension
=
345 registry
->enabled_extensions().GetByID(params
.extension_id
);
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
,
357 ExtensionAPI::GetSharedInstance(),
363 UIThreadExtensionFunction
* function_ui
=
364 function
->AsUIThreadExtensionFunction();
369 function_ui
->SetRenderFrameHost(render_frame_host
);
370 function_ui
->set_dispatcher(AsWeakPtr());
371 function_ui
->set_browser_context(browser_context_
);
373 ExtensionsBrowserClient::Get()->CanExtensionCrossIncognito(
374 extension
, browser_context_
)) {
375 function
->set_include_incognito(true);
378 if (!CheckPermissions(function
.get(), params
, callback
))
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();
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(),
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();
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
))
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
) {
432 ProcessManager::Get(browser_context_
)
433 ->DecrementLazyKeepaliveCount(extension
);
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();
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());
467 ExtensionFunction
* ExtensionFunctionDispatcher::CreateExtensionFunction(
468 const ExtensionHostMsg_Request_Params
& params
,
469 const Extension
* extension
,
470 int requesting_process_id
,
471 const ProcessMap
& process_map
,
474 const ExtensionFunction::ResponseCallback
& callback
) {
475 ExtensionFunction
* function
=
476 ExtensionFunctionRegistry::GetInstance()->NewFunction(params
.name
);
478 LOG(ERROR
) << "Unknown Extension API - " << params
.name
;
479 SendAccessDenied(callback
, function
->histogram_value());
483 function
->SetArgs(¶ms
.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
);
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