[gnome-shell] shellDBus: Add a real error reporting system to InstallExtensionRemote



commit 79493979588166ffdb88c606ed71873cdd7d4ea6
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Fri Jun 15 23:20:36 2012 -0400

    shellDBus: Add a real error reporting system to InstallExtensionRemote
    
    Instead of using the 'extension-state-changed' signal to relay errors,
    use DBus's native error mechanism to inform the method caller that the
    call has failed. This requires making the method actually asynchronous
    so that we don't block the browser, which is stuck waiting for a reply
    from the browser plugin. To ensure this, we need to modify the browser
    plugin API to ensure its extesion installation method is asynchronous.
    
    Additionally, this lets us remove the awful, broken hacks that we used
    when a user clicked the "Cancel" button, replacing it by a DBus return
    value.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=679099

 browser-plugin/browser-plugin.c |   68 ++++++++++++++++++++++++++++++++++++--
 js/ui/extensionDownloader.js    |   54 ++++++++++++++----------------
 js/ui/shellDBus.js              |    5 ++-
 3 files changed, 92 insertions(+), 35 deletions(-)
---
diff --git a/browser-plugin/browser-plugin.c b/browser-plugin/browser-plugin.c
index 6daea99..b47b41c 100644
--- a/browser-plugin/browser-plugin.c
+++ b/browser-plugin/browser-plugin.c
@@ -41,7 +41,7 @@
       "It can be used only by extensions.gnome.org"
 #define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type";
 
-#define PLUGIN_API_VERSION 4
+#define PLUGIN_API_VERSION 5
 
 typedef struct {
   GDBusProxy *proxy;
@@ -489,6 +489,12 @@ parse_args (const gchar     *format_str,
 
           *(gboolean *) arg_location = NPVARIANT_TO_BOOLEAN (arg);
           break;
+
+        case 'o':
+          if (!NPVARIANT_IS_OBJECT (arg))
+            goto out;
+
+          *(NPObject **) arg_location = NPVARIANT_TO_OBJECT (arg);
         }
     }
 
@@ -580,6 +586,53 @@ plugin_enable_extension (PluginObject    *obj,
   return ret;
 }
 
+typedef struct _AsyncClosure AsyncClosure;
+
+struct _AsyncClosure {
+  PluginObject *obj;
+  NPObject *callback;
+  NPObject *errback;
+};
+
+static void
+install_extension_cb (GObject      *proxy,
+                      GAsyncResult *async_res,
+                      gpointer      user_data)
+{
+  AsyncClosure *async_closure = (AsyncClosure *) user_data;
+  GError *error = NULL;
+  GVariant *res;
+  NPVariant args[1];
+  NPVariant result = { NPVariantType_Void };
+  NPObject *callback;
+
+  res = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), async_res, &error);
+
+  if (res == NULL)
+    {
+      if (g_dbus_error_is_remote_error (error))
+        g_dbus_error_strip_remote_error (error);
+      STRINGZ_TO_NPVARIANT (error->message, args[0]);
+      callback = async_closure->errback;
+    }
+  else
+    {
+      char *string_result;
+      g_variant_get (res, "(&s)", &string_result);
+      STRINGZ_TO_NPVARIANT (string_result, args[0]);
+      callback = async_closure->callback;
+    }
+
+  funcs.invokeDefault (async_closure->obj->instance,
+                       callback, args, 1, &result);
+
+  funcs.releasevariantvalue (&result);
+
+  funcs.releaseobject (async_closure->callback);
+  funcs.releaseobject (async_closure->errback);
+  g_slice_free (AsyncClosure, async_closure);
+}
+
 static gboolean
 plugin_install_extension (PluginObject    *obj,
                           uint32_t         argc,
@@ -587,18 +640,25 @@ plugin_install_extension (PluginObject    *obj,
                           NPVariant       *result)
 {
   gchar *uuid;
+  NPObject *callback, *errback;
+  AsyncClosure *async_closure;
 
-  if (!parse_args ("u", argc, argv, &uuid))
+  if (!parse_args ("uoo", argc, argv, &uuid, &callback, &errback))
     return FALSE;
 
+  async_closure = g_slice_new (AsyncClosure);
+  async_closure->obj = obj;
+  async_closure->callback = funcs.retainobject (callback);
+  async_closure->errback = funcs.retainobject (errback);
+
   g_dbus_proxy_call (obj->proxy,
                      "InstallRemoteExtension",
                      g_variant_new ("(s)", uuid),
                      G_DBUS_CALL_FLAGS_NONE,
                      -1, /* timeout */
                      NULL, /* cancellable */
-                     NULL, /* callback */
-                     NULL /* user_data */);
+                     install_extension_cb,
+                     async_closure);
 
   g_free (uuid);
 
diff --git a/js/ui/extensionDownloader.js b/js/ui/extensionDownloader.js
index e9f4952..1d4c468 100644
--- a/js/ui/extensionDownloader.js
+++ b/js/ui/extensionDownloader.js
@@ -22,7 +22,7 @@ const REPOSITORY_URL_INFO =     REPOSITORY_URL_BASE + '/extension-info/';
 
 let _httpSession;
 
-function installExtensionFromUUID(uuid) {
+function installExtensionFromUUID(uuid, invocation) {
     let params = { uuid: uuid,
                    shell_version: Config.PACKAGE_VERSION };
 
@@ -31,6 +31,7 @@ function installExtensionFromUUID(uuid) {
     _httpSession.queue_message(message, function(session, message) {
         if (message.status_code != Soup.KnownStatusCode.OK) {
             ExtensionSystem.logExtensionError(uuid, 'downloading info: ' + message.status_code);
+            invocation.return_dbus_error('org.gnome.Shell.DownloadInfoError', message.status_code.toString());
             return;
         }
 
@@ -39,10 +40,11 @@ function installExtensionFromUUID(uuid) {
             info = JSON.parse(message.response_body.data);
         } catch (e) {
             ExtensionSystem.logExtensionError(uuid, 'parsing info: ' + e);
+            invocation.return_dbus_error('org.gnome.Shell.ParseInfoError', e.toString());
             return;
         }
 
-        let dialog = new InstallExtensionDialog(uuid, info);
+        let dialog = new InstallExtensionDialog(uuid, info, invocation);
         dialog.open(global.get_current_time());
     });
 }
@@ -63,9 +65,9 @@ function uninstallExtensionFromUUID(uuid) {
     return true;
 }
 
-function gotExtensionZipFile(session, message, uuid) {
+function gotExtensionZipFile(session, message, uuid, callback, errback) {
     if (message.status_code != Soup.KnownStatusCode.OK) {
-        ExtensionSystem.logExtensionError(uuid, 'downloading extension: ' + message.status_code);
+        errback('DownloadExtensionError', message.status_code);
         return;
     }
 
@@ -74,7 +76,7 @@ function gotExtensionZipFile(session, message, uuid) {
         if (!dir.query_exists(null))
             dir.make_directory_with_parents(null);
     } catch (e) {
-        ExtensionSystem.logExtensionError(uuid, 'Could not create extension directory');
+        errback('CreateExtensionDirectoryError', e);
         return;
     }
 
@@ -89,7 +91,7 @@ function gotExtensionZipFile(session, message, uuid) {
                                           null);
 
     if (!success) {
-        ExtensionSystem.logExtensionError(uuid, 'extract: could not extract');
+        errback('ExtractExtensionError');
         return;
     }
 
@@ -97,8 +99,7 @@ function gotExtensionZipFile(session, message, uuid) {
         GLib.spawn_close_pid(pid);
 
         if (status != 0) {
-            ExtensionSystem.logExtensionError(uuid, 'extract: could not extract');
-            invocation.return_dbus_error('org.gnome.Shell.ExtractExtensionError', '');
+            errback('ExtractExtensionError');
             return;
         }
 
@@ -111,6 +112,7 @@ function gotExtensionZipFile(session, message, uuid) {
 
         let extension = ExtensionUtils.createExtensionObject(uuid, dir, ExtensionUtils.ExtensionType.PER_USER);
         ExtensionSystem.loadExtension(extension);
+        callback();
     });
 }
 
@@ -118,11 +120,12 @@ const InstallExtensionDialog = new Lang.Class({
     Name: 'InstallExtensionDialog',
     Extends: ModalDialog.ModalDialog,
 
-    _init: function(uuid, info) {
+    _init: function(uuid, info, invocation) {
         this.parent({ styleClass: 'extension-dialog' });
 
         this._uuid = uuid;
         this._info = info;
+        this._invocation = invocation;
 
         this.setButtons([{ label: _("Cancel"),
                            action: Lang.bind(this, this._onCancelButtonPressed),
@@ -147,34 +150,27 @@ const InstallExtensionDialog = new Lang.Class({
 
     _onCancelButtonPressed: function(button, event) {
         this.close(global.get_current_time());
-
-        // Even though the extension is already "uninstalled", send through
-        // a state-changed signal for any users who want to know if the install
-        // went through correctly -- using proper async DBus would block more
-        // traditional clients like the plugin
-        let meta = { uuid: this._uuid,
-                     state: ExtensionSystem.ExtensionState.UNINSTALLED,
-                     error: '' };
-
-        _signals.emit('extension-state-changed', meta);
+        this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
     },
 
     _onInstallButtonPressed: function(button, event) {
-        let state = { uuid: this._uuid,
-                      state: ExtensionSystem.ExtensionState.DOWNLOADING,
-                      error: '' };
-
-        _signals.emit('extension-state-changed', state);
-
         let params = { shell_version: Config.PACKAGE_VERSION };
 
         let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
         let message = Soup.form_request_new_from_hash('GET', url, params);
 
-        _httpSession.queue_message(message,
-                                   Lang.bind(this, function(session, message) {
-                                       gotExtensionZipFile(session, message, this._uuid);
-                                   }));
+        let invocation = this._invocation;
+        function errback(code, message) {
+            invocation.return_dbus_error('org.gnome.Shell.' + code, message ? message.toString() : '');
+        }
+
+        function callback() {
+            invocation.return_value(GLib.Variant.new('(s)', 'successful'));
+        }
+
+        _httpSession.queue_message(message, Lang.bind(this, function(session, message) {
+            gotExtensionZipFile(session, message, this._uuid, callback, errback);
+        }));
 
         this.close(global.get_current_time());
     }
diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js
index 910d5cf..e402cab 100644
--- a/js/ui/shellDBus.js
+++ b/js/ui/shellDBus.js
@@ -202,6 +202,7 @@ const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions">
 </signal>
 <method name="InstallRemoteExtension">
     <arg type="s" direction="in" name="uuid"/>
+    <arg type="s" direction="out" name="result"/>
 </method>
 <method name="UninstallExtension">
     <arg type="s" direction="in" name="uuid"/>
@@ -285,8 +286,8 @@ const GnomeShellExtensions = new Lang.Class({
         return extension.errors;
     },
 
-    InstallRemoteExtension: function(uuid) {
-        ExtensionDownloader.installExtensionFromUUID(uuid);
+    InstallRemoteExtensionAsync: function([uuid], invocation) {
+        return ExtensionDownloader.installExtensionFromUUID(uuid, invocation);
     },
 
     UninstallExtension: function(uuid) {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]