summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/meson.build7
-rw-r--r--data/org.gnome.Epiphany.WebAppProvider.service.in3
-rw-r--r--embed/ephy-embed-shell.c2
-rw-r--r--lib/ephy-web-app-utils.c154
-rw-r--r--lib/ephy-web-app-utils.h24
-rw-r--r--org.gnome.Epiphany.Canary.json.in3
-rw-r--r--org.gnome.Epiphany.json3
-rw-r--r--src/ephy-main.c2
-rw-r--r--src/meson.build23
-rw-r--r--src/webapp-provider/ephy-webapp-provider-main.c61
-rw-r--r--src/webapp-provider/ephy-webapp-provider.c282
-rw-r--r--src/webapp-provider/ephy-webapp-provider.h36
-rw-r--r--src/webapp-provider/org.gnome.Epiphany.WebAppProvider.xml85
-rw-r--r--src/window-commands.c3
-rw-r--r--tests/ephy-web-app-utils-test.c4
15 files changed, 659 insertions, 33 deletions
diff --git a/data/meson.build b/data/meson.build
index eac6b8224..3108a0060 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -96,6 +96,13 @@ configure_file(
install_dir: servicedir
)
+configure_file(
+ input: 'org.gnome.Epiphany.WebAppProvider.service.in',
+ output: 'org.gnome.Epiphany.WebAppProvider.service',
+ configuration: service_conf,
+ install_dir: servicedir
+)
+
search_provider_conf = configuration_data()
search_provider_conf.set('appid', application_id)
search_provider_conf.set('profile', profile != '' ? '/' + profile : '')
diff --git a/data/org.gnome.Epiphany.WebAppProvider.service.in b/data/org.gnome.Epiphany.WebAppProvider.service.in
new file mode 100644
index 000000000..1ab1699a6
--- /dev/null
+++ b/data/org.gnome.Epiphany.WebAppProvider.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.Epiphany.WebAppProvider
+Exec=@libexecdir@/epiphany-webapp-provider
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index 99d1fb0ef..cc5f9832c 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -340,7 +340,7 @@ web_process_extension_about_apps_message_received_cb (WebKitUserContentManager *
g_autofree char *app_id = NULL;
app_id = jsc_value_to_string (webkit_javascript_result_get_js_value (message));
- ephy_web_application_delete (app_id);
+ ephy_web_application_delete (app_id, NULL);
}
static char *
diff --git a/lib/ephy-web-app-utils.c b/lib/ephy-web-app-utils.c
index dd2291833..233999942 100644
--- a/lib/ephy-web-app-utils.c
+++ b/lib/ephy-web-app-utils.c
@@ -242,6 +242,21 @@ ephy_web_application_get_profile_directory (const char *id)
return ephy_web_application_get_directory_under (id, g_get_user_data_dir ());
}
+/**
+ * ephy_web_application_get_desktop_path:
+ * @app: the #EphyWebApplication
+ *
+ * Gets the path to the .desktop file for @app
+ *
+ * Returns: (transfer full): A newly allocated string.
+ **/
+char *
+ephy_web_application_get_desktop_path (EphyWebApplication *app)
+{
+ g_autofree char *profile_dir = ephy_web_application_get_profile_directory (app->id);
+ return g_build_filename (profile_dir, app->desktop_file, NULL);
+}
+
static char *
ephy_web_application_get_cache_directory (const char *id)
{
@@ -256,7 +271,10 @@ ephy_web_application_get_config_directory (const char *id)
/**
* ephy_web_application_delete:
- * @id: the identifier of the web application do delete
+ * @id: the identifier of the web application to delete
+ * @out_app_found: return location for a #EphyWebAppFound. This will be set to
+ * %EPHY_WEB_APP_NOT_FOUND if deleting the app failed due to it not being
+ * installed, and %EPHY_WEB_APP_FOUND otherwise.
*
* Deletes all the data associated with a Web Application created by
* Epiphany.
@@ -264,7 +282,8 @@ ephy_web_application_get_config_directory (const char *id)
* Returns: %TRUE if the web app was succesfully deleted, %FALSE otherwise
**/
gboolean
-ephy_web_application_delete (const char *id)
+ephy_web_application_delete (const char *id,
+ EphyWebAppFound *out_app_found)
{
g_autofree char *profile_dir = NULL;
g_autofree char *cache_dir = NULL;
@@ -276,6 +295,9 @@ ephy_web_application_delete (const char *id)
g_assert (id);
+ if (out_app_found)
+ *out_app_found = EPHY_WEB_APP_FOUND;
+
profile_dir = ephy_web_application_get_profile_directory (id);
if (!profile_dir)
return FALSE;
@@ -284,6 +306,8 @@ ephy_web_application_delete (const char *id)
* exist. */
if (!g_file_test (profile_dir, G_FILE_TEST_IS_DIR)) {
g_warning ("No application with id '%s' is installed.\n", id);
+ if (out_app_found)
+ *out_app_found = EPHY_WEB_APP_NOT_FOUND;
return FALSE;
}
@@ -330,12 +354,43 @@ ephy_web_application_delete (const char *id)
return TRUE;
}
+/**
+ * ephy_web_application_delete_by_desktop_file_id:
+ * @desktop_file_id: the .desktop file name for the web app to be deleted, with
+ * the extension
+ * @out_app_found: return location for a #EphyWebAppFound. This will be set to
+ * %EPHY_WEB_APP_NOT_FOUND if deleting the app failed due to it not being
+ * installed, and %EPHY_WEB_APP_FOUND otherwise.
+ *
+ * Deletes all the data associated with a Web Application created by
+ * Epiphany.
+ *
+ * Returns: %TRUE if the web app was succesfully deleted, %FALSE otherwise
+ **/
+gboolean
+ephy_web_application_delete_by_desktop_file_id (const char *desktop_file_id,
+ EphyWebAppFound *out_app_found)
+{
+ const char *id;
+ g_autofree char *gapp_id = NULL;
+
+ g_assert (desktop_file_id);
+
+ gapp_id = g_strdup (desktop_file_id);
+ if (g_str_has_suffix (desktop_file_id, ".desktop"))
+ gapp_id[strlen (desktop_file_id) - strlen (".desktop")] = '\0';
+
+ id = get_app_id_from_gapplication_id (gapp_id);
+
+ return ephy_web_application_delete (id, out_app_found);
+}
+
static char *
create_desktop_file (const char *id,
const char *name,
const char *address,
const char *profile_dir,
- GdkPixbuf *icon)
+ const char *icon_path)
{
g_autofree char *filename = NULL;
g_autoptr (GKeyFile) file = NULL;
@@ -365,18 +420,8 @@ create_desktop_file (const char *id,
g_key_file_set_value (file, "Desktop Entry", "Type", "Application");
g_key_file_set_value (file, "Desktop Entry", "Categories", "GNOME;GTK;");
- if (icon) {
- g_autoptr (GOutputStream) stream = NULL;
- g_autofree char *path = NULL;
- g_autoptr (GFile) image = NULL;
-
- path = g_build_filename (profile_dir, EPHY_WEB_APP_ICON_NAME, NULL);
- image = g_file_new_for_path (path);
-
- stream = (GOutputStream *)g_file_create (image, 0, NULL, NULL);
- gdk_pixbuf_save_to_stream (icon, stream, "png", NULL, NULL, NULL);
- g_key_file_set_value (file, "Desktop Entry", "Icon", path);
- }
+ if (icon_path)
+ g_key_file_set_value (file, "Desktop Entry", "Icon", icon_path);
wm_class = g_strconcat (EPHY_WEB_APP_GAPPLICATION_ID_PREFIX, id, NULL);
g_key_file_set_value (file, "Desktop Entry", "StartupWMClass", wm_class);
@@ -409,7 +454,10 @@ create_desktop_file (const char *id,
* @id: the identifier for the new web application
* @address: the address of the new web application
* @name: the name for the new web application
- * @icon: the icon for the new web application
+ * @icon_pixbuf: the icon for the new web application as a #GdkPixbuf
+ * @icon_path: the path to the icon, used instead of @icon_pixbuf
+ * @install_token: the install token acquired via portal, used for
+ * non-interactive sandboxed installation
* @options: the options for the new web application
*
* Creates a new Web Application for @address.
@@ -420,14 +468,19 @@ char *
ephy_web_application_create (const char *id,
const char *address,
const char *name,
- GdkPixbuf *icon,
+ GdkPixbuf *icon_pixbuf,
+ const char *icon_path,
+ const char *install_token,
EphyWebApplicationOptions options)
{
g_autofree char *app_file = NULL;
g_autofree char *profile_dir = NULL;
g_autofree char *desktop_file_path = NULL;
+ g_autofree char *icon_path_owned = NULL;
int fd;
+ g_return_val_if_fail (!icon_pixbuf || !icon_path, NULL);
+
/* If there's already a WebApp profile for the contents of this
* view, do nothing. */
profile_dir = ephy_web_application_get_profile_directory (id);
@@ -454,8 +507,22 @@ ephy_web_application_create (const char *id,
}
close (fd);
+ /* Write the icon to a file */
+ if (icon_pixbuf) {
+ g_autoptr (GOutputStream) stream = NULL;
+ g_autoptr (GFile) image = NULL;
+
+ icon_path_owned = g_build_filename (profile_dir, EPHY_WEB_APP_ICON_NAME, NULL);
+ image = g_file_new_for_path (icon_path_owned);
+
+ stream = (GOutputStream *)g_file_create (image, 0, NULL, NULL);
+ gdk_pixbuf_save_to_stream (icon_pixbuf, stream, "png", NULL, NULL, NULL);
+ } else {
+ icon_path_owned = g_strdup (icon_path);
+ }
+
/* Create the deskop file. */
- desktop_file_path = create_desktop_file (id, name, address, profile_dir, icon);
+ desktop_file_path = create_desktop_file (id, name, address, profile_dir, icon_path_owned);
if (desktop_file_path)
ephy_web_application_initialize_settings (profile_dir, options);
@@ -586,7 +653,6 @@ ephy_web_application_for_profile_directory (const char *profile_dir)
g_auto (GStrv) argv = NULL;
g_autoptr (GFile) file = NULL;
g_autoptr (GFileInfo) file_info = NULL;
- guint64 created;
g_autoptr (GDate) date = NULL;
id = get_app_id_from_profile_directory (profile_dir);
@@ -614,10 +680,10 @@ ephy_web_application_for_profile_directory (const char *profile_dir)
/* FIXME: this should use TIME_CREATED but it does not seem to be working. */
file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, 0, NULL, NULL);
- created = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+ app->install_date_uint64 = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
date = g_date_new ();
- g_date_set_time_t (date, (time_t)created);
+ g_date_set_time_t (date, (time_t)app->install_date_uint64);
g_date_strftime (app->install_date, 127, "%x", date);
return g_steal_pointer (&app);
@@ -705,7 +771,6 @@ ephy_web_application_get_legacy_application_list (void)
return ephy_web_application_get_application_list_internal (TRUE);
}
-
/**
* ephy_web_application_free_application_list:
* @list: an #EphyWebApplication GList
@@ -719,6 +784,51 @@ ephy_web_application_free_application_list (GList *list)
}
/**
+ * ephy_web_application_get_desktop_id_list:
+ *
+ * Gets a list of the currently installed web applications' .desktop filenames.
+ * This is useful even though we don't have access to the actual .desktop files
+ * when running under Flatpak, because we return it over D-Bus in the
+ * WebAppProvider service.
+ *
+ * Returns: (transfer-full): a %NULL-terminated array of strings
+ **/
+char **
+ephy_web_application_get_desktop_id_list (void)
+{
+ g_autoptr (GFileEnumerator) children = NULL;
+ g_autoptr (GFile) parent_directory = NULL;
+ GPtrArray *desktop_file_ids;
+
+ parent_directory = g_file_new_for_path (g_get_user_data_dir ());
+ children = g_file_enumerate_children (parent_directory,
+ "standard::name",
+ 0, NULL, NULL);
+ if (!children)
+ return NULL;
+
+ desktop_file_ids = g_ptr_array_new_with_free_func (g_free);
+ for (;;) {
+ g_autoptr (GFileInfo) info = g_file_enumerator_next_file (children, NULL, NULL);
+ const char *name;
+
+ if (!info)
+ break;
+
+ name = g_file_info_get_name (info);
+ if (g_str_has_prefix (name, get_gapplication_id_prefix ())) {
+ g_autofree char *desktop_file_id = NULL;
+ desktop_file_id = g_strconcat (name, ".desktop", NULL);
+ g_ptr_array_add (desktop_file_ids, g_steal_pointer (&desktop_file_id));
+ }
+ }
+
+ g_ptr_array_add (desktop_file_ids, NULL);
+
+ return (char **)g_ptr_array_free (desktop_file_ids, FALSE);
+}
+
+/**
* ephy_web_application_exists:
* @id: the potential identifier of the web application
*
diff --git a/lib/ephy-web-app-utils.h b/lib/ephy-web-app-utils.h
index 4f98fc678..900b101f5 100644
--- a/lib/ephy-web-app-utils.h
+++ b/lib/ephy-web-app-utils.h
@@ -33,6 +33,7 @@ typedef struct {
char *url;
char *desktop_file;
char install_date[128];
+ guint64 install_date_uint64;
} EphyWebApplication;
/**
@@ -51,17 +52,32 @@ typedef enum {
EPHY_WEB_APPLICATION_SYSTEM,
} EphyWebApplicationOptions;
+typedef enum {
+ EPHY_WEB_APP_FOUND,
+ EPHY_WEB_APP_NOT_FOUND,
+} EphyWebAppFound;
+
#define EPHY_WEB_APP_ICON_NAME "app-icon.png"
char *ephy_web_application_get_app_id_from_name (const char *name);
const char *ephy_web_application_get_gapplication_id_from_profile_directory (const char *profile_dir);
-char *ephy_web_application_create (const char *id, const char *address, const char *name, GdkPixbuf *icon, EphyWebApplicationOptions options);
+char *ephy_web_application_create (const char *id,
+ const char *address,
+ const char *name,
+ GdkPixbuf *icon_pixbuf,
+ const char *icon_path,
+ const char *install_token,
+ EphyWebApplicationOptions options);
char *ephy_web_application_ensure_for_app_info (GAppInfo *app_info);
-gboolean ephy_web_application_delete (const char *id);
+gboolean ephy_web_application_delete (const char *id,
+ EphyWebAppFound *out_app_found);
+
+gboolean ephy_web_application_delete_by_desktop_file_id (const char *desktop_file_id,
+ EphyWebAppFound *out_app_found);
void ephy_web_application_setup_from_profile_directory (const char *profile_directory);
@@ -69,6 +85,8 @@ void ephy_web_application_setup_from_desktop_file (GDesktopAppInf
char *ephy_web_application_get_profile_directory (const char *id);
+char *ephy_web_application_get_desktop_path (EphyWebApplication *app);
+
EphyWebApplication *ephy_web_application_for_profile_directory (const char *profile_dir);
void ephy_web_application_free (EphyWebApplication *app);
@@ -79,6 +97,8 @@ GList *ephy_web_application_get_application_list (void);
GList *ephy_web_application_get_legacy_application_list (void);
+char **ephy_web_application_get_desktop_id_list (void);
+
void ephy_web_application_free_application_list (GList *list);
void ephy_web_application_initialize_settings (const char *profile_directory, EphyWebApplicationOptions options);
diff --git a/org.gnome.Epiphany.Canary.json.in b/org.gnome.Epiphany.Canary.json.in
index 17fc14086..bade6e118 100644
--- a/org.gnome.Epiphany.Canary.json.in
+++ b/org.gnome.Epiphany.Canary.json.in
@@ -17,7 +17,8 @@
"--socket=fallback-x11",
"--socket=pulseaudio",
"--socket=wayland",
- "--system-talk-name=org.freedesktop.GeoClue2"
+ "--system-talk-name=org.freedesktop.GeoClue2",
+ "--own-name=org.gnome.Epiphany.WebAppProvider"
],
"modules" : [
{
diff --git a/org.gnome.Epiphany.json b/org.gnome.Epiphany.json
index 78ef33483..e0dddbf79 100644
--- a/org.gnome.Epiphany.json
+++ b/org.gnome.Epiphany.json
@@ -17,7 +17,8 @@
"--socket=fallback-x11",
"--socket=pulseaudio",
"--socket=wayland",
- "--system-talk-name=org.freedesktop.GeoClue2"
+ "--system-talk-name=org.freedesktop.GeoClue2",
+ "--own-name=org.gnome.Epiphany.WebAppProvider"
],
"modules" : [
{
diff --git a/src/ephy-main.c b/src/ephy-main.c
index 48b6c684b..bcf5ce703 100644
--- a/src/ephy-main.c
+++ b/src/ephy-main.c
@@ -370,7 +370,7 @@ main (int argc,
/* Delete the requested web application, if any. Must happen after
* ephy_file_helpers_init (). */
if (application_to_delete) {
- ephy_web_application_delete (application_to_delete);
+ ephy_web_application_delete (application_to_delete, NULL);
exit (0);
}
diff --git a/src/meson.build b/src/meson.build
index eaee92180..50f6710b3 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -111,7 +111,7 @@ ephy_profile_migrator = executable('ephy-profile-migrator',
)
-codegen = gnome.gdbus_codegen('ephy-shell-search-provider-generated',
+search_provider_codegen = gnome.gdbus_codegen('ephy-shell-search-provider-generated',
'search-provider/org.gnome.ShellSearchProvider2.xml',
interface_prefix: 'org.gnome',
namespace: 'Ephy'
@@ -120,7 +120,7 @@ codegen = gnome.gdbus_codegen('ephy-shell-search-provider-generated',
search_provider_sources = [
'search-provider/ephy-search-provider.c',
'search-provider/ephy-search-provider-main.c',
- codegen
+ search_provider_codegen
]
executable('epiphany-search-provider',
@@ -131,6 +131,25 @@ executable('epiphany-search-provider',
install_rpath: pkglibdir
)
+webapp_codegen = gnome.gdbus_codegen('ephy-webapp-provider-generated',
+ 'webapp-provider/org.gnome.Epiphany.WebAppProvider.xml',
+ interface_prefix: 'org.gnome.Epiphany',
+ namespace: 'Ephy'
+)
+
+webapp_provider_sources = [
+ 'webapp-provider/ephy-webapp-provider.c',
+ 'webapp-provider/ephy-webapp-provider-main.c',
+ webapp_codegen
+]
+
+executable('epiphany-webapp-provider',
+ webapp_provider_sources,
+ dependencies: ephymain_dep,
+ install: true,
+ install_dir: libexecdir,
+ install_rpath: pkglibdir
+)
resource_files = files('resources/epiphany.gresource.xml')
resources = gnome.compile_resources('epiphany-resources',
diff --git a/src/webapp-provider/ephy-webapp-provider-main.c b/src/webapp-provider/ephy-webapp-provider-main.c
new file mode 100644
index 000000000..e4fdcfb55
--- /dev/null
+++ b/src/webapp-provider/ephy-webapp-provider-main.c
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (c) 2013 Igalia S.L.
+ *
+ * This file is part of Epiphany.
+ *
+ * Epiphany is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-webapp-provider.h"
+#include "ephy-file-helpers.h"
+
+#include <glib/gi18n.h>
+#include <locale.h>
+
+int
+main (int argc,
+ char **argv)
+{
+ g_autoptr (EphyWebAppProviderService) webapp_provider = NULL;
+ int status;
+ GError *error = NULL;
+
+ g_setenv ("GIO_USE_VFS", "local", TRUE);
+
+ g_debug ("started %s", argv[0]);
+
+ /* Initialize the i18n stuff */
+ setlocale (LC_ALL, "");
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ if (!ephy_file_helpers_init (NULL, 0, &error)) {
+ g_printerr ("%s\n", error->message);
+ g_error_free (error);
+ return 1;
+ }
+
+ webapp_provider = ephy_web_app_provider_service_new ();
+ status = g_application_run (G_APPLICATION (webapp_provider), argc, argv);
+
+ ephy_file_helpers_shutdown ();
+
+ g_debug ("stopping %s with status %d", argv[0], status);
+
+ return status;
+}
diff --git a/src/webapp-provider/ephy-webapp-provider.c b/src/webapp-provider/ephy-webapp-provider.c
new file mode 100644
index 000000000..af5fd7e05
--- /dev/null
+++ b/src/webapp-provider/ephy-webapp-provider.c
@@ -0,0 +1,282 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (c) 2021 Matthew Leeds <mwleeds@protonmail.com>
+ *
+ * This file is part of Epiphany.
+ *
+ * Epiphany is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-webapp-provider.h"
+
+#include "ephy-web-app-utils.h"
+#include "ephy-flatpak-utils.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+
+struct _EphyWebAppProviderService {
+ GApplication parent_instance;
+
+ EphyWebAppProvider *skeleton;
+};
+
+struct _EphyWebAppProviderServiceClass {
+ GApplicationClass parent_class;
+};
+
+G_DEFINE_TYPE (EphyWebAppProviderService, ephy_web_app_provider_service, G_TYPE_APPLICATION)
+
+#define INACTIVITY_TIMEOUT 60 * 1000 /* One minute, in milliseconds */
+
+typedef enum {
+ EPHY_WEBAPP_PROVIDER_ERROR_FAILED,
+ EPHY_WEBAPP_PROVIDER_ERROR_INVALID_ARGS,
+ EPHY_WEBAPP_PROVIDER_ERROR_NOT_INSTALLED,
+ EPHY_WEBAPP_PROVIDER_ERROR_LAST = EPHY_WEBAPP_PROVIDER_ERROR_NOT_INSTALLED, /*< skip >*/
+} EphyWebAppProviderError;
+
+static const GDBusErrorEntry ephy_webapp_provider_error_entries[] = {
+ { EPHY_WEBAPP_PROVIDER_ERROR_FAILED, "org.gnome.Epiphany.WebAppProvider.Error.Failed" },
+ { EPHY_WEBAPP_PROVIDER_ERROR_INVALID_ARGS, "org.gnome.Epiphany.WebAppProvider.Error.InvalidArgs" },
+ { EPHY_WEBAPP_PROVIDER_ERROR_NOT_INSTALLED, "org.gnome.Epiphany.WebAppProvider.Error.NotInstalled" },
+};
+
+/* Ensure that every error code has an associated D-Bus error name */
+G_STATIC_ASSERT (G_N_ELEMENTS (ephy_webapp_provider_error_entries) == EPHY_WEBAPP_PROVIDER_ERROR_LAST + 1);
+
+#define EPHY_WEBAPP_PROVIDER_ERROR (ephy_webapp_provider_error_quark ())
+GQuark
+ephy_webapp_provider_error_quark (void)
+{
+ static gsize quark = 0;
+ g_dbus_error_register_error_domain ("ephy-webapp-provider-error-quark",
+ &quark,
+ ephy_webapp_provider_error_entries,
+ G_N_ELEMENTS (ephy_webapp_provider_error_entries));
+ return (GQuark)quark;
+}
+
+static gboolean
+handle_get_installed_apps (EphyWebAppProvider *skeleton,
+ GDBusMethodInvocation *invocation,
+ EphyWebAppProviderService *self)
+{
+ g_auto (GStrv) desktop_ids = NULL;
+
+ g_debug ("%s", G_STRFUNC);
+
+ g_application_hold (G_APPLICATION (self));
+
+ desktop_ids = ephy_web_application_get_desktop_id_list ();
+
+ ephy_web_app_provider_complete_get_installed_apps (skeleton, invocation,
+ (const gchar * const *)desktop_ids);
+
+ g_application_release (G_APPLICATION (self));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+handle_install (EphyWebAppProvider *skeleton,
+ GDBusMethodInvocation *invocation,
+ char *url,
+ char *name,
+ char *install_token,
+ EphyWebAppProviderService *self)
+{
+ g_autofree char *id = NULL;
+ g_autofree char *desktop_path = NULL;
+ g_autofree char *desktop_file_id = NULL;
+
+ g_debug ("%s", G_STRFUNC);
+
+ g_application_hold (G_APPLICATION (self));
+
+ /* We need an install token acquired by a trusted system component such as
+ * gnome-software because otherwise the Flatpak/Snap sandbox prevents us from
+ * installing the app without using a portal (which would not be appropriate
+ * since Epiphany is not the focused application). We use the same code path
+ * when not running under a sandbox too.
+ */
+ if (!install_token || *install_token == '\0') {
+ g_dbus_method_invocation_return_error (invocation, EPHY_WEBAPP_PROVIDER_ERROR,
+ EPHY_WEBAPP_PROVIDER_ERROR_INVALID_ARGS,
+ _("The install_token is required for the Install() method"));
+ goto out;
+ }
+ if (!g_uri_is_valid (url, G_URI_FLAGS_NONE, NULL)) {
+ g_dbus_method_invocation_return_error (invocation, EPHY_WEBAPP_PROVIDER_ERROR,
+ EPHY_WEBAPP_PROVIDER_ERROR_INVALID_ARGS,
+ _("The url passed was not valid: ‘%s’"), url);
+ goto out;
+ }
+ if (!name || *name == '\0') {
+ g_dbus_method_invocation_return_error (invocation, EPHY_WEBAPP_PROVIDER_ERROR,
+ EPHY_WEBAPP_PROVIDER_ERROR_INVALID_ARGS,
+ _("The name passed was not valid"));
+ goto out;
+ }
+
+ id = ephy_web_application_get_app_id_from_name (name);
+
+ desktop_path = ephy_web_application_create (id, url, name,
+ NULL, NULL, /* icon_pixbuf, icon_path */
+ install_token,
+ EPHY_WEB_APPLICATION_NONE);
+ if (!desktop_path) {
+ g_dbus_method_invocation_return_error (invocation, EPHY_WEBAPP_PROVIDER_ERROR,
+ EPHY_WEBAPP_PROVIDER_ERROR_FAILED,
+ _("Installing the web application ‘%s’ (%s) failed"),
+ name, url);
+ goto out;
+ }
+
+ desktop_file_id = g_path_get_basename (desktop_path);
+ ephy_web_app_provider_complete_install (skeleton, invocation, desktop_file_id);
+
+out:
+ g_application_release (G_APPLICATION (self));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+handle_uninstall (EphyWebAppProvider *skeleton,
+ GDBusMethodInvocation *invocation,
+ char *desktop_file_id,
+ EphyWebAppProviderService *self)
+{
+ EphyWebAppFound app_found;
+
+ g_debug ("%s", G_STRFUNC);
+
+ g_application_hold (G_APPLICATION (self));
+
+ if (!desktop_file_id || !g_str_has_suffix (desktop_file_id, ".desktop")) {
+ g_dbus_method_invocation_return_error (invocation, EPHY_WEBAPP_PROVIDER_ERROR,
+ EPHY_WEBAPP_PROVIDER_ERROR_INVALID_ARGS,
+ _("The desktop file ID passed ‘%s’ was not valid"),
+ desktop_file_id ? desktop_file_id : "(null)");
+ goto out;
+ }
+
+ if (!ephy_web_application_delete_by_desktop_file_id (desktop_file_id, &app_found)) {
+ if (app_found == EPHY_WEB_APP_NOT_FOUND) {
+ g_dbus_method_invocation_return_error (invocation, EPHY_WEBAPP_PROVIDER_ERROR,
+ EPHY_WEBAPP_PROVIDER_ERROR_NOT_INSTALLED,
+ _("The web application ‘%s’ does not exist"),
+ desktop_file_id);
+ } else {
+ g_dbus_method_invocation_return_error (invocation, EPHY_WEBAPP_PROVIDER_ERROR,
+ EPHY_WEBAPP_PROVIDER_ERROR_FAILED,
+ _("The web application ‘%s’ could not be deleted"),
+ desktop_file_id);
+ }
+ goto out;
+ }
+
+ ephy_web_app_provider_complete_uninstall (skeleton, invocation);
+
+out:
+ g_application_release (G_APPLICATION (self));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+ephy_web_app_provider_service_init (EphyWebAppProviderService *self)
+{
+ g_application_set_flags (G_APPLICATION (self), G_APPLICATION_IS_SERVICE);
+
+ g_application_set_inactivity_timeout (G_APPLICATION (self), INACTIVITY_TIMEOUT);
+}
+
+static gboolean
+ephy_web_app_provider_service_dbus_register (GApplication *application,
+ GDBusConnection *connection,
+ const gchar *object_path,
+ GError **error)
+{
+ EphyWebAppProviderService *self;
+
+ g_debug ("registering at object path %s", object_path);
+
+ if (!G_APPLICATION_CLASS (ephy_web_app_provider_service_parent_class)->dbus_register (application,
+ connection,
+ object_path,
+ error))
+ return FALSE;
+
+ self = EPHY_WEB_APP_PROVIDER_SERVICE (application);
+ self->skeleton = ephy_web_app_provider_skeleton_new ();
+ ephy_web_app_provider_set_version (EPHY_WEB_APP_PROVIDER (self->skeleton), 1);
+
+ g_signal_connect (self->skeleton, "handle-get-installed-apps",
+ G_CALLBACK (handle_get_installed_apps), self);
+ g_signal_connect (self->skeleton, "handle-install",
+ G_CALLBACK (handle_install), self);
+ g_signal_connect (self->skeleton, "handle-uninstall",
+ G_CALLBACK (handle_uninstall), self);
+
+ return g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self->skeleton),
+ connection, object_path, error);
+}
+
+static void
+ephy_web_app_provider_service_dbus_unregister (GApplication *application,
+ GDBusConnection *connection,
+ const gchar *object_path)
+{
+ EphyWebAppProviderService *self;
+ GDBusInterfaceSkeleton *skeleton;
+
+ g_debug ("unregistering at object path %s", object_path);
+
+ self = EPHY_WEB_APP_PROVIDER_SERVICE (application);
+ skeleton = G_DBUS_INTERFACE_SKELETON (self->skeleton);
+ if (g_dbus_interface_skeleton_has_connection (skeleton, connection))
+ g_dbus_interface_skeleton_unexport_from_connection (skeleton, connection);
+
+ g_clear_object (&self->skeleton);
+
+ G_APPLICATION_CLASS (ephy_web_app_provider_service_parent_class)->dbus_unregister (application,
+ connection,
+ object_path);
+}
+
+static void
+ephy_web_app_provider_service_class_init (EphyWebAppProviderServiceClass *klass)
+{
+ GApplicationClass *application_class = G_APPLICATION_CLASS (klass);
+
+ application_class->dbus_register = ephy_web_app_provider_service_dbus_register;
+ application_class->dbus_unregister = ephy_web_app_provider_service_dbus_unregister;
+}
+
+EphyWebAppProviderService *
+ephy_web_app_provider_service_new (void)
+{
+ /* Note the application ID is constant for release/devel/canary builds
+ * because we want to always use the same well-known D-Bus name.
+ */
+ g_autofree gchar *app_id = g_strconcat ("org.gnome.Epiphany.WebAppProvider", NULL);
+
+ return g_object_new (EPHY_TYPE_WEB_APP_PROVIDER_SERVICE,
+ "application-id", app_id,
+ NULL);
+}
diff --git a/src/webapp-provider/ephy-webapp-provider.h b/src/webapp-provider/ephy-webapp-provider.h
new file mode 100644
index 000000000..1286cf0e0
--- /dev/null
+++ b/src/webapp-provider/ephy-webapp-provider.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (c) 2021 Matthew Leeds <mwleeds@protonmail.com>
+ *
+ * This file is part of Epiphany.
+ *
+ * Epiphany is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-webapp-provider-generated.h"
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_WEB_APP_PROVIDER_SERVICE (ephy_web_app_provider_service_get_type ())
+
+G_DECLARE_FINAL_TYPE (EphyWebAppProviderService, ephy_web_app_provider_service, EPHY, WEB_APP_PROVIDER_SERVICE, GApplication)
+
+EphyWebAppProviderService *ephy_web_app_provider_service_new (void);
+
+G_END_DECLS
diff --git a/src/webapp-provider/org.gnome.Epiphany.WebAppProvider.xml b/src/webapp-provider/org.gnome.Epiphany.WebAppProvider.xml
new file mode 100644
index 000000000..6c2954dc3
--- /dev/null
+++ b/src/webapp-provider/org.gnome.Epiphany.WebAppProvider.xml
@@ -0,0 +1,85 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+
+ <!--
+ org.gnome.Epiphany.WebAppProvider:
+ @short_description: Webapp provider interface
+
+ The interface used for handling Epiphany Webapps in GNOME Software, or other
+ clients (version 1).
+ -->
+ <interface name="org.gnome.Epiphany.WebAppProvider">
+ <!--
+ GetInstalledApps:
+ @desktop_file_ids: An array of .desktop file names, one for each
+ installed web app, with the .desktop suffix included
+
+ Returns the set of installed Epiphany web applications. The caller can
+ use them with g_desktop_app_info_new() if outside the sandbox.
+ -->
+ <method name="GetInstalledApps">
+ <arg type="as" name="webapps" direction="out" />
+ </method>
+
+ <!--
+ Install:
+ @url: the URL of the web app
+ @name: the human readable name of the web app
+ @install_token: the token acquired via org.freedesktop.portal.InstallDynamicLauncher
+ @desktop_file_id: the desktop file id of the installed app, with a
+ ".desktop" suffix
+
+ Installs a web app. This interface is expected to be used by trusted
+ system components such as GNOME Software, which can acquire an
+ @install_token using the portal method
+ org.freedesktop.portal.DynamicLauncher.RequestInstallToken(). This allows Epiphany
+ to install the web app without user interaction and despite being sandboxed.
+ This is desirable because the user would've already clicked "Install" in
+ Software; they should not have to confirm the operation again in a different
+ app (Epiphany).
+
+ The @install_token must be provided so that Epiphany can complete the
+ installation without a user-facing dialog. The icon given to
+ org.freedesktop.portal.InstallDynamicLauncher.RequestInstallToken() will
+ be used, and the name given to that method should match the @name given here.
+
+ If the arguments passed are invalid this method returns the error
+ `org.gnome.Epiphany.WebAppProvider.Error.InvalidArgs`, and otherwise
+ `org.gnome.Epiphany.WebAppProvider.Error.Failed`.
+ -->
+ <method name="Install">
+ <arg type="s" name="url" direction="in" />
+ <arg type="s" name="name" direction="in" />
+ <arg type="s" name="install_token" direction="in" />
+ <arg type="s" name="desktop_file_id" direction="out" />
+ </method>
+
+ <!--
+ Uninstall:
+ @desktop_file_id: the filename of the .desktop file for an installed web
+ app, with the .desktop suffix
+
+ Uninstalls a web app. Note that the @desktop_file_id is just a filename
+ not a full path, and it's the same one returned by the
+ GetInstalledWebApps() method.
+
+ The error `org.gnome.Epiphany.WebAppProvider.Error.NotInstalled` will be
+ returned if the specified web app is not installed. The other possible
+ error values are `org.gnome.Epiphany.WebAppProvider.Error.InvalidArgs`
+ and `org.gnome.Epiphany.WebAppProvider.Error.Failed`.
+ -->
+ <method name="Uninstall">
+ <arg type="s" name="desktop_file_id" direction="in" />
+ </method>
+ <!--
+ Version:
+
+ The API version number, to be incremented for backwards compatible
+ changes so clients can determine which features are available. For
+ backwards incompatible changes, the interface name will change.
+ -->
+ <property name="Version" type="u" access="read"/>
+ </interface>
+</node>
diff --git a/src/window-commands.c b/src/window-commands.c
index 91d62b14c..32f10091d 100644
--- a/src/window-commands.c
+++ b/src/window-commands.c
@@ -1793,6 +1793,7 @@ save_as_application_proceed (EphyApplicationDialogData *data)
webkit_web_view_get_uri (WEBKIT_WEB_VIEW (data->view)),
app_name,
gtk_image_get_pixbuf (GTK_IMAGE (data->image)),
+ NULL, NULL, /* icon_path, install_token */
data->webapp_options);
if (desktop_file)
@@ -1842,7 +1843,7 @@ dialog_save_as_application_confirmation_cb (GtkDialog *dialog,
gtk_widget_destroy (GTK_WIDGET (dialog));
if (response == GTK_RESPONSE_OK) {
- ephy_web_application_delete (app_id);
+ ephy_web_application_delete (app_id, NULL);
save_as_application_proceed (data);
}
}
diff --git a/tests/ephy-web-app-utils-test.c b/tests/ephy-web-app-utils-test.c
index 5026388db..8f32570fe 100644
--- a/tests/ephy-web-app-utils-test.c
+++ b/tests/ephy-web-app-utils-test.c
@@ -68,7 +68,7 @@ test_web_app_lifetime (void)
/* Test creation */
id = ephy_web_application_get_app_id_from_name (test.name);
- desktop_file = ephy_web_application_create (id, test.url, test.name, NULL, EPHY_WEB_APPLICATION_NONE);
+ desktop_file = ephy_web_application_create (id, test.url, test.name, NULL, NULL, NULL, EPHY_WEB_APPLICATION_NONE);
g_assert_true (g_str_has_prefix (desktop_file, ephy_profile_dir ()));
g_assert_true (g_file_test (desktop_file, G_FILE_TEST_EXISTS));
@@ -119,7 +119,7 @@ test_web_app_lifetime (void)
/* Test delete API */
g_test_message ("DELETE: %s", test.name);
- g_assert_true (ephy_web_application_delete (id));
+ g_assert_true (ephy_web_application_delete (id, NULL));
g_assert_false (g_file_test (desktop_link, G_FILE_TEST_EXISTS));
g_assert_false (g_file_test (desktop_link, G_FILE_TEST_IS_SYMLINK));