[glib/wip/oholy/gappinfo-async: 2/3] gappinfo: Use g_app_info_launch_uris_async() for async calls



commit ec5dce87b15a587e25a41da3e3a801a4b5c93c4b
Author: Ondrej Holy <oholy redhat com>
Date:   Tue Jan 22 15:44:10 2019 +0100

    gappinfo: Use g_app_info_launch_uris_async() for async calls
    
    Currently, the g_app_info_launch_default_for_uri_async() function uses
    g_app_info_launch_uris(), which is not fully asynchronous and may cause
    for example Nautilus freezes. Let's use the g_app_info_launch_uris_async()
    function to prevent the freezes.
    
    Closes: https://gitlab.gnome.org/GNOME/glib/issues/1347
    https://gitlab.gnome.org/GNOME/glib/issues/1249

 gio/gappinfo.c | 241 +++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 181 insertions(+), 60 deletions(-)
---
diff --git a/gio/gappinfo.c b/gio/gappinfo.c
index cf8a6d00d..789e2cdca 100644
--- a/gio/gappinfo.c
+++ b/gio/gappinfo.c
@@ -765,15 +765,31 @@ g_app_info_should_show (GAppInfo *appinfo)
   return (* iface->should_show) (appinfo);
 }
 
-static gboolean
-launch_default_for_uri (const char         *uri,
-                        GAppLaunchContext  *context,
-                        GError            **error)
+/**
+ * g_app_info_launch_default_for_uri:
+ * @uri: the uri to show
+ * @context: (nullable): an optional #GAppLaunchContext
+ * @error: (nullable): return location for an error, or %NULL
+ *
+ * Utility function that launches the default application
+ * registered to handle the specified uri. Synchronous I/O
+ * is done on the uri to detect the type of the file if
+ * required.
+ *
+ * The DBus-activated applications don't have to be started if your application
+ * terminates too soon after this function. To prevent this, use
+ * g_app_info_launch_default_for_uri() instead.
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ **/
+gboolean
+g_app_info_launch_default_for_uri (const char         *uri,
+                                   GAppLaunchContext  *launch_context,
+                                   GError            **error)
 {
   char *uri_scheme;
   GAppInfo *app_info = NULL;
-  GList l;
-  gboolean res;
+  gboolean res = FALSE;
 
   /* g_file_query_default_handler() calls
    * g_app_info_get_default_for_uri_scheme() too, but we have to do it
@@ -793,56 +809,146 @@ launch_default_for_uri (const char         *uri,
       g_object_unref (file);
     }
 
-  if (app_info == NULL)
-    return FALSE;
+  if (app_info)
+    {
+      GList l;
 
-  l.data = (char *)uri;
-  l.next = l.prev = NULL;
-  res = g_app_info_launch_uris (app_info, &l, context, error);
+      l.data = (char *)uri;
+      l.next = l.prev = NULL;
+      res = g_app_info_launch_uris (app_info, &l, launch_context, error);
+      g_object_unref (app_info);
+    }
 
-  g_object_unref (app_info);
+#ifdef G_OS_UNIX
+  if (!res && glib_should_use_portal ())
+    {
+      const char *parent_window = NULL;
+
+      /* Reset any error previously set by launch_default_for_uri */
+      g_clear_error (error);
+
+      if (launch_context && launch_context->priv->envp)
+        parent_window = g_environ_getenv (launch_context->priv->envp, "PARENT_WINDOW_ID");
+
+      return g_openuri_portal_open_uri (uri, parent_window, error);
+    }
+#endif
 
   return res;
 }
 
-/**
- * g_app_info_launch_default_for_uri:
- * @uri: the uri to show
- * @context: (nullable): an optional #GAppLaunchContext
- * @error: (nullable): return location for an error, or %NULL
- *
- * Utility function that launches the default application
- * registered to handle the specified uri. Synchronous I/O
- * is done on the uri to detect the type of the file if
- * required.
- * 
- * Returns: %TRUE on success, %FALSE on error.
- **/
-gboolean
-g_app_info_launch_default_for_uri (const char         *uri,
-                                  GAppLaunchContext  *launch_context,
-                                  GError            **error)
+typedef struct
 {
-  if (launch_default_for_uri (uri, launch_context, error))
-    return TRUE;
+  gchar *uri;
+  GAppLaunchContext *context;
+} LaunchDefaultForUriData;
+
+static void
+launch_default_for_uri_data_free (LaunchDefaultForUriData *data)
+{
+  g_free (data->uri);
+  g_clear_object (&data->context);
+  g_free (data);
+}
 
 #ifdef G_OS_UNIX
