Commit 323013a9 authored by Kevin Marshall's avatar Kevin Marshall Committed by Commit Bot

Fuchsia: Implement spawning portions of ContextProvider FIDL service.

Implements and tests the Context child process spawning aspects of
ContextProvider.

Additionally moves non-main portions of "//webrunner:webrunner" into a
component, so that they can be called from unit tests.

Bug: 850743
Change-Id: I4f4aafac71ac56d72c6931db3751201bc516d92a
Reviewed-on: https://chromium-review.googlesource.com/1096453
Commit-Queue: Kevin Marshall <kmarshall@chromium.org>
Reviewed-by: default avatarJochen Eisinger <jochen@chromium.org>
Reviewed-by: default avatarSergey Ulanov <sergeyu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568161}
parent 3c59d0b5
......@@ -755,6 +755,7 @@ group("gn_all") {
"//headless:headless_shell",
"//headless:headless_tests",
"//webrunner",
"//webrunner:webrunner_unittests",
]
}
}
......
......@@ -6,11 +6,27 @@ assert(is_fuchsia)
import("//build/config/fuchsia/fidl_library.gni")
import("//build/config/fuchsia/rules.gni")
import("//testing/test.gni")
import("//tools/grit/repack.gni")
config("webrunner_implementation") {
defines = [ "WEBRUNNER_IMPLEMENTATION" ]
}
executable("webrunner") {
deps = [
":fidl",
":webrunner_lib",
"//base",
"//content/public/app:both",
]
sources = [
"app/webrunner_main.cc",
]
}
component("webrunner_lib") {
deps = [
":webrunner_pak",
"//base",
"//components/version_info",
......@@ -29,9 +45,16 @@ executable("webrunner") {
data = [
"$root_out_dir/webrunner.pak",
]
public_deps = [
":fidl",
]
configs += [ ":webrunner_implementation" ]
sources = [
"app/webrunner_main.cc",
"app/context_provider/context_provider_impl.cc",
"app/context_provider/context_provider_impl.h",
"app/switches.cc",
"app/switches.h",
"app/webrunner_main_delegate.cc",
"app/webrunner_main_delegate.h",
"browser/webrunner_browser_context.cc",
......@@ -50,6 +73,7 @@ executable("webrunner") {
"browser/webrunner_url_request_context_getter.h",
"common/webrunner_content_client.cc",
"common/webrunner_content_client.h",
"common/webrunner_export.h",
]
}
......@@ -95,8 +119,24 @@ repack("webrunner_pak") {
output = "$root_out_dir/webrunner.pak"
}
test("webrunner_unittests") {
sources = [
"app/context_provider/context_provider_impl_unittest.cc",
]
deps = [
":fidl",
":webrunner_lib",
"//base/test:run_all_unittests",
"//base/test:test_support",
"//testing/gmock",
"//testing/gtest",
]
}
fidl_library("fidl") {
library_name = "chromium.web"
library_name = "web"
namespace = "chromium"
namespace_path = "chromium"
sources = [
"fidl/context.fidl",
......@@ -106,7 +146,8 @@ fidl_library("fidl") {
"fidl/navigation_controller.fidl",
]
deps = [
public_deps = [
"//third_party/fuchsia-sdk:gfx",
"//third_party/fuchsia-sdk:sys",
"//third_party/fuchsia-sdk:views_v1",
]
......
include_rules = [
"+chromium/web/cpp", # FIDL generated headers
"+content/public/app",
"+ui/base",
]
\ No newline at end of file
]
// Copyright 2018 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 "webrunner/app/context_provider/context_provider_impl.h"
#include <fuchsia/sys/cpp/fidl.h>
#include <zircon/processargs.h>
#include <utility>
#include "base/bind.h"
#include "base/callback_forward.h"
#include "base/command_line.h"
#include "base/fuchsia/default_job.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/scoped_zx_handle.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "webrunner/app/switches.h"
namespace fuchsia {
namespace {
// Relaunches the current executable as a Context process.
base::Process LaunchContextProcess(const base::LaunchOptions& launch_options) {
base::CommandLine launch_command = *base::CommandLine::ForCurrentProcess();
DCHECK(!launch_command.HasSwitch(kContextProcess));
launch_command.AppendSwitch(kContextProcess);
return base::LaunchProcess(launch_command, launch_options);
}
} // namespace
ContextProviderImpl::ContextProviderImpl()
: launch_(base::BindRepeating(&LaunchContextProcess)) {}
ContextProviderImpl::~ContextProviderImpl() = default;
void ContextProviderImpl::SetLaunchCallbackForTests(
const LaunchContextProcessCallback& launch) {
launch_ = launch;
}
void ContextProviderImpl::Create(
chromium::web::CreateContextParams params,
::fidl::InterfaceRequest<chromium::web::Context> context_request) {
DCHECK(context_request.is_valid());
if (params.dataDirectory) {
// TODO(https://crbug.com/850743): Implement this.
NOTIMPLEMENTED()
<< "Persistent data directory binding is not yet implemented.";
}
// Transfer the ContextRequest handle to a well-known location in the child
// process' handle table.
base::LaunchOptions launch_options;
base::ScopedZxHandle context_handle(context_request.TakeChannel().release());
launch_options.handles_to_transfer.push_back(
{PA_HND(PA_USER0, 0), context_handle.get()});
// Isolate the child Context processes by containing them within their own
// respective jobs.
base::ScopedZxHandle job;
zx_status_t status = zx_job_create(base::GetDefaultJob(), 0, job.receive());
ZX_CHECK(status == ZX_OK, status) << "zx_job_create";
ignore_result(launch_.Run(launch_options));
ignore_result(context_handle.release());
ignore_result(job.release());
}
void ContextProviderImpl::Bind(
fidl::InterfaceRequest<chromium::web::ContextProvider> request) {
bindings_.AddBinding(this, std::move(request));
}
} // namespace fuchsia
// Copyright 2018 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 WEBRUNNER_APP_CONTEXT_PROVIDER_CONTEXT_PROVIDER_IMPL_H_
#define WEBRUNNER_APP_CONTEXT_PROVIDER_CONTEXT_PROVIDER_IMPL_H_
#include <lib/fidl/cpp/binding_set.h>
#include "base/callback.h"
#include "base/macros.h"
#include "chromium/web/cpp/fidl.h"
#include "webrunner/common/webrunner_export.h"
namespace base {
struct LaunchOptions;
class Process;
} // namespace base
namespace fuchsia {
class WEBRUNNER_EXPORT ContextProviderImpl
: public chromium::web::ContextProvider {
public:
ContextProviderImpl();
~ContextProviderImpl() override;
// Binds |this| object instance to |request|.
// The service will persist and continue to serve other channels in the event
// that a bound channel is dropped.
void Bind(fidl::InterfaceRequest<chromium::web::ContextProvider> request);
// chromium::web::ContextProvider implementation.
void Create(chromium::web::CreateContextParams params,
::fidl::InterfaceRequest<chromium::web::Context> context_request)
override;
private:
using LaunchContextProcessCallback =
base::RepeatingCallback<base::Process(const base::LaunchOptions&)>;
friend class ContextProviderImplTest;
// Overrides the default child process launching logic to call |launch|
// instead.
void SetLaunchCallbackForTests(const LaunchContextProcessCallback& launch);
// Spawns a Context child process.
LaunchContextProcessCallback launch_;
fidl::BindingSet<chromium::web::ContextProvider> bindings_;
DISALLOW_COPY_AND_ASSIGN(ContextProviderImpl);
};
} // namespace fuchsia
#endif // WEBRUNNER_APP_CONTEXT_PROVIDER_CONTEXT_PROVIDER_IMPL_H_
// Copyright 2018 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 "webrunner/app/context_provider/context_provider_impl.h"
#include <lib/fidl/cpp/binding.h>
#include <zircon/processargs.h>
#include <utility>
#include <vector>
#include "base/base_switches.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file.h"
#include "base/fuchsia/file_utils.h"
#include "base/fuchsia/services_directory.h"
#include "base/message_loop/message_loop.h"
#include "base/test/multiprocess_test.h"
#include "testing/gmock/include/gmock/gmock-actions.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
namespace fuchsia {
namespace {
class MockFrameObserver : public chromium::web::FrameObserver {
public:
void OnNavigationStateChanged(
chromium::web::NavigationStateChangeDetails change,
OnNavigationStateChangedCallback callback) override {
OnNavigationStateChanged();
}
MOCK_METHOD0(OnNavigationStateChanged, void(void));
};
void DoNothing() {}
class FakeContext : public chromium::web::Context {
public:
void CreateFrame(
::fidl::InterfaceHandle<chromium::web::FrameObserver> observer,
::fidl::InterfaceRequest<chromium::web::Frame> frame) override {
chromium::web::NavigationStateChangeDetails details;
details.entry.url = "";
details.entry.title = "";
observer.Bind()->OnNavigationStateChanged(details, &DoNothing);
}
};
MULTIPROCESS_TEST_MAIN(SpawnContextServer) {
base::MessageLoopForIO message_loop;
FakeContext fake_context;
zx_handle_t context_handle = zx_take_startup_handle(PA_HND(PA_USER0, 0));
CHECK_NE(ZX_HANDLE_INVALID, context_handle);
fidl::Binding<chromium::web::Context> binding(&fake_context,
zx::channel(context_handle));
// Service the MessageLoop until the child process is torn down.
base::RunLoop().Run();
return 0;
}
} // namespace
class ContextProviderImplTest : public base::MultiProcessTest {
public:
ContextProviderImplTest() {
provider_.SetLaunchCallbackForTests(base::BindRepeating(
&ContextProviderImplTest::LaunchProcess, base::Unretained(this)));
provider_.Bind(provider_ptr_.NewRequest());
}
~ContextProviderImplTest() override = default;
void TearDown() override {
for (auto& process : context_processes_) {
process.Terminate(0, true);
}
}
// Start a new child process whose main function is defined in
// MULTIPROCESSING_TEST_MAIN(SpawnContextServer).
base::Process LaunchProcess(const base::LaunchOptions& options) {
auto cmdline = base::GetMultiProcessTestChildBaseCommandLine();
cmdline.AppendSwitchASCII(switches::kTestChildProcess,
"SpawnContextServer");
auto context_process = base::LaunchProcess(cmdline, options);
EXPECT_TRUE(context_process.IsValid());
context_processes_.push_back(context_process.Duplicate());
return context_process;
}
protected:
base::MessageLoopForIO message_loop_;
ContextProviderImpl provider_;
chromium::web::ContextProviderPtr provider_ptr_;
// The spawned Process object for a Context.
std::vector<base::Process> context_processes_;
private:
DISALLOW_COPY_AND_ASSIGN(ContextProviderImplTest);
};
TEST_F(ContextProviderImplTest, LaunchContext) {
// Connect to a new context process.
auto data_dir = base::fuchsia::GetHandleFromFile(base::File(
base::FilePath("/data"), base::File::FLAG_OPEN | base::File::FLAG_READ));
fidl::InterfacePtr<chromium::web::Context> context;
chromium::web::CreateContextParams create_params;
provider_ptr_->Create(std::move(create_params), context.NewRequest());
// Call a Context method and wait for it to invoke an observer call.
MockFrameObserver frame_observer;
chromium::web::FramePtr frame_ptr;
fidl::Binding<chromium::web::FrameObserver> frame_observer_binding(
&frame_observer);
base::RunLoop r;
EXPECT_CALL(frame_observer, OnNavigationStateChanged())
.WillOnce(testing::Invoke(&r, &base::RunLoop::Quit));
context->CreateFrame(frame_observer_binding.NewBinding(),
frame_ptr.NewRequest());
r.Run();
}
TEST_F(ContextProviderImplTest, MultipleClients) {
{
chromium::web::ContextProviderPtr provider_2_ptr;
provider_.Bind(provider_2_ptr.NewRequest());
// Allow provider_2_ptr to go out of scope and disconnect.
}
// Connect with a third client.
chromium::web::ContextProviderPtr provider_3_ptr;
provider_.Bind(provider_3_ptr.NewRequest());
fidl::InterfacePtr<chromium::web::Context> context;
chromium::web::CreateContextParams create_params;
provider_3_ptr->Create(std::move(create_params), context.NewRequest());
MockFrameObserver frame_observer;
chromium::web::FramePtr frame_ptr;
fidl::Binding<chromium::web::FrameObserver> frame_observer_binding(
&frame_observer);
base::RunLoop r;
EXPECT_CALL(frame_observer, OnNavigationStateChanged())
.WillOnce(testing::Invoke(&r, &base::RunLoop::Quit));
context->CreateFrame(frame_observer_binding.NewBinding(),
frame_ptr.NewRequest());
r.Run();
}
} // namespace fuchsia
// Copyright 2018 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 "webrunner/app/switches.h"
namespace fuchsia {
const char kContextProcess[] = "context-process";
} // namespace fuchsia
// Copyright 2018 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 WEBRUNNER_APP_SWITCHES_H_
#define WEBRUNNER_APP_SWITCHES_H_
#include "webrunner/common/webrunner_export.h"
namespace fuchsia {
// "--context-process" signifies the process should be launched as a Context
// service.
// Omitting the switch signifies that the process should be a ContextProvider.
extern WEBRUNNER_EXPORT const char kContextProcess[];
} // namespace fuchsia
#endif // WEBRUNNER_APP_SWITCHES_H_
......@@ -9,6 +9,7 @@
#include "base/macros.h"
#include "content/public/app/content_main_delegate.h"
#include "webrunner/common/webrunner_export.h"
namespace content {
class ContentClient;
......@@ -16,7 +17,8 @@ class ContentClient;
namespace webrunner {
class WebRunnerMainDelegate : public content::ContentMainDelegate {
class WEBRUNNER_EXPORT WebRunnerMainDelegate
: public content::ContentMainDelegate {
public:
WebRunnerMainDelegate();
~WebRunnerMainDelegate() override;
......
// Copyright 2018 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 WEBRUNNER_COMMON_WEBRUNNER_EXPORT_H_
#define WEBRUNNER_COMMON_WEBRUNNER_EXPORT_H_
#if defined(COMPONENT_BUILD)
#if defined(WEBRUNNER_IMPLEMENTATION)
#define WEBRUNNER_EXPORT __attribute__((visibility("default")))
#else
#define WEBRUNNER_EXPORT
#endif
#else // defined(COMPONENT_BUILD)
#define WEBRUNNER_EXPORT
#endif
#endif // WEBRUNNER_COMMON_WEBRUNNER_EXPORT_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment