From 04670a452d8183367e90fe2b5dae29b69fbaefa3 Mon Sep 17 00:00:00 2001 From: James Henstridge Date: Tue, 1 Feb 2022 20:12:49 +0800 Subject: [PATCH 01/21] webextensions: add a portal for managing WebExtensions native messaging servers. This is intended to provide a way for a confined web browser to start native code helpers for their extensions. At present it can start the servers installed on the host system. But in future this could be extended to cover sandboxed native messaging servers too. --- data/meson.build | 1 + data/org.freedesktop.portal.WebExtensions.xml | 97 ++++ src/meson.build | 1 + src/web-extensions.c | 471 ++++++++++++++++++ src/web-extensions.h | 23 + src/xdg-desktop-portal.c | 2 + 6 files changed, 595 insertions(+) create mode 100644 data/org.freedesktop.portal.WebExtensions.xml create mode 100644 src/web-extensions.c create mode 100644 src/web-extensions.h diff --git a/data/meson.build b/data/meson.build index eea49adda..63ea5fbf2 100644 --- a/data/meson.build +++ b/data/meson.build @@ -37,6 +37,7 @@ portal_sources = files( 'org.freedesktop.portal.Settings.xml', 'org.freedesktop.portal.Trash.xml', 'org.freedesktop.portal.Wallpaper.xml', + 'org.freedesktop.portal.WebExtensions.xml', ) portal_impl_sources = files( diff --git a/data/org.freedesktop.portal.WebExtensions.xml b/data/org.freedesktop.portal.WebExtensions.xml new file mode 100644 index 000000000..02284791f --- /dev/null +++ b/data/org.freedesktop.portal.WebExtensions.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/meson.build b/src/meson.build index 884b6f60f..b9dd63cee 100644 --- a/src/meson.build +++ b/src/meson.build @@ -95,6 +95,7 @@ xdg_desktop_portal_sources = files( 'settings.c', 'trash.c', 'wallpaper.c', + 'web-extensions.c', 'xdg-desktop-portal.c', ) diff --git a/src/web-extensions.c b/src/web-extensions.c new file mode 100644 index 000000000..8b9db7891 --- /dev/null +++ b/src/web-extensions.c @@ -0,0 +1,471 @@ +/* + * Copyright © 2022 Canonical Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "session.h" +#include "web-extensions.h" +#include "request.h" +#include "permissions.h" +#include "xdp-dbus.h" +#include "xdp-impl-dbus.h" +#include "xdp-utils.h" + +typedef struct _WebExtensions WebExtensions; +typedef struct _WebExtensionsClass WebExtensionsClass; + +struct _WebExtensions +{ + XdpDbusWebExtensionsSkeleton parent_instance; +}; + +struct _WebExtensionsClass +{ + XdpDbusWebExtensionsSkeletonClass parent_class; +}; + +static WebExtensions *web_extensions; + +GType web_extensions_get_type (void); +static void web_extensions_iface_init (XdpDbusWebExtensionsIface *iface); + +G_DEFINE_TYPE_WITH_CODE (WebExtensions, web_extensions, XDP_DBUS_TYPE_WEB_EXTENSIONS_SKELETON, + G_IMPLEMENT_INTERFACE (XDP_DBUS_TYPE_WEB_EXTENSIONS, + web_extensions_iface_init)); + +typedef enum _WebExtensionsSessionState +{ + WEB_EXTENSIONS_SESSION_STATE_INIT, + WEB_EXTENSIONS_SESSION_STATE_STARTED, + WEB_EXTENSIONS_SESSION_STATE_CLOSED, +} WebExtensionsSessionState; + +typedef struct _WebExtensionsSession +{ + Session parent; + + WebExtensionsSessionState state; + + GPid child_pid; + guint child_watch_id; + + int standard_input; + int standard_output; +} WebExtensionsSession; + +typedef struct _WebExtensionsSessionClass +{ + SessionClass parent_class; +} WebExtensionsSessionClass; + +GType web_extensions_session_get_type (void); + +G_DEFINE_TYPE (WebExtensionsSession, web_extensions_session, session_get_type ()); + +static void +web_extensions_session_init (WebExtensionsSession *session) +{ + session->child_pid = -1; + session->child_watch_id = 0; + + session->standard_input = -1; + session->standard_output = -1; +} + +static void +web_extensions_session_close (Session *session) +{ + WebExtensionsSession *we_session = (WebExtensionsSession *)session; + + if (we_session->state == WEB_EXTENSIONS_SESSION_STATE_CLOSED) return; + + we_session->state = WEB_EXTENSIONS_SESSION_STATE_CLOSED; + if (we_session->child_watch_id != 0) + { + g_source_remove (we_session->child_watch_id); + we_session->child_watch_id = 0; + } + + if (we_session->child_pid > 0) + { + kill (we_session->child_pid, SIGTERM); + waitpid (we_session->child_pid, NULL, 0); + g_spawn_close_pid (we_session->child_pid); + we_session->child_pid = -1; + } + + if (we_session->standard_input >= 0) + { + close (we_session->standard_input); + we_session->standard_input = -1; + } + if (we_session->standard_output >= 0) + { + close (we_session->standard_output); + we_session->standard_output = -1; + } +} + +static void +web_extensions_session_finalize (GObject *object) +{ + Session *session = (Session *)object; + + web_extensions_session_close (session); + + G_OBJECT_CLASS (web_extensions_session_parent_class)->finalize (object); +} + +static void +web_extensions_session_class_init (WebExtensionsSessionClass *klass) +{ + GObjectClass *object_class; + SessionClass *session_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = web_extensions_session_finalize; + + session_class = (SessionClass *)klass; + session_class->close = web_extensions_session_close; +} + +static WebExtensionsSession * +web_extensions_session_new (GVariant *options, + Call *call, + GDBusConnection *connection, + GError **error) +{ + Session *session; + const char *session_token; + + session_token = lookup_session_token (options); + session = g_initable_new (web_extensions_session_get_type (), NULL, error, + "sender", call->sender, + "app-id", xdp_app_info_get_id (call->app_info), + "token", session_token, + "connection", connection, + NULL); + + if (session) + g_debug ("screen cast session owned by '%s' created", session->sender); + + return (WebExtensionsSession *)session; +} + +static gboolean +handle_create_session (XdpDbusWebExtensions *object, + GDBusMethodInvocation *invocation, + GVariant *arg_options) +{ + Call *call = call_from_invocation (invocation); + GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation); + g_autoptr(GError) error = NULL; + Session *session; + + session = (Session *)web_extensions_session_new (arg_options, call, connection, &error); + if (!session) + { + g_dbus_method_invocation_return_gerror (invocation, error); + return TRUE; + } + if (!session_export (session, &error)) + { + g_dbus_method_invocation_return_gerror (invocation, error); + session_close (session, FALSE); + return TRUE; + } + session_register (session); + + xdp_dbus_web_extensions_complete_create_session (object, invocation, session->id); + + return TRUE; +} + +static void +on_server_exited (GPid pid, + gint status, + gpointer user_data) +{ + WebExtensionsSession *we_session = user_data; + + we_session->child_pid = -1; + we_session->child_watch_id = 0; + session_close ((Session *)we_session, TRUE); +} + +static gboolean +array_contains (JsonArray *array, + const char *value) +{ + guint length, i; + + if (array == NULL) + return FALSE; + + length = json_array_get_length (array); + for (i = 0; i < length; i++) + { + const char *element = json_array_get_string_element (array, i); + if (g_strcmp0 (element, value) == 0) + return TRUE; + } + return FALSE; +} + +static gboolean +is_valid_name (const char *name) +{ + return g_regex_match_simple ("^\\w+(\\.\\w+)*$", name, 0, 0); +} + +static char * +find_server (const char *server_name, + const char *extension_or_origin, + GError **error) +{ + const char *hosts_path_str; + g_auto(GStrv) hosts_path; + g_autoptr(JsonParser) parser = NULL; + g_autofree char *metadata_basename = NULL; + int i; + + /* Check that the we have a valid native messaging host name */ + if (!is_valid_name (server_name)) + { + g_set_error (error, XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, "invalid native messaging server name"); + return NULL; + } + + hosts_path_str = g_getenv ("XDG_DESKTOP_PORTAL_WEB_EXTENSIONS_PATH"); + if (hosts_path_str == NULL) + { + hosts_path_str = "/usr/lib/mozilla/native-messaging-hosts:/etc/opt/chrome/native-messaging-hosts:/etc/chromium/native-messaging-hosts"; + } + + hosts_path = g_strsplit (hosts_path_str, ":", -1); + parser = json_parser_new (); + metadata_basename = g_strconcat (server_name, ".json", NULL); + + for (i = 0; hosts_path[i] != NULL; i++) + { + g_autofree char *metadata_filename = NULL; + JsonObject *metadata_root; + + metadata_filename = g_build_filename (hosts_path[i], metadata_basename, NULL); + if (!g_file_test (metadata_filename, G_FILE_TEST_EXISTS)) + continue; + + if (!json_parser_load_from_file (parser, metadata_filename, error)) + return NULL; + + metadata_root = json_node_get_object (json_parser_get_root (parser)); + + /* Skip if metadata contains an unexpected name */ + if (g_strcmp0 (json_object_get_string_member (metadata_root, "name"), server_name) != 0) + continue; + + /* Skip if this is not a "stdio" type native messaging server */ + if (g_strcmp0 (json_object_get_string_member (metadata_root, "type"), "stdio") != 0) + continue; + + /* Skip if this server isn't available to the extension. Note + * that this ID is provided by the sandboxed browser, so this + * check is just to help implement its security policy. */ + if (!array_contains (json_object_get_array_member (metadata_root, "allowed_extensions"), extension_or_origin) && + !array_contains (json_object_get_array_member (metadata_root, "allowed_origins"), extension_or_origin)) + continue; + + /* Server matches: return its executable path */ + return g_strdup (json_object_get_string_member (metadata_root, "path")); + } + + g_set_error (error, XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_NOT_FOUND, "cannot find native messaging server"); + return NULL; +} + +static gboolean +handle_start (XdpDbusWebExtensions *object, + GDBusMethodInvocation *invocation, + const char *arg_session_handle, + const char *arg_name, + const char *arg_extension_or_origin, + GVariant *arg_options) +{ + Call *call = call_from_invocation (invocation); + Session *session; + WebExtensionsSession *we_session; + g_autofree char *server_path = NULL; + char *argv[] = {NULL, NULL}; + g_autoptr(GError) error = NULL; + + session = acquire_session_from_call (arg_session_handle, call); + if (!session) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Invalid session"); + return TRUE; + } + + SESSION_AUTOLOCK_UNREF (session); + we_session = (WebExtensionsSession *)session; + + if (we_session->state != WEB_EXTENSIONS_SESSION_STATE_INIT) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Session already started"); + return TRUE; + } + + server_path = find_server (arg_name, arg_extension_or_origin, &error); + if (server_path == NULL) + { + session_close(session, TRUE); + g_dbus_method_invocation_return_gerror (invocation, error); + return TRUE; + } + + argv[0] = server_path; + if (!g_spawn_async_with_pipes (NULL, /* working_directory */ + argv, + NULL, /* envp */ + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, /* child_setup */ + NULL, /* user_data */ + &we_session->child_pid, + &we_session->standard_input, + &we_session->standard_output, + NULL, /* standard_error */ + &error)) + { + we_session->child_pid = -1; + session_close(session, TRUE); + g_dbus_method_invocation_return_gerror (invocation, error); + return TRUE; + } + + we_session->child_watch_id = g_child_watch_add ( + we_session->child_pid, on_server_exited, we_session); + we_session->state = WEB_EXTENSIONS_SESSION_STATE_STARTED; + + xdp_dbus_web_extensions_complete_start (object, invocation); + + return TRUE; +} + +static gboolean +handle_get_pipes (XdpDbusWebExtensions *object, + GDBusMethodInvocation *invocation, + GUnixFDList *fd_list, + const char *arg_session_handle, + GVariant *arg_options) +{ + Call *call = call_from_invocation (invocation); + Session *session; + WebExtensionsSession *we_session; + g_autoptr(GUnixFDList) out_fd_list = NULL; + int stdin_id, stdout_id; + g_autoptr(GError) error = NULL; + + session = acquire_session_from_call (arg_session_handle, call); + if (!session) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Invalid session"); + return TRUE; + } + + SESSION_AUTOLOCK_UNREF (session); + we_session = (WebExtensionsSession *)session; + + if (we_session->state != WEB_EXTENSIONS_SESSION_STATE_STARTED) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Session not started"); + return TRUE; + } + + out_fd_list = g_unix_fd_list_new (); + stdin_id = g_unix_fd_list_append ( + out_fd_list, we_session->standard_input, &error); + if (stdin_id == -1) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Failed to append fd: %s", + error->message); + return TRUE; + } + + stdout_id = g_unix_fd_list_append ( + out_fd_list, we_session->standard_output, &error); + if (stdout_id == -1) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Failed to append fd: %s", + error->message); + return TRUE; + } + + xdp_dbus_web_extensions_complete_get_pipes (object, invocation, out_fd_list, + g_variant_new_handle (stdin_id), + g_variant_new_handle (stdout_id)); + return TRUE; +} + +static void +web_extensions_iface_init (XdpDbusWebExtensionsIface *iface) +{ + iface->handle_create_session = handle_create_session; + iface->handle_start = handle_start; + iface->handle_get_pipes = handle_get_pipes; +} + +static void +web_extensions_init (WebExtensions *web_extensions) +{ + xdp_dbus_web_extensions_set_version (XDP_DBUS_WEB_EXTENSIONS (web_extensions), 1); +} + +static void +web_extensions_class_init (WebExtensionsClass *klass) +{ +} + +GDBusInterfaceSkeleton * +web_extensions_create (GDBusConnection *connection) +{ + web_extensions = g_object_new (web_extensions_get_type (), NULL); + return G_DBUS_INTERFACE_SKELETON (web_extensions); +} diff --git a/src/web-extensions.h b/src/web-extensions.h new file mode 100644 index 000000000..9dd9c5e3b --- /dev/null +++ b/src/web-extensions.h @@ -0,0 +1,23 @@ +/* + * Copyright © 2022 Canonical Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#pragma once + +#include + +GDBusInterfaceSkeleton * web_extensions_create (GDBusConnection *connection); diff --git a/src/xdg-desktop-portal.c b/src/xdg-desktop-portal.c index fdac56ef8..3ed43d758 100644 --- a/src/xdg-desktop-portal.c +++ b/src/xdg-desktop-portal.c @@ -66,6 +66,7 @@ #include "settings.h" #include "trash.h" #include "wallpaper.h" +#include "web-extensions.h" static GMainLoop *loop = NULL; @@ -227,6 +228,7 @@ on_bus_acquired (GDBusConnection *connection, export_portal_implementation (connection, trash_create (connection)); export_portal_implementation (connection, game_mode_create (connection)); export_portal_implementation (connection, realtime_create (connection)); + export_portal_implementation (connection, web_extensions_create (connection)); impls = find_all_portal_implementations ("org.freedesktop.impl.portal.Settings"); export_portal_implementation (connection, settings_create (connection, impls)); From ba6da46255c0a4f3d91734c60f870324ea948608 Mon Sep 17 00:00:00 2001 From: James Henstridge Date: Fri, 4 Mar 2022 18:24:19 +0800 Subject: [PATCH 02/21] webextensions: add a permission prompt when starting an extension --- data/org.freedesktop.portal.WebExtensions.xml | 14 ++ src/request.c | 12 ++ src/web-extensions.c | 203 ++++++++++++++---- src/web-extensions.h | 3 +- src/xdg-desktop-portal.c | 4 +- 5 files changed, 198 insertions(+), 38 deletions(-) diff --git a/data/org.freedesktop.portal.WebExtensions.xml b/data/org.freedesktop.portal.WebExtensions.xml index 02284791f..246f6304e 100644 --- a/data/org.freedesktop.portal.WebExtensions.xml +++ b/data/org.freedesktop.portal.WebExtensions.xml @@ -60,6 +60,7 @@ @name: name of the native messaging server @extension_or_origin: extension ID or origin URI identifying the extension @options: Vardict with optional further information + @handle: Object path for the #org.freedesktop.portal.Request object representing this call Start the named native messaging server. The caller must indicate the requesting web extension (either by extension ID @@ -68,12 +69,25 @@ If the server can't be started, or invalid data is provided, the session will be closed. + + Supported keys in the @options vardict include: + + + handle_token s + + A string that will be used as the last element of the @handle. Must be a valid + object path element. See the #org.freedesktop.portal.Request documentation for + more information about the @handle. + + + --> + + + + + + + @@ -114,7 +114,8 @@ @stderr: File descriptor representing the server's stderr. Retrieve file descriptors for the native messaging server - identified by the session. + identified by the session. This method should only be called + after the Start request recveives a successful response. --> diff --git a/src/web-extensions.c b/src/web-extensions.c index c8fdc40f5..181ed2129 100644 --- a/src/web-extensions.c +++ b/src/web-extensions.c @@ -103,39 +103,39 @@ web_extensions_session_init (WebExtensionsSession *session) static void web_extensions_session_close (Session *session) { - WebExtensionsSession *we_session = (WebExtensionsSession *)session; + WebExtensionsSession *web_extensions_session = (WebExtensionsSession *)session; - if (we_session->state == WEB_EXTENSIONS_SESSION_STATE_CLOSED) return; + if (web_extensions_session->state == WEB_EXTENSIONS_SESSION_STATE_CLOSED) return; - we_session->state = WEB_EXTENSIONS_SESSION_STATE_CLOSED; - if (we_session->child_watch_id != 0) + web_extensions_session->state = WEB_EXTENSIONS_SESSION_STATE_CLOSED; + if (web_extensions_session->child_watch_id != 0) { - g_source_remove (we_session->child_watch_id); - we_session->child_watch_id = 0; + g_source_remove (web_extensions_session->child_watch_id); + web_extensions_session->child_watch_id = 0; } - if (we_session->child_pid > 0) + if (web_extensions_session->child_pid > 0) { - kill (we_session->child_pid, SIGTERM); - waitpid (we_session->child_pid, NULL, 0); - g_spawn_close_pid (we_session->child_pid); - we_session->child_pid = -1; + kill (web_extensions_session->child_pid, SIGTERM); + waitpid (web_extensions_session->child_pid, NULL, 0); + g_spawn_close_pid (web_extensions_session->child_pid); + web_extensions_session->child_pid = -1; } - if (we_session->standard_input >= 0) + if (web_extensions_session->standard_input >= 0) { - close (we_session->standard_input); - we_session->standard_input = -1; + close (web_extensions_session->standard_input); + web_extensions_session->standard_input = -1; } - if (we_session->standard_output >= 0) + if (web_extensions_session->standard_output >= 0) { - close (we_session->standard_output); - we_session->standard_output = -1; + close (web_extensions_session->standard_output); + web_extensions_session->standard_output = -1; } - if (we_session->standard_error >= 0) + if (web_extensions_session->standard_error >= 0) { - close (we_session->standard_error); - we_session->standard_error = -1; + close (web_extensions_session->standard_error); + web_extensions_session->standard_error = -1; } } @@ -219,11 +219,11 @@ on_server_exited (GPid pid, gpointer user_data) { Session *session = user_data; - WebExtensionsSession *we_session = (WebExtensionsSession *)session; + WebExtensionsSession *web_extensions_session = (WebExtensionsSession *)session; SESSION_AUTOLOCK (session); - we_session->child_pid = -1; - we_session->child_watch_id = 0; + web_extensions_session->child_pid = -1; + web_extensions_session->child_watch_id = 0; session_close (session, TRUE); } @@ -249,6 +249,14 @@ array_contains (JsonArray *array, static gboolean is_valid_name (const char *name) { + /* This regexp comes from the Mozilla documentation on valid native + messaging server names: + + https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#native_messaging_manifests + + That is, one or more dot-separated groups composed of + alphanumeric characters and underscores. + */ return g_regex_match_simple ("^\\w+(\\.\\w+)*$", name, 0, 0); } @@ -293,11 +301,11 @@ get_manifest_search_path (void) static char * find_server (const char *server_name, const char *extension_or_origin, - char **server_description, - char **json_manifest, + char **out_server_description, + char **out_json_manifest, GError **error) { - g_auto(GStrv) search_path; + g_auto(GStrv) search_path = NULL; g_autoptr(JsonParser) parser = NULL; g_autofree char *metadata_basename = NULL; int i; @@ -305,7 +313,10 @@ find_server (const char *server_name, /* Check that the we have a valid native messaging host name */ if (!is_valid_name (server_name)) { - g_set_error (error, XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, "invalid native messaging server name"); + g_set_error (error, + XDG_DESKTOP_PORTAL_ERROR, + XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, + "Invalid native messaging server name"); return NULL; } @@ -316,14 +327,19 @@ find_server (const char *server_name, for (i = 0; search_path[i] != NULL; i++) { g_autofree char *metadata_filename = NULL; + g_autoptr(GError) load_error = NULL; JsonObject *metadata_root; metadata_filename = g_build_filename (search_path[i], metadata_basename, NULL); - if (!g_file_test (metadata_filename, G_FILE_TEST_EXISTS)) - continue; - - if (!json_parser_load_from_file (parser, metadata_filename, error)) - return NULL; + if (!json_parser_load_from_file (parser, metadata_filename, &load_error)) + { + /* If the file doesn't exist, continue searching. Error out + on anything else. */ + if (g_error_matches (load_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) + continue; + g_propagate_error (error, g_steal_pointer (&load_error)); + return NULL; + } metadata_root = json_node_get_object (json_parser_get_root (parser)); @@ -343,14 +359,17 @@ find_server (const char *server_name, continue; /* Server matches: return its executable path and description */ - if (server_description != NULL) - *server_description = g_strdup (json_object_get_string_member (metadata_root, "description")); - if (json_manifest != NULL) - *json_manifest = json_to_string (json_parser_get_root (parser), FALSE); + if (out_server_description != NULL) + *out_server_description = g_strdup (json_object_get_string_member (metadata_root, "description")); + if (out_json_manifest != NULL) + *out_json_manifest = json_to_string (json_parser_get_root (parser), FALSE); return g_strdup (json_object_get_string_member (metadata_root, "path")); } - g_set_error (error, XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_NOT_FOUND, "cannot find native messaging server"); + g_set_error (error, + XDG_DESKTOP_PORTAL_ERROR, + XDG_DESKTOP_PORTAL_ERROR_NOT_FOUND, + "Could not find native messaging server"); return NULL; } @@ -363,7 +382,7 @@ handle_get_manifest (XdpDbusWebExtensions *object, { Call *call = call_from_invocation (invocation); Session *session; - WebExtensionsSession *we_session; + WebExtensionsSession *web_extensions_session; g_autofree char *server_path = NULL; g_autofree char *json_manifest = NULL; g_autoptr(GError) error = NULL; @@ -379,9 +398,9 @@ handle_get_manifest (XdpDbusWebExtensions *object, } SESSION_AUTOLOCK_UNREF (session); - we_session = (WebExtensionsSession *)session; + web_extensions_session = (WebExtensionsSession *)session; - if (we_session->state != WEB_EXTENSIONS_SESSION_STATE_INIT) + if (web_extensions_session->state != WEB_EXTENSIONS_SESSION_STATE_INIT) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, @@ -410,7 +429,7 @@ handle_start_in_thread (GTask *task, { Request *request = (Request *)task_data; Session *session; - WebExtensionsSession *we_session; + WebExtensionsSession *web_extensions_session; const char *arg_name; const char *arg_extension_or_origin; const char *app_id; @@ -427,9 +446,9 @@ handle_start_in_thread (GTask *task, session = g_object_get_data (G_OBJECT (request), "session"); SESSION_AUTOLOCK_UNREF (g_object_ref (session)); g_object_set_data (G_OBJECT (request), "session", NULL); - we_session = (WebExtensionsSession *)session; + web_extensions_session = (WebExtensionsSession *)session; - if (!request->exported || we_session->state != WEB_EXTENSIONS_SESSION_STATE_STARTING) + if (!request->exported || web_extensions_session->state != WEB_EXTENSIONS_SESSION_STATE_STARTING) goto out; arg_name = g_object_get_data (G_OBJECT (request), "name"); @@ -439,6 +458,8 @@ handle_start_in_thread (GTask *task, if (server_path == NULL) { g_warning ("Could not find WebExtensions backend: %s", error->message); + fflush(stderr); + fflush(stdout); goto out; } @@ -465,7 +486,6 @@ handle_start_in_thread (GTask *task, display_name = info ? g_app_info_get_display_name (info) : app_id; title = g_strdup_printf (_("Allow %s to start WebExtension backend?"), display_name); subtitle = g_strdup_printf (_("%s is requesting to launch \"%s\" (%s)."), display_name, server_description, arg_name); - /* This UI does not currently exist */ body = g_strdup (_("This permission can be changed at any time from the privacy settings.")); g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT); @@ -493,7 +513,9 @@ handle_start_in_thread (GTask *task, set_permission_sync (app_id, PERMISSION_TABLE, arg_name, allowed ? PERMISSION_YES : PERMISSION_NO); } else - allowed = permission == PERMISSION_YES ? TRUE : FALSE; + { + allowed = permission == PERMISSION_YES ? TRUE : FALSE; + } if (!allowed) { @@ -508,20 +530,22 @@ handle_start_in_thread (GTask *task, G_SPAWN_DO_NOT_REAP_CHILD, NULL, /* child_setup */ NULL, /* user_data */ - &we_session->child_pid, - &we_session->standard_input, - &we_session->standard_output, - &we_session->standard_error, + &web_extensions_session->child_pid, + &web_extensions_session->standard_input, + &web_extensions_session->standard_output, + &web_extensions_session->standard_error, &error)) { - we_session->child_pid = -1; + web_extensions_session->child_pid = -1; goto out; } - we_session->child_watch_id = g_child_watch_add_full ( - G_PRIORITY_DEFAULT, we_session->child_pid, on_server_exited, - g_object_ref (we_session), g_object_unref); - we_session->state = WEB_EXTENSIONS_SESSION_STATE_STARTED; + web_extensions_session->child_watch_id = g_child_watch_add_full (G_PRIORITY_DEFAULT, + web_extensions_session->child_pid, + on_server_exited, + g_object_ref (web_extensions_session), + g_object_unref); + web_extensions_session->state = WEB_EXTENSIONS_SESSION_STATE_STARTED; response = XDG_DESKTOP_PORTAL_RESPONSE_SUCCESS; @@ -551,7 +575,7 @@ handle_start (XdpDbusWebExtensions *object, { Request *request = request_from_invocation (invocation); Session *session; - WebExtensionsSession *we_session; + WebExtensionsSession *web_extensions_session; g_autoptr(GTask) task = NULL; REQUEST_AUTOLOCK (request); @@ -567,9 +591,9 @@ handle_start (XdpDbusWebExtensions *object, } SESSION_AUTOLOCK_UNREF (session); - we_session = (WebExtensionsSession *)session; + web_extensions_session = (WebExtensionsSession *)session; - if (we_session->state != WEB_EXTENSIONS_SESSION_STATE_INIT) + if (web_extensions_session->state != WEB_EXTENSIONS_SESSION_STATE_INIT) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, @@ -578,7 +602,7 @@ handle_start (XdpDbusWebExtensions *object, return TRUE; } - we_session->state = WEB_EXTENSIONS_SESSION_STATE_STARTING; + web_extensions_session->state = WEB_EXTENSIONS_SESSION_STATE_STARTING; g_object_set_data_full (G_OBJECT (request), "session", g_object_ref (session), g_object_unref); g_object_set_data_full (G_OBJECT (request), "name", g_strdup (arg_name), g_free); g_object_set_data_full (G_OBJECT (request), "extension-or-origin", g_strdup (arg_extension_or_origin), g_free); @@ -603,7 +627,8 @@ handle_get_pipes (XdpDbusWebExtensions *object, { Call *call = call_from_invocation (invocation); Session *session; - WebExtensionsSession *we_session; + WebExtensionsSession *web_extensions_session; + int fds[3]; g_autoptr(GUnixFDList) out_fd_list = NULL; session = acquire_session_from_call (arg_session_handle, call); @@ -617,9 +642,9 @@ handle_get_pipes (XdpDbusWebExtensions *object, } SESSION_AUTOLOCK_UNREF (session); - we_session = (WebExtensionsSession *)session; + web_extensions_session = (WebExtensionsSession *)session; - if (we_session->state != WEB_EXTENSIONS_SESSION_STATE_STARTED) + if (web_extensions_session->state != WEB_EXTENSIONS_SESSION_STATE_STARTED) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, @@ -628,9 +653,9 @@ handle_get_pipes (XdpDbusWebExtensions *object, return TRUE; } - if (we_session->standard_input < 0 || - we_session->standard_output < 0 || - we_session->standard_error < 0) + if (web_extensions_session->standard_input < 0 || + web_extensions_session->standard_output < 0 || + web_extensions_session->standard_error < 0) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, @@ -639,16 +664,14 @@ handle_get_pipes (XdpDbusWebExtensions *object, return TRUE; } - int fds[3] = { - we_session->standard_input, - we_session->standard_output, - we_session->standard_error, - }; + fds[0] = web_extensions_session->standard_input; + fds[1] = web_extensions_session->standard_output; + fds[2] = web_extensions_session->standard_error; out_fd_list = g_unix_fd_list_new_from_array (fds, G_N_ELEMENTS (fds)); /* out_fd_list now owns the file descriptors */ - we_session->standard_input = -1; - we_session->standard_output = -1; - we_session->standard_error = -1; + web_extensions_session->standard_input = -1; + web_extensions_session->standard_output = -1; + web_extensions_session->standard_error = -1; xdp_dbus_web_extensions_complete_get_pipes (object, invocation, out_fd_list, g_variant_new_handle (0), diff --git a/tests/test-portals.c b/tests/test-portals.c index b45286b88..27aebd281 100644 --- a/tests/test-portals.c +++ b/tests/test-portals.c @@ -607,6 +607,7 @@ main (int argc, char **argv) g_test_add_func ("/portal/notification/bad-button", test_notification_bad_button); g_test_add_func ("/portal/webextensions/basic", test_web_extensions_basic); + g_test_add_func ("/portal/webextensions/bad-name", test_web_extensions_bad_name); #endif global_setup (); diff --git a/tests/web-extensions.c b/tests/web-extensions.c index 66231248f..761f6d5ef 100644 --- a/tests/web-extensions.c +++ b/tests/web-extensions.c @@ -1,6 +1,7 @@ #include #include "web-extensions.h" +#include "xdp-utils.h" #include #include @@ -428,6 +429,7 @@ cancel_call (gpointer data) typedef struct { GCancellable *cancellable; char *session_handle; + const char *server_name; } TestData; static void @@ -471,7 +473,10 @@ get_pipes_cb (GObject *object, GAsyncResult *result, gpointer data) close (stdout_fileno); close (stderr_fileno); - close_session (test_data->session_handle, test_data->cancellable, close_session_cb, test_data); + close_session (test_data->session_handle, + test_data->cancellable, + close_session_cb, + test_data); } static void @@ -485,7 +490,10 @@ start_cb (GObject *object, GAsyncResult *result, gpointer data) g_assert_no_error (error); g_assert_true (ret); - get_pipes (test_data->session_handle, test_data->cancellable, get_pipes_cb, test_data); + get_pipes (test_data->session_handle, + test_data->cancellable, + get_pipes_cb, + test_data); } @@ -500,7 +508,12 @@ get_manifest_cb (GObject *object, GAsyncResult *result, gpointer data) g_assert_no_error (error); g_assert_cmpstr (json_manifest, ==, "{\"name\":\"org.example.testing\",\"description\":\"Test native messaging host\",\"path\":\"/bin/cat\",\"type\":\"stdio\",\"allowed_extensions\":[\"some-extension@example.org\"]}"); - start (test_data->session_handle, "org.example.testing", "some-extension@example.org", test_data->cancellable, start_cb, test_data); + start (test_data->session_handle, + "org.example.testing", + "some-extension@example.org", + test_data->cancellable, + start_cb, + test_data); } static void @@ -513,7 +526,12 @@ create_session_cb (GObject *object, GAsyncResult *result, gpointer data) g_assert_no_error (error); g_assert_nonnull (test_data->session_handle); - get_manifest (test_data->session_handle, "org.example.testing", "some-extension@example.org", test_data->cancellable, get_manifest_cb, test_data); + get_manifest (test_data->session_handle, + "org.example.testing", + "some-extension@example.org", + test_data->cancellable, + get_manifest_cb, + test_data); } void @@ -532,3 +550,61 @@ test_web_extensions_basic (void) g_main_context_iteration (NULL, TRUE); g_free (test_data.session_handle); } + +static void +start_bad_name_cb (GObject *object, GAsyncResult *result, gpointer data) +{ + g_autoptr(GError) error = NULL; + gboolean ret; + + ret = start_finish (result, &error); + g_assert_false (ret); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + + got_info++; + g_main_context_wakeup (NULL); +} + +static void +create_session_bad_name_cb (GObject *object, GAsyncResult *result, gpointer data) +{ + TestData *test_data = data; + g_autoptr(GError) error = NULL; + + test_data->session_handle = create_session_finish (result, &error); + g_assert_no_error (error); + g_assert_nonnull (test_data->session_handle); + + start (test_data->session_handle, + test_data->server_name, + "some-extension@example.org", + test_data->cancellable, + start_bad_name_cb, + test_data); +} + +void +test_web_extensions_bad_name (void) +{ + const char *server_name[] = { + "no-dashes", + "../foo", + "no_trailing_dot.", + }; + int i; + + for (i = 0; i < G_N_ELEMENTS (server_name); i++) + { + g_autoptr(GCancellable) cancellable = NULL; + TestData test_data = { cancellable, NULL, server_name[i] }; + + got_info = 0; + set_web_extensions_permissions ("yes"); + create_session (cancellable, create_session_bad_name_cb, &test_data); + + g_timeout_add (100, cancel_call, cancellable); + while (!got_info) + g_main_context_iteration (NULL, TRUE); + g_free (test_data.session_handle); + } +} diff --git a/tests/web-extensions.h b/tests/web-extensions.h index 5b4a0a927..0090184b9 100644 --- a/tests/web-extensions.h +++ b/tests/web-extensions.h @@ -1 +1,2 @@ void test_web_extensions_basic (void); +void test_web_extensions_bad_name (void); From 2d10f539e7f6b050e6c3de62b884694469cdad8b Mon Sep 17 00:00:00 2001 From: James Henstridge Date: Sat, 4 Nov 2023 14:48:17 +0200 Subject: [PATCH 15/21] web-extensions: send a SIGKILL rather than SIGTERM when closing the session. At this point, the browser has signalled the native messaging host to exit by closing its standard input. If the process is still running when the browser closes the session, then it is not responding. --- src/web-extensions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web-extensions.c b/src/web-extensions.c index 181ed2129..7acffb109 100644 --- a/src/web-extensions.c +++ b/src/web-extensions.c @@ -116,7 +116,7 @@ web_extensions_session_close (Session *session) if (web_extensions_session->child_pid > 0) { - kill (web_extensions_session->child_pid, SIGTERM); + kill (web_extensions_session->child_pid, SIGKILL); waitpid (web_extensions_session->child_pid, NULL, 0); g_spawn_close_pid (web_extensions_session->child_pid); web_extensions_session->child_pid = -1; From b6febbc98722c33c6a45713552b9c636f4542566 Mon Sep 17 00:00:00 2001 From: James Henstridge Date: Tue, 7 Nov 2023 09:07:14 +0200 Subject: [PATCH 16/21] webextensions: add a mode option to CreateSession that determines whether to provide Chromium-ish or Mozilla-ish behaviour This affects the following behaviour: 1. the search path for manifest files. 2. whether to match the "allowed_origins" or "allowed_extensions" key. 3. the command line arguments passed to the native messaging server. The tests have also been updated to use a shell script as the test native messaging server. Previously we just used /bin/cat, since it would stick around until its standard input was closed. This broke once we started passing arguments to the server. So now use a shell script that calls cat with no arguments. --- data/org.freedesktop.portal.WebExtensions.xml | 8 ++ src/web-extensions.c | 136 +++++++++++++----- tests/native-messaging-hosts/meson.build | 32 ++++- ...sting.json => org.example.testing.json.in} | 2 +- tests/native-messaging-hosts/server.sh | 3 + tests/test-portals.c | 2 +- tests/web-extensions.c | 8 +- 7 files changed, 147 insertions(+), 44 deletions(-) rename tests/native-messaging-hosts/{org.example.testing.json => org.example.testing.json.in} (86%) create mode 100755 tests/native-messaging-hosts/server.sh diff --git a/data/org.freedesktop.portal.WebExtensions.xml b/data/org.freedesktop.portal.WebExtensions.xml index e4ce12c16..8d1983609 100644 --- a/data/org.freedesktop.portal.WebExtensions.xml +++ b/data/org.freedesktop.portal.WebExtensions.xml @@ -40,6 +40,14 @@ Supported keys in the @options vardict include: + + mode s + + A string indicating which behaviour the portal should + use when locating and starting native messaging + servers. Valid values are "mozilla" and "chromium". + + session_handle_token s diff --git a/src/web-extensions.c b/src/web-extensions.c index 7acffb109..89f3ba059 100644 --- a/src/web-extensions.c +++ b/src/web-extensions.c @@ -58,6 +58,12 @@ G_DEFINE_TYPE_WITH_CODE (WebExtensions, web_extensions, XDP_DBUS_TYPE_WEB_EXTENS G_IMPLEMENT_INTERFACE (XDP_DBUS_TYPE_WEB_EXTENSIONS, web_extensions_iface_init)); +typedef enum _WebExtensionsSessionMode +{ + WEB_EXTENSIONS_SESSION_MODE_CHROMIUM, + WEB_EXTENSIONS_SESSION_MODE_MOZILLA, +} WebExtensionsSessionMode; + typedef enum _WebExtensionsSessionState { WEB_EXTENSIONS_SESSION_STATE_INIT, @@ -70,6 +76,7 @@ typedef struct _WebExtensionsSession { Session parent; + WebExtensionsSessionMode mode; WebExtensionsSessionState state; GPid child_pid; @@ -168,8 +175,28 @@ web_extensions_session_new (GVariant *options, GError **error) { Session *session; + WebExtensionsSession *web_extensions_session; + WebExtensionsSessionMode mode = WEB_EXTENSIONS_SESSION_MODE_CHROMIUM; + const char *mode_str = NULL; const char *session_token; + g_variant_lookup (options, "mode", "&s", &mode_str); + if (mode_str != NULL) + { + if (!strcmp(mode_str, "chromium")) + mode = WEB_EXTENSIONS_SESSION_MODE_CHROMIUM; + else if (!strcmp(mode_str, "mozilla")) + mode = WEB_EXTENSIONS_SESSION_MODE_MOZILLA; + else + { + g_set_error (error, + XDG_DESKTOP_PORTAL_ERROR, + XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, + "Invalid mode"); + return NULL; + } + } + session_token = lookup_session_token (options); session = g_initable_new (web_extensions_session_get_type (), NULL, error, "sender", call->sender, @@ -181,7 +208,9 @@ web_extensions_session_new (GVariant *options, if (session) g_debug ("webextensions session owned by '%s' created", session->sender); - return (WebExtensionsSession *)session; + web_extensions_session = (WebExtensionsSession *)session; + web_extensions_session->mode = mode; + return web_extensions_session; } static gboolean @@ -261,7 +290,7 @@ is_valid_name (const char *name) } static GStrv -get_manifest_search_path (void) +get_manifest_search_path (WebExtensionsSessionMode mode) { const char *hosts_path_str; g_autoptr(GPtrArray) search_path = NULL; @@ -270,38 +299,48 @@ get_manifest_search_path (void) if (hosts_path_str != NULL) return g_strsplit (hosts_path_str, ":", -1); - /* By default, use the native messaging search paths of Firefox, - * Chrome, and Chromium, as documented here: - * - * https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#manifest_location - * https://developer.chrome.com/docs/apps/nativeMessaging/#native-messaging-host-location - */ - search_path = g_ptr_array_new_with_free_func (g_free); - /* Add per-user directories */ - g_ptr_array_add (search_path, g_build_filename (g_get_home_dir (), ".mozilla", "native-messaging-hosts", NULL)); - g_ptr_array_add (search_path, g_build_filename (g_get_user_config_dir (), "google-chrome", "NativeMessagingHosts", NULL)); - g_ptr_array_add (search_path, g_build_filename (g_get_user_config_dir (), "chromium", "NativeMessagingHosts", NULL)); - - /* Add system wide directories */ - g_ptr_array_add (search_path, g_strdup ("/usr/lib/mozilla/native-messaging-hosts")); - g_ptr_array_add (search_path, g_strdup ("/usr/lib64/mozilla/native-messaging-hosts")); - g_ptr_array_add (search_path, g_strdup ("/etc/opt/chrome/native-messaging-hosts")); - g_ptr_array_add (search_path, g_strdup ("/etc/chromium/native-messaging-hosts")); - - /* And the same for xdg-desktop-portal's configured prefix */ - g_ptr_array_add (search_path, g_strdup (LIBDIR "mozilla/native-messaging-hosts")); - g_ptr_array_add (search_path, g_strdup (SYSCONFDIR "opt/chrome/native-messaging-hosts")); - g_ptr_array_add (search_path, g_strdup (SYSCONFDIR "chromium/native-messaging-hosts")); + switch (mode) + { + case WEB_EXTENSIONS_SESSION_MODE_CHROMIUM: + /* Chrome and Chromium search paths documented here: + * https://developer.chrome.com/docs/apps/nativeMessaging/#native-messaging-host-location + */ + /* Add per-user directories */ + g_ptr_array_add (search_path, g_build_filename (g_get_user_config_dir (), "google-chrome", "NativeMessagingHosts", NULL)); + g_ptr_array_add (search_path, g_build_filename (g_get_user_config_dir (), "chromium", "NativeMessagingHosts", NULL)); + /* Add system wide directories */ + g_ptr_array_add (search_path, g_strdup ("/etc/opt/chrome/native-messaging-hosts")); + g_ptr_array_add (search_path, g_strdup ("/etc/chromium/native-messaging-hosts")); + /* And the same for xdg-desktop-portal's configured prefix */ + g_ptr_array_add (search_path, g_strdup (SYSCONFDIR "opt/chrome/native-messaging-hosts")); + g_ptr_array_add (search_path, g_strdup (SYSCONFDIR "chromium/native-messaging-hosts")); + break; + + case WEB_EXTENSIONS_SESSION_MODE_MOZILLA: + /* Firefox search paths documented here: + * https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#manifest_location + */ + /* Add per-user directories */ + g_ptr_array_add (search_path, g_build_filename (g_get_home_dir (), ".mozilla", "native-messaging-hosts", NULL)); + /* Add system wide directories */ + g_ptr_array_add (search_path, g_strdup ("/usr/lib/mozilla/native-messaging-hosts")); + g_ptr_array_add (search_path, g_strdup ("/usr/lib64/mozilla/native-messaging-hosts")); + /* And the same for xdg-desktop-portal's configured prefix */ + g_ptr_array_add (search_path, g_strdup (LIBDIR "mozilla/native-messaging-hosts")); + break; + } g_ptr_array_add (search_path, NULL); return (GStrv)g_ptr_array_free (g_steal_pointer (&search_path), FALSE); } static char * -find_server (const char *server_name, +find_server (WebExtensionsSessionMode mode, + const char *server_name, const char *extension_or_origin, char **out_server_description, + char **out_manifest_filename, char **out_json_manifest, GError **error) { @@ -320,7 +359,7 @@ find_server (const char *server_name, return NULL; } - search_path = get_manifest_search_path (); + search_path = get_manifest_search_path (mode); parser = json_parser_new (); metadata_basename = g_strconcat (server_name, ".json", NULL); @@ -354,15 +393,25 @@ find_server (const char *server_name, /* Skip if this server isn't available to the extension. Note * that this ID is provided by the sandboxed browser, so this * check is just to help implement its security policy. */ - if (!array_contains (json_object_get_array_member (metadata_root, "allowed_extensions"), extension_or_origin) && - !array_contains (json_object_get_array_member (metadata_root, "allowed_origins"), extension_or_origin)) - continue; + switch (mode) + { + case WEB_EXTENSIONS_SESSION_MODE_CHROMIUM: + if (!array_contains (json_object_get_array_member (metadata_root, "allowed_origins"), extension_or_origin)) + continue; + break; + case WEB_EXTENSIONS_SESSION_MODE_MOZILLA: + if (!array_contains (json_object_get_array_member (metadata_root, "allowed_extensions"), extension_or_origin)) + continue; + break; + } /* Server matches: return its executable path and description */ if (out_server_description != NULL) - *out_server_description = g_strdup (json_object_get_string_member (metadata_root, "description")); + *out_server_description = g_strdup (json_object_get_string_member (metadata_root, "description")); + if (out_manifest_filename != NULL) + *out_manifest_filename = g_strdup (metadata_filename); if (out_json_manifest != NULL) - *out_json_manifest = json_to_string (json_parser_get_root (parser), FALSE); + *out_json_manifest = json_to_string (json_parser_get_root (parser), FALSE); return g_strdup (json_object_get_string_member (metadata_root, "path")); } @@ -409,8 +458,9 @@ handle_get_manifest (XdpDbusWebExtensions *object, return TRUE; } - server_path = find_server (arg_name, arg_extension_or_origin, - NULL, &json_manifest, &error); + server_path = find_server (web_extensions_session->mode, + arg_name, arg_extension_or_origin, + NULL, NULL, &json_manifest, &error); if (!server_path) { g_dbus_method_invocation_return_gerror (invocation, error); @@ -431,15 +481,16 @@ handle_start_in_thread (GTask *task, Session *session; WebExtensionsSession *web_extensions_session; const char *arg_name; - const char *arg_extension_or_origin; + char *arg_extension_or_origin; const char *app_id; g_autofree char *server_path = NULL; g_autofree char *server_description = NULL; + g_autofree char *manifest_filename = NULL; guint response = XDG_DESKTOP_PORTAL_RESPONSE_OTHER; gboolean should_close_session; Permission permission; gboolean allowed; - char *argv[] = {NULL, NULL}; + char *argv[] = {NULL, NULL, NULL, NULL}; g_autoptr(GError) error = NULL; REQUEST_AUTOLOCK (request); @@ -454,7 +505,10 @@ handle_start_in_thread (GTask *task, arg_name = g_object_get_data (G_OBJECT (request), "name"); arg_extension_or_origin = g_object_get_data (G_OBJECT (request), "extension-or-origin"); - server_path = find_server (arg_name, arg_extension_or_origin, &server_description, NULL, &error); + server_path = find_server (web_extensions_session->mode, + arg_name, arg_extension_or_origin, + &server_description, &manifest_filename, NULL, + &error); if (server_path == NULL) { g_warning ("Could not find WebExtensions backend: %s", error->message); @@ -524,6 +578,16 @@ handle_start_in_thread (GTask *task, } argv[0] = server_path; + switch (web_extensions_session->mode) + { + case WEB_EXTENSIONS_SESSION_MODE_CHROMIUM: + argv[1] = arg_extension_or_origin; + break; + case WEB_EXTENSIONS_SESSION_MODE_MOZILLA: + argv[1] = manifest_filename; + argv[2] = arg_extension_or_origin; + break; + } if (!g_spawn_async_with_pipes (NULL, /* working_directory */ argv, NULL, /* envp */ diff --git a/tests/native-messaging-hosts/meson.build b/tests/native-messaging-hosts/meson.build index 726f54fe2..5ee790531 100644 --- a/tests/native-messaging-hosts/meson.build +++ b/tests/native-messaging-hosts/meson.build @@ -1,6 +1,28 @@ -test_portal = configure_file(input: 'org.example.testing.json', - output: '@PLAINNAME@', - copy: true, - install: enable_installed_tests, - install_dir: installed_tests_dir / 'native-messaging-hosts', +configure_file(input: 'server.sh', + output: '@PLAINNAME@', + copy: true, + install_mode: 'rwxr-xr-x', + install: enable_installed_tests, + install_dir: installed_tests_dir / 'native-messaging-hosts', +) + +config = configuration_data() +config.set('server_path', meson.current_build_dir() / 'server.sh') +configure_file(input: 'org.example.testing.json.in', + output: '@BASENAME@', + configuration: config, ) + +# create a second version to be installed +if enable_installed_tests + config = configuration_data() + config.set('server_path', installed_tests_dir / 'native-messaging-hosts/server.sh') + configure_file(input: 'org.example.testing.json.in', + output: 'installed-org.example.testing.json', + configuration: config, + ) + install_data(meson.current_build_dir() / 'installed-org.example.testing.json', + rename: ['org.example.testing.json'], + install_dir: installed_tests_dir / 'native-messaging-hosts', + ) +endif diff --git a/tests/native-messaging-hosts/org.example.testing.json b/tests/native-messaging-hosts/org.example.testing.json.in similarity index 86% rename from tests/native-messaging-hosts/org.example.testing.json rename to tests/native-messaging-hosts/org.example.testing.json.in index 93b2f11e4..58cd31029 100644 --- a/tests/native-messaging-hosts/org.example.testing.json +++ b/tests/native-messaging-hosts/org.example.testing.json.in @@ -1,7 +1,7 @@ { "name": "org.example.testing", "description": "Test native messaging host", - "path": "/bin/cat", + "path": "@server_path@", "type": "stdio", "allowed_extensions": [ "some-extension@example.org" diff --git a/tests/native-messaging-hosts/server.sh b/tests/native-messaging-hosts/server.sh new file mode 100755 index 000000000..b0f3e402d --- /dev/null +++ b/tests/native-messaging-hosts/server.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec cat diff --git a/tests/test-portals.c b/tests/test-portals.c index 27aebd281..1cb880773 100644 --- a/tests/test-portals.c +++ b/tests/test-portals.c @@ -280,7 +280,7 @@ global_setup (void) NULL); portal_dir = g_test_build_filename (G_TEST_BUILT, "portals", "test", NULL); - web_extensions_dir = g_test_build_filename (G_TEST_DIST, "native-messaging-hosts", NULL); + web_extensions_dir = g_test_build_filename (G_TEST_BUILT, "native-messaging-hosts", NULL); g_clear_object (&launcher); launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE); diff --git a/tests/web-extensions.c b/tests/web-extensions.c index 761f6d5ef..276e76841 100644 --- a/tests/web-extensions.c +++ b/tests/web-extensions.c @@ -51,6 +51,7 @@ create_session (GCancellable *cancellable, session_token = g_strdup_printf ("portal%d", g_random_int_range (0, G_MAXINT)); g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&options, "{sv}", "mode", g_variant_new_string ("mozilla")); g_variant_builder_add (&options, "{sv}", "session_handle_token", g_variant_new_string (session_token)); g_dbus_connection_call (session_bus, "org.freedesktop.portal.Desktop", @@ -503,10 +504,15 @@ get_manifest_cb (GObject *object, GAsyncResult *result, gpointer data) TestData *test_data = data; g_autoptr(GError) error = NULL; g_autofree char *json_manifest = NULL; + g_autofree char *server_path = NULL; + g_autofree char *expected = NULL; + + server_path = g_test_build_filename (G_TEST_BUILT, "native-messaging-hosts", "server.sh", NULL); + expected = g_strdup_printf ("{\"name\":\"org.example.testing\",\"description\":\"Test native messaging host\",\"path\":\"%s\",\"type\":\"stdio\",\"allowed_extensions\":[\"some-extension@example.org\"]}", server_path); json_manifest = get_manifest_finish (result, &error); g_assert_no_error (error); - g_assert_cmpstr (json_manifest, ==, "{\"name\":\"org.example.testing\",\"description\":\"Test native messaging host\",\"path\":\"/bin/cat\",\"type\":\"stdio\",\"allowed_extensions\":[\"some-extension@example.org\"]}"); + g_assert_cmpstr (json_manifest, ==, expected); start (test_data->session_handle, "org.example.testing", From bb0acb638d21e97cdfaf76517d2d3a6899ae1210 Mon Sep 17 00:00:00 2001 From: James Henstridge Date: Tue, 7 Nov 2023 16:16:20 +0200 Subject: [PATCH 17/21] webextensions: default to mozilla behaviour While Chromium based browsers are more common, we only have an implementation for Firefox for now, which is not setting this option. --- data/org.freedesktop.portal.WebExtensions.xml | 3 ++- src/web-extensions.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/data/org.freedesktop.portal.WebExtensions.xml b/data/org.freedesktop.portal.WebExtensions.xml index 8d1983609..ef832c020 100644 --- a/data/org.freedesktop.portal.WebExtensions.xml +++ b/data/org.freedesktop.portal.WebExtensions.xml @@ -45,7 +45,8 @@ A string indicating which behaviour the portal should use when locating and starting native messaging - servers. Valid values are "mozilla" and "chromium". + servers. Valid values are "mozilla" and "chromium". By + default, mozilla behaviour is used. diff --git a/src/web-extensions.c b/src/web-extensions.c index 89f3ba059..743628047 100644 --- a/src/web-extensions.c +++ b/src/web-extensions.c @@ -176,7 +176,7 @@ web_extensions_session_new (GVariant *options, { Session *session; WebExtensionsSession *web_extensions_session; - WebExtensionsSessionMode mode = WEB_EXTENSIONS_SESSION_MODE_CHROMIUM; + WebExtensionsSessionMode mode = WEB_EXTENSIONS_SESSION_MODE_MOZILLA; const char *mode_str = NULL; const char *session_token; From 31df14c041f83efb5a3ae92c674d33b1887eda22 Mon Sep 17 00:00:00 2001 From: James Henstridge Date: Tue, 7 Nov 2023 16:37:34 +0200 Subject: [PATCH 18/21] webextensions: reject manifest files with non-absolute paths for the executable --- src/web-extensions.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/web-extensions.c b/src/web-extensions.c index 743628047..107a2e330 100644 --- a/src/web-extensions.c +++ b/src/web-extensions.c @@ -368,6 +368,7 @@ find_server (WebExtensionsSessionMode mode, g_autofree char *metadata_filename = NULL; g_autoptr(GError) load_error = NULL; JsonObject *metadata_root; + const char *server_path; metadata_filename = g_build_filename (search_path[i], metadata_basename, NULL); if (!json_parser_load_from_file (parser, metadata_filename, &load_error)) @@ -405,14 +406,24 @@ find_server (WebExtensionsSessionMode mode, break; } + server_path = json_object_get_string_member (metadata_root, "path"); + if (!g_path_is_absolute (server_path)) + { + g_set_error (error, + XDG_DESKTOP_PORTAL_ERROR, + XDG_DESKTOP_PORTAL_ERROR_FAILED, + "Native messaging host path is not absolute"); + return NULL; + } + /* Server matches: return its executable path and description */ if (out_server_description != NULL) *out_server_description = g_strdup (json_object_get_string_member (metadata_root, "description")); if (out_manifest_filename != NULL) - *out_manifest_filename = g_strdup (metadata_filename); + *out_manifest_filename = g_steal_pointer (&metadata_filename); if (out_json_manifest != NULL) *out_json_manifest = json_to_string (json_parser_get_root (parser), FALSE); - return g_strdup (json_object_get_string_member (metadata_root, "path")); + return g_strdup (server_path); } g_set_error (error, From 4f25231eea464f904a69e463dee71aa085747b6c Mon Sep 17 00:00:00 2001 From: James Henstridge Date: Wed, 8 Nov 2023 11:33:21 +0200 Subject: [PATCH 19/21] webextensions: update terminology to consistently use "native messaging host" --- data/org.freedesktop.portal.WebExtensions.xml | 26 +++--- src/web-extensions.c | 80 +++++++++---------- tests/web-extensions.c | 16 ++-- 3 files changed, 61 insertions(+), 61 deletions(-) diff --git a/data/org.freedesktop.portal.WebExtensions.xml b/data/org.freedesktop.portal.WebExtensions.xml index ef832c020..80ccc75f6 100644 --- a/data/org.freedesktop.portal.WebExtensions.xml +++ b/data/org.freedesktop.portal.WebExtensions.xml @@ -22,7 +22,7 @@ @short_description: WebExtensions portal The WebExtensions portal allows sandboxed web browsers to start - native messaging servers installed on the host system. + native messaging hosts installed on the host system. This documentation describes version 1 of this interface. --> @@ -45,7 +45,7 @@ A string indicating which behaviour the portal should use when locating and starting native messaging - servers. Valid values are "mozilla" and "chromium". By + hosts. Valid values are "mozilla" and "chromium". By default, mozilla behaviour is used. @@ -66,11 +66,11 @@ @@ -82,17 +82,17 @@ diff --git a/src/web-extensions.c b/src/web-extensions.c index 107a2e330..27acca324 100644 --- a/src/web-extensions.c +++ b/src/web-extensions.c @@ -243,9 +243,9 @@ handle_create_session (XdpDbusWebExtensions *object, } static void -on_server_exited (GPid pid, - gint status, - gpointer user_data) +on_host_exited (GPid pid, + gint status, + gpointer user_data) { Session *session = user_data; WebExtensionsSession *web_extensions_session = (WebExtensionsSession *)session; @@ -279,7 +279,7 @@ static gboolean is_valid_name (const char *name) { /* This regexp comes from the Mozilla documentation on valid native - messaging server names: + messaging host names: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#native_messaging_manifests @@ -336,13 +336,13 @@ get_manifest_search_path (WebExtensionsSessionMode mode) } static char * -find_server (WebExtensionsSessionMode mode, - const char *server_name, - const char *extension_or_origin, - char **out_server_description, - char **out_manifest_filename, - char **out_json_manifest, - GError **error) +find_messaging_host (WebExtensionsSessionMode mode, + const char *messaging_host_name, + const char *extension_or_origin, + char **out_description, + char **out_manifest_filename, + char **out_json_manifest, + GError **error) { g_auto(GStrv) search_path = NULL; g_autoptr(JsonParser) parser = NULL; @@ -350,25 +350,25 @@ find_server (WebExtensionsSessionMode mode, int i; /* Check that the we have a valid native messaging host name */ - if (!is_valid_name (server_name)) + if (!is_valid_name (messaging_host_name)) { g_set_error (error, XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid native messaging server name"); + "Invalid native messaging host name"); return NULL; } search_path = get_manifest_search_path (mode); parser = json_parser_new (); - metadata_basename = g_strconcat (server_name, ".json", NULL); + metadata_basename = g_strconcat (messaging_host_name, ".json", NULL); for (i = 0; search_path[i] != NULL; i++) { g_autofree char *metadata_filename = NULL; g_autoptr(GError) load_error = NULL; JsonObject *metadata_root; - const char *server_path; + const char *host_path; metadata_filename = g_build_filename (search_path[i], metadata_basename, NULL); if (!json_parser_load_from_file (parser, metadata_filename, &load_error)) @@ -384,14 +384,14 @@ find_server (WebExtensionsSessionMode mode, metadata_root = json_node_get_object (json_parser_get_root (parser)); /* Skip if metadata contains an unexpected name */ - if (g_strcmp0 (json_object_get_string_member (metadata_root, "name"), server_name) != 0) + if (g_strcmp0 (json_object_get_string_member (metadata_root, "name"), messaging_host_name) != 0) continue; - /* Skip if this is not a "stdio" type native messaging server */ + /* Skip if this is not a "stdio" type native messaging host */ if (g_strcmp0 (json_object_get_string_member (metadata_root, "type"), "stdio") != 0) continue; - /* Skip if this server isn't available to the extension. Note + /* Skip if this host isn't available to the extension. Note * that this ID is provided by the sandboxed browser, so this * check is just to help implement its security policy. */ switch (mode) @@ -406,8 +406,8 @@ find_server (WebExtensionsSessionMode mode, break; } - server_path = json_object_get_string_member (metadata_root, "path"); - if (!g_path_is_absolute (server_path)) + host_path = json_object_get_string_member (metadata_root, "path"); + if (!g_path_is_absolute (host_path)) { g_set_error (error, XDG_DESKTOP_PORTAL_ERROR, @@ -416,20 +416,20 @@ find_server (WebExtensionsSessionMode mode, return NULL; } - /* Server matches: return its executable path and description */ - if (out_server_description != NULL) - *out_server_description = g_strdup (json_object_get_string_member (metadata_root, "description")); + /* Host matches: return its executable path and description */ + if (out_description != NULL) + *out_description = g_strdup (json_object_get_string_member (metadata_root, "description")); if (out_manifest_filename != NULL) *out_manifest_filename = g_steal_pointer (&metadata_filename); if (out_json_manifest != NULL) *out_json_manifest = json_to_string (json_parser_get_root (parser), FALSE); - return g_strdup (server_path); + return g_strdup (host_path); } g_set_error (error, XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_NOT_FOUND, - "Could not find native messaging server"); + "Could not find native messaging host"); return NULL; } @@ -443,7 +443,7 @@ handle_get_manifest (XdpDbusWebExtensions *object, Call *call = call_from_invocation (invocation); Session *session; WebExtensionsSession *web_extensions_session; - g_autofree char *server_path = NULL; + g_autofree char *host_path = NULL; g_autofree char *json_manifest = NULL; g_autoptr(GError) error = NULL; @@ -469,10 +469,10 @@ handle_get_manifest (XdpDbusWebExtensions *object, return TRUE; } - server_path = find_server (web_extensions_session->mode, - arg_name, arg_extension_or_origin, - NULL, NULL, &json_manifest, &error); - if (!server_path) + host_path = find_messaging_host (web_extensions_session->mode, + arg_name, arg_extension_or_origin, + NULL, NULL, &json_manifest, &error); + if (!host_path) { g_dbus_method_invocation_return_gerror (invocation, error); return TRUE; @@ -494,8 +494,8 @@ handle_start_in_thread (GTask *task, const char *arg_name; char *arg_extension_or_origin; const char *app_id; - g_autofree char *server_path = NULL; - g_autofree char *server_description = NULL; + g_autofree char *host_path = NULL; + g_autofree char *description = NULL; g_autofree char *manifest_filename = NULL; guint response = XDG_DESKTOP_PORTAL_RESPONSE_OTHER; gboolean should_close_session; @@ -516,11 +516,11 @@ handle_start_in_thread (GTask *task, arg_name = g_object_get_data (G_OBJECT (request), "name"); arg_extension_or_origin = g_object_get_data (G_OBJECT (request), "extension-or-origin"); - server_path = find_server (web_extensions_session->mode, - arg_name, arg_extension_or_origin, - &server_description, &manifest_filename, NULL, - &error); - if (server_path == NULL) + host_path = find_messaging_host (web_extensions_session->mode, + arg_name, arg_extension_or_origin, + &description, &manifest_filename, NULL, + &error); + if (host_path == NULL) { g_warning ("Could not find WebExtensions backend: %s", error->message); fflush(stderr); @@ -550,7 +550,7 @@ handle_start_in_thread (GTask *task, } display_name = info ? g_app_info_get_display_name (info) : app_id; title = g_strdup_printf (_("Allow %s to start WebExtension backend?"), display_name); - subtitle = g_strdup_printf (_("%s is requesting to launch \"%s\" (%s)."), display_name, server_description, arg_name); + subtitle = g_strdup_printf (_("%s is requesting to launch \"%s\" (%s)."), display_name, description, arg_name); body = g_strdup (_("This permission can be changed at any time from the privacy settings.")); g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT); @@ -588,7 +588,7 @@ handle_start_in_thread (GTask *task, goto out; } - argv[0] = server_path; + argv[0] = host_path; switch (web_extensions_session->mode) { case WEB_EXTENSIONS_SESSION_MODE_CHROMIUM: @@ -617,7 +617,7 @@ handle_start_in_thread (GTask *task, web_extensions_session->child_watch_id = g_child_watch_add_full (G_PRIORITY_DEFAULT, web_extensions_session->child_pid, - on_server_exited, + on_host_exited, g_object_ref (web_extensions_session), g_object_unref); web_extensions_session->state = WEB_EXTENSIONS_SESSION_STATE_STARTED; diff --git a/tests/web-extensions.c b/tests/web-extensions.c index 276e76841..711a6cace 100644 --- a/tests/web-extensions.c +++ b/tests/web-extensions.c @@ -430,7 +430,7 @@ cancel_call (gpointer data) typedef struct { GCancellable *cancellable; char *session_handle; - const char *server_name; + const char *messaging_host_name; } TestData; static void @@ -504,11 +504,11 @@ get_manifest_cb (GObject *object, GAsyncResult *result, gpointer data) TestData *test_data = data; g_autoptr(GError) error = NULL; g_autofree char *json_manifest = NULL; - g_autofree char *server_path = NULL; + g_autofree char *host_path = NULL; g_autofree char *expected = NULL; - server_path = g_test_build_filename (G_TEST_BUILT, "native-messaging-hosts", "server.sh", NULL); - expected = g_strdup_printf ("{\"name\":\"org.example.testing\",\"description\":\"Test native messaging host\",\"path\":\"%s\",\"type\":\"stdio\",\"allowed_extensions\":[\"some-extension@example.org\"]}", server_path); + host_path = g_test_build_filename (G_TEST_BUILT, "native-messaging-hosts", "server.sh", NULL); + expected = g_strdup_printf ("{\"name\":\"org.example.testing\",\"description\":\"Test native messaging host\",\"path\":\"%s\",\"type\":\"stdio\",\"allowed_extensions\":[\"some-extension@example.org\"]}", host_path); json_manifest = get_manifest_finish (result, &error); g_assert_no_error (error); @@ -582,7 +582,7 @@ create_session_bad_name_cb (GObject *object, GAsyncResult *result, gpointer data g_assert_nonnull (test_data->session_handle); start (test_data->session_handle, - test_data->server_name, + test_data->messaging_host_name, "some-extension@example.org", test_data->cancellable, start_bad_name_cb, @@ -592,17 +592,17 @@ create_session_bad_name_cb (GObject *object, GAsyncResult *result, gpointer data void test_web_extensions_bad_name (void) { - const char *server_name[] = { + const char *messaging_host_name[] = { "no-dashes", "../foo", "no_trailing_dot.", }; int i; - for (i = 0; i < G_N_ELEMENTS (server_name); i++) + for (i = 0; i < G_N_ELEMENTS (messaging_host_name); i++) { g_autoptr(GCancellable) cancellable = NULL; - TestData test_data = { cancellable, NULL, server_name[i] }; + TestData test_data = { cancellable, NULL, messaging_host_name[i] }; got_info = 0; set_web_extensions_permissions ("yes"); From d2a05aa2be044723446724db7a836a5a5a0a6d1d Mon Sep 17 00:00:00 2001 From: Amin Bandali Date: Tue, 5 Mar 2024 13:00:54 -0500 Subject: [PATCH 20/21] webextensions: small documentation updates and path typo fixes --- src/web-extensions.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/web-extensions.c b/src/web-extensions.c index 27acca324..df6c28095 100644 --- a/src/web-extensions.c +++ b/src/web-extensions.c @@ -112,6 +112,10 @@ web_extensions_session_close (Session *session) { WebExtensionsSession *web_extensions_session = (WebExtensionsSession *)session; + /* This function can be called repeatedly, e.g. by an explicit + "org.freedesktop.portal.Session::Close" message followed by + a call to finalize due to session's refcount reaching zero. + */ if (web_extensions_session->state == WEB_EXTENSIONS_SESSION_STATE_CLOSED) return; web_extensions_session->state = WEB_EXTENSIONS_SESSION_STATE_CLOSED; @@ -165,6 +169,7 @@ web_extensions_session_class_init (WebExtensionsSessionClass *klass) object_class->finalize = web_extensions_session_finalize; session_class = (SessionClass *)klass; + /* Register handler for org.freedesktop.portal.Session::Close */ session_class->close = web_extensions_session_close; } @@ -304,7 +309,7 @@ get_manifest_search_path (WebExtensionsSessionMode mode) { case WEB_EXTENSIONS_SESSION_MODE_CHROMIUM: /* Chrome and Chromium search paths documented here: - * https://developer.chrome.com/docs/apps/nativeMessaging/#native-messaging-host-location + * https://developer.chrome.com/docs/extensions/nativeMessaging/#native-messaging-host-location */ /* Add per-user directories */ g_ptr_array_add (search_path, g_build_filename (g_get_user_config_dir (), "google-chrome", "NativeMessagingHosts", NULL)); @@ -313,8 +318,8 @@ get_manifest_search_path (WebExtensionsSessionMode mode) g_ptr_array_add (search_path, g_strdup ("/etc/opt/chrome/native-messaging-hosts")); g_ptr_array_add (search_path, g_strdup ("/etc/chromium/native-messaging-hosts")); /* And the same for xdg-desktop-portal's configured prefix */ - g_ptr_array_add (search_path, g_strdup (SYSCONFDIR "opt/chrome/native-messaging-hosts")); - g_ptr_array_add (search_path, g_strdup (SYSCONFDIR "chromium/native-messaging-hosts")); + g_ptr_array_add (search_path, g_strdup (SYSCONFDIR "/opt/chrome/native-messaging-hosts")); + g_ptr_array_add (search_path, g_strdup (SYSCONFDIR "/chromium/native-messaging-hosts")); break; case WEB_EXTENSIONS_SESSION_MODE_MOZILLA: @@ -326,8 +331,13 @@ get_manifest_search_path (WebExtensionsSessionMode mode) /* Add system wide directories */ g_ptr_array_add (search_path, g_strdup ("/usr/lib/mozilla/native-messaging-hosts")); g_ptr_array_add (search_path, g_strdup ("/usr/lib64/mozilla/native-messaging-hosts")); - /* And the same for xdg-desktop-portal's configured prefix */ - g_ptr_array_add (search_path, g_strdup (LIBDIR "mozilla/native-messaging-hosts")); + /* And the same for xdg-desktop-portal's configured prefix. + This is helpful on Debian-based systems where LIBDIR is + suffixed with 'dpkg-architecture -qDEB_HOST_MULTIARCH', + e.g. '/usr/lib/x86_64-linux-gnu'. + https://salsa.debian.org/debian/debhelper/-/blob/5b96b19b456fe5e094f2870327a753b4b3ece0dc/lib/Debian/Debhelper/Buildsystem/meson.pm#L78 + */ + g_ptr_array_add (search_path, g_strdup (LIBDIR "/mozilla/native-messaging-hosts")); break; } From c73983517ddc90a023e0b308cc5e8abeff496f5a Mon Sep 17 00:00:00 2001 From: James Henstridge Date: Tue, 12 Mar 2024 15:59:03 +0800 Subject: [PATCH 21/21] web-extensions: pass the cancellable to xdp_..._call_access_dialog_sync() --- src/web-extensions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web-extensions.c b/src/web-extensions.c index df6c28095..a00cc2bad 100644 --- a/src/web-extensions.c +++ b/src/web-extensions.c @@ -576,7 +576,7 @@ handle_start_in_thread (GTask *task, g_variant_builder_end (&opt_builder), &access_response, &access_results, - NULL, + cancellable, &error)) { g_warning ("AccessDialog call failed: %s", error->message);