Commit 2110720c authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

[mojo] Introduce ServiceFactory API

ServiceFactory is relatively simple helper API for Mojo consumers
to handle service interface requests, typically in a sandboxed
service process.

This is used here to reduce error-prone and inconvenient boilerplate
around out-of-process service launching in Content and its embedders.

Bug: 977637
Change-Id: Iae3dcd1335280fc2ec96014d7009ce5183060638
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1717474
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#681451}
parent f9987048
...@@ -15,6 +15,8 @@ static_library("utility") { ...@@ -15,6 +15,8 @@ static_library("utility") {
sources = [ sources = [
"chrome_content_utility_client.cc", "chrome_content_utility_client.cc",
"chrome_content_utility_client.h", "chrome_content_utility_client.h",
"services.cc",
"services.h",
] ]
defines = [] defines = []
......
...@@ -13,18 +13,16 @@ ...@@ -13,18 +13,16 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/no_destructor.h"
#include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/common/buildflags.h" #include "chrome/common/buildflags.h"
#include "chrome/utility/services.h"
#include "components/mirroring/mojom/constants.mojom.h" #include "components/mirroring/mojom/constants.mojom.h"
#include "components/mirroring/service/features.h" #include "components/mirroring/service/features.h"
#include "components/mirroring/service/mirroring_service.h" #include "components/mirroring/service/mirroring_service.h"
#include "components/safe_browsing/buildflags.h" #include "components/safe_browsing/buildflags.h"
#include "components/services/patch/patch_service.h" #include "components/services/patch/patch_service.h"
#include "components/services/patch/public/mojom/constants.mojom.h" #include "components/services/patch/public/mojom/constants.mojom.h"
#include "components/services/unzip/public/mojom/unzipper.mojom.h"
#include "components/services/unzip/unzipper_impl.h"
#include "content/public/common/content_features.h" #include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h" #include "content/public/common/content_switches.h"
#include "content/public/common/service_manager_connection.h" #include "content/public/common/service_manager_connection.h"
...@@ -40,13 +38,9 @@ ...@@ -40,13 +38,9 @@
#include "chrome/utility/importer/profile_import_impl.h" #include "chrome/utility/importer/profile_import_impl.h"
#include "chrome/utility/importer/profile_import_service.h" #include "chrome/utility/importer/profile_import_service.h"
#include "services/network/url_request_context_builder_mojo.h" #include "services/network/url_request_context_builder_mojo.h"
#include "services/proxy_resolver/proxy_resolver_factory_impl.h" // nogncheck
#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h"
#endif // !defined(OS_ANDROID) #endif // !defined(OS_ANDROID)
#if defined(OS_WIN) #if defined(OS_WIN)
#include "chrome/services/util_win/public/mojom/util_win.mojom.h"
#include "chrome/services/util_win/util_win_impl.h"
#include "components/services/quarantine/public/cpp/quarantine_features_win.h" // nogncheck #include "components/services/quarantine/public/cpp/quarantine_features_win.h" // nogncheck
#include "components/services/quarantine/public/mojom/quarantine.mojom.h" // nogncheck #include "components/services/quarantine/public/mojom/quarantine.mojom.h" // nogncheck
#include "components/services/quarantine/quarantine_service.h" // nogncheck #include "components/services/quarantine/quarantine_service.h" // nogncheck
...@@ -316,29 +310,13 @@ void ChromeContentUtilityClient::RegisterNetworkBinders( ...@@ -316,29 +310,13 @@ void ChromeContentUtilityClient::RegisterNetworkBinders(
g_network_binder_creation_callback.Get().Run(registry); g_network_binder_creation_callback.Get().Run(registry);
} }
void ChromeContentUtilityClient::RunMainThreadService( mojo::ServiceFactory*
mojo::GenericPendingReceiver receiver) { ChromeContentUtilityClient::GetMainThreadServiceFactory() {
#if defined(OS_WIN) return ::GetMainThreadServiceFactory();
if (auto util_receiver = receiver.As<chrome::mojom::UtilWin>()) {
static base::NoDestructor<UtilWinImpl> service(std::move(util_receiver));
return;
}
#endif
if (auto unzipper_receiver = receiver.As<unzip::mojom::Unzipper>()) {
static base::NoDestructor<unzip::UnzipperImpl> service(
std::move(unzipper_receiver));
}
} }
void ChromeContentUtilityClient::RunIOThreadService( mojo::ServiceFactory* ChromeContentUtilityClient::GetIOThreadServiceFactory() {
mojo::GenericPendingReceiver* receiver) { return ::GetIOThreadServiceFactory();
#if !defined(OS_ANDROID)
if (auto factory_receiver =
receiver->As<proxy_resolver::mojom::ProxyResolverFactory>()) {
static base::NoDestructor<proxy_resolver::ProxyResolverFactoryImpl> factory(
std::move(factory_receiver));
}
#endif // !defined(OS_ANDROID)
} }
// static // static
......
...@@ -33,8 +33,8 @@ class ChromeContentUtilityClient : public content::ContentUtilityClient { ...@@ -33,8 +33,8 @@ class ChromeContentUtilityClient : public content::ContentUtilityClient {
service_manager::mojom::ServiceRequest request) override; service_manager::mojom::ServiceRequest request) override;
void RegisterNetworkBinders( void RegisterNetworkBinders(
service_manager::BinderRegistry* registry) override; service_manager::BinderRegistry* registry) override;
void RunMainThreadService(mojo::GenericPendingReceiver receiver) override; mojo::ServiceFactory* GetMainThreadServiceFactory() override;
void RunIOThreadService(mojo::GenericPendingReceiver* receiver) override; mojo::ServiceFactory* GetIOThreadServiceFactory() override;
// See NetworkBinderProvider above. // See NetworkBinderProvider above.
static void SetNetworkBinderCreationCallback( static void SetNetworkBinderCreationCallback(
......
// Copyright 2019 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 "chrome/utility/services.h"
#include <utility>
#include "base/macros.h"
#include "base/no_destructor.h"
#include "build/build_config.h"
#include "components/services/unzip/public/mojom/unzipper.mojom.h"
#include "components/services/unzip/unzipper_impl.h"
#include "mojo/public/cpp/bindings/service_factory.h"
#if defined(OS_WIN)
#include "chrome/services/util_win/public/mojom/util_win.mojom.h"
#include "chrome/services/util_win/util_win_impl.h"
#endif // defined(OS_WIN)
#if !defined(OS_ANDROID)
#include "services/proxy_resolver/proxy_resolver_factory_impl.h" // nogncheck
#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h"
#endif // !defined(OS_ANDROID)
namespace {
auto RunUnzipper(mojo::PendingReceiver<unzip::mojom::Unzipper> receiver) {
return std::make_unique<unzip::UnzipperImpl>(std::move(receiver));
}
#if defined(OS_WIN)
auto RunWindowsUtility(mojo::PendingReceiver<chrome::mojom::UtilWin> receiver) {
return std::make_unique<UtilWinImpl>(std::move(receiver));
}
#endif // defined(OS_WIN)
#if !defined(OS_ANDROID)
auto RunProxyResolver(
mojo::PendingReceiver<proxy_resolver::mojom::ProxyResolverFactory>
receiver) {
return std::make_unique<proxy_resolver::ProxyResolverFactoryImpl>(
std::move(receiver));
}
#endif // !defined(OS_ANDROID)
} // namespace
mojo::ServiceFactory* GetMainThreadServiceFactory() {
// clang-format off
static base::NoDestructor<mojo::ServiceFactory> factory {
RunUnzipper,
#if defined(OS_WIN)
RunWindowsUtility,
#endif // defined(OS_WIN)
};
// clang-format on
return factory.get();
}
mojo::ServiceFactory* GetIOThreadServiceFactory() {
// clang-format off
static base::NoDestructor<mojo::ServiceFactory> factory {
#if !defined(OS_ANDROID)
RunProxyResolver,
#endif // !defined(OS_ANDROID)
};
// clang-format on
return factory.get();
}
// Copyright 2019 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 CHROME_UTILITY_SERVICES_H_
#define CHROME_UTILITY_SERVICES_H_
namespace mojo {
class ServiceFactory;
}
// Helpers to run out-of-process services in a dedicated utility process. All
// out-of-process services will need to have their implementation hooked up in
// one of these helpers.
mojo::ServiceFactory* GetMainThreadServiceFactory();
mojo::ServiceFactory* GetIOThreadServiceFactory();
#endif // CHROME_UTILITY_SERVICES_H_
...@@ -16,10 +16,12 @@ bool ContentUtilityClient::HandleServiceRequest( ...@@ -16,10 +16,12 @@ bool ContentUtilityClient::HandleServiceRequest(
return false; return false;
} }
void ContentUtilityClient::RunIOThreadService( mojo::ServiceFactory* ContentUtilityClient::GetIOThreadServiceFactory() {
mojo::GenericPendingReceiver* receiver) {} return nullptr;
}
void ContentUtilityClient::RunMainThreadService( mojo::ServiceFactory* ContentUtilityClient::GetMainThreadServiceFactory() {
mojo::GenericPendingReceiver receiver) {} return nullptr;
}
} // namespace content } // namespace content
...@@ -16,6 +16,10 @@ ...@@ -16,6 +16,10 @@
#include "services/service_manager/public/cpp/service.h" #include "services/service_manager/public/cpp/service.h"
#include "services/service_manager/public/mojom/service.mojom.h" #include "services/service_manager/public/mojom/service.mojom.h"
namespace mojo {
class ServiceFactory;
}
namespace content { namespace content {
// Embedder API for participating in utility process logic. // Embedder API for participating in utility process logic.
...@@ -44,15 +48,18 @@ class CONTENT_EXPORT ContentUtilityClient { ...@@ -44,15 +48,18 @@ class CONTENT_EXPORT ContentUtilityClient {
service_manager::mojom::ServiceRequest request); service_manager::mojom::ServiceRequest request);
// Allows the embedder to handle an incoming service interface request to run // Allows the embedder to handle an incoming service interface request to run
// a service on the IO thread. |*receiver| is always valid when this called, // a service on the IO thread. Should return a ServiceFactory instance which
// and the embedder is free to take ownership if handling the request. If the // lives at least as long as the IO thread, or nullptr.
// embedder does not wish to handle this request on the I/O thread, it must //
// not modify |*receiver|. // Only called from the IO thread.
virtual void RunIOThreadService(mojo::GenericPendingReceiver* receiver); virtual mojo::ServiceFactory* GetIOThreadServiceFactory();
// Allows the embedder to handle an incoming service interface request to run // Allows the embedder to handle an incoming service interface request to run
// a service on the main thread. |receiver| is always valid when this called. // a service on the main thread. Should return a ServiceFactory instance which
virtual void RunMainThreadService(mojo::GenericPendingReceiver receiver); // which effectively lives forever, or nullptr.
//
// Only called from the main thread.
virtual mojo::ServiceFactory* GetMainThreadServiceFactory();
virtual void RegisterNetworkBinders( virtual void RegisterNetworkBinders(
service_manager::BinderRegistry* registry) {} service_manager::BinderRegistry* registry) {}
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "content/public/utility/utility_thread.h" #include "content/public/utility/utility_thread.h"
#include "content/shell/common/power_monitor_test_impl.h" #include "content/shell/common/power_monitor_test_impl.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "mojo/public/cpp/bindings/service_factory.h"
#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/cpp/system/buffer.h" #include "mojo/public/cpp/system/buffer.h"
#include "services/service_manager/public/cpp/binder_registry.h" #include "services/service_manager/public/cpp/binder_registry.h"
...@@ -81,6 +82,10 @@ class TestUtilityServiceImpl : public mojom::TestService { ...@@ -81,6 +82,10 @@ class TestUtilityServiceImpl : public mojom::TestService {
DISALLOW_COPY_AND_ASSIGN(TestUtilityServiceImpl); DISALLOW_COPY_AND_ASSIGN(TestUtilityServiceImpl);
}; };
auto RunEchoService(mojo::PendingReceiver<echo::mojom::EchoService> receiver) {
return std::make_unique<echo::EchoService>(std::move(receiver));
}
} // namespace } // namespace
ShellContentUtilityClient::ShellContentUtilityClient(bool is_browsertest) { ShellContentUtilityClient::ShellContentUtilityClient(bool is_browsertest) {
...@@ -127,13 +132,11 @@ bool ShellContentUtilityClient::HandleServiceRequest( ...@@ -127,13 +132,11 @@ bool ShellContentUtilityClient::HandleServiceRequest(
return false; return false;
} }
void ShellContentUtilityClient::RunIOThreadService( mojo::ServiceFactory* ShellContentUtilityClient::GetIOThreadServiceFactory() {
mojo::GenericPendingReceiver* receiver) { static base::NoDestructor<mojo::ServiceFactory> factory{
if (auto echo_receiver = receiver->As<echo::mojom::EchoService>()) { RunEchoService,
static base::NoDestructor<echo::EchoService> service( };
std::move(echo_receiver)); return factory.get();
return;
}
} }
void ShellContentUtilityClient::RegisterNetworkBinders( void ShellContentUtilityClient::RegisterNetworkBinders(
......
...@@ -22,7 +22,7 @@ class ShellContentUtilityClient : public ContentUtilityClient { ...@@ -22,7 +22,7 @@ class ShellContentUtilityClient : public ContentUtilityClient {
bool HandleServiceRequest( bool HandleServiceRequest(
const std::string& service_name, const std::string& service_name,
service_manager::mojom::ServiceRequest request) override; service_manager::mojom::ServiceRequest request) override;
void RunIOThreadService(mojo::GenericPendingReceiver* receiver) override; mojo::ServiceFactory* GetIOThreadServiceFactory() override;
void RegisterNetworkBinders( void RegisterNetworkBinders(
service_manager::BinderRegistry* registry) override; service_manager::BinderRegistry* registry) override;
void RegisterAudioBinders(service_manager::BinderMap* binders) override; void RegisterAudioBinders(service_manager::BinderMap* binders) override;
......
...@@ -9,36 +9,59 @@ ...@@ -9,36 +9,59 @@
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "content/public/utility/content_utility_client.h" #include "content/public/utility/content_utility_client.h"
#include "mojo/public/cpp/bindings/service_factory.h"
#include "services/video_capture/public/mojom/video_capture_service.mojom.h" #include "services/video_capture/public/mojom/video_capture_service.mojom.h"
#include "services/video_capture/video_capture_service_impl.h" #include "services/video_capture/video_capture_service_impl.h"
namespace content { namespace content {
namespace {
auto RunVideoCapture(
mojo::PendingReceiver<video_capture::mojom::VideoCaptureService> receiver) {
return std::make_unique<video_capture::VideoCaptureServiceImpl>(
std::move(receiver), base::ThreadTaskRunnerHandle::Get());
}
mojo::ServiceFactory& GetMainThreadServiceFactory() {
static base::NoDestructor<mojo::ServiceFactory> factory{
RunVideoCapture,
};
return *factory;
}
} // namespace
void HandleServiceRequestOnIOThread( void HandleServiceRequestOnIOThread(
mojo::GenericPendingReceiver receiver, mojo::GenericPendingReceiver receiver,
base::SequencedTaskRunner* main_thread_task_runner) { base::SequencedTaskRunner* main_thread_task_runner) {
// If the request was handled already, we should not reach this point. // If the request was handled already, we should not reach this point.
DCHECK(receiver.is_valid()); DCHECK(receiver.is_valid());
GetContentClient()->utility()->RunIOThreadService(&receiver); auto* embedder_factory =
GetContentClient()->utility()->GetIOThreadServiceFactory();
if (embedder_factory && embedder_factory->MaybeRunService(&receiver))
return;
if (receiver.is_valid()) { DCHECK(receiver.is_valid());
main_thread_task_runner->PostTask( main_thread_task_runner->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&HandleServiceRequestOnMainThread, std::move(receiver))); base::BindOnce(&HandleServiceRequestOnMainThread, std::move(receiver)));
}
} }
void HandleServiceRequestOnMainThread(mojo::GenericPendingReceiver receiver) { void HandleServiceRequestOnMainThread(mojo::GenericPendingReceiver receiver) {
if (auto video_capture_receiver = if (GetMainThreadServiceFactory().MaybeRunService(&receiver))
receiver.As<video_capture::mojom::VideoCaptureService>()) {
static base::NoDestructor<video_capture::VideoCaptureServiceImpl> service(
std::move(video_capture_receiver), base::ThreadTaskRunnerHandle::Get());
return; return;
}
// If the request was handled already, we should not reach this point. // If the request was handled already, we should not reach this point.
DCHECK(receiver.is_valid()); DCHECK(receiver.is_valid());
GetContentClient()->utility()->RunMainThreadService(std::move(receiver)); auto* embedder_factory =
GetContentClient()->utility()->GetMainThreadServiceFactory();
if (embedder_factory && embedder_factory->MaybeRunService(&receiver))
return;
DCHECK(receiver.is_valid());
DLOG(ERROR) << "Unhandled out-of-process service request for "
<< receiver.interface_name().value();
} }
} // namespace content } // namespace content
...@@ -187,6 +187,8 @@ component("bindings") { ...@@ -187,6 +187,8 @@ component("bindings") {
"remote_set.h", "remote_set.h",
"self_owned_receiver.h", "self_owned_receiver.h",
"sequence_local_sync_event_watcher.h", "sequence_local_sync_event_watcher.h",
"service_factory.cc",
"service_factory.h",
"shared_associated_remote.h", "shared_associated_remote.h",
"shared_remote.h", "shared_remote.h",
"strong_associated_binding.h", "strong_associated_binding.h",
......
// Copyright 2019 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/public/cpp/bindings/service_factory.h"
#include "base/bind.h"
namespace mojo {
ServiceFactory::~ServiceFactory() = default;
bool ServiceFactory::MaybeRunService(mojo::GenericPendingReceiver* receiver) {
DCHECK(receiver->is_valid());
// We grab a weak handle to the receiver's message pipe first. If any function
// accepts the receiver, we will tie its returned object's lifetime to the
// connection state of that pipe.
MessagePipeHandle pipe = receiver->pipe();
for (const auto& callback : callbacks_) {
if (auto instance = callback.Run(receiver)) {
DCHECK(!receiver->is_valid());
instance->WatchPipe(
pipe, base::BindOnce(&ServiceFactory::OnInstanceDisconnected,
weak_ptr_factory_.GetWeakPtr(), instance.get()));
instances_.insert(std::move(instance));
return true;
}
DCHECK(receiver->is_valid());
}
return false;
}
void ServiceFactory::OnInstanceDisconnected(InstanceHolderBase* instance) {
instances_.erase(instance);
}
ServiceFactory::InstanceHolderBase::InstanceHolderBase()
: watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC) {}
ServiceFactory::InstanceHolderBase::~InstanceHolderBase() = default;
void ServiceFactory::InstanceHolderBase::WatchPipe(
MessagePipeHandle pipe,
base::OnceClosure disconnect_callback) {
DCHECK(!disconnect_callback_);
disconnect_callback_ = std::move(disconnect_callback);
watcher_.Watch(pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
base::BindRepeating(&InstanceHolderBase::OnDisconnect,
base::Unretained(this)));
}
void ServiceFactory::InstanceHolderBase::OnDisconnect(
MojoResult result,
const HandleSignalsState& state) {
// It doesn't matter what the parameters are, since the only way the watcher
// can signal is if the peer was closed or the local pipe handle was closed.
// The callback always destroys |this| when run, so there's also no chance of
// this method running more than once.
DCHECK(disconnect_callback_);
std::move(disconnect_callback_).Run();
}
} // namespace mojo
// Copyright 2019 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_PUBLIC_CPP_BINDINGS_SERVICE_FACTORY_H_
#define MOJO_PUBLIC_CPP_BINDINGS_SERVICE_FACTORY_H_
#include <memory>
#include "base/bind.h"
#include "base/callback.h"
#include "base/component_export.h"
#include "base/containers/flat_set.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "mojo/public/cpp/system/simple_watcher.h"
namespace mojo {
namespace internal {
template <typename Func>
struct ServiceFactoryTraits;
}
// ServiceFactory is a helper that Mojo consumers can use to conveniently handle
// dynamic service interface requests via a GenericPendingReceiver by matching
// the receiver's interface type against a series of strongly-typed factory
// function pointers, each with the signature:
//
// std::unique_ptr<T>(mojo::PendingReceiver<Interface>)
//
// where |T| is any type (generally an implementation of |Interface|), and
// |Interface| is a mojom interface.
//
// Any time |MaybeRunService()| is called on the ServiceFactory, it will match
// the GenericPendingReceiver argument's interface type against the list of
// factories it has available, and if it finds a match it will run that function
// and retain ownership of the returned object until the corresponding receiver
// is disconnected.
//
// Typical usage might look something like:
//
// auto RunFooService(mojo::PendingReceiver<foo::mojom::Foo> receiver) {
// return std::make_unique<foo::FooImpl>(std::move(receiver));
// }
//
// auto RunFooService(mojo::PendingReceiver<foo::mojom::Foo> receiver) {
// return std::make_unique<foo::FooImpl>(std::move(receiver));
// }
//
// void HandleServiceRequest(mojo::GenericPendingReceiver receiver) {
// static base::NoDestructor<mojo::ServiceFactory> factory{
// RunFooService,
// RunBarService,
// };
//
// if (!factory->MaybeRunService(&receiver)) {
// // The receiver was for neither the Foo nor Bar service. Sad!
// LOG(ERROR) << "Unknown service: " << *receiver.interface_name();
// }
// }
//
class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) ServiceFactory {
public:
template <typename... Funcs>
explicit ServiceFactory(Funcs... fns)
: callbacks_({base::BindRepeating(&RunFunction<Funcs>, fns)...}) {}
~ServiceFactory();
// Attempts to run a service supported by this factory.
//
// Returns |true| and consumes |*receiver| if it is a suitable match for some
// function known by the factory; otherwise returns |false| and leaves
// |*receiver| intact.
bool MaybeRunService(GenericPendingReceiver* receiver);
private:
class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InstanceHolderBase {
public:
InstanceHolderBase();
virtual ~InstanceHolderBase();
void WatchPipe(MessagePipeHandle pipe,
base::OnceClosure disconnect_callback);
private:
void OnDisconnect(MojoResult result, const HandleSignalsState& state);
SimpleWatcher watcher_;
base::OnceClosure disconnect_callback_;
DISALLOW_COPY_AND_ASSIGN(InstanceHolderBase);
};
template <typename Interface>
class InstanceHolder : public InstanceHolderBase {
public:
explicit InstanceHolder(std::unique_ptr<Interface> instance)
: instance_(std::move(instance)) {}
~InstanceHolder() override = default;
private:
const std::unique_ptr<Interface> instance_;
DISALLOW_COPY_AND_ASSIGN(InstanceHolder);
};
template <typename Func>
static std::unique_ptr<InstanceHolderBase> RunFunction(
Func fn,
GenericPendingReceiver* receiver) {
using Interface = typename internal::ServiceFactoryTraits<Func>::Interface;
if (auto typed_receiver = receiver->As<Interface>()) {
return std::make_unique<InstanceHolder<Interface>>(
fn(std::move(typed_receiver)));
}
return nullptr;
}
void OnInstanceDisconnected(InstanceHolderBase* instance);
using GenericCallback =
base::RepeatingCallback<std::unique_ptr<InstanceHolderBase>(
GenericPendingReceiver*)>;
const std::vector<GenericCallback> callbacks_;
base::flat_set<std::unique_ptr<InstanceHolderBase>, base::UniquePtrComparator>
instances_;
base::WeakPtrFactory<ServiceFactory> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(ServiceFactory);
};
namespace internal {
template <typename Impl, typename InterfaceType>
struct ServiceFactoryTraits<std::unique_ptr<Impl> (*)(
PendingReceiver<InterfaceType>)> {
using Interface = InterfaceType;
};
} // namespace internal
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_SERVICE_FACTORY_H_
...@@ -42,6 +42,7 @@ source_set("tests") { ...@@ -42,6 +42,7 @@ source_set("tests") {
"router_test_util.h", "router_test_util.h",
"sample_service_unittest.cc", "sample_service_unittest.cc",
"serialization_warning_unittest.cc", "serialization_warning_unittest.cc",
"service_factory_unittest.cc",
"struct_unittest.cc", "struct_unittest.cc",
"sync_handle_registry_unittest.cc", "sync_handle_registry_unittest.cc",
"sync_method_unittest.cc", "sync_method_unittest.cc",
...@@ -136,6 +137,7 @@ mojom("test_mojom") { ...@@ -136,6 +137,7 @@ mojom("test_mojom") {
"connection_group_unittest.test-mojom", "connection_group_unittest.test-mojom",
"idle_tracking_unittest.test-mojom", "idle_tracking_unittest.test-mojom",
"receiver_unittest.test-mojom", "receiver_unittest.test-mojom",
"service_factory_unittest.test-mojom",
] ]
public_deps = [ public_deps = [
......
// Copyright 2019 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/public/cpp/bindings/service_factory.h"
#include "base/callback.h"
#include "base/no_destructor.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/tests/service_factory_unittest.test-mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace test {
namespace service_factory_unittest {
class ServiceFactoryTest : public testing::Test {
public:
ServiceFactoryTest() = default;
private:
base::test::ScopedTaskEnvironment task_environment_;
DISALLOW_COPY_AND_ASSIGN(ServiceFactoryTest);
};
class TestService1Impl : public mojom::TestService1 {
public:
explicit TestService1Impl(PendingReceiver<mojom::TestService1> receiver)
: receiver_(this, std::move(receiver)) {
++num_instances_;
}
~TestService1Impl() override {
--num_instances_;
if (destruction_wait_loop_)
destruction_wait_loop_->Quit();
}
static int num_instances() { return num_instances_; }
static void WaitForInstanceDestruction() {
static base::NoDestructor<base::Optional<base::RunLoop>> loop;
loop->emplace();
destruction_wait_loop_ = &loop->value();
(*loop)->Run();
destruction_wait_loop_ = nullptr;
}
private:
// mojom::TestService1:
void GetIdentity(GetIdentityCallback callback) override {
std::move(callback).Run(1);
}
void Quit() override { receiver_.reset(); }
Receiver<mojom::TestService1> receiver_;
static int num_instances_;
static base::RunLoop* destruction_wait_loop_;
DISALLOW_COPY_AND_ASSIGN(TestService1Impl);
};
class TestService2Impl : public mojom::TestService2 {
public:
explicit TestService2Impl(PendingReceiver<mojom::TestService2> receiver)
: receiver_(this, std::move(receiver)) {}
~TestService2Impl() override = default;
private:
// mojom::TestService2:
void GetIdentity(GetIdentityCallback callback) override {
std::move(callback).Run(2);
}
Receiver<mojom::TestService2> receiver_;
DISALLOW_COPY_AND_ASSIGN(TestService2Impl);
};
int TestService1Impl::num_instances_ = 0;
base::RunLoop* TestService1Impl::destruction_wait_loop_ = nullptr;
auto RunTestService1(PendingReceiver<mojom::TestService1> receiver) {
return std::make_unique<TestService1Impl>(std::move(receiver));
}
auto RunTestService2(PendingReceiver<mojom::TestService2> receiver) {
return std::make_unique<TestService2Impl>(std::move(receiver));
}
TEST_F(ServiceFactoryTest, BasicMatching) {
ServiceFactory factory{RunTestService1, RunTestService2};
Remote<mojom::TestService1> remote1;
GenericPendingReceiver receiver = remote1.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory.MaybeRunService(&receiver));
EXPECT_FALSE(receiver.is_valid());
// Verify that we connected to an instance of TestService1.
int32_t id = 0;
EXPECT_TRUE(remote1->GetIdentity(&id));
EXPECT_EQ(1, id);
Remote<mojom::TestService2> remote2;
receiver = remote2.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory.MaybeRunService(&receiver));
EXPECT_FALSE(receiver.is_valid());
// Verify that we connected to an instance of TestService2.
EXPECT_TRUE(remote2->GetIdentity(&id));
EXPECT_EQ(2, id);
Remote<mojom::TestService3> remote3;
receiver = remote3.BindNewPipeAndPassReceiver();
EXPECT_FALSE(factory.MaybeRunService(&receiver));
EXPECT_TRUE(receiver.is_valid());
EXPECT_TRUE(receiver.As<mojom::TestService3>());
}
TEST_F(ServiceFactoryTest, DestroyInstanceOnClientDisconnect) {
ServiceFactory factory{RunTestService1};
Remote<mojom::TestService1> remote1;
GenericPendingReceiver receiver = remote1.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory.MaybeRunService(&receiver));
Remote<mojom::TestService1> remote2;
receiver = remote2.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory.MaybeRunService(&receiver));
remote1.FlushForTesting();
remote2.FlushForTesting();
EXPECT_EQ(2, TestService1Impl::num_instances());
remote1.reset();
TestService1Impl::WaitForInstanceDestruction();
EXPECT_EQ(1, TestService1Impl::num_instances());
remote2.FlushForTesting();
EXPECT_EQ(1, TestService1Impl::num_instances());
remote2.reset();
TestService1Impl::WaitForInstanceDestruction();
EXPECT_EQ(0, TestService1Impl::num_instances());
}
TEST_F(ServiceFactoryTest, DestroyInstanceOnServiceDisconnect) {
ServiceFactory factory{RunTestService1};
Remote<mojom::TestService1> remote;
GenericPendingReceiver receiver = remote.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory.MaybeRunService(&receiver));
remote.FlushForTesting();
EXPECT_EQ(1, TestService1Impl::num_instances());
remote->Quit();
remote.FlushForTesting();
EXPECT_EQ(0, TestService1Impl::num_instances());
}
TEST_F(ServiceFactoryTest, DestroyInstancesOnFactoryDestruction) {
base::Optional<ServiceFactory> factory{base::in_place, RunTestService1};
Remote<mojom::TestService1> remote1;
GenericPendingReceiver receiver = remote1.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory->MaybeRunService(&receiver));
remote1.FlushForTesting();
EXPECT_EQ(1, TestService1Impl::num_instances());
Remote<mojom::TestService1> remote2;
receiver = remote2.BindNewPipeAndPassReceiver();
EXPECT_TRUE(factory->MaybeRunService(&receiver));
remote2.FlushForTesting();
EXPECT_EQ(2, TestService1Impl::num_instances());
factory.reset();
EXPECT_EQ(0, TestService1Impl::num_instances());
remote1.FlushForTesting();
remote2.FlushForTesting();
EXPECT_FALSE(remote1.is_connected());
EXPECT_FALSE(remote2.is_connected());
}
} // namespace service_factory_unittest
} // namespace test
} // namespace mojo
// Copyright 2019 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.test.service_factory_unittest.mojom;
interface TestService1 {
[Sync] GetIdentity() => (int32 id);
Quit();
};
interface TestService2 {
[Sync] GetIdentity() => (int32 id);
};
interface TestService3 {};
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