blob: f588846f2b9f74925c7c06bd94eb98f915e5d1d3 [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 LIB_APP_DRIVER_CPP_MODULE_DRIVER_H_
#define LIB_APP_DRIVER_CPP_MODULE_DRIVER_H_
#include <memory>
#include "lib/app/cpp/application_context.h"
#include "lib/app/fidl/service_provider.fidl.h"
#include "lib/fidl/cpp/bindings/binding.h"
#include "lib/fidl/cpp/bindings/interface_request.h"
#include "lib/fsl/tasks/message_loop.h"
#include "lib/fxl/logging.h"
#include "lib/lifecycle/cpp/lifecycle_impl.h"
#include "lib/module/cpp/module_impl.h"
#include "lib/module/fidl/module.fidl.h"
#include "lib/module/fidl/module_context.fidl.h"
#include "lib/ui/views/fidl/view_provider.fidl.h"
namespace modular {
// This interface is passed to the |Impl| object that ModuleDriver initializes.
class ModuleHost {
public:
virtual app::ApplicationContext* application_context() = 0;
virtual ModuleContext* module_context() = 0;
};
// ModuleDriver provides a way to write modules and participate in application
// lifecycle. The |Impl| class supplied to ModuleDriver is instantiated when
// the Module and ViewProvider services are requested by the framework.
//
// Usage:
// The |Impl| class must implement:
//
// // A constructor with the following signature:
// Constructor(
// modular::ModuleHost* module_host,
// fidl::InterfaceRequest<mozart::ViewProvider> view_provider_request,
// fidl::InterfaceRequest<app::ServiceProvider> outgoing_services);
//
// |outgoing_services| must contain the services that this module wants to
// expose to the module that created it.
//
// // Called by ModuleDriver. Call |done| once shutdown sequence is
// // complete, at which point |this| will be deleted.
// void Terminate(const std::function<void()>& done);
//
// Example:
//
// class HelloWorldModule {
// public:
// HelloWorldModule(
// modular::ModuleHost* module_host,
// fidl::InterfaceRequest<mozart::ViewProvider> view_provider_request,
// fidl::InterfaceRequest<app::ServiceProvider> outgoing_services) {}
//
// // Called by ModuleDriver.
// void Terminate(const std::function<void()>& done) { done(); }
// };
//
// int main(int argc, const char** argv) {
// fsl::MessageLoop loop;
// auto app_context = app::ApplicationContext::CreateFromStartupInfo();
// modular::ModuleDriver<HelloWorldApp> driver(app_context.get(),
// [&loop] { loop.QuitNow(); });
// loop.Run();
// return 0;
// }
template <typename Impl>
class ModuleDriver : LifecycleImpl::Delegate, ModuleImpl::Delegate, ModuleHost {
public:
ModuleDriver(app::ApplicationContext* const app_context,
std::function<void()> on_terminated)
: app_context_(app_context),
lifecycle_impl_(app_context->outgoing_services(), this),
module_impl_(std::make_unique<ModuleImpl>(
app_context->outgoing_services(),
static_cast<ModuleImpl::Delegate*>(this))),
on_terminated_(std::move(on_terminated)) {
// There is no guarantee that |ViewProvider| will be requested from us
// before ModuleHost.set_view_provider_handler() is called from |Impl|, so
// we buffer both events until they are both satisfied.
app_context_->outgoing_services()->AddService<mozart::ViewProvider>(
[this](fidl::InterfaceRequest<mozart::ViewProvider> request) {
view_provider_request_ = std::move(request);
MaybeInstantiateImpl();
});
}
private:
// |ModuleHost|
app::ApplicationContext* application_context() override {
return app_context_;
}
// |ModuleHost|
ModuleContext* module_context() override {
FXL_DCHECK(module_context_);
return module_context_.get();
}
// |ModuleImpl::Delegate|
void ModuleInit(
fidl::InterfaceHandle<ModuleContext> module_context,
fidl::InterfaceRequest<app::ServiceProvider> outgoing_services) override {
module_context_.Bind(std::move(module_context));
outgoing_module_services_ = std::move(outgoing_services);
MaybeInstantiateImpl();
}
// |LifecycleImpl::Delegate|
void Terminate() override {
// It's possible that we process the |Lifecycle.Terminate| message before
// the |Module.Initialize| message, even when both messages are ready to be
// processed at the same time. In this case, because |impl_| hasn't been
// instantiated yet, we cannot delegate the |Lifecycle.Terminate| message.
module_impl_.reset();
if (impl_) {
impl_->Terminate([this] {
// Cf. AppDriver::Terminate().
fsl::MessageLoop::GetCurrent()->task_runner()->PostTask([this] {
impl_.reset();
on_terminated_();
});
});
} else {
on_terminated_();
}
}
void MaybeInstantiateImpl() {
if (view_provider_request_ && module_context_) {
impl_ = std::make_unique<Impl>(static_cast<ModuleHost*>(this),
std::move(view_provider_request_),
std::move(outgoing_module_services_));
}
}
app::ApplicationContext* const app_context_;
LifecycleImpl lifecycle_impl_;
std::unique_ptr<ModuleImpl> module_impl_;
std::function<void()> on_terminated_;
ModuleContextPtr module_context_;
// The following are only valid until |impl_| is instantiated.
fidl::InterfaceRequest<mozart::ViewProvider> view_provider_request_;
fidl::InterfaceRequest<app::ServiceProvider> outgoing_module_services_;
std::unique_ptr<Impl> impl_;
FXL_DISALLOW_COPY_AND_ASSIGN(ModuleDriver);
};
} // namespace modular
#endif // LIB_APP_DRIVER_CPP_MODULE_DRIVER_H_