diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c index 73c2a979b..da408be41 100644 --- a/gio/gdesktopappinfo.c +++ b/gio/gdesktopappinfo.c @@ -2951,6 +2951,7 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info, gboolean completed = FALSE; GList *old_uris; GList *dup_uris; + GList *ruris = NULL; char **argv, **envp; int argc; @@ -2964,6 +2965,30 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info, else envp = g_get_environ (); +#ifdef G_OS_UNIX + if (uris && info->keyfile) + { + char *snap_instance; + char *app_id = NULL; + + snap_instance = g_desktop_app_info_get_string (info, "X-SnapInstanceName"); + + if (snap_instance && *snap_instance) + app_id = g_strconcat ("snap.", snap_instance, NULL); + + g_free (snap_instance); + + if (app_id) + { + ruris = g_document_portal_add_documents (uris, app_id, NULL); + if (ruris != NULL) + uris = ruris; + } + + g_clear_pointer (&app_id, g_free); + } +#endif + /* The GList* passed to expand_application_parameters() will be modified * internally by expand_macro(), so we need to pass a copy of it instead, * and also use that copy to control the exit condition of the loop below. @@ -3153,6 +3178,7 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info, out: g_strfreev (argv); g_strfreev (envp); + g_list_free_full (ruris, g_free); return completed; } diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c index 6a2577739..f880a3dc8 100644 --- a/gio/tests/desktop-app-info.c +++ b/gio/tests/desktop-app-info.c @@ -33,6 +33,9 @@ #include #include +#include "fake-document-portal.h" +#include "gdbus-sessionbus.h" + G_DECLARE_FINAL_TYPE (TestLaunchContext, test_launch_context, TEST, LAUNCH_CONTEXT, GAppLaunchContext); @@ -1432,6 +1435,76 @@ test_default_uri_handler_async (void) g_free (file_path); } +static void +launch_snap_uris_with_portal (void) +{ + GDesktopAppInfo *appinfo; + GError *error = NULL; + gboolean retval; + const gchar *path; + const gchar *path2; + gboolean invoked = FALSE; + gboolean launched = FALSE; + gboolean failed = FALSE; + gboolean child_waited = FALSE; + GAppLaunchContext *context; + GList *uris = NULL; + GFakeDocumentPortalThread *thread = NULL; + + /* Run a fake-document-portal */ + session_bus_up (); + thread = g_fake_document_portal_thread_new (session_bus_get_address (), + "snap.snap-app"); + g_fake_document_portal_thread_run (thread); + + path = g_test_get_filename (G_TEST_BUILT, "snap-app_appinfo-test.desktop", NULL); + appinfo = g_desktop_app_info_new_from_filename (path); + g_assert_true (G_IS_APP_INFO (appinfo)); + + path2 = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); + g_assert_true (g_file_test (path2, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)); + + context = g_object_new (test_launch_context_get_type (), NULL); + g_signal_connect (context, "launch-started", + G_CALLBACK (on_launch_started), + &invoked); + g_signal_connect (context, "launched", + G_CALLBACK (on_launched), + &launched); + g_signal_connect (context, "launch-failed", + G_CALLBACK (on_launch_failed), + &failed); + g_app_launch_context_setenv (context, "DOCUMENT_PORTAL_MOUNT_POINT", + g_fake_document_portal_thread_get_mount_point (thread)); + + uris = g_list_append (uris, g_strconcat ("file://", path, NULL)); + uris = g_list_append (uris, g_strconcat ("file://", path2, NULL)); + + retval = g_desktop_app_info_launch_uris_as_manager (appinfo, uris, + context, + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, + NULL, + wait_child_completed, + &child_waited, + &error); + + g_assert_no_error (error); + g_assert_true (retval); + g_assert_true (invoked); + g_assert_true (launched); + g_assert_true (child_waited); + g_assert_false (failed); + + g_clear_list (&uris, g_free); + g_object_unref (appinfo); + g_assert_finalize_object (context); + + g_fake_document_portal_thread_stop (thread); + g_clear_object (&thread); + session_bus_down (); +} + /* Test if Desktop-File Id is correctly formed */ static void test_id (void) @@ -2062,6 +2135,7 @@ main (int argc, g_test_add_func ("/desktop-app-info/launch-as-manager/fail", test_launch_as_manager_fail); g_test_add_func ("/desktop-app-info/launch-default-uri-handler", test_default_uri_handler); g_test_add_func ("/desktop-app-info/launch-default-uri-handler-async", test_default_uri_handler_async); + g_test_add_func ("/desktop-app-info/launch-snap-uri-with-portal", launch_snap_uris_with_portal); g_test_add_func ("/desktop-app-info/id", test_id); for (i = 0; i < G_N_ELEMENTS (supported_terminals); i++) diff --git a/gio/tests/meson.build b/gio/tests/meson.build index 0b5e6c912..580a6af86 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -370,13 +370,6 @@ if host_machine.system() != 'windows' 'install' : false, 'extra_programs' : ['appinfo-test'], }, - 'desktop-app-info' : { - 'install' : false, - 'depends' : gio_launch_desktop, - 'extra_programs' : ['apps', 'appinfo-test'], - # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/3148 - 'can_fail' : host_system == 'gnu', - }, } endif @@ -384,6 +377,7 @@ if host_machine.system() != 'windows' 'basic-application' : {}, 'dbus-launch' : {}, 'appinfo-test' : {}, + 'snapinfo-test' : {}, } if not glib_have_cocoa @@ -622,6 +616,14 @@ if host_machine.system() != 'windows' 'dbus-appinfo' : { 'extra_sources' : [extra_sources, fake_document_portal_sources], }, + 'desktop-app-info' : { + 'install' : false, + 'depends' : gio_launch_desktop, + 'extra_programs' : ['apps', 'appinfo-test', 'snapinfo-test'], + # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/3148 + 'can_fail' : host_system == 'gnu', + 'extra_sources' : [extra_sources, fake_document_portal_sources], + }, } endif @@ -750,6 +752,7 @@ appinfo_test_desktop_files = [ 'appinfo-test-path', 'appinfo-test', 'appinfo-test2', + 'snap-app_appinfo-test', ] foreach appinfo_test_desktop_file : appinfo_test_desktop_files diff --git a/gio/tests/snap-app_appinfo-test.desktop.in b/gio/tests/snap-app_appinfo-test.desktop.in new file mode 100644 index 000000000..d79f6db69 --- /dev/null +++ b/gio/tests/snap-app_appinfo-test.desktop.in @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2025 Marco Trevisan (Treviño) +[Desktop Entry] +Type=Application +Version=1.0 +Name=Snap Test +X-SnapInstanceName=snap-app +X-SnapAppName=appinfo-test +X-SnapCommonID=org.gio.snap-app.appinfo-test +DBusActivatable=false +Exec=@installed_tests_dir@/snapinfo-test %U +StartupNotify=true diff --git a/gio/tests/snapinfo-test.c b/gio/tests/snapinfo-test.c new file mode 100644 index 000000000..0dd51a40f --- /dev/null +++ b/gio/tests/snapinfo-test.c @@ -0,0 +1,51 @@ +/** + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 Marco Trevisan (Treviño) + */ + +#include +#include + +int +main (int argc, char *argv[]) +{ + const char *envvar; + const char *document_portal_mount; + char *expected; + char *expected_files[3] = {0}; + gint pid_from_env; + + g_test_init (&argc, &argv, NULL); + + envvar = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE"); + g_assert_nonnull (envvar); + + expected = g_test_build_filename (G_TEST_BUILT, "snap-app_appinfo-test.desktop", NULL); + g_assert_cmpstr (envvar, ==, expected); + g_free (expected); + + envvar = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE_PID"); + g_assert (envvar != NULL); + pid_from_env = atoi (envvar); + g_assert_cmpint (pid_from_env, ==, getpid ()); + + document_portal_mount = g_getenv ("DOCUMENT_PORTAL_MOUNT_POINT"); + g_assert_nonnull (document_portal_mount); + + expected_files[0] = g_build_filename (document_portal_mount, + "document-id-0", "snap-app_appinfo-test.desktop", + NULL); + expected_files[1] = g_build_filename (document_portal_mount, + "document-id-1", "appinfo-test.desktop", + NULL); + + g_assert_cmpint (argc, ==, 3); + + for (size_t i = 0; i < G_N_ELEMENTS (expected_files); ++i) + { + g_assert_cmpstr (argv[i+1], ==, expected_files[i]); + g_clear_pointer (&expected_files[i], g_free); + } + + return 0; +}