Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AsyncDBus.h"
#include "gio/gio.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/XREAppData.h"
namespace mozilla::widget {
static void CreateProxyCallback(GObject*, GAsyncResult* aResult,
gpointer aUserData) {
RefPtr<DBusProxyPromise::Private> promise =
dont_AddRef(static_cast<DBusProxyPromise::Private*>(aUserData));
GUniquePtr<GError> error;
RefPtr<GDBusProxy> proxy = dont_AddRef(
g_dbus_proxy_new_for_bus_finish(aResult, getter_Transfers(error)));
if (proxy) {
promise->Resolve(std::move(proxy), __func__);
} else {
promise->Reject(std::move(error), __func__);
}
}
RefPtr<DBusProxyPromise> CreateDBusProxyForBus(
GBusType aBusType, GDBusProxyFlags aFlags,
GDBusInterfaceInfo* aInterfaceInfo, const char* aName,
const char* aObjectPath, const char* aInterfaceName,
GCancellable* aCancellable) {
auto promise = MakeRefPtr<DBusProxyPromise::Private>(__func__);
g_dbus_proxy_new_for_bus(aBusType, aFlags, aInterfaceInfo, aName, aObjectPath,
aInterfaceName, aCancellable, CreateProxyCallback,
do_AddRef(promise).take());
return promise.forget();
}
static void ProxyCallCallback(GObject* aSourceObject, GAsyncResult* aResult,
gpointer aUserData) {
RefPtr<DBusCallPromise::Private> promise =
dont_AddRef(static_cast<DBusCallPromise::Private*>(aUserData));
GUniquePtr<GError> error;
RefPtr<GVariant> result = dont_AddRef(g_dbus_proxy_call_finish(
G_DBUS_PROXY(aSourceObject), aResult, getter_Transfers(error)));
if (result) {
promise->Resolve(std::move(result), __func__);
} else {
promise->Reject(std::move(error), __func__);
}
}
RefPtr<DBusCallPromise> DBusProxyCall(GDBusProxy* aProxy, const char* aMethod,
GVariant* aArgs, GDBusCallFlags aFlags,
gint aTimeout,
GCancellable* aCancellable) {
auto promise = MakeRefPtr<DBusCallPromise::Private>(__func__);
g_dbus_proxy_call(aProxy, aMethod, aArgs, aFlags, aTimeout, aCancellable,
ProxyCallCallback, do_AddRef(promise).take());
return promise.forget();
}
static void ProxyCallWithUnixFDListCallback(GObject* aSourceObject,
GAsyncResult* aResult,
gpointer aUserData) {
RefPtr<DBusCallPromise::Private> promise =
dont_AddRef(static_cast<DBusCallPromise::Private*>(aUserData));
GUniquePtr<GError> error;
GUnixFDList** aFDList = nullptr;
RefPtr<GVariant> result =
dont_AddRef(g_dbus_proxy_call_with_unix_fd_list_finish(
G_DBUS_PROXY(aSourceObject), aFDList, aResult,
getter_Transfers(error)));
if (result) {
promise->Resolve(std::move(result), __func__);
} else {
promise->Reject(std::move(error), __func__);
}
}
RefPtr<DBusCallPromise> DBusProxyCallWithUnixFDList(
GDBusProxy* aProxy, const char* aMethod, GVariant* aArgs,
GDBusCallFlags aFlags, gint aTimeout, GUnixFDList* aFDList,
GCancellable* aCancellable) {
auto promise = MakeRefPtr<DBusCallPromise::Private>(__func__);
g_dbus_proxy_call_with_unix_fd_list(
aProxy, aMethod, aArgs, aFlags, aTimeout, aFDList, aCancellable,
ProxyCallWithUnixFDListCallback, do_AddRef(promise).take());
return promise.forget();
}
// Tokens should be unique and not guessable. To avoid clashes with calls
// made from unrelated libraries, it is a good idea to use a per-library
// prefix combined with a random number.
// Here, we build the token by concatenating MOZ_APP_NAME (e.g. "firefox"),
// with some unique name from the caller, plus a serial and random number.
void MakePortalRequestToken(const nsCString& aType, nsACString& aToken) {
static Atomic<unsigned, MemoryOrdering::Relaxed> sTokenSerial;
aToken.Truncate();
aToken.AppendPrintf(MOZ_APP_NAME "_%s_%u_%u", aType.get(), sTokenSerial++,
g_random_int());
XREAppData::SanitizeNameForDBus(aToken);
}
// The portal request paths are well known:
//
// Since version 0.9 of xdg-desktop-portal, the handle will be of the form
// /org/freedesktop/portal/desktop/request/SENDER/TOKEN
// where SENDER is the callers unique name, with the initial ':' removed and
// all '.' replaced by '_', and TOKEN is a unique token that the caller
// provided with the handle_token key in the options vardict.
//
void GetPortalRequestPath(GDBusProxy* aProxy, const nsCString& aRequestToken,
nsACString& aOutPath) {
aOutPath.Truncate();
GDBusConnection* connection = g_dbus_proxy_get_connection(aProxy);
nsAutoCString senderName(g_dbus_connection_get_unique_name(connection));
senderName.ReplaceChar('.', '_');
aOutPath.AppendPrintf("/org/freedesktop/portal/desktop/request/%s/%s",
senderName.get() + 1, aRequestToken.get());
}
struct PortalResponseData {
explicit PortalResponseData(PortalResponseListener aCallback)
: mCallback(std::move(aCallback)) {
MOZ_COUNT_CTOR(PortalResponseData);
}
MOZ_COUNTED_DTOR(PortalResponseData);
PortalResponseListener mCallback;
guint mSubscriptionId = 0;
static void Release(gpointer aData) {
delete static_cast<PortalResponseData*>(aData);
}
static void Fired(GDBusConnection* connection, const gchar* sender_name,
const gchar* object_path, const gchar* interface_name,
const gchar* signal_name, GVariant* parameters,
gpointer user_data) {
auto* data = static_cast<PortalResponseData*>(user_data);
auto callback = std::move(data->mCallback);
g_dbus_connection_signal_unsubscribe(connection, data->mSubscriptionId);
data = nullptr; // Dangling now!
callback(parameters);
}
};
guint OnDBusPortalResponse(GDBusProxy* aProxy, const nsCString& aRequestToken,
PortalResponseListener aCallback) {
auto boxedData = MakeUnique<PortalResponseData>(std::move(aCallback));
nsAutoCString requestPath;
GetPortalRequestPath(aProxy, aRequestToken, requestPath);
auto* data = boxedData.get();
guint subscriptionId = g_dbus_connection_signal_subscribe(
g_dbus_proxy_get_connection(aProxy), "org.freedesktop.portal.Desktop",
"org.freedesktop.portal.Request", "Response", requestPath.get(), nullptr,
G_DBUS_SIGNAL_FLAGS_NONE, PortalResponseData::Fired, boxedData.release(),
PortalResponseData::Release);
data->mSubscriptionId = subscriptionId;
return subscriptionId;
}
} // namespace mozilla::widget