diff options
Diffstat (limited to 'components/gservice/src/nsPackageKitService.cpp')
-rw-r--r-- | components/gservice/src/nsPackageKitService.cpp | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/components/gservice/src/nsPackageKitService.cpp b/components/gservice/src/nsPackageKitService.cpp new file mode 100644 index 000000000..c32a91367 --- /dev/null +++ b/components/gservice/src/nsPackageKitService.cpp @@ -0,0 +1,267 @@ +/* -*- 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 "nsArrayUtils.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "nsISupportsPrimitives.h" +#include "nsPackageKitService.h" +#include "nsString.h" +#include "prlink.h" +#include "mozilla/Unused.h" +#include "mozilla/UniquePtr.h" + +#include <glib.h> +#include <glib-object.h> +#include <limits> + +using namespace mozilla; + +typedef struct _GAsyncResult GAsyncResult; +typedef enum { + G_BUS_TYPE_STARTER = -1, + G_BUS_TYPE_NONE = 0, + G_BUS_TYPE_SYSTEM = 1, + G_BUS_TYPE_SESSION = 2 +} GBusType; +typedef struct _GCancellable GCancellable; +typedef enum { + G_DBUS_CALL_FLAGS_NONE = 0, + G_DBUS_CALL_FLAGS_NO_AUTO_START = (1<<0) +} GDBusCallFlags; +typedef struct _GDBusInterfaceInfo GDBusInterfaceInfo; +typedef struct _GDBusProxy GDBusProxy; +typedef enum { + G_DBUS_PROXY_FLAGS_NONE = 0, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = (1<<0), + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1), + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START = (1<<2), + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES = (1<<3) +} GDBusProxyFlags; +typedef struct _GVariant GVariant; +typedef void (*GAsyncReadyCallback) (GObject *source_object, + GAsyncResult *res, + gpointer user_data); + +#define GDBUS_FUNCTIONS \ + FUNC(g_dbus_proxy_call, void, (GDBusProxy *proxy, const gchar *method_name, GVariant *parameters, GDBusCallFlags flags, gint timeout_msec, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)) \ + FUNC(g_dbus_proxy_call_finish, GVariant*, (GDBusProxy *proxy, GAsyncResult *res, GError **error)) \ + FUNC(g_dbus_proxy_new_finish, GDBusProxy*, (GAsyncResult *res, GError **error)) \ + FUNC(g_dbus_proxy_new_for_bus, void, (GBusType bus_type, GDBusProxyFlags flags, GDBusInterfaceInfo *info, const gchar *name, const gchar *object_path, const gchar *interface_name, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)) \ + FUNC(g_variant_is_floating, gboolean, (GVariant *value)) \ + FUNC(g_variant_new, GVariant*, (const gchar *format_string, ...)) \ + FUNC(g_variant_unref, void, (GVariant* value)) + +#define FUNC(name, type, params) \ + typedef type (*_##name##_fn) params; \ + static _##name##_fn _##name; + +GDBUS_FUNCTIONS + +#undef FUNC + +#define g_dbus_proxy_call _g_dbus_proxy_call +#define g_dbus_proxy_call_finish _g_dbus_proxy_call_finish +#define g_dbus_proxy_new_finish _g_dbus_proxy_new_finish +#define g_dbus_proxy_new_for_bus _g_dbus_proxy_new_for_bus +#define g_variant_is_floating _g_variant_is_floating +#define g_variant_new _g_variant_new +#define g_variant_unref _g_variant_unref + +static PRLibrary *gioLib = nullptr; + +typedef void (*nsGDBusFunc)(); +struct nsGDBusDynamicFunction { + const char *functionName; + nsGDBusFunc *function; +}; + +nsresult +nsPackageKitService::Init() +{ +#define FUNC(name, type, params) { #name, (nsGDBusFunc *)&_##name }, + const nsGDBusDynamicFunction kGDBusSymbols[] = { + GDBUS_FUNCTIONS + }; +#undef FUNC + + if (!gioLib) { + gioLib = PR_LoadLibrary("libgio-2.0.so.0"); + if (!gioLib) + return NS_ERROR_FAILURE; + } + + for (uint32_t i = 0; i < ArrayLength(kGDBusSymbols); i++) { + *kGDBusSymbols[i].function = + PR_FindFunctionSymbol(gioLib, kGDBusSymbols[i].functionName); + if (!*kGDBusSymbols[i].function) { + return NS_ERROR_FAILURE; + } + } + + return NS_OK; +} + +NS_IMPL_ISUPPORTS(nsPackageKitService, nsIPackageKitService) + +nsPackageKitService::~nsPackageKitService() +{ + if (gioLib) { + PR_UnloadLibrary(gioLib); + gioLib = nullptr; + } +} + +static const char* InstallPackagesMethods[] = { + "InstallPackageNames", + "InstallMimeTypes", + "InstallFontconfigResources", + "InstallGStreamerResources" +}; + +struct InstallPackagesProxyNewData { + nsCOMPtr<nsIObserver> observer; + uint32_t method; + GVariant* parameters; +}; + +static void +InstallPackagesNotifyObserver(nsIObserver* aObserver, + gchar* aErrorMessage) +{ + if (aObserver) { + aObserver->Observe(nullptr, "packagekit-install", + aErrorMessage ? + NS_ConvertUTF8toUTF16(aErrorMessage).get() : + nullptr); + } +} + +static void +InstallPackagesProxyCallCallback(GObject *aSourceObject, + GAsyncResult *aResult, + gpointer aUserData) +{ + nsCOMPtr<nsIObserver> observer = static_cast<nsIObserver*>(aUserData); + GDBusProxy* proxy = reinterpret_cast<GDBusProxy*>(aSourceObject); + + GError* error = nullptr; + GVariant* result = g_dbus_proxy_call_finish(proxy, aResult, &error); + if (result) { + InstallPackagesNotifyObserver(observer, nullptr); + g_variant_unref(result); + } else { + NS_ASSERTION(error, "g_dbus_proxy_call_finish should set error when it returns NULL"); + InstallPackagesNotifyObserver(observer, error->message); + g_error_free(error); + } + + g_object_unref(proxy); + Unused << observer.forget().take(); +} + +static void +InstallPackagesProxyNewCallback(GObject *aSourceObject, + GAsyncResult *aResult, + gpointer aUserData) +{ + InstallPackagesProxyNewData* userData = + static_cast<InstallPackagesProxyNewData*>(aUserData); + + NS_ASSERTION(g_variant_is_floating(userData->parameters), + "userData->parameters should be a floating reference."); + + GError* error = nullptr; + GDBusProxy* proxy = g_dbus_proxy_new_finish(aResult, &error); + + if (proxy) { + // Send the asynchronous request to install the packages + // This call will consume the floating reference userData->parameters so we + // don't need to release it explicitly. + nsIObserver* observer; + userData->observer.forget(&observer); + g_dbus_proxy_call(proxy, + InstallPackagesMethods[userData->method], + userData->parameters, + G_DBUS_CALL_FLAGS_NONE, + G_MAXINT, + nullptr, + &InstallPackagesProxyCallCallback, + static_cast<gpointer>(observer)); + } else { + NS_ASSERTION(error, "g_dbus_proxy_new_finish should set error when it returns NULL"); + InstallPackagesNotifyObserver(userData->observer, error->message); + g_error_free(error); + g_variant_unref(userData->parameters); + } + delete userData; +} + +NS_IMETHODIMP +nsPackageKitService::InstallPackages(uint32_t aInstallMethod, + nsIArray* aPackageArray, + nsIObserver* aObserver) +{ + NS_ENSURE_ARG(aPackageArray); + + uint32_t arrayLength; + aPackageArray->GetLength(&arrayLength); + if (arrayLength == 0 || + arrayLength == std::numeric_limits<uint32_t>::max() || + aInstallMethod >= PK_INSTALL_METHOD_COUNT) { + return NS_ERROR_INVALID_ARG; + } + + // Create the GVariant* parameter from the list of packages. + GVariant* parameters = nullptr; + auto packages = MakeUnique<gchar*[]>(arrayLength + 1); + + nsresult rv = NS_OK; + for (uint32_t i = 0; i < arrayLength; i++) { + nsCOMPtr<nsISupportsString> package = + do_QueryElementAt(aPackageArray, i); + if (!package) { + rv = NS_ERROR_FAILURE; + break; + } + nsString data; + package->GetData(data); + packages[i] = g_strdup(NS_ConvertUTF16toUTF8(data).get()); + if (!packages[i]) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + } + packages[arrayLength] = nullptr; + + if (NS_SUCCEEDED(rv)) { + // We create a new GVariant object to send parameters to PackageKit. + parameters = g_variant_new("(u^ass)", static_cast<guint32>(0), + packages.get(), "hide-finished"); + if (!parameters) { + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + for (uint32_t i = 0; i < arrayLength; i++) { + g_free(packages[i]); + } + NS_ENSURE_SUCCESS(rv, rv); + + // Send the asynchronous request to load the bus proxy + InstallPackagesProxyNewData* data = new InstallPackagesProxyNewData; + data->observer = aObserver; + data->method = aInstallMethod; + data->parameters = parameters; + g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + nullptr, + "org.freedesktop.PackageKit", + "/org/freedesktop/PackageKit", + "org.freedesktop.PackageKit.Modify", + nullptr, + &InstallPackagesProxyNewCallback, + static_cast<gpointer>(data)); + return NS_OK; +} |