blob: 7cb4449b5e1bcfb8723ae115c225fea7500952cc [file] [log] [blame]
// Copyright 2013 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 "shell/context.h"
#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/strings/string_split.h"
#include "build/build_config.h"
#include "mojo/application_manager/application_loader.h"
#include "mojo/application_manager/application_manager.h"
#include "mojo/application_manager/background_shell_application_loader.h"
#include "mojo/common/tracing_impl.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/embedder/simple_platform_support.h"
#include "mojo/public/cpp/application/application_connection.h"
#include "mojo/public/cpp/application/application_delegate.h"
#include "mojo/public/cpp/application/application_impl.h"
#include "mojo/spy/spy.h"
#include "services/tracing/tracing.mojom.h"
#include "shell/dynamic_application_loader.h"
#include "shell/external_application_listener.h"
#include "shell/in_process_dynamic_service_runner.h"
#include "shell/out_of_process_dynamic_service_runner.h"
#include "shell/switches.h"
#include "shell/ui_application_loader_android.h"
#include "url/gurl.h"
#if defined(OS_ANDROID)
#include "services/gles2/gpu_impl.h"
#include "services/native_viewport/native_viewport_impl.h"
#include "shell/android/android_handler_loader.h"
#include "shell/network_application_loader.h"
#endif // defined(OS_ANDROID)
namespace mojo {
namespace shell {
namespace {
// These mojo: URLs are loaded directly from the local filesystem. They
// correspond to shared libraries bundled alongside the mojo_shell.
const char* kLocalMojoURLs[] = {
"mojo:network_service",
};
// Used to ensure we only init once.
class Setup {
public:
Setup() {
embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
new mojo::embedder::SimplePlatformSupport()));
}
~Setup() {}
private:
DISALLOW_COPY_AND_ASSIGN(Setup);
};
static base::LazyInstance<Setup>::Leaky setup = LAZY_INSTANCE_INITIALIZER;
void InitContentHandlers(DynamicApplicationLoader* loader,
base::CommandLine* command_line) {
// Default content handlers.
loader->RegisterContentHandler("application/pdf", GURL("mojo:pdf_viewer"));
loader->RegisterContentHandler("image/png", GURL("mojo:png_viewer"));
loader->RegisterContentHandler("text/html", GURL("mojo:html_viewer"));
// Command-line-specified content handlers.
std::string handlers_spec =
command_line->GetSwitchValueASCII(switches::kContentHandlers);
if (handlers_spec.empty())
return;
std::vector<std::string> parts;
base::SplitString(handlers_spec, ',', &parts);
if (parts.size() % 2 != 0) {
LOG(ERROR) << "Invalid value for switch " << switches::kContentHandlers
<< ": must be a comma-separated list of mimetype/url pairs.";
return;
}
for (size_t i = 0; i < parts.size(); i += 2) {
GURL url(parts[i + 1]);
if (!url.is_valid()) {
LOG(ERROR) << "Invalid value for switch " << switches::kContentHandlers
<< ": '" << parts[i + 1] << "' is not a valid URL.";
return;
}
loader->RegisterContentHandler(parts[i], url);
}
}
class EmptyServiceProvider : public InterfaceImpl<ServiceProvider> {
private:
void ConnectToService(const mojo::String& service_name,
ScopedMessagePipeHandle client_handle) override {}
};
bool ConfigureURLMappings(const std::string& mappings,
mojo::shell::MojoURLResolver* resolver) {
base::StringPairs pairs;
if (!base::SplitStringIntoKeyValuePairs(mappings, '=', ',', &pairs))
return false;
using StringPair = std::pair<std::string, std::string>;
for (const StringPair& pair : pairs) {
const GURL from(pair.first);
const GURL to(pair.second);
if (!from.is_valid() || !to.is_valid())
return false;
resolver->AddCustomMapping(from, to);
}
return true;
}
} // namespace
#if defined(OS_ANDROID)
class Context::NativeViewportApplicationLoader
: public ApplicationLoader,
public ApplicationDelegate,
public InterfaceFactory<NativeViewport>,
public InterfaceFactory<Gpu> {
public:
NativeViewportApplicationLoader() : gpu_state_(new gles2::GpuImpl::State) {}
virtual ~NativeViewportApplicationLoader() {}
private:
// ApplicationLoader implementation.
virtual void Load(ApplicationManager* manager,
const GURL& url,
ScopedMessagePipeHandle shell_handle,
LoadCallback callback) override {
DCHECK(shell_handle.is_valid());
app_.reset(new ApplicationImpl(this, shell_handle.Pass()));
}
virtual void OnApplicationError(ApplicationManager* manager,
const GURL& url) override {}
// ApplicationDelegate implementation.
virtual bool ConfigureIncomingConnection(
mojo::ApplicationConnection* connection) override {
connection->AddService<NativeViewport>(this);
connection->AddService<Gpu>(this);
return true;
}
// InterfaceFactory<NativeViewport> implementation.
virtual void Create(ApplicationConnection* connection,
InterfaceRequest<NativeViewport> request) override {
BindToRequest(new NativeViewportImpl(app_.get(), false), &request);
}
// InterfaceFactory<Gpu> implementation.
virtual void Create(ApplicationConnection* connection,
InterfaceRequest<Gpu> request) override {
new gles2::GpuImpl(request.Pass(), gpu_state_);
}
scoped_refptr<gles2::GpuImpl::State> gpu_state_;
scoped_ptr<ApplicationImpl> app_;
DISALLOW_COPY_AND_ASSIGN(NativeViewportApplicationLoader);
};
#endif
Context::Context() : application_manager_(this) {
DCHECK(!base::MessageLoop::current());
}
Context::~Context() {
DCHECK(!base::MessageLoop::current());
}
void Context::EnsureEmbedderIsInitialized() {
setup.Get();
}
bool Context::Init() {
EnsureEmbedderIsInitialized();
task_runners_.reset(
new TaskRunners(base::MessageLoop::current()->message_loop_proxy()));
for (size_t i = 0; i < arraysize(kLocalMojoURLs); ++i)
mojo_url_resolver_.AddLocalFileMapping(GURL(kLocalMojoURLs[i]));
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kEnableExternalApplications)) {
listener_ = ExternalApplicationListener::Create(
task_runners_->shell_runner(), task_runners_->io_runner());
base::FilePath socket_path =
command_line->GetSwitchValuePath(switches::kEnableExternalApplications);
if (socket_path.empty())
socket_path = ExternalApplicationListener::ConstructDefaultSocketPath();
listener_->ListenInBackground(
socket_path,
base::Bind(&ApplicationManager::RegisterExternalApplication,
base::Unretained(&application_manager_)));
}
if (command_line->HasSwitch(switches::kOrigin)) {
mojo_url_resolver()->SetBaseURL(
GURL(command_line->GetSwitchValueASCII(switches::kOrigin)));
}
if (command_line->HasSwitch(switches::kURLMappings) &&
!ConfigureURLMappings(
command_line->GetSwitchValueASCII(switches::kURLMappings),
mojo_url_resolver())) {
return false;
}
scoped_ptr<DynamicServiceRunnerFactory> runner_factory;
if (command_line->HasSwitch(switches::kEnableMultiprocess))
runner_factory.reset(new OutOfProcessDynamicServiceRunnerFactory());
else
runner_factory.reset(new InProcessDynamicServiceRunnerFactory());
DynamicApplicationLoader* dynamic_application_loader =
new DynamicApplicationLoader(this, runner_factory.Pass());
InitContentHandlers(dynamic_application_loader, command_line);
application_manager_.set_default_loader(
scoped_ptr<ApplicationLoader>(dynamic_application_loader));
// The native viewport service synchronously waits for certain messages. If we
// don't run it on its own thread we can easily deadlock. Long term native
// viewport should run its own process so that this isn't an issue.
#if defined(OS_ANDROID)
application_manager_.SetLoaderForURL(
scoped_ptr<ApplicationLoader>(new UIApplicationLoader(
scoped_ptr<ApplicationLoader>(new NativeViewportApplicationLoader()),
this)),
GURL("mojo:native_viewport_service"));
#endif
if (command_line->HasSwitch(switches::kSpy)) {
spy_.reset(
new mojo::Spy(&application_manager_,
command_line->GetSwitchValueASCII(switches::kSpy)));
// TODO(cpu): the spy can snoop, but can't tell anybody until
// the Spy::WebSocketDelegate is implemented. In the original repo this
// was implemented by src\mojo\spy\websocket_server.h and .cc.
}
#if defined(OS_ANDROID)
// On android, the network service is bundled with the shell because the
// network stack depends on the android runtime.
{
scoped_ptr<BackgroundShellApplicationLoader> loader(
new BackgroundShellApplicationLoader(
scoped_ptr<ApplicationLoader>(new NetworkApplicationLoader()),
"network_service", base::MessageLoop::TYPE_IO));
application_manager_.SetLoaderForURL(loader.Pass(),
GURL("mojo:network_service"));
}
{
scoped_ptr<BackgroundShellApplicationLoader> loader(
new BackgroundShellApplicationLoader(
scoped_ptr<ApplicationLoader>(new AndroidHandlerLoader()),
"android_handler", base::MessageLoop::TYPE_DEFAULT));
application_manager_.SetLoaderForURL(loader.Pass(),
GURL("mojo:android_handler"));
}
#endif
tracing::TraceDataCollectorPtr trace_data_collector_ptr;
application_manager_.ConnectToService(GURL("mojo:tracing"),
&trace_data_collector_ptr);
TracingImpl::Create(trace_data_collector_ptr.Pass());
if (listener_)
listener_->WaitForListening();
return true;
}
void Context::OnApplicationError(const GURL& url) {
if (app_urls_.find(url) != app_urls_.end()) {
app_urls_.erase(url);
if (app_urls_.empty() && base::MessageLoop::current()->is_running())
base::MessageLoop::current()->Quit();
}
}
GURL Context::ResolveURL(const GURL& url) {
return mojo_url_resolver_.Resolve(url);
}
void Context::Run(const GURL& url) {
EmptyServiceProvider* sp = new EmptyServiceProvider;
ServiceProviderPtr spp;
BindToProxy(sp, &spp);
app_urls_.insert(url);
application_manager_.ConnectToApplication(url, GURL(), spp.Pass());
}
ScopedMessagePipeHandle Context::ConnectToServiceByName(
const GURL& application_url,
const std::string& service_name) {
app_urls_.insert(application_url);
return application_manager_.ConnectToServiceByName(application_url,
service_name).Pass();
}
} // namespace shell
} // namespace mojo