blob: aee1a2eb17d4b3fd87d5e4577420989fdf60ed2b [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/single_thread_task_runner.h"
#include "base/strings/utf_string_conversions.h"
#include "base/thread_task_runner_handle.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/services/network/public/interfaces/url_loader.mojom.h"
#include "mojo/shell/application_loader.h"
#include "mojo/shell/static_application_loader.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/string.h"
#if defined(ENABLE_MOJO_MEDIA_IN_BROWSER_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 StartProcessOnIOThread(mojo::InterfaceRequest<ProcessControl> request,
bool use_sandbox) {
UtilityProcessHost* process_host =
UtilityProcessHost::Create(nullptr, nullptr);
// TODO(rockot): Make it possible for the embedder to associate app URLs with
// app names so we can have more meaningful process names here.
process_host->SetName(base::UTF8ToUTF16("Mojo Application"));
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 launches a utility
// process and forwards the Load request the ProcessControl service there.
// The utility process is sandboxed if |use_sandbox| is true and vice versa.
class UtilityProcessLoader : public mojo::shell::ApplicationLoader {
public:
explicit UtilityProcessLoader(bool use_sandbox) : 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(&StartProcessOnIOThread, base::Passed(&process_request),
use_sandbox_));
process_control->LoadApplication(url.spec(), application_request.Pass(),
base::Bind(&OnApplicationLoaded, url));
}
const bool use_sandbox_;
DISALLOW_COPY_AND_ASSIGN(UtilityProcessLoader);
};
} // 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) {
if (task_runner_ == base::ThreadTaskRunnerHandle::Get()) {
if (shell_context_) {
shell_context_->ConnectToApplicationOnOwnThread(
url, requestor_url, request.Pass(), exposed_services.Pass(),
filter);
}
} 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));
}
}
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()
: application_manager_(new mojo::shell::ApplicationManager(this)) {
proxy_.Get().reset(new Proxy(this));
application_manager_->set_default_loader(
scoped_ptr<mojo::shell::ApplicationLoader>(
new UtilityProcessLoader(true /* use_sandbox */)));
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);
}
std::vector<GURL> urls;
GetContentClient()
->browser()
->RegisterUnsandboxedOutOfProcessMojoApplications(&urls);
for (const auto& url : urls) {
application_manager_->SetLoaderForURL(
scoped_ptr<mojo::shell::ApplicationLoader>(
new UtilityProcessLoader(false /* use_sandbox */)),
url);
}
#if (ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS)
application_manager_->SetLoaderForURL(
scoped_ptr<mojo::shell::ApplicationLoader>(
new mojo::shell::StaticApplicationLoader(
base::Bind(&media::MojoMediaApplication::CreateApp))),
media::MojoMediaApplication::AppUrl());
#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) {
proxy_.Get()->ConnectToApplication(url, requestor_url, request.Pass(),
exposed_services.Pass(), filter);
}
void MojoShellContext::ConnectToApplicationOnOwnThread(
const GURL& url,
const GURL& requestor_url,
mojo::InterfaceRequest<mojo::ServiceProvider> request,
mojo::ServiceProviderPtr exposed_services,
const mojo::shell::CapabilityFilter& filter) {
mojo::URLRequestPtr url_request = mojo::URLRequest::New();
url_request->url = mojo::String::From(url);
application_manager_->ConnectToApplication(
nullptr, url_request.Pass(), std::string(), requestor_url, request.Pass(),
exposed_services.Pass(), filter, base::Bind(&base::DoNothing));
}
GURL MojoShellContext::ResolveMappings(const GURL& url) {
return url;
}
GURL MojoShellContext::ResolveMojoURL(const GURL& url) {
return url;
}
bool MojoShellContext::CreateFetcher(
const GURL& url,
const mojo::shell::Fetcher::FetchCallback& loader_callback) {
return false;
}
} // namespace content