summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErnestas Kulik <ernestask@gnome.org>2017-04-09 10:01:22 +0300
committerCarlos Soriano <csoriano@gnome.org>2017-04-22 12:16:19 +0200
commit2d848bba7bd24f9d29f372c1e37507aacfbedda5 (patch)
tree3cfd4091d374e5db03de118fc0323a7185251423
parentabcfab5c9a021cea99caaef5b7770a248b9debd7 (diff)
downloadnautilus-2d848bba7bd24f9d29f372c1e37507aacfbedda5.tar.gz
mime-actions: launch default uri handlers when activating files
Currently, Nautilus tries to find the default applications for files itself, which does not work well in a sandbox. This commit makes Nautilus blindly launch the default applications, which makes use of the documents portal indirectly. https://bugzilla.gnome.org/show_bug.cgi?id=781132
-rw-r--r--src/nautilus-mime-actions.c294
-rw-r--r--src/nautilus-program-choosing.c277
-rw-r--r--src/nautilus-program-choosing.h7
3 files changed, 357 insertions, 221 deletions
diff --git a/src/nautilus-mime-actions.c b/src/nautilus-mime-actions.c
index 14fe44bc3..f4a25063b 100644
--- a/src/nautilus-mime-actions.c
+++ b/src/nautilus-mime-actions.c
@@ -67,12 +67,6 @@ typedef struct
typedef struct
{
- GAppInfo *application;
- GList *uris;
-} ApplicationLaunchParameters;
-
-typedef struct
-{
NautilusWindowSlot *slot;
gpointer window;
GtkWindow *parent_window;
@@ -90,6 +84,13 @@ typedef struct
gboolean user_confirmation;
} ActivateParameters;
+typedef struct
+{
+ ActivateParameters *activation_params;
+ GQueue *uris;
+ GQueue *unhandled_uris;
+} ApplicationLaunchParameters;
+
struct
{
char *name;
@@ -343,27 +344,19 @@ launch_locations_from_file_list (GList *list)
}
static ApplicationLaunchParameters *
-application_launch_parameters_new (GAppInfo *application,
- GList *uris)
+application_launch_parameters_new (ActivateParameters *activation_params,
+ GQueue *uris)
{
ApplicationLaunchParameters *result;
result = g_new0 (ApplicationLaunchParameters, 1);
- result->application = g_object_ref (application);
- result->uris = g_list_copy_deep (uris, (GCopyFunc) g_strdup, NULL);
+ result->activation_params = activation_params;
+ result->uris = uris;
+ result->unhandled_uris = g_queue_new ();
return result;
}
-static void
-application_launch_parameters_free (ApplicationLaunchParameters *parameters)
-{
- g_object_unref (parameters->application);
- g_list_free_full (parameters->uris, g_free);
-
- g_free (parameters);
-}
-
static gboolean
nautilus_mime_actions_check_if_required_attributes_ready (NautilusFile *file)
{
@@ -797,114 +790,6 @@ nautilus_mime_file_opens_in_external_app (NautilusFile *file)
return (activation_action == ACTIVATION_ACTION_OPEN_IN_APPLICATION);
}
-
-static unsigned int
-mime_application_hash (GAppInfo *app)
-{
- const char *id;
-
- id = g_app_info_get_id (app);
-
- if (id == NULL)
- {
- return GPOINTER_TO_UINT (app);
- }
-
- return g_str_hash (id);
-}
-
-static void
-list_to_parameters_foreach (GAppInfo *application,
- GList *uris,
- GList **ret)
-{
- ApplicationLaunchParameters *parameters;
-
- uris = g_list_reverse (uris);
-
- parameters = application_launch_parameters_new
- (application, uris);
- *ret = g_list_prepend (*ret, parameters);
-}
-
-
-/**
- * make_activation_parameters
- *
- * Construct a list of ApplicationLaunchParameters from a list of NautilusFiles,
- * where files that have the same default application are put into the same
- * launch parameter, and others are put into the unhandled_files list.
- *
- * @files: Files to use for construction.
- * @unhandled_files: Files without any default application will be put here.
- *
- * Return value: Newly allocated list of ApplicationLaunchParameters.
- **/
-static GList *
-make_activation_parameters (GList *uris,
- GList **unhandled_uris)
-{
- GList *ret, *l, *app_uris;
- NautilusFile *file;
- GAppInfo *app, *old_app;
- GHashTable *app_table;
- char *uri;
-
- ret = NULL;
- *unhandled_uris = NULL;
-
- app_table = g_hash_table_new_full
- ((GHashFunc) mime_application_hash,
- (GEqualFunc) g_app_info_equal,
- (GDestroyNotify) g_object_unref,
- (GDestroyNotify) g_list_free);
-
- for (l = uris; l != NULL; l = l->next)
- {
- uri = l->data;
- file = nautilus_file_get_by_uri (uri);
-
- app = nautilus_mime_get_default_application_for_file (file);
- if (app != NULL)
- {
- app_uris = NULL;
-
- if (g_hash_table_lookup_extended (app_table, app,
- (gpointer *) &old_app,
- (gpointer *) &app_uris))
- {
- g_hash_table_steal (app_table, old_app);
-
- app_uris = g_list_prepend (app_uris, uri);
-
- g_object_unref (app);
- app = old_app;
- }
- else
- {
- app_uris = g_list_prepend (NULL, uri);
- }
-
- g_hash_table_insert (app_table, app, app_uris);
- }
- else
- {
- *unhandled_uris = g_list_prepend (*unhandled_uris, uri);
- }
- nautilus_file_unref (file);
- }
-
- g_hash_table_foreach (app_table,
- (GHFunc) list_to_parameters_foreach,
- &ret);
-
- g_hash_table_destroy (app_table);
-
- *unhandled_uris = g_list_reverse (*unhandled_uris);
-
- return g_list_reverse (ret);
-}
-
static gboolean
file_was_cancelled (NautilusFile *file)
{
@@ -957,6 +842,16 @@ activation_parameters_free (ActivateParameters *parameters)
}
static void
+application_launch_parameters_free (ApplicationLaunchParameters *parameters)
+{
+ g_queue_free (parameters->unhandled_uris);
+ g_queue_free (parameters->uris);
+ activation_parameters_free (parameters->activation_params);
+
+ g_free (parameters);
+}
+
+static void
cancel_activate_callback (gpointer callback_data)
{
ActivateParameters *parameters = callback_data;
@@ -1629,21 +1524,65 @@ activate_desktop_file (ActivateParameters *parameters,
}
static void
+on_launch_default_for_uri (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ ApplicationLaunchParameters *params;
+ ActivateParameters *activation_params;
+ char *uri;
+
+ params = user_data;
+ activation_params = params->activation_params;
+ uri = g_queue_pop_head (params->uris);
+
+ if (!nautilus_launch_default_for_uri_finish (res, NULL))
+ {
+ g_queue_push_tail (params->unhandled_uris, uri);
+ }
+
+ if (!g_queue_is_empty (params->uris))
+ {
+ nautilus_launch_default_for_uri_async (g_queue_peek_head (params->uris),
+ activation_params->parent_window,
+ activation_params->cancellable,
+ on_launch_default_for_uri,
+ params);
+ }
+ else
+ {
+ gboolean should_close;
+ NautilusWindow *window;
+
+ should_close = activation_params->flags &
+ NAUTILUS_WINDOW_OPEN_FLAG_CLOSE_BEHIND;
+ window = nautilus_window_slot_get_window (activation_params->slot);
+
+ if (should_close && window != NULL)
+ {
+ nautilus_window_close (window);
+ }
+ else
+ {
+ while ((uri = g_queue_pop_head (params->unhandled_uris)) != NULL)
+ {
+ application_unhandled_uri (activation_params, uri);
+ }
+ }
+
+ application_launch_parameters_free (params);
+ }
+}
+
+static void
activate_files (ActivateParameters *parameters)
{
NautilusFile *file;
NautilusWindow *window;
NautilusWindowOpenFlags flags;
- g_autoptr (GList) open_in_app_parameters = NULL;
- g_autoptr (GList) unhandled_open_in_app_uris = NULL;
- ApplicationLaunchParameters *one_parameters;
int count;
g_autofree char *old_working_dir = NULL;
GdkScreen *screen;
- gint num_apps;
- gint num_unhandled;
- gint num_files;
- gboolean open_files;
gboolean closed_window;
g_autoptr (GQueue) launch_desktop_files = NULL;
g_autoptr (GQueue) launch_files = NULL;
@@ -1890,84 +1829,37 @@ activate_files (ActivateParameters *parameters)
}
}
- if (open_in_app_uris != NULL)
+ if (g_queue_is_empty (open_in_app_uris))
{
- open_in_app_parameters = make_activation_parameters (g_queue_peek_head_link (open_in_app_uris),
- &unhandled_open_in_app_uris);
- }
-
- num_apps = g_list_length (open_in_app_parameters);
- num_unhandled = g_list_length (unhandled_open_in_app_uris);
- num_files = g_queue_get_length (open_in_app_uris);
- open_files = TRUE;
-
- if (g_queue_is_empty (open_in_app_uris) &&
- (!parameters->user_confirmation ||
- num_files + num_unhandled > SILENT_OPEN_LIMIT) &&
- num_apps > 1)
- {
- GtkDialog *dialog;
- char *prompt;
- g_autofree char *detail = NULL;
- int response;
-
- pause_activation_timed_cancel (parameters);
-
- prompt = _("Are you sure you want to open all files?");
- detail = g_strdup_printf (ngettext ("This will open %d separate application.",
- "This will open %d separate applications.", num_apps), num_apps);
- dialog = eel_show_yes_no_dialog (prompt, detail,
- _("_OK"), _("_Cancel"),
- parameters->parent_window);
- response = gtk_dialog_run (dialog);
- gtk_widget_destroy (GTK_WIDGET (dialog));
-
- unpause_activation_timed_cancel (parameters);
-
- if (response != GTK_RESPONSE_YES)
+ window = NULL;
+ if (parameters->slot != NULL)
{
- open_files = FALSE;
+ window = nautilus_window_slot_get_window (parameters->slot);
}
- }
- if (open_files)
- {
- for (l = open_in_app_parameters; l != NULL; l = l->next)
- {
- one_parameters = l->data;
-
- nautilus_launch_application_by_uri (one_parameters->application,
- one_parameters->uris,
- parameters->parent_window);
- application_launch_parameters_free (one_parameters);
- }
-
- for (l = unhandled_open_in_app_uris; l != NULL; l = l->next)
- {
- char *uri = l->data;
-
- /* this does not block */
- application_unhandled_uri (parameters, uri);
- }
- }
-
- window = NULL;
- if (parameters->slot != NULL)
- {
- window = nautilus_window_slot_get_window (parameters->slot);
- }
-
- if (open_in_app_parameters != NULL ||
- unhandled_open_in_app_uris != NULL)
- {
if ((parameters->flags & NAUTILUS_WINDOW_OPEN_FLAG_CLOSE_BEHIND) != 0 &&
window != NULL)
{
nautilus_window_close (window);
}
+
+ activation_parameters_free (parameters);
}
+ else
+ {
+ const char *uri;
+ ApplicationLaunchParameters *params;
- activation_parameters_free (parameters);
+ uri = g_queue_peek_head (open_in_app_uris);
+ params = application_launch_parameters_new (parameters,
+ g_queue_copy (open_in_app_uris));
+
+ nautilus_launch_default_for_uri_async (uri,
+ parameters->parent_window,
+ parameters->cancellable,
+ on_launch_default_for_uri,
+ params);
+ }
}
static void
diff --git a/src/nautilus-program-choosing.c b/src/nautilus-program-choosing.c
index 92c6ddcf7..bb3c5c393 100644
--- a/src/nautilus-program-choosing.c
+++ b/src/nautilus-program-choosing.c
@@ -86,6 +86,32 @@ nautilus_launch_application (GAppInfo *application,
g_list_free_full (uris, g_free);
}
+static GdkAppLaunchContext *
+get_launch_context (GtkWindow *parent_window)
+{
+ GdkDisplay *display;
+ GdkAppLaunchContext *launch_context;
+
+ if (parent_window != NULL)
+ {
+ display = gtk_widget_get_display (GTK_WIDGET (parent_window));
+ }
+ else
+ {
+ display = gdk_display_get_default ();
+ }
+
+ launch_context = gdk_display_get_app_launch_context (display);
+
+ if (parent_window != NULL)
+ {
+ gdk_app_launch_context_set_screen (launch_context,
+ gtk_window_get_screen (parent_window));
+ }
+
+ return launch_context;
+}
+
void
nautilus_launch_application_by_uri (GAppInfo *application,
GList *uris,
@@ -97,8 +123,7 @@ nautilus_launch_application_by_uri (GAppInfo *application,
NautilusFile *file;
gboolean result;
GError *error;
- GdkDisplay *display;
- GdkAppLaunchContext *launch_context;
+ g_autoptr (GdkAppLaunchContext) launch_context = NULL;
NautilusIconInfo *icon;
int count, total;
@@ -121,22 +146,7 @@ nautilus_launch_application_by_uri (GAppInfo *application,
}
locations = g_list_reverse (locations);
- if (parent_window != NULL)
- {
- display = gtk_widget_get_display (GTK_WIDGET (parent_window));
- }
- else
- {
- display = gdk_display_get_default ();
- }
-
- launch_context = gdk_display_get_app_launch_context (display);
-
- if (parent_window != NULL)
- {
- gdk_app_launch_context_set_screen (launch_context,
- gtk_window_get_screen (parent_window));
- }
+ launch_context = get_launch_context (parent_window);
file = nautilus_file_get_by_uri (uris->data);
icon = nautilus_file_get_icon (file,
@@ -172,8 +182,6 @@ nautilus_launch_application_by_uri (GAppInfo *application,
&error);
}
- g_object_unref (launch_context);
-
if (result)
{
for (l = uris; l != NULL; l = l->next)
@@ -430,3 +438,232 @@ nautilus_launch_desktop_file (GdkScreen *screen,
g_object_unref (context);
g_object_unref (app_info);
}
+
+/* HAX
+ *
+ * TODO: remove everything below once it’s doable from GTK+.
+ */
+
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/gdkwayland.h>
+#endif
+
+typedef void (*GtkWindowHandleExported) (GtkWindow *window,
+ const char *handle,
+ gpointer user_data);
+
+#ifdef GDK_WINDOWING_WAYLAND
+typedef struct
+{
+ GtkWindow *window;
+ GtkWindowHandleExported callback;
+ gpointer user_data;
+} WaylandWindowHandleExportedData;
+
+static void
+wayland_window_handle_exported (GdkWindow *window,
+ const char *wayland_handle_str,
+ gpointer user_data)
+{
+ WaylandWindowHandleExportedData *data = user_data;
+ char *handle_str;
+
+ handle_str = g_strdup_printf ("wayland:%s", wayland_handle_str);
+ data->callback (data->window, handle_str, data->user_data);
+ g_free (handle_str);
+
+ g_free (data);
+}
+#endif
+
+static gboolean
+window_export_handle (GtkWindow *window,
+ GtkWindowHandleExported callback,
+ gpointer user_data)
+{
+
+#ifdef GDK_WINDOWING_X11
+ if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window))))
+ {
+ GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
+ char *handle_str;
+ guint32 xid = (guint32) gdk_x11_window_get_xid (gdk_window);
+
+ handle_str = g_strdup_printf ("x11:%x", xid);
+ callback (window, handle_str, user_data);
+
+ return TRUE;
+ }
+#endif
+#ifdef GDK_WINDOWING_WAYLAND
+ if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window))))
+ {
+ GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
+ WaylandWindowHandleExportedData *data;
+
+ data = g_new0 (WaylandWindowHandleExportedData, 1);
+ data->window = window;
+ data->callback = callback;
+ data->user_data = user_data;
+
+ if (!gdk_wayland_window_export_handle (gdk_window,
+ wayland_window_handle_exported,
+ data,
+ g_free))
+ {
+ g_free (data);
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+#endif
+
+ g_warning ("Couldn't export handle, unsupported windowing system");
+
+ return FALSE;
+}
+
+void
+gtk_window_unexport_handle (GtkWindow *window)
+{
+#ifdef GDK_WINDOWING_WAYLAND
+ if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window))))
+ {
+ GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
+
+ gdk_wayland_window_unexport_handle (gdk_window);
+ }
+#endif
+}
+
+static void
+on_launch_default_for_uri (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ GTask *task;
+ GtkWindow *window;
+ gboolean success;
+ GError *error = NULL;
+
+ task = data;
+ window = g_task_get_source_object (task);
+
+ success = g_app_info_launch_default_for_uri_finish (result, &error);
+
+ if (window)
+ {
+ gtk_window_unexport_handle (window);
+ }
+
+ if (success)
+ {
+ g_task_return_boolean (task, success);
+ }
+ else
+ {
+ g_task_return_error (task, error);
+ }
+
+ /* Reffed in the call to window_export_handle */
+ g_object_unref (task);
+}
+
+static void
+on_window_handle_export (GtkWindow *window,
+ const char *handle_str,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ GAppLaunchContext *context = g_task_get_task_data (task);
+ const char *uri;
+
+ uri = g_object_get_data (G_OBJECT (context), "uri");
+
+ g_app_launch_context_setenv (context, "PARENT_WINDOW_ID", handle_str);
+
+ g_app_info_launch_default_for_uri_async (uri,
+ context,
+ g_task_get_cancellable (task),
+ on_launch_default_for_uri,
+ task);
+}
+
+static void
+launch_default_for_uri_thread_func (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GAppLaunchContext *launch_context;
+ const char *uri;
+ gboolean success;
+ GError *error = NULL;
+
+ launch_context = task_data;
+ uri = g_object_get_data (G_OBJECT (launch_context), "uri");
+ success = g_app_info_launch_default_for_uri (uri, launch_context, &error);
+
+ if (success)
+ {
+ g_task_return_boolean (task, success);
+ }
+ else
+ {
+ g_task_return_error (task, error);
+ }
+}
+
+void
+nautilus_launch_default_for_uri_async (const char *uri,
+ GtkWindow *parent_window,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer callback_data)
+{
+ g_autoptr (GdkAppLaunchContext) launch_context = NULL;
+ g_autoptr (GTask) task = NULL;
+
+ g_return_if_fail (uri != NULL);
+
+ launch_context = get_launch_context (parent_window);
+ task = g_task_new (parent_window, cancellable, callback, callback_data);
+
+ gdk_app_launch_context_set_timestamp (launch_context, GDK_CURRENT_TIME);
+
+ g_object_set_data_full (G_OBJECT (launch_context),
+ "uri", g_strdup (uri), g_free);
+ g_task_set_task_data (task,
+ g_object_ref (launch_context), g_object_unref);
+
+ if (parent_window != NULL)
+ {
+ gboolean handle_exported;
+
+ handle_exported = window_export_handle (parent_window,
+ on_window_handle_export,
+ g_object_ref (task));
+
+ if (handle_exported)
+ {
+ /* Launching will now be handled from the callback */
+ return;
+ }
+ }
+
+ g_task_run_in_thread (task, launch_default_for_uri_thread_func);
+}
+
+gboolean
+nautilus_launch_default_for_uri_finish (GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+/* END OF HAX */
diff --git a/src/nautilus-program-choosing.h b/src/nautilus-program-choosing.h
index b92fc98fd..ded108aba 100644
--- a/src/nautilus-program-choosing.h
+++ b/src/nautilus-program-choosing.h
@@ -52,5 +52,12 @@ void nautilus_launch_desktop_file (GdkScreen
const char *desktop_file_uri,
const GList *parameter_uris,
GtkWindow *parent_window);
+void nautilus_launch_default_for_uri_async (const char *uri,
+ GtkWindow *parent_window,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer callback_data);
+gboolean nautilus_launch_default_for_uri_finish (GAsyncResult *result,
+ GError **error);
#endif /* NAUTILUS_PROGRAM_CHOOSING_H */