blob: c6076ce6e0b7c1cb56f9c433032dd5dc5718b80d [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/service_manager/service_manager_context.h"
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/single_thread_task_runner.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/service_manager/merge_dictionary.h"
#include "content/common/service_manager/service_manager_connection_impl.h"
#include "content/grit/content_resources.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_manager_connection.h"
#include "content/public/common/service_names.mojom.h"
#include "mojo/edk/embedder/embedder.h"
#include "services/catalog/catalog.h"
#include "services/catalog/manifest_provider.h"
#include "services/catalog/public/interfaces/constants.mojom.h"
#include "services/catalog/store.h"
#include "services/file/public/interfaces/constants.mojom.h"
#include "services/service_manager/connect_params.h"
#include "services/service_manager/public/cpp/connector.h"
#include "services/service_manager/public/cpp/service.h"
#include "services/service_manager/public/interfaces/service.mojom.h"
#include "services/service_manager/runner/common/client_util.h"
#include "services/service_manager/service_manager.h"
namespace content {
namespace {
base::LazyInstance<std::unique_ptr<service_manager::Connector>>::Leaky
g_io_thread_connector = LAZY_INSTANCE_INITIALIZER;
void DestroyConnectorOnIOThread() { g_io_thread_connector.Get().reset(); }
void StartUtilityProcessOnIOThread(
service_manager::mojom::ServiceFactoryRequest 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->Start();
process_host->GetRemoteInterfaces()->GetInterface(std::move(request));
}
void StartServiceInUtilityProcess(
const std::string& service_name,
const base::string16& process_name,
bool use_sandbox,
service_manager::mojom::ServiceRequest request) {
service_manager::mojom::ServiceFactoryPtr service_factory;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&StartUtilityProcessOnIOThread,
base::Passed(MakeRequest(&service_factory)), process_name,
use_sandbox));
service_factory->CreateService(std::move(request), service_name);
}
#if (ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
// Request service_manager::mojom::ServiceFactory from GPU process host. Must be
// called on
// IO thread.
void RequestGpuServiceFactory(
service_manager::mojom::ServiceFactoryRequest request) {
GpuProcessHost* process_host =
GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED);
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 ServiceFactory will also fail. Make sure we handle
// these cases correctly.
process_host->GetRemoteInterfaces()->GetInterface(std::move(request));
}
void StartServiceInGpuProcess(const std::string& service_name,
service_manager::mojom::ServiceRequest request) {
service_manager::mojom::ServiceFactoryPtr service_factory;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&RequestGpuServiceFactory,
base::Passed(MakeRequest(&service_factory))));
service_factory->CreateService(std::move(request), service_name);
}
#endif // ENABLE_MOJO_MEDIA_IN_GPU_PROCESS
// A ManifestProvider which resolves application names to builtin manifest
// resources for the catalog service to consume.
class BuiltinManifestProvider : public catalog::ManifestProvider {
public:
BuiltinManifestProvider() {}
~BuiltinManifestProvider() override {}
void AddServiceManifest(base::StringPiece name, int resource_id) {
std::string contents =
GetContentClient()
->GetDataResource(resource_id, ui::ScaleFactor::SCALE_FACTOR_NONE)
.as_string();
DCHECK(!contents.empty());
std::unique_ptr<base::Value> manifest_value =
base::JSONReader::Read(contents);
DCHECK(manifest_value);
std::unique_ptr<base::Value> overlay_value =
GetContentClient()->browser()->GetServiceManifestOverlay(name);
if (overlay_value) {
base::DictionaryValue* manifest_dictionary = nullptr;
bool result = manifest_value->GetAsDictionary(&manifest_dictionary);
DCHECK(result);
base::DictionaryValue* overlay_dictionary = nullptr;
result = overlay_value->GetAsDictionary(&overlay_dictionary);
DCHECK(result);
MergeDictionary(manifest_dictionary, overlay_dictionary);
}
auto result = manifests_.insert(
std::make_pair(name.as_string(), std::move(manifest_value)));
DCHECK(result.second) << "Duplicate manifest entry: " << name;
}
private:
// catalog::ManifestProvider:
std::unique_ptr<base::Value> GetManifest(const std::string& name) override {
auto it = manifests_.find(name);
return it != manifests_.end() ? it->second->CreateDeepCopy() : nullptr;
}
std::map<std::string, std::unique_ptr<base::Value>> manifests_;
DISALLOW_COPY_AND_ASSIGN(BuiltinManifestProvider);
};
class NullServiceProcessLauncherFactory
: public service_manager::ServiceProcessLauncherFactory {
public:
NullServiceProcessLauncherFactory() {}
~NullServiceProcessLauncherFactory() override {}
private:
std::unique_ptr<service_manager::ServiceProcessLauncher> Create(
const base::FilePath& service_path) override {
LOG(ERROR) << "Attempting to run unsupported native service: "
<< service_path.value();
return nullptr;
}
DISALLOW_COPY_AND_ASSIGN(NullServiceProcessLauncherFactory);
};
} // namespace
// State which lives on the IO thread and drives the ServiceManager.
class ServiceManagerContext::InProcessServiceManagerContext
: public base::RefCountedThreadSafe<InProcessServiceManagerContext> {
public:
InProcessServiceManagerContext() {}
service_manager::mojom::ServiceRequest Start(
std::unique_ptr<BuiltinManifestProvider> manifest_provider) {
service_manager::mojom::ServicePtr embedder_service_proxy;
service_manager::mojom::ServiceRequest embedder_service_request(
&embedder_service_proxy);
service_manager::mojom::ServicePtrInfo embedder_service_proxy_info =
embedder_service_proxy.PassInterface();
BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)->PostTask(
FROM_HERE,
base::Bind(&InProcessServiceManagerContext::StartOnIOThread, this,
base::Passed(&manifest_provider),
base::Passed(&embedder_service_proxy_info)));
return embedder_service_request;
}
void ShutDown() {
BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)->PostTask(
FROM_HERE,
base::Bind(&InProcessServiceManagerContext::ShutDownOnIOThread, this));
}
private:
friend class base::RefCountedThreadSafe<InProcessServiceManagerContext>;
~InProcessServiceManagerContext() {}
void StartOnIOThread(
std::unique_ptr<BuiltinManifestProvider> manifest_provider,
service_manager::mojom::ServicePtrInfo embedder_service_proxy_info) {
manifest_provider_ = std::move(manifest_provider);
base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool();
catalog_ = base::MakeUnique<catalog::Catalog>(
blocking_pool, nullptr, manifest_provider_.get());
service_manager_ = base::MakeUnique<service_manager::ServiceManager>(
base::MakeUnique<NullServiceProcessLauncherFactory>(),
catalog_->TakeService());
service_manager::mojom::ServiceRequest request =
service_manager_->StartEmbedderService(mojom::kBrowserServiceName);
mojo::FuseInterface(
std::move(request), std::move(embedder_service_proxy_info));
}
void ShutDownOnIOThread() {
service_manager_.reset();
catalog_.reset();
manifest_provider_.reset();
}
std::unique_ptr<BuiltinManifestProvider> manifest_provider_;
std::unique_ptr<catalog::Catalog> catalog_;
std::unique_ptr<service_manager::ServiceManager> service_manager_;
DISALLOW_COPY_AND_ASSIGN(InProcessServiceManagerContext);
};
ServiceManagerContext::ServiceManagerContext() {
service_manager::mojom::ServiceRequest request;
if (service_manager::ServiceManagerIsRemote()) {
mojo::edk::SetParentPipeHandleFromCommandLine();
request = service_manager::GetServiceRequestFromCommandLine();
} else {
std::unique_ptr<BuiltinManifestProvider> manifest_provider =
base::MakeUnique<BuiltinManifestProvider>();
static const struct ManifestInfo {
const char* name;
int resource_id;
} kManifests[] = {
{ mojom::kBrowserServiceName, IDR_MOJO_CONTENT_BROWSER_MANIFEST },
{ mojom::kGpuServiceName, IDR_MOJO_CONTENT_GPU_MANIFEST },
{ mojom::kPluginServiceName, IDR_MOJO_CONTENT_PLUGIN_MANIFEST },
{ mojom::kRendererServiceName, IDR_MOJO_CONTENT_RENDERER_MANIFEST },
{ mojom::kUtilityServiceName, IDR_MOJO_CONTENT_UTILITY_MANIFEST },
{ catalog::mojom::kServiceName, IDR_MOJO_CATALOG_MANIFEST },
{ file::mojom::kServiceName, IDR_MOJO_FILE_MANIFEST }
};
for (size_t i = 0; i < arraysize(kManifests); ++i) {
manifest_provider->AddServiceManifest(kManifests[i].name,
kManifests[i].resource_id);
}
for (const auto& manifest :
GetContentClient()->browser()->GetExtraServiceManifests()) {
manifest_provider->AddServiceManifest(manifest.name,
manifest.resource_id);
}
in_process_context_ = new InProcessServiceManagerContext;
request = in_process_context_->Start(std::move(manifest_provider));
}
ServiceManagerConnection::SetForProcess(ServiceManagerConnection::Create(
std::move(request),
BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)));
ContentBrowserClient::StaticServiceMap services;
GetContentClient()->browser()->RegisterInProcessServices(&services);
for (const auto& entry : services) {
ServiceManagerConnection::GetForProcess()->AddEmbeddedService(entry.first,
entry.second);
}
// This is safe to assign directly from any thread, because
// ServiceManagerContext must be constructed before anyone can call
// GetConnectorForIOThread().
g_io_thread_connector.Get() =
ServiceManagerConnection::GetForProcess()->GetConnector()->Clone();
ServiceManagerConnection::GetForProcess()->Start();
ContentBrowserClient::OutOfProcessServiceMap sandboxed_services;
GetContentClient()
->browser()
->RegisterOutOfProcessServices(&sandboxed_services);
for (const auto& service : sandboxed_services) {
ServiceManagerConnection::GetForProcess()->AddServiceRequestHandler(
service.first,
base::Bind(&StartServiceInUtilityProcess, service.first, service.second,
true /* use_sandbox */));
}
ContentBrowserClient::OutOfProcessServiceMap unsandboxed_services;
GetContentClient()
->browser()
->RegisterUnsandboxedOutOfProcessServices(&unsandboxed_services);
for (const auto& service : unsandboxed_services) {
ServiceManagerConnection::GetForProcess()->AddServiceRequestHandler(
service.first,
base::Bind(&StartServiceInUtilityProcess, service.first, service.second,
false /* use_sandbox */));
}
#if (ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
ServiceManagerConnection::GetForProcess()->AddServiceRequestHandler(
"media", base::Bind(&StartServiceInGpuProcess, "media"));
#endif
}
ServiceManagerContext::~ServiceManagerContext() {
// NOTE: The in-process ServiceManager MUST be destroyed before the browser
// process-wide ServiceManagerConnection. Otherwise it's possible for the
// ServiceManager to receive connection requests for service:content_browser
// which it may attempt to service by launching a new instance of the browser.
if (in_process_context_)
in_process_context_->ShutDown();
if (ServiceManagerConnection::GetForProcess())
ServiceManagerConnection::DestroyForProcess();
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&DestroyConnectorOnIOThread));
}
// static
service_manager::Connector* ServiceManagerContext::GetConnectorForIOThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
return g_io_thread_connector.Get().get();
}
} // namespace content