From 46f02746c8d437f4c3924e86e1a7cc8a2196546e Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 13 Nov 2015 08:28:06 -0800 Subject: [PATCH] Implement CreateInstanceForHandle. A method on ApplicationManager that allows a process that launches another process to register the launched process with the ApplicationManager. In the Shell, this creates an ApplicationInstance, OutOfProcessNativeRunner and ChildProcessHost. This change makes it possible for a ChildProcessHost to be created for an existing process rather than one it must launch. I think in a future CL I should refactor this class more to make the difference between the type the shell starts and the type someone else starts a bit clearer. Currently it just triggers on whether or not app_path_ is empty which I think is a bit brittle. Adds an apptest for this that: - exposes a service from the apptest - apptest starts a native exe ("driver") via the shell - native exe ("driver") starts another native exe ("target") via base::LaunchProcess - driver creates platform channel and passes one side to target and the other side to the shell via CreateInstanceForHandle - target connects to service exposed by the apptest. R=jam@chromium.org http://crbug.com/551253 Committed: https://crrev.com/0cfab1b202d03d68fc1a8a99bb19b41c8d709a15 Cr-Commit-Position: refs/heads/master@{#359502} Review URL: https://codereview.chromium.org/1434083002 Cr-Commit-Position: refs/heads/master@{#359562} --- mojo/runner/BUILD.gn | 1 + mojo/runner/child/BUILD.gn | 27 +++- mojo/runner/child/native_apptest_target.cc | 67 +-------- mojo/runner/child/test_native_main.cc | 80 ++++++++++ mojo/runner/child/test_native_main.h | 17 +++ mojo/runner/host/child_process_host.cc | 14 +- mojo/runner/host/child_process_host.h | 3 + mojo/runner/host/in_process_native_runner.cc | 6 + mojo/runner/host/in_process_native_runner.h | 4 +- .../host/out_of_process_native_runner.cc | 12 +- .../host/out_of_process_native_runner.h | 4 +- mojo/shell/BUILD.gn | 64 ++++++++ mojo/shell/DEPS | 11 ++ mojo/shell/application_manager.cc | 51 +++++-- mojo/shell/application_manager.h | 10 +- mojo/shell/application_manager.mojom | 3 +- mojo/shell/application_manager_apptest.cc | 111 ++++++++++++++ .../application_manager_apptest_driver.cc | 137 ++++++++++++++++++ .../application_manager_apptest_target.cc | 51 +++++++ mojo/shell/application_manager_apptests.mojom | 13 ++ mojo/shell/native_runner.h | 5 + mojo/shell/shell_application_delegate.cc | 8 +- mojo/shell/shell_application_delegate.h | 4 +- 23 files changed, 612 insertions(+), 91 deletions(-) create mode 100644 mojo/runner/child/test_native_main.cc create mode 100644 mojo/runner/child/test_native_main.h create mode 100644 mojo/shell/application_manager_apptest.cc create mode 100644 mojo/shell/application_manager_apptest_driver.cc create mode 100644 mojo/shell/application_manager_apptest_target.cc create mode 100644 mojo/shell/application_manager_apptests.mojom diff --git a/mojo/runner/BUILD.gn b/mojo/runner/BUILD.gn index 159af7d1159a44..49efb46dc661d8 100644 --- a/mojo/runner/BUILD.gn +++ b/mojo/runner/BUILD.gn @@ -104,6 +104,7 @@ source_set("init") { deps = [ "//mojo/runner/host:switches", "//base", + "//base:base_static", "//base:i18n", ] } diff --git a/mojo/runner/child/BUILD.gn b/mojo/runner/child/BUILD.gn index edf5f97b77ccd3..028c1d7297ed99 100644 --- a/mojo/runner/child/BUILD.gn +++ b/mojo/runner/child/BUILD.gn @@ -42,6 +42,26 @@ mojom("interfaces") { import_dirs = [ "//mojo/services" ] } +source_set("test_native_main") { + sources = [ + "test_native_main.cc", + "test_native_main.h", + ] + + public_deps = [ + "//mojo/runner:init", + ] + + deps = [ + "//base", + "//mojo/application/public/cpp", + "//mojo/gles2", + "//mojo/message_pump", + "//mojo/runner/child:lib", + "//third_party/mojo/src/mojo/edk/system", + ] +} + mojom("apptest_interfaces") { sources = [ "test_native_service.mojom", @@ -89,14 +109,9 @@ executable("native_target") { deps = [ ":apptest_interfaces", ":lib", + ":test_native_main", "//base", - "//base:base_static", "//mojo/application/public/cpp", - "//mojo/application/public/interfaces", "//mojo/common:common_base", - "//mojo/gles2", - "//mojo/message_pump", - "//mojo/runner:init", - "//third_party/mojo/src/mojo/edk/system", ] } diff --git a/mojo/runner/child/native_apptest_target.cc b/mojo/runner/child/native_apptest_target.cc index a4b230117dd5f7..f1844004485433 100644 --- a/mojo/runner/child/native_apptest_target.cc +++ b/mojo/runner/child/native_apptest_target.cc @@ -4,28 +4,14 @@ #include "base/at_exit.h" #include "base/command_line.h" -#include "base/debug/stack_trace.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/process/launch.h" -#include "base/threading/thread.h" -#include "build/build_config.h" #include "mojo/application/public/cpp/application_connection.h" #include "mojo/application/public/cpp/application_delegate.h" #include "mojo/application/public/cpp/application_impl.h" #include "mojo/application/public/cpp/interface_factory.h" -#include "mojo/application/public/interfaces/application.mojom.h" #include "mojo/common/weak_binding_set.h" -#include "mojo/message_pump/message_pump_mojo.h" -#include "mojo/runner/child/runner_connection.h" +#include "mojo/runner/child/test_native_main.h" #include "mojo/runner/child/test_native_service.mojom.h" #include "mojo/runner/init.h" -#include "third_party/mojo/src/mojo/edk/embedder/embedder.h" -#include "third_party/mojo/src/mojo/edk/embedder/process_delegate.h" - -#if defined(OS_WIN) -#include -#endif namespace { @@ -63,17 +49,6 @@ class TargetApplicationDelegate DISALLOW_COPY_AND_ASSIGN(TargetApplicationDelegate); }; -class ProcessDelegate : public mojo::embedder::ProcessDelegate { - public: - ProcessDelegate() {} - ~ProcessDelegate() override {} - - private: - void OnShutdownComplete() override {} - - DISALLOW_COPY_AND_ASSIGN(ProcessDelegate); -}; - } // namespace int main(int argc, char** argv) { @@ -81,43 +56,7 @@ int main(int argc, char** argv) { base::CommandLine::Init(argc, argv); mojo::runner::InitializeLogging(); - mojo::runner::WaitForDebuggerIfNecessary(); - -#if !defined(OFFICIAL_BUILD) - base::debug::EnableInProcessStackDumping(); -#if defined(OS_WIN) - base::RouteStdioToConsole(false); -#endif -#endif - - { - mojo::embedder::Init(); - - ProcessDelegate process_delegate; - base::Thread io_thread("io_thread"); - base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0); - CHECK(io_thread.StartWithOptions(io_thread_options)); - - mojo::embedder::InitIPCSupport( - mojo::embedder::ProcessType::NONE, io_thread.task_runner().get(), - &process_delegate, io_thread.task_runner().get(), - mojo::embedder::ScopedPlatformHandle()); - - mojo::InterfaceRequest application_request; - scoped_ptr connection( - mojo::runner::RunnerConnection::ConnectToRunner(&application_request)); - - TargetApplicationDelegate delegate; - { - base::MessageLoop loop(mojo::common::MessagePumpMojo::Create()); - mojo::ApplicationImpl impl(&delegate, application_request.Pass()); - loop.Run(); - } - - connection.reset(); - - mojo::embedder::ShutdownIPCSupport(); - } - return 0; + TargetApplicationDelegate delegate; + return mojo::runner::TestNativeMain(&delegate); } diff --git a/mojo/runner/child/test_native_main.cc b/mojo/runner/child/test_native_main.cc new file mode 100644 index 00000000000000..7bf3072fefda48 --- /dev/null +++ b/mojo/runner/child/test_native_main.cc @@ -0,0 +1,80 @@ +// 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 "mojo/runner/child/test_native_main.h" + +#include "base/debug/stack_trace.h" +#include "base/message_loop/message_loop.h" +#include "base/process/launch.h" +#include "base/threading/thread.h" +#include "build/build_config.h" +#include "mojo/application/public/cpp/application_delegate.h" +#include "mojo/application/public/cpp/application_impl.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/runner/child/runner_connection.h" +#include "mojo/runner/init.h" +#include "third_party/mojo/src/mojo/edk/embedder/embedder.h" +#include "third_party/mojo/src/mojo/edk/embedder/process_delegate.h" + +namespace mojo { +namespace runner { +namespace { + +class ProcessDelegate : public mojo::embedder::ProcessDelegate { + public: + ProcessDelegate() {} + ~ProcessDelegate() override {} + + private: + void OnShutdownComplete() override {} + + DISALLOW_COPY_AND_ASSIGN(ProcessDelegate); +}; + +} // namespace + +int TestNativeMain(mojo::ApplicationDelegate* application_delegate) { + mojo::runner::WaitForDebuggerIfNecessary(); + +#if !defined(OFFICIAL_BUILD) + base::debug::EnableInProcessStackDumping(); +#if defined(OS_WIN) + base::RouteStdioToConsole(false); +#endif +#endif + + { + mojo::embedder::Init(); + + ProcessDelegate process_delegate; + base::Thread io_thread("io_thread"); + base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0); + CHECK(io_thread.StartWithOptions(io_thread_options)); + + mojo::embedder::InitIPCSupport( + mojo::embedder::ProcessType::NONE, io_thread.task_runner().get(), + &process_delegate, io_thread.task_runner().get(), + mojo::embedder::ScopedPlatformHandle()); + + mojo::InterfaceRequest application_request; + scoped_ptr connection( + mojo::runner::RunnerConnection::ConnectToRunner(&application_request)); + + base::MessageLoop loop(mojo::common::MessagePumpMojo::Create()); + { + mojo::ApplicationImpl impl(application_delegate, + application_request.Pass()); + loop.Run(); + } + + connection.reset(); + + mojo::embedder::ShutdownIPCSupport(); + } + + return 0; +} + +} // namespace runner +} // namespace mojo diff --git a/mojo/runner/child/test_native_main.h b/mojo/runner/child/test_native_main.h new file mode 100644 index 00000000000000..b1931af16d4846 --- /dev/null +++ b/mojo/runner/child/test_native_main.h @@ -0,0 +1,17 @@ +// 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. + +#ifndef MOJO_RUNNER_CHILD_TEST_NATIVE_MAIN_H_ +#define MOJO_RUNNER_CHILD_TEST_NATIVE_MAIN_H_ + +namespace mojo { +class ApplicationDelegate; +namespace runner { + +int TestNativeMain(mojo::ApplicationDelegate* application_delegate); + +} // namespace runner +} // namespace mojo + +#endif // MOJO_RUNNER_CHILD_TEST_NATIVE_MAIN_H_ diff --git a/mojo/runner/host/child_process_host.cc b/mojo/runner/host/child_process_host.cc index 6074b5b5acfc7c..268deff3df6b74 100644 --- a/mojo/runner/host/child_process_host.cc +++ b/mojo/runner/host/child_process_host.cc @@ -39,8 +39,20 @@ ChildProcessHost::ChildProcessHost(base::TaskRunner* launch_process_runner, CHECK(platform_channel_.is_valid()); } +ChildProcessHost::ChildProcessHost(ScopedHandle channel) + : launch_process_runner_(nullptr), + start_sandboxed_(false), + channel_info_(nullptr), + start_child_process_event_(false, false), + weak_factory_(this) { + CHECK(channel.is_valid()); + ScopedMessagePipeHandle handle(MessagePipeHandle(channel.release().value())); + controller_.Bind(InterfacePtrInfo(handle.Pass(), 0u)); +} + ChildProcessHost::~ChildProcessHost() { - CHECK(!controller_) << "Destroying ChildProcessHost before calling Join"; + if (!app_path_.empty()) + CHECK(!controller_) << "Destroying ChildProcessHost before calling Join"; } void ChildProcessHost::Start() { diff --git a/mojo/runner/host/child_process_host.h b/mojo/runner/host/child_process_host.h index 68ea6c2bce4619..a4d15103029413 100644 --- a/mojo/runner/host/child_process_host.h +++ b/mojo/runner/host/child_process_host.h @@ -41,6 +41,9 @@ class ChildProcessHost { ChildProcessHost(base::TaskRunner* launch_process_runner, bool start_sandboxed, const base::FilePath& app_path); + // Allows a ChildProcessHost to be instantiated for an existing channel + // created by someone else (e.g. an app that launched its own process). + explicit ChildProcessHost(ScopedHandle channel); virtual ~ChildProcessHost(); // |Start()|s the child process; calls |DidStart()| (on the thread on which diff --git a/mojo/runner/host/in_process_native_runner.cc b/mojo/runner/host/in_process_native_runner.cc index ee5e9e35ddc209..1182fb6d4c4628 100644 --- a/mojo/runner/host/in_process_native_runner.cc +++ b/mojo/runner/host/in_process_native_runner.cc @@ -50,6 +50,12 @@ void InProcessNativeRunner::Start( thread_->Start(); } +void InProcessNativeRunner::InitHost( + ScopedHandle channel, + InterfaceRequest application_request) { + NOTREACHED(); // Can't host another process in this runner. +} + void InProcessNativeRunner::Run() { DVLOG(2) << "Loading/running Mojo app in process from library: " << app_path_.value() diff --git a/mojo/runner/host/in_process_native_runner.h b/mojo/runner/host/in_process_native_runner.h index ddd83956cefa27..4aa08404f63edd 100644 --- a/mojo/runner/host/in_process_native_runner.h +++ b/mojo/runner/host/in_process_native_runner.h @@ -29,11 +29,13 @@ class InProcessNativeRunner : public shell::NativeRunner, InProcessNativeRunner(); ~InProcessNativeRunner() override; - // |NativeRunner| method: + // NativeRunner: void Start(const base::FilePath& app_path, bool start_sandboxed, InterfaceRequest application_request, const base::Closure& app_completed_callback) override; + void InitHost(ScopedHandle channel, + InterfaceRequest application_request) override; private: // |base::DelegateSimpleThread::Delegate| method: diff --git a/mojo/runner/host/out_of_process_native_runner.cc b/mojo/runner/host/out_of_process_native_runner.cc index 0388c953d11166..2bc561e4b864fb 100644 --- a/mojo/runner/host/out_of_process_native_runner.cc +++ b/mojo/runner/host/out_of_process_native_runner.cc @@ -20,7 +20,7 @@ OutOfProcessNativeRunner::OutOfProcessNativeRunner( : launch_process_runner_(launch_process_runner) {} OutOfProcessNativeRunner::~OutOfProcessNativeRunner() { - if (child_process_host_) + if (child_process_host_ && !app_path_.empty()) child_process_host_->Join(); } @@ -44,6 +44,16 @@ void OutOfProcessNativeRunner::Start( base::Unretained(this))); } +void OutOfProcessNativeRunner::InitHost( + ScopedHandle channel, + InterfaceRequest application_request) { + child_process_host_.reset(new ChildProcessHost(channel.Pass())); + child_process_host_->StartApp( + application_request.Pass(), + base::Bind(&OutOfProcessNativeRunner::AppCompleted, + base::Unretained(this))); +} + void OutOfProcessNativeRunner::AppCompleted(int32_t result) { DVLOG(2) << "OutOfProcessNativeRunner::AppCompleted(" << result << ")"; diff --git a/mojo/runner/host/out_of_process_native_runner.h b/mojo/runner/host/out_of_process_native_runner.h index 6cec2419c6ba9d..0c913ed5936d6b 100644 --- a/mojo/runner/host/out_of_process_native_runner.h +++ b/mojo/runner/host/out_of_process_native_runner.h @@ -27,11 +27,13 @@ class OutOfProcessNativeRunner : public shell::NativeRunner { explicit OutOfProcessNativeRunner(base::TaskRunner* launch_process_runner); ~OutOfProcessNativeRunner() override; - // |NativeRunner| method: + // NativeRunner: void Start(const base::FilePath& app_path, bool start_sandboxed, InterfaceRequest application_request, const base::Closure& app_completed_callback) override; + void InitHost(ScopedHandle channel, + InterfaceRequest application_request) override; private: // |ChildController::StartApp()| callback: diff --git a/mojo/shell/BUILD.gn b/mojo/shell/BUILD.gn index b54c7b505de9f0..8478000f337589 100644 --- a/mojo/shell/BUILD.gn +++ b/mojo/shell/BUILD.gn @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//mojo/public/mojo_application.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//testing/test.gni") @@ -108,6 +109,7 @@ test("mojo_shell_unittests") { mojom("test_bindings") { sources = [ + "application_manager_apptests.mojom", "capability_filter_unittest.mojom", "test.mojom", ] @@ -118,3 +120,65 @@ mojom("interfaces") { "application_manager.mojom", ] } + +mojo_native_application("apptests") { + output_name = "mojo_shell_apptests" + testonly = true + + sources = [ + "application_manager_apptest.cc", + ] + + deps = [ + ":test_bindings", + "//base", + "//base/test:test_config", + "//mojo/application/public/cpp:sources", + "//mojo/application/public/cpp:test_support", + "//mojo/common:common_base", + "//mojo/converters/network", + ] + + data_deps = [ + ":application_manager_apptest_driver", + ":application_manager_apptest_target", + ] +} + +executable("application_manager_apptest_driver") { + testonly = true + + sources = [ + "application_manager_apptest_driver.cc", + ] + + deps = [ + ":test_bindings", + "//base", + "//base:base_static", + "//mojo/application/public/cpp", + "//mojo/common:common_base", + "//mojo/converters/network", + "//mojo/runner:init", + "//mojo/runner/child:test_native_main", + "//mojo/shell:interfaces", + "//third_party/mojo/src/mojo/edk/system", + ] +} + +executable("application_manager_apptest_target") { + testonly = true + + sources = [ + "application_manager_apptest_target.cc", + ] + + deps = [ + ":test_bindings", + "//base", + "//mojo/application/public/cpp", + "//mojo/common:common_base", + "//mojo/converters/network", + "//mojo/runner/child:test_native_main", + ] +} diff --git a/mojo/shell/DEPS b/mojo/shell/DEPS index f660650190262a..5a272f05b3ef93 100644 --- a/mojo/shell/DEPS +++ b/mojo/shell/DEPS @@ -1,3 +1,14 @@ include_rules = [ "-mojo/runner", ] + +specific_include_rules = { + "application_manager_apptest_driver.cc": [ + "+mojo/runner/child", + "+mojo/runner/init.h", + ], + "application_manager_apptest_target.cc": [ + "+mojo/runner/child", + "+mojo/runner/init.h", + ], +} diff --git a/mojo/shell/application_manager.cc b/mojo/shell/application_manager.cc index 16c483d25312fb..d011049d104346 100644 --- a/mojo/shell/application_manager.cc +++ b/mojo/shell/application_manager.cc @@ -89,7 +89,7 @@ void ApplicationManager::ConnectToApplication( ApplicationLoader* loader = GetLoaderForURL(params->target().url()); if (loader) { GURL url = params->target().url(); - loader->Load(url, CreateInstance(params.Pass(), nullptr)); + loader->Load(url, CreateAndConnectToInstance(params.Pass(), nullptr)); return; } @@ -110,31 +110,57 @@ bool ApplicationManager::ConnectToRunningApplication( return true; } -InterfaceRequest ApplicationManager::CreateInstance( +ApplicationInstance* ApplicationManager::GetApplicationInstance( + const Identity& identity) const { + const auto& it = identity_to_instance_.find(identity); + return it != identity_to_instance_.end() ? it->second : nullptr; +} + +void ApplicationManager::CreateInstanceForHandle(ScopedHandle channel, + const GURL& url, + const std::string& qualifier) { + // Instances created by others are considered unique, and thus have no + // identity. As such they cannot be connected to by anyone else, and so we + // never call ConnectToClient(). + Identity target_id(url, qualifier, GetPermissiveCapabilityFilter()); + InterfaceRequest application_request = + CreateInstance(target_id, base::Closure(), nullptr); + NativeRunner* runner = + native_runner_factory_->Create(base::FilePath()).release(); + native_runners_.push_back(runner); + runner->InitHost(channel.Pass(), application_request.Pass()); +} + +InterfaceRequest ApplicationManager::CreateAndConnectToInstance( scoped_ptr params, ApplicationInstance** resulting_instance) { - Identity target_id = params->target(); + ApplicationInstance* instance = nullptr; + InterfaceRequest application_request = + CreateInstance(params->target(), params->on_application_end(), &instance); + instance->ConnectToClient(params.Pass()); + if (resulting_instance) + *resulting_instance = instance; + return application_request.Pass(); +} + +InterfaceRequest ApplicationManager::CreateInstance( + const Identity& target_id, + const base::Closure& on_application_end, + ApplicationInstance** resulting_instance) { ApplicationPtr application; InterfaceRequest application_request = GetProxy(&application); ApplicationInstance* instance = new ApplicationInstance( application.Pass(), this, target_id, Shell::kInvalidContentHandlerID, - params->on_application_end()); + on_application_end); DCHECK(identity_to_instance_.find(target_id) == identity_to_instance_.end()); identity_to_instance_[target_id] = instance; instance->InitializeApplication(); - instance->ConnectToClient(params.Pass()); if (resulting_instance) *resulting_instance = instance; return application_request.Pass(); } -ApplicationInstance* ApplicationManager::GetApplicationInstance( - const Identity& identity) const { - const auto& it = identity_to_instance_.find(identity); - return it != identity_to_instance_.end() ? it->second : nullptr; -} - void ApplicationManager::HandleFetchCallback( scoped_ptr params, scoped_ptr fetcher) { @@ -171,7 +197,8 @@ void ApplicationManager::HandleFetchCallback( params->connect_callback(); params->set_connect_callback(EmptyConnectCallback()); ApplicationInstance* app = nullptr; - InterfaceRequest request(CreateInstance(params.Pass(), &app)); + InterfaceRequest request( + CreateAndConnectToInstance(params.Pass(), &app)); uint32_t content_handler_id = package_manager_->HandleWithContentHandler( fetcher.get(), source, target.url(), target.filter(), &request); diff --git a/mojo/shell/application_manager.h b/mojo/shell/application_manager.h index 16afd4451ea25f..bab4248171686e 100644 --- a/mojo/shell/application_manager.h +++ b/mojo/shell/application_manager.h @@ -92,6 +92,10 @@ class ApplicationManager { ApplicationInstance* GetApplicationInstance(const Identity& identity) const; + void CreateInstanceForHandle(ScopedHandle channel, + const GURL& url, + const std::string& qualifier); + private: using IdentityToInstanceMap = std::map; using URLToLoaderMap = std::map; @@ -100,9 +104,13 @@ class ApplicationManager { bool ConnectToRunningApplication( scoped_ptr* params); - InterfaceRequest CreateInstance( + InterfaceRequest CreateAndConnectToInstance( scoped_ptr params, ApplicationInstance** instance); + InterfaceRequest CreateInstance( + const Identity& target_id, + const base::Closure& on_application_end, + ApplicationInstance** resulting_instance); // Called once |fetcher| has found app. |params->app_url()| is the url of // the requested application before any mappings/resolution have been applied. diff --git a/mojo/shell/application_manager.mojom b/mojo/shell/application_manager.mojom index c9135bb6209c62..3132da6c1bfb79 100644 --- a/mojo/shell/application_manager.mojom +++ b/mojo/shell/application_manager.mojom @@ -10,5 +10,6 @@ interface ApplicationManager { // initialization. This assumes the target process will bind the other end of // channel to an implementation of ChildController and bind an Application // request there. - CreateInstanceForHandle(handle channel); + // TODO(beng): we should probably have an Identity mojom struct. + CreateInstanceForHandle(handle channel, string url, string qualifier); }; diff --git a/mojo/shell/application_manager_apptest.cc b/mojo/shell/application_manager_apptest.cc new file mode 100644 index 00000000000000..c6b14a2714160b --- /dev/null +++ b/mojo/shell/application_manager_apptest.cc @@ -0,0 +1,111 @@ +// 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 "base/bind.h" +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "mojo/application/public/cpp/application_impl.h" +#include "mojo/application/public/cpp/application_test_base.h" +#include "mojo/application/public/cpp/interface_factory.h" +#include "mojo/converters/network/network_type_converters.h" +#include "mojo/shell/application_manager_apptests.mojom.h" + +using mojo::shell::test::mojom::CreateInstanceForHandleTest; + +namespace mojo { +namespace shell { +namespace { + +class ApplicationManagerAppTestDelegate + : public ApplicationDelegate, + public InterfaceFactory, + public CreateInstanceForHandleTest { + public: + ApplicationManagerAppTestDelegate() : binding_(this) {} + ~ApplicationManagerAppTestDelegate() override {} + + const std::string& data() const { return data_; } + + private: + // ApplicationDelegate: + void Initialize(ApplicationImpl* app) override {} + bool ConfigureIncomingConnection(ApplicationConnection* connection) override { + connection->AddService(this); + return true; + } + + // InterfaceFactory: + void Create( + ApplicationConnection* connection, + InterfaceRequest request) override { + binding_.Bind(request.Pass()); + } + + // CreateInstanceForHandleTest: + void Ping(const String& data) override { + data_ = data; + base::MessageLoop::current()->QuitWhenIdle(); + } + + std::string data_; + + Binding binding_; + + DISALLOW_COPY_AND_ASSIGN(ApplicationManagerAppTestDelegate); +}; + +} // namespace + +class ApplicationManagerAppTest : public mojo::test::ApplicationTestBase { + public: + ApplicationManagerAppTest() : delegate_(nullptr) {} + ~ApplicationManagerAppTest() override {} + + void OnDriverQuit() { + base::MessageLoop::current()->QuitNow(); + } + + protected: + const std::string& data() const { + DCHECK(delegate_); + return delegate_->data(); + } + + ApplicationManagerAppTestDelegate* delegate() { return delegate_; } + + private: + // test::ApplicationTestBase: + ApplicationDelegate* GetApplicationDelegate() override { + delegate_ = new ApplicationManagerAppTestDelegate; + return delegate_; + } + + ApplicationManagerAppTestDelegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(ApplicationManagerAppTest); +}; + +TEST_F(ApplicationManagerAppTest, CreateInstanceForHandle) { + // 1. Launch a process. (Actually, have the runner launch a process that + // launches a process. #becauselinkerrors). + mojo::shell::test::mojom::DriverPtr driver; + application_impl()->ConnectToService( + URLRequest::From(std::string("exe:application_manager_apptest_driver")), + &driver); + + // 2. Wait for it to connect to us. (via mojo:application_manager_apptests) + base::MessageLoop::current()->Run(); + + // 3. Profit! + EXPECT_EQ(data(), "From Target"); + + driver.set_connection_error_handler( + base::Bind(&ApplicationManagerAppTest::OnDriverQuit, + base::Unretained(this))); + driver->QuitDriver(); + base::MessageLoop::current()->Run(); +} + +} // namespace shell +} // namespace mojo diff --git a/mojo/shell/application_manager_apptest_driver.cc b/mojo/shell/application_manager_apptest_driver.cc new file mode 100644 index 00000000000000..06f2fad970ad33 --- /dev/null +++ b/mojo/shell/application_manager_apptest_driver.cc @@ -0,0 +1,137 @@ +// 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 "base/at_exit.h" +#include "base/base_paths.h" +#include "base/base_switches.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/path_service.h" +#include "base/process/process.h" +#include "base/thread_task_runner_handle.h" +#include "mojo/application/public/cpp/application_connection.h" +#include "mojo/application/public/cpp/application_delegate.h" +#include "mojo/application/public/cpp/application_impl.h" +#include "mojo/application/public/cpp/interface_factory.h" +#include "mojo/common/weak_binding_set.h" +#include "mojo/converters/network/network_type_converters.h" +#include "mojo/runner/child/test_native_main.h" +#include "mojo/runner/init.h" +#include "mojo/shell/application_manager.mojom.h" +#include "mojo/shell/application_manager_apptests.mojom.h" +#include "third_party/mojo/src/mojo/edk/embedder/embedder.h" +#include "third_party/mojo/src/mojo/edk/embedder/platform_channel_pair.h" +#include "third_party/mojo/src/mojo/edk/embedder/process_delegate.h" +#include "third_party/mojo/src/mojo/edk/embedder/scoped_platform_handle.h" + +using mojo::shell::test::mojom::Driver; + +namespace { + +class TargetApplicationDelegate : public mojo::ApplicationDelegate, + public mojo::InterfaceFactory, + public Driver { + public: + TargetApplicationDelegate() : app_(nullptr), weak_factory_(this) {} + ~TargetApplicationDelegate() override {} + + private: + // mojo::ApplicationDelegate: + void Initialize(mojo::ApplicationImpl* app) override { + app_ = app; + mojo::shell::mojom::ApplicationManagerPtr application_manager; + app_->ConnectToService(mojo::URLRequest::From(std::string("mojo:shell")), + &application_manager); + + base::FilePath target_path; + CHECK(base::PathService::Get(base::DIR_EXE, &target_path)); + #if defined(OS_WIN) + target_path = target_path.Append( + FILE_PATH_LITERAL("application_manager_apptest_target.exe")); + #else + target_path = target_path.Append( + FILE_PATH_LITERAL("application_manager_apptest_target")); + #endif + + base::CommandLine child_command_line(target_path); + // Forward the wait-for-debugger flag but nothing else - we don't want to + // stamp on the platform-channel flag. + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kWaitForDebugger)) { + child_command_line.AppendSwitch(switches::kWaitForDebugger); + } + if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) + child_command_line.AppendSwitch("use-new-edk"); + + mojo::embedder::HandlePassingInformation handle_passing_info; + + // Create the channel to be shared with the target process. + mojo::embedder::PlatformChannelPair platform_channel_pair; + // Give one end to the shell so that it can create an instance. + mojo::embedder::ScopedPlatformHandle platform_channel = + platform_channel_pair.PassServerHandle(); + + mojo::ScopedMessagePipeHandle handle(mojo::embedder::CreateChannel( + platform_channel.Pass(), + base::Bind(&TargetApplicationDelegate::DidCreateChannel, + weak_factory_.GetWeakPtr()), + base::ThreadTaskRunnerHandle::Get())); + + application_manager->CreateInstanceForHandle( + mojo::ScopedHandle(mojo::Handle(handle.release().value())), + "exe:application_manager_apptest_target", + "0"); + // Put the other end on the command line used to launch the target. + platform_channel_pair.PrepareToPassClientHandleToChildProcess( + &child_command_line, &handle_passing_info); + + base::LaunchOptions options; + #if defined(OS_WIN) + options.handles_to_inherit = &handle_passing_info; + #elif defined(OS_POSIX) + options.fds_to_remap = &handle_passing_info; + #endif + target_ = base::LaunchProcess(child_command_line, options); + } + bool ConfigureIncomingConnection(mojo::ApplicationConnection* connection) { + connection->AddService(this); + return true; + } + + // mojo::InterfaceFactory: + void Create(mojo::ApplicationConnection* connection, + mojo::InterfaceRequest request) override { + bindings_.AddBinding(this, request.Pass()); + } + + // Driver: + void QuitDriver() override { + target_.Terminate(0, false); + app_->Quit(); + } + + void DidCreateChannel(mojo::embedder::ChannelInfo* channel_info) {} + + mojo::ApplicationImpl* app_; + base::WeakPtrFactory weak_factory_; + base::Process target_; + mojo::WeakBindingSet bindings_; + + DISALLOW_COPY_AND_ASSIGN(TargetApplicationDelegate); +}; + +} // namespace + +int main(int argc, char** argv) { + base::AtExitManager at_exit; + base::CommandLine::Init(argc, argv); + + mojo::runner::InitializeLogging(); + + TargetApplicationDelegate delegate; + return mojo::runner::TestNativeMain(&delegate); +} diff --git a/mojo/shell/application_manager_apptest_target.cc b/mojo/shell/application_manager_apptest_target.cc new file mode 100644 index 00000000000000..aa7bc7847674c9 --- /dev/null +++ b/mojo/shell/application_manager_apptest_target.cc @@ -0,0 +1,51 @@ +// 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 "base/at_exit.h" +#include "base/command_line.h" +#include "mojo/application/public/cpp/application_connection.h" +#include "mojo/application/public/cpp/application_delegate.h" +#include "mojo/application/public/cpp/application_impl.h" +#include "mojo/converters/network/network_type_converters.h" +#include "mojo/runner/child/test_native_main.h" +#include "mojo/runner/init.h" +#include "mojo/shell/application_manager_apptests.mojom.h" + +using mojo::shell::test::mojom::CreateInstanceForHandleTestPtr; + +namespace { + +class TargetApplicationDelegate : public mojo::ApplicationDelegate { + public: + TargetApplicationDelegate() {} + ~TargetApplicationDelegate() override {} + + private: + // mojo::ApplicationDelegate: + void Initialize(mojo::ApplicationImpl* app) override { + CreateInstanceForHandleTestPtr service; + app->ConnectToService( + mojo::URLRequest::From(std::string("mojo:mojo_shell_apptests")), + &service); + service->Ping("From Target"); + } + bool ConfigureIncomingConnection( + mojo::ApplicationConnection* connection) override { + return true; + } + + DISALLOW_COPY_AND_ASSIGN(TargetApplicationDelegate); +}; + +} // namespace + +int main(int argc, char** argv) { + base::AtExitManager at_exit; + base::CommandLine::Init(argc, argv); + + mojo::runner::InitializeLogging(); + + TargetApplicationDelegate delegate; + return mojo::runner::TestNativeMain(&delegate); +} diff --git a/mojo/shell/application_manager_apptests.mojom b/mojo/shell/application_manager_apptests.mojom new file mode 100644 index 00000000000000..7ca390ae764e02 --- /dev/null +++ b/mojo/shell/application_manager_apptests.mojom @@ -0,0 +1,13 @@ +// 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. + +module mojo.shell.test.mojom; + +interface CreateInstanceForHandleTest { + Ping(string data); +}; + +interface Driver { + QuitDriver(); +}; diff --git a/mojo/shell/native_runner.h b/mojo/shell/native_runner.h index 77e73c8caa6d83..ebde8405c9512a 100644 --- a/mojo/shell/native_runner.h +++ b/mojo/shell/native_runner.h @@ -38,6 +38,11 @@ class NativeRunner { bool start_sandboxed, InterfaceRequest application_request, const base::Closure& app_completed_callback) = 0; + + // Like Start(), but used to initialize the host for a child process started + // by someone else. Provides |application_request| via |channel|. + virtual void InitHost(ScopedHandle channel, + InterfaceRequest application_request) = 0; }; class NativeRunnerFactory { diff --git a/mojo/shell/shell_application_delegate.cc b/mojo/shell/shell_application_delegate.cc index 00c4485b324ac8..25153fc1afabd0 100644 --- a/mojo/shell/shell_application_delegate.cc +++ b/mojo/shell/shell_application_delegate.cc @@ -5,6 +5,7 @@ #include "mojo/shell/shell_application_delegate.h" #include "mojo/application/public/cpp/application_connection.h" +#include "mojo/shell/application_manager.h" namespace mojo { namespace shell { @@ -27,8 +28,11 @@ void ShellApplicationDelegate::Create( bindings_.AddBinding(this, request.Pass()); } -void ShellApplicationDelegate::CreateInstanceForHandle(ScopedHandle channel) { - // TODO(beng): create the instance. +void ShellApplicationDelegate::CreateInstanceForHandle( + ScopedHandle channel, + const String& url, + const String& qualifier) { + manager_->CreateInstanceForHandle(channel.Pass(), GURL(url), qualifier); } } // namespace shell diff --git a/mojo/shell/shell_application_delegate.h b/mojo/shell/shell_application_delegate.h index e0dcce3953c0cf..cb03dd1811e3dc 100644 --- a/mojo/shell/shell_application_delegate.h +++ b/mojo/shell/shell_application_delegate.h @@ -35,7 +35,9 @@ class ShellApplicationDelegate InterfaceRequest request) override; // Overridden from mojom::ApplicationManager: - void CreateInstanceForHandle(ScopedHandle channel) override; + void CreateInstanceForHandle(ScopedHandle channel, + const String& url, + const String& qualifier) override; mojo::shell::ApplicationManager* manager_;