blob: 8736d7ed427b6724263bb1061747eb67a3eb6e7c [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_WEBUI_WEB_UI_MANAGED_INTERFACE_H_
#define CONTENT_BROWSER_WEBUI_WEB_UI_MANAGED_INTERFACE_H_
#include <memory>
#include <utility>
#include "base/memory/raw_ptr.h"
#include "base/supports_user_data.h"
#include "content/common/content_export.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_managed_interface.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace content {
using WebUIManagedInterfaceNoPageHandler = void;
// WebUIManagedInterface is an optional base class for implementing classes
// that interact with Mojo endpoints e.g. implement a Mojo interface or
// communicate with a Mojo Remote.
//
// This class provides automated handling of Mojo endpoint bindings and
// lifetime management. The subclass object is created when Create() is
// called and destroyed when the associated WebUI document is removed or
// navigates away.
//
// To implement interface Foo, use the following code:
// class FooImpl : public WebUIManagedInterface<FooImpl, Foo> { ... }
//
// Additionally, to communiate with remote interface Bar:
// class FooBarImpl : public WebUIManagedInterface<FooBarImpl, Foo, Bar> { ...
// }
//
// To implement no interface but only communicate with a remote interface:
// class Baz : public WebUIManagedInterface<
// Baz,
// WebUIManagedInterfaceNoPageHandler,
// BarObserver> { ... }
//
// TODO(crbug.com/40257252): provide helpers to retrieve InterfaceImpl objects.
template <typename InterfaceImpl, typename PageHandler, typename Page = void>
requires(!std::is_void_v<PageHandler> || !std::is_void_v<Page>)
class WebUIManagedInterface : public WebUIManagedInterfaceBase {
public:
template <typename T = Page>
requires(std::is_void_v<T>)
static void Create(WebUIController* web_ui_controller,
mojo::PendingReceiver<PageHandler> pending_receiver) {
return WebUIManagedInterface::Create(
web_ui_controller, std::move(pending_receiver), mojo::NullRemote());
}
template <typename T = PageHandler>
requires(std::is_void_v<T>)
static void Create(WebUIController* webui_controller,
mojo::PendingRemote<Page> pending_remote) {
return WebUIManagedInterface::Create(webui_controller, mojo::NullReceiver(),
std::move(pending_remote));
}
static void Create(WebUIController* webui_controller,
mojo::PendingReceiver<PageHandler> pending_receiver,
mojo::PendingRemote<Page> pending_remote) {
auto interface_impl = std::make_unique<InterfaceImpl>();
auto* interface_impl_ptr = interface_impl.get();
interface_impl->webui_controller_ = webui_controller;
if constexpr (!std::is_void_v<PageHandler>) {
interface_impl->page_handler_receiver_.emplace(
interface_impl_ptr, std::move(pending_receiver));
}
if constexpr (!std::is_void_v<Page>) {
interface_impl->page_remote_.Bind(std::move(pending_remote));
}
SaveWebUIManagedInterfaceInDocument(webui_controller,
std::move(interface_impl));
interface_impl_ptr->ready_ = true;
interface_impl_ptr->OnReady();
}
// Invoked when Mojo endpoints are bound and ready to use.
// TODO(crbug.com/40257247): Remove this by saving endpoints in an external
// storage before constructing `InterfaceImpl`.
virtual void OnReady() {}
template <typename P = PageHandler>
requires(!std::is_void_v<P>)
mojo::Receiver<PageHandler>& receiver() {
CHECK(ready_) << "Mojo endpoints are not ready. Please use OnReady().";
return page_handler_receiver_.value();
}
template <typename P = Page>
requires(!std::is_void_v<P>)
mojo::Remote<Page>& remote() {
CHECK(ready_) << "Mojo endpoints are not ready. Please use OnReady().";
return page_remote_;
}
WebUIController* webui_controller() {
CHECK(ready_) << "webui_controller() is not ready. Please use OnReady().";
return webui_controller_;
}
private:
bool ready_ = false;
// When `PageHandler` is void, declare an unused bool member variable instead
// of a mojo::Receiver<PageHandler>. This allows us to share the same class
// instead of having three different specializations at the cost of an extra
// bool member.
using PageHandlerReceiverType =
std::conditional_t<std::is_void_v<PageHandler>,
bool,
mojo::Receiver<PageHandler>>;
std::optional<PageHandlerReceiverType> page_handler_receiver_;
// When `Page` is void, declare a unused bool member variable instead of a
// mojo::Remote<PageHandler>. This allows us to share the same class
// instead of having three different specializations at the cost of an extra
// bool member.
using PageRemoteType =
std::conditional_t<std::is_void_v<Page>, bool, mojo::Remote<Page>>;
PageRemoteType page_remote_;
raw_ptr<WebUIController> webui_controller_;
};
} // namespace content
#endif // CONTENT_BROWSER_WEBUI_WEB_UI_MANAGED_INTERFACE_H_