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 256669 58acfa044b49cc547a5d55cfb8d1992d9f8df5cd
parent 256668 d979b7a7ed9c1f3c323ebf2357b98c96eeca0f18
child 256670 6faa3c88e59764ef269e4dfbf86acd05df605651
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs467729
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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"
+%}