// 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 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 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 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 task_runner_; DISALLOW_COPY_AND_ASSIGN(Proxy); }; // static base::LazyInstance> 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( 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( new mojo::shell::StaticApplicationLoader(entry.second)), entry.first); } std::vector urls; GetContentClient() ->browser() ->RegisterUnsandboxedOutOfProcessMojoApplications(&urls); for (const auto& url : urls) { application_manager_->SetLoaderForURL( scoped_ptr( new UtilityProcessLoader(false /* use_sandbox */)), url); } #if (ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS) application_manager_->SetLoaderForURL( scoped_ptr( 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 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 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