blob: 140a7f05dd67326189292a7b51f0b4a7373e472d [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_DBUS_XDG_REQUEST_H_
#define COMPONENTS_DBUS_XDG_REQUEST_H_
#include <map>
#include <string>
#include "base/component_export.h"
#include "base/functional/callback.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/types/expected.h"
#include "components/dbus/utils/variant.h"
#include "components/dbus/utils/write_value.h"
namespace dbus {
class Bus;
class ObjectProxy;
} // namespace dbus
namespace dbus_xdg {
// Error codes returned by the callback.
enum class COMPONENT_EXPORT(COMPONENTS_DBUS) ResponseError {
kSignalConnectionFailed,
kMethodCallFailed,
kInvalidMethodResponse,
kInvalidSignalResponse,
kRequestCancelledByUser,
kRequestCancelledOther,
kInvalidResponseCode,
};
using Dictionary = std::map<std::string, dbus_utils::Variant>;
using ResponseCallback = base::OnceCallback<void(
/*results=*/base::expected<Dictionary, ResponseError>)>;
class COMPONENT_EXPORT(COMPONENTS_DBUS) Request {
public:
// Makes a DBus XDG request and runs `callback` with the results. The
// `options` dictionary contains any options except for handle_token which
// will be set and managed internally.
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Request.html
template <typename... Args>
Request(scoped_refptr<dbus::Bus> bus,
dbus::ObjectProxy* object_proxy,
const std::string& interface_name,
const std::string& method_name,
Dictionary&& options,
ResponseCallback callback,
const Args&... arguments)
requires(dbus_utils::IsSupportedDBusType<Args> && ...)
: Request(std::move(bus), std::move(callback)) {
dbus::MethodCall method_call(interface_name, method_name);
dbus::MessageWriter writer(&method_call);
(dbus_utils::WriteValue(writer, arguments), ...);
Initialize(object_proxy, &method_call, &writer, std::move(options),
std::string());
}
// The same as the constructor, except `portal_service_name` may be provided
// to override in tests.
template <typename... Args>
static std::unique_ptr<Request> CreateWithPortalServiceName(
scoped_refptr<dbus::Bus> bus,
dbus::ObjectProxy* object_proxy,
const std::string& interface_name,
const std::string& method_name,
Dictionary&& options,
ResponseCallback callback,
const std::string& portal_service_name,
const Args&... arguments)
requires(dbus_utils::IsSupportedDBusType<Args> && ...)
{
auto request =
base::WrapUnique(new Request(std::move(bus), std::move(callback)));
dbus::MethodCall method_call(interface_name, method_name);
dbus::MessageWriter writer(&method_call);
(dbus_utils::WriteValue(writer, arguments), ...);
request->Initialize(object_proxy, &method_call, &writer, std::move(options),
portal_service_name);
return request;
}
Request(Request&& other) noexcept = delete;
Request& operator=(Request&& other) noexcept = delete;
// If the request has not finished, the destructor will call the "Close"
// method on the request object, and the callback will not be run.
~Request();
private:
Request(scoped_refptr<dbus::Bus> bus, ResponseCallback callback);
void OnMethodResponse(dbus::Response* response,
dbus::ErrorResponse* error_response);
void OnResponseSignal(dbus::Signal* signal);
void OnSignalConnected(const std::string& interface_name,
const std::string& signal_name,
bool success);
void Initialize(dbus::ObjectProxy* object_proxy,
dbus::MethodCall* method_call,
dbus::MessageWriter* writer,
Dictionary&& options,
const std::string& portal_service_name);
// `this` may be destructed after Finish() runs.
void Finish(base::expected<Dictionary, ResponseError>&& result);
scoped_refptr<dbus::Bus> bus_;
dbus::ObjectPath request_object_path_;
ResponseCallback callback_;
std::string portal_service_name_;
base::WeakPtrFactory<Request> weak_ptr_factory_{this};
};
} // namespace dbus_xdg
#endif // COMPONENTS_DBUS_XDG_REQUEST_H_