blob: 36ec546d96e46a05bd68b5a2ac4f768cf2f0e533 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/mojo/mojo_shell_context.h"
#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/path_service.h"
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/common/gpu/gpu_process_launch_causes.h"
#include "content/common/process_control.mojom.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/utility_process_host.h"
#include "content/public/browser/utility_process_host_client.h"
#include "content/public/common/content_client.h"
#include "content/public/common/service_registry.h"
#include "mojo/application/public/cpp/application_delegate.h"
#include "mojo/common/url_type_converters.h"
#include "mojo/package_manager/package_manager_impl.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/bindings/string.h"
#include "mojo/shell/application_loader.h"
#include "mojo/shell/connect_to_application_params.h"
#include "mojo/shell/identity.h"
#include "mojo/shell/static_application_loader.h"
#if defined(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS) || \
defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
#include "media/mojo/services/mojo_media_application.h"
#endif
namespace content {
namespace {
// An extra set of apps to register on initialization, if set by a test.
const MojoShellContext::StaticApplicationMap* g_applications_for_test;
void StartUtilityProcessOnIOThread(
mojo::InterfaceRequest<ProcessControl> request,
const base::string16& process_name,
bool use_sandbox) {
UtilityProcessHost* process_host =
UtilityProcessHost::Create(nullptr, nullptr);
process_host->SetName(process_name);
if (!use_sandbox)
process_host->DisableSandbox();
process_host->StartMojoMode();
ServiceRegistry* services = process_host->GetServiceRegistry();
services->ConnectToRemoteService(request.Pass());
}
void OnApplicationLoaded(const GURL& url, bool success) {
if (!success)
LOG(ERROR) << "Failed to launch Mojo application for " << url.spec();
}
// The default loader to use for all applications. This does nothing but drop
// the Application request.
class DefaultApplicationLoader : public mojo::shell::ApplicationLoader {
public:
DefaultApplicationLoader() {}
~DefaultApplicationLoader() override {}
private:
// mojo::shell::ApplicationLoader:
void Load(
const GURL& url,
mojo::InterfaceRequest<mojo::Application> application_request) override {}
DISALLOW_COPY_AND_ASSIGN(DefaultApplicationLoader);
};
// This launches a utility process and forwards the Load request the
// ProcessControl service there. The utility process is sandboxed iff
// |use_sandbox| is true.
class UtilityProcessLoader : public mojo::shell::ApplicationLoader {
public:
UtilityProcessLoader(const base::string16& process_name, bool use_sandbox)
: process_name_(process_name), use_sandbox_(use_sandbox) {}
~UtilityProcessLoader() override {}
private:
// mojo::shell::ApplicationLoader:
void Load(
const GURL& url,
mojo::InterfaceRequest<mojo::Application> application_request) override {
ProcessControlPtr process_control;
auto process_request = mojo::GetProxy(&process_control);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&StartUtilityProcessOnIOThread,
base::Passed(&process_request),
process_name_, use_sandbox_));
process_control->LoadApplication(url.spec(), application_request.Pass(),
base::Bind(&OnApplicationLoaded, url));
}
const base::string16 process_name_;
const bool use_sandbox_;
DISALLOW_COPY_AND_ASSIGN(UtilityProcessLoader);
};
// Request ProcessControl from GPU process host. Must be called on IO thread.
void RequestGpuProcessControl(mojo::InterfaceRequest<ProcessControl> request) {
BrowserChildProcessHostDelegate* process_host =
GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
CAUSE_FOR_GPU_LAUNCH_MOJO_SETUP);
if (!process_host) {
DLOG(ERROR) << "GPU process host not available.";
return;
}
// TODO(xhwang): It's possible that |process_host| is non-null, but the actual
// process is dead. In that case, |request| will be dropped and application
// load requests through ProcessControl will also fail. Make sure we handle
// these cases correctly.
process_host->GetServiceRegistry()->ConnectToRemoteService(request.Pass());
}
// Forwards the load request to the GPU process.
class GpuProcessLoader : public mojo::shell::ApplicationLoader {
public:
GpuProcessLoader() {}
~GpuProcessLoader() override {}
private:
// mojo::shell::ApplicationLoader:
void Load(
const GURL& url,
mojo::InterfaceRequest<mojo::Application> application_request) override {
ProcessControlPtr process_control;
auto process_request = mojo::GetProxy(&process_control);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&RequestGpuProcessControl, base::Passed(&process_request)));
process_control->LoadApplication(url.spec(), application_request.Pass(),
base::Bind(&OnApplicationLoaded, url));
}
DISALLOW_COPY_AND_ASSIGN(GpuProcessLoader);
};
} // namespace
// Thread-safe proxy providing access to the shell context from any thread.
class MojoShellContext::Proxy {
public:
Proxy(MojoShellContext* shell_context)
: shell_context_(shell_context),
task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
~Proxy() {}
void ConnectToApplication(
const GURL& url,
const GURL& requestor_url,
mojo::InterfaceRequest<mojo::ServiceProvider> request,
mojo::ServiceProviderPtr exposed_services,
const mojo::shell::CapabilityFilter& filter,
const mojo::Shell::ConnectToApplicationCallback& callback) {
if (task_runner_ == base::ThreadTaskRunnerHandle::Get()) {
if (shell_context_) {
shell_context_->ConnectToApplicationOnOwnThread(
url, requestor_url, request.Pass(), exposed_services.Pass(), filter,
callback);
}
} else {
// |shell_context_| outlives the main MessageLoop, so it's safe for it to
// be unretained here.
task_runner_->PostTask(
FROM_HERE,
base::Bind(&MojoShellContext::ConnectToApplicationOnOwnThread,
base::Unretained(shell_context_), url, requestor_url,
base::Passed(&request), base::Passed(&exposed_services),
filter, callback));
}
}
private:
MojoShellContext* shell_context_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(Proxy);
};
// static
base::LazyInstance<scoped_ptr<MojoShellContext::Proxy>>
MojoShellContext::proxy_ = LAZY_INSTANCE_INITIALIZER;
void MojoShellContext::SetApplicationsForTest(
const StaticApplicationMap* apps) {
g_applications_for_test = apps;
}
MojoShellContext::MojoShellContext() {
proxy_.Get().reset(new Proxy(this));
// Construct with an empty filepath since mojo: urls can't be registered now
// the url scheme registry is locked.
scoped_ptr<mojo::package_manager::PackageManagerImpl> package_manager(
new mojo::package_manager::PackageManagerImpl(base::FilePath(), nullptr));
application_manager_.reset(
new mojo::shell::ApplicationManager(package_manager.Pass()));
application_manager_->set_default_loader(
scoped_ptr<mojo::shell::ApplicationLoader>(new DefaultApplicationLoader));
StaticApplicationMap apps;
GetContentClient()->browser()->RegisterInProcessMojoApplications(&apps);
if (g_applications_for_test) {
// Add testing apps to the map, potentially overwriting whatever the
// browser client registered.
for (const auto& entry : *g_applications_for_test)
apps[entry.first] = entry.second;
}
for (const auto& entry : apps) {
application_manager_->SetLoaderForURL(
scoped_ptr<mojo::shell::ApplicationLoader>(
new mojo::shell::StaticApplicationLoader(entry.second)),
entry.first);
}
ContentBrowserClient::OutOfProcessMojoApplicationMap sandboxed_apps;
GetContentClient()
->browser()
->RegisterOutOfProcessMojoApplications(&sandboxed_apps);
for (const auto& app : sandboxed_apps) {
application_manager_->SetLoaderForURL(
scoped_ptr<mojo::shell::ApplicationLoader>(
new UtilityProcessLoader(app.second, true /* use_sandbox */)),
app.first);
}
ContentBrowserClient::OutOfProcessMojoApplicationMap unsandboxed_apps;
GetContentClient()
->browser()
->RegisterUnsandboxedOutOfProcessMojoApplications(&unsandboxed_apps);
for (const auto& app : unsandboxed_apps) {
application_manager_->SetLoaderForURL(
scoped_ptr<mojo::shell::ApplicationLoader>(
new UtilityProcessLoader(app.second, false /* use_sandbox */)),
app.first);
}
#if (ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
application_manager_->SetLoaderForURL(
scoped_ptr<mojo::shell::ApplicationLoader>(
new mojo::shell::StaticApplicationLoader(
base::Bind(&media::MojoMediaApplication::CreateApp))),
GURL("mojo:media"));
#elif(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
application_manager_->SetLoaderForURL(
scoped_ptr<mojo::shell::ApplicationLoader>(new GpuProcessLoader()),
GURL("mojo:media"));
#endif
}
MojoShellContext::~MojoShellContext() {
}
// static
void MojoShellContext::ConnectToApplication(
const GURL& url,
const GURL& requestor_url,
mojo::InterfaceRequest<mojo::ServiceProvider> request,
mojo::ServiceProviderPtr exposed_services,
const mojo::shell::CapabilityFilter& filter,
const mojo::Shell::ConnectToApplicationCallback& callback) {
proxy_.Get()->ConnectToApplication(url, requestor_url,
request.Pass(), exposed_services.Pass(),
filter, callback);
}
void MojoShellContext::ConnectToApplicationOnOwnThread(
const GURL& url,
const GURL& requestor_url,
mojo::InterfaceRequest<mojo::ServiceProvider> request,
mojo::ServiceProviderPtr exposed_services,
const mojo::shell::CapabilityFilter& filter,
const mojo::Shell::ConnectToApplicationCallback& callback) {
scoped_ptr<mojo::shell::ConnectToApplicationParams> params(
new mojo::shell::ConnectToApplicationParams);
params->set_source(
mojo::shell::Identity(requestor_url, std::string(),
mojo::shell::GetPermissiveCapabilityFilter()));
params->SetTarget(mojo::shell::Identity(url, std::string(), filter));
params->set_services(request.Pass());
params->set_exposed_services(exposed_services.Pass());
params->set_on_application_end(base::Bind(&base::DoNothing));
params->set_connect_callback(callback);
application_manager_->ConnectToApplication(params.Pass());
}
} // namespace content