910e62b5创建于 1月15日历史提交
// 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_