Bug 467729 - Add a PackageKit XPCOM API. r=karlt
authorFrédéric Wang <fred.wang@free.fr>
Mon, 16 Feb 2015 23:59:00 -0500
changeset 247045 58acfa044b49cc547a5d55cfb8d1992d9f8df5cd
parent 247044 d979b7a7ed9c1f3c323ebf2357b98c96eeca0f18
child 247046 6faa3c88e59764ef269e4dfbf86acd05df605651
push id7677
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 18:11:24 +0000
treeherdermozilla-aurora@f531d838c055 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs467729
milestone38.0a1
Bug 467729 - Add a PackageKit XPCOM API. r=karlt
toolkit/system/gnome/moz.build
toolkit/system/gnome/nsGnomeModule.cpp
toolkit/system/gnome/nsPackageKitService.cpp
toolkit/system/gnome/nsPackageKitService.h
xpcom/system/moz.build
xpcom/system/nsIPackageKitService.idl
--- a/toolkit/system/gnome/moz.build
+++ b/toolkit/system/gnome/moz.build
@@ -19,16 +19,17 @@ if CONFIG['MOZ_ENABLE_GNOMEVFS']:
     SOURCES += [
         'nsGnomeVFSService.cpp',
     ]
 
 if CONFIG['MOZ_ENABLE_GIO']:
     SOURCES += [
         'nsGIOService.cpp',
         'nsGSettingsService.cpp',
+        'nsPackageKitService.cpp'
     ]
 
 XPCOMBinaryComponent('mozgnome')
 
 FAIL_ON_WARNINGS = True
 
 LOCAL_INCLUDES += [
     '/toolkit/components/build/',
--- a/toolkit/system/gnome/nsGnomeModule.cpp
+++ b/toolkit/system/gnome/nsGnomeModule.cpp
@@ -14,59 +14,64 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGC
 #endif
 #ifdef MOZ_ENABLE_GNOMEVFS
 #include "nsGnomeVFSService.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGnomeVFSService, Init)
 #endif
 #ifdef MOZ_ENABLE_GIO
 #include "nsGIOService.h"
 #include "nsGSettingsService.h"
+#include "nsPackageKitService.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsGIOService)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGSettingsService, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPackageKitService, Init)
 #endif
 #include "nsSystemAlertsService.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSystemAlertsService, Init)
 
 #ifdef MOZ_ENABLE_GCONF
 NS_DEFINE_NAMED_CID(NS_GCONFSERVICE_CID);
 #endif
 #ifdef MOZ_ENABLE_GNOMEVFS
 NS_DEFINE_NAMED_CID(NS_GNOMEVFSSERVICE_CID);
 #endif
 #ifdef MOZ_ENABLE_GIO
 NS_DEFINE_NAMED_CID(NS_GIOSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_GSETTINGSSERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_PACKAGEKITSERVICE_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_SYSTEMALERTSSERVICE_CID);
 
 static const mozilla::Module::CIDEntry kGnomeCIDs[] = {
 #ifdef MOZ_ENABLE_GCONF
   { &kNS_GCONFSERVICE_CID, false, nullptr, nsGConfServiceConstructor },
 #endif
 #ifdef MOZ_ENABLE_GNOMEVFS
   { &kNS_GNOMEVFSSERVICE_CID, false, nullptr, nsGnomeVFSServiceConstructor },
 #endif
 #ifdef MOZ_ENABLE_GIO
   { &kNS_GIOSERVICE_CID, false, nullptr, nsGIOServiceConstructor },
   { &kNS_GSETTINGSSERVICE_CID, false, nullptr, nsGSettingsServiceConstructor },
+  { &kNS_PACKAGEKITSERVICE_CID, false, nullptr, nsPackageKitServiceConstructor },
 #endif
   { &kNS_SYSTEMALERTSSERVICE_CID, false, nullptr, nsSystemAlertsServiceConstructor },
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kGnomeContracts[] = {
 #ifdef MOZ_ENABLE_GCONF
   { NS_GCONFSERVICE_CONTRACTID, &kNS_GCONFSERVICE_CID },
 #endif
 #ifdef MOZ_ENABLE_GNOMEVFS
   { NS_GNOMEVFSSERVICE_CONTRACTID, &kNS_GNOMEVFSSERVICE_CID },
 #endif
 #ifdef MOZ_ENABLE_GIO
   { NS_GIOSERVICE_CONTRACTID, &kNS_GIOSERVICE_CID },
   { NS_GSETTINGSSERVICE_CONTRACTID, &kNS_GSETTINGSSERVICE_CID },
+  { NS_PACKAGEKITSERVICE_CONTRACTID, &kNS_PACKAGEKITSERVICE_CID },
 #endif
   { NS_SYSTEMALERTSERVICE_CONTRACTID, &kNS_SYSTEMALERTSSERVICE_CID },
   { nullptr }
 };
 
 static nsresult
 InitGType ()
 {
new file mode 100644
--- /dev/null
+++ b/toolkit/system/gnome/nsPackageKitService.cpp
@@ -0,0 +1,265 @@
+/* -*- 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 "nsAutoPtr.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsISupportsPrimitives.h"
+#include "nsPackageKitService.h"
+#include "nsStringAPI.h"
+#include "prlink.h"
+
+#include <glib.h>
+#include <glib-object.h>
+
+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);
+  observer->Release();
+}
+
+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;
+  nsAutoArrayPtr<gchar*> packages(new 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;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/system/gnome/nsPackageKitService.h
@@ -0,0 +1,26 @@
+/* -*- 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/. */
+
+#ifndef nsPackageKitService_h_
+#define nsPackageKitService_h_
+
+#include "nsIPackageKitService.h"
+
+#define NS_PACKAGEKITSERVICE_CID \
+{0x9c95515e, 0x611d, 0x11e4, {0xb9, 0x7e, 0x60, 0xa4, 0x4c, 0x71, 0x70, 0x42}}
+
+class nsPackageKitService MOZ_FINAL : public nsIPackageKitService
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIPACKAGEKITSERVICE
+
+  nsresult Init();
+
+private:
+  ~nsPackageKitService();
+};
+
+#endif
--- a/xpcom/system/moz.build
+++ b/xpcom/system/moz.build
@@ -8,16 +8,17 @@ XPIDL_SOURCES += [
     'nsIBlocklistService.idl',
     'nsIDeviceSensors.idl',
     'nsIGConfService.idl',
     'nsIGeolocationProvider.idl',
     'nsIGIOService.idl',
     'nsIGnomeVFSService.idl',
     'nsIGSettingsService.idl',
     'nsIHapticFeedback.idl',
+    'nsIPackageKitService.idl',
     'nsIXULAppInfo.idl',
     'nsIXULRuntime.idl',
 ]
 
 if CONFIG['MOZ_CRASHREPORTER']:
     XPIDL_SOURCES += [
         'nsICrashReporter.idl',
     ]
new file mode 100644
--- /dev/null
+++ b/xpcom/system/nsIPackageKitService.idl
@@ -0,0 +1,46 @@
+/* -*- Mode: IDL; 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 "nsISupports.idl"
+
+interface nsIArray;
+interface nsIObserver;
+
+[scriptable, uuid(89bb04f6-ce2a-11e3-8f4f-60a44c717042)]
+interface nsIPackageKitService : nsISupports
+{
+
+  /* PackageKit installation methods */
+  /* See https://github.com/nekohayo/gnome-packagekit/blob/master/src/org.freedesktop.PackageKit.xml */
+  const unsigned long PK_INSTALL_PACKAGE_NAMES = 0;
+  const unsigned long PK_INSTALL_MIME_TYPES = 1;
+  const unsigned long PK_INSTALL_FONTCONFIG_RESOURCES = 2;
+  const unsigned long PK_INSTALL_GSTREAMER_RESOURCES = 3;
+  const unsigned long PK_INSTALL_METHOD_COUNT = 4;
+
+  /* Ask to install a list of packages via PackageKit
+   * @param   packageKitMethod
+   *          The PackageKit installation method
+   * @param   packageArray
+   *          A nonempty array of strings describing the list of packages to
+   *          install.
+   * @param   An object implementing nsIObserver that will be notified with
+   *          a message of topic "packagekit-install". The message data
+   *          contains the error returned by PackageKit if the installation
+   *          fails and is null otherwise.
+   *
+   * This function may raise an NS_ERROR_INVALID_ARG, NS_ERROR_FAILURE or
+   * NS_ERROR_OUT_OF_MEMORY exception. Otherwise, the observer will be notified
+   * when the operation completes.
+   *
+   */
+  void installPackages(in unsigned long packageKitMethod,
+                       in nsIArray packageArray,
+                       in nsIObserver observer);
+};
+
+%{C++
+#define NS_PACKAGEKITSERVICE_CONTRACTID "@mozilla.org/packagekit-service;1"
+%}