+static void
+launch_default_for_uri_portal_open_uri_cb (GObject      *object,
+                                           GAsyncResult *result,
+                                           gpointer      user_data)
+{
+  GTask *task = G_TASK (user_data);
+  GError *error = NULL;
+
+  if (g_openuri_portal_open_uri_finish (result, &error))
+    g_task_return_boolean (task, TRUE);
+  else
+    g_task_return_error (task, g_steal_pointer (&error));
+  g_object_unref (task);
+}
+#endif
+
+static void
+launch_default_for_uri_portal_open_uri (GTask *task, GError *error)
+{
+#ifdef G_OS_UNIX
+  LaunchDefaultForUriData *data = g_task_get_task_data (task);
+
   if (glib_should_use_portal ())
     {
       const char *parent_window = NULL;
 
       /* Reset any error previously set by launch_default_for_uri */
-      g_clear_error (error);
+      g_error_free (error);
 
-      if (launch_context && launch_context->priv->envp)
-        parent_window = g_environ_getenv (launch_context->priv->envp, "PARENT_WINDOW_ID");
-
-      return g_openuri_portal_open_uri (uri, parent_window, error);
+      if (data->context && data->context->priv->envp)
+        parent_window = g_environ_getenv (data->context->priv->envp,
+                                          "PARENT_WINDOW_ID");
 
+      g_openuri_portal_open_uri_async (data->uri,
+                                       parent_window,
+                                       g_task_get_cancellable (task),
+                                       launch_default_for_uri_portal_open_uri_cb,
+                                       g_steal_pointer (&task));
+      return;
     }
 #endif
 
-  return FALSE;
+  g_task_return_error (task, g_steal_pointer (&error));
+  g_object_unref (task);
+}
+
+static void
+launch_default_for_uri_launch_uris_cb (GObject      *object,
+                                       GAsyncResult *result,
+                                       gpointer      user_data)
+{
+  GAppInfo *app_info = G_APP_INFO (object);
+  GTask *task = G_TASK (user_data);
+  GError *error = NULL;
+
+  if (g_app_info_launch_uris_finish (app_info, result, &error))
+    {
+      g_task_return_boolean (task, TRUE);
+      g_object_unref (task);
+    }
+  else
+    launch_default_for_uri_portal_open_uri (g_steal_pointer (&task), g_steal_pointer (&error));
+}
+
+static void
+launch_default_for_uri_launch_uris (GTask *task,
+                                    GAppInfo *app_info)
+{
+  GList l;
+  LaunchDefaultForUriData *data = g_task_get_task_data (task);
+
+  l.data = (char *)data->uri;
+  l.next = l.prev = NULL;
+  g_app_info_launch_uris_async (app_info,
+                                &l,
+                                data->context,
+                                g_task_get_cancellable (task),
+                                launch_default_for_uri_launch_uris_cb,
+                                g_steal_pointer (&task));
+  g_object_unref (app_info);
+}
+
+static void
+launch_default_for_uri_default_handler_cb (GObject      *object,
+                                           GAsyncResult *result,
+                                           gpointer      user_data)
+{
+  GFile *file = G_FILE (object);
+  GTask *task = G_TASK (user_data);
+  GAppInfo *app_info = NULL;
+  GError *error = NULL;
+
+  app_info = g_file_query_default_handler_finish (file, result, &error);
+  if (app_info)
+    launch_default_for_uri_launch_uris (g_steal_pointer (&task), g_steal_pointer (&app_info));
+  else
+    launch_default_for_uri_portal_open_uri (g_steal_pointer (&task), g_steal_pointer (&error));
 }
 
 /**
@@ -860,6 +966,10 @@ g_app_info_launch_default_for_uri (const char         *uri,
  * sandboxed and the portal may present an application chooser
  * dialog to the user.
  *
+ * This is also useful if you want to be sure that the DBus-activated
+ * applications are really started before termination and if you are interested
+ * in receiving error information from their activation.
+ *
  * Since: 2.50
  */
 void
@@ -869,32 +979,45 @@ g_app_info_launch_default_for_uri_async (const char          *uri,
                                          GAsyncReadyCallback  callback,
                                          gpointer             user_data)
 {
-  gboolean res;
-  GError *error = NULL;
   GTask *task;
+  char *uri_scheme;
+  GAppInfo *app_info = NULL;
+  LaunchDefaultForUriData *data;
 
-  res = launch_default_for_uri (uri, context, &error);
+  g_return_if_fail (uri != NULL);
 
-#ifdef G_OS_UNIX
-  if (!res && glib_should_use_portal ())
-    {
-      const  char *parent_window = NULL;
+  task = g_task_new (NULL, cancellable, callback, user_data);
+  g_task_set_source_tag (task, g_app_info_launch_default_for_uri_async);
 
-      if (context && context->priv->envp)
-        parent_window = g_environ_getenv (context->priv->envp, "PARENT_WINDOW_ID");
+  data = g_new (LaunchDefaultForUriData, 1);
+  data->uri = g_strdup (uri);
+  data->context = (context != NULL) ? g_object_ref (context) : NULL;
+  g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) launch_default_for_uri_data_free);
 
-      g_openuri_portal_open_uri_async (uri, parent_window, cancellable, callback, user_data);
-      return;
-    }
-#endif
+  /* g_file_query_default_handler_async() calls
+   * g_app_info_get_default_for_uri_scheme() too, but we have to do it
+   * here anyway in case GFile can't parse @uri correctly.
+   */
+  uri_scheme = g_uri_parse_scheme (uri);
+  if (uri_scheme && uri_scheme[0] != '\0')
+    /* FIXME: The following still uses blocking calls. */
+    app_info = g_app_info_get_default_for_uri_scheme (uri_scheme);
+  g_free (uri_scheme);
 
-  task = g_task_new (context, cancellable, callback, user_data);
-  if (!res)
-    g_task_return_error (task, error);
-  else
-    g_task_return_boolean (task, TRUE);
+  if (!app_info)
+    {
+      GFile *file;
 
-  g_object_unref (task);
+      file = g_file_new_for_uri (uri);
+      g_file_query_default_handler_async (file,
+                                          G_PRIORITY_DEFAULT,
+                                          cancellable,
+                                          launch_default_for_uri_default_handler_cb,
+                                          g_steal_pointer (&task));
+      g_object_unref (file);
+    }
+  else
+    launch_default_for_uri_launch_uris (g_steal_pointer (&task), g_steal_pointer (&app_info));
 }
 
 /**
@@ -912,11 +1035,9 @@ gboolean
 g_app_info_launch_default_for_uri_finish (GAsyncResult  *result,
                                           GError       **error)
 {
-#ifdef G_OS_UNIX
-  return g_openuri_portal_open_uri_finish (result, error);
-#else
+  g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
+
   return g_task_propagate_boolean (G_TASK (result), error);
-#endif
 }
 
 /**


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