[epiphany/mwleeds/webapp-dbus-api: 1/5] Add a WebAppProvider D-Bus interface




commit a412b624038d9ab813d8221b0ddcd7029ab53d54
Author: Phaedrus Leeds <mwleeds protonmail com>
Date:   Mon Nov 29 17:53:02 2021 -0800

    Add a WebAppProvider D-Bus interface
    
    This commit adds a D-Bus service that exposes the web apps managed by
    Epiphany so that a client can enumerate them, install a new one, or
    remove an installed one. The intended client is GNOME Software, since I
    am soon going to add back a webapp plugin to Software which will make
    use of this interface. This is part of a larger effort to improve the
    support for Progressive Web Apps in GNOME (though Epiphany's web apps do
    not support PWA manifest features).
    
    The great thing about having this be a service exposed by Epiphany,
    rather than having Software try to install Epiphany-compatible web apps
    on its own as it used to do, is that the implementation of how to create
    and manage the web apps can stay in Epiphany, and there can never be
    disagreement between Epiphany and Software about the proper on-disk
    format for them (e.g. the algorithm used for generating the app ID from
    the name).
    
    The goal for the PWA project is to support Flatpak'd Epiphany, whereas
    currently only non-Flatpak Epiphany can do web apps. This will be
    accomplished with new portals to allow installing and removing the
    .desktop launchers. The Flatpak support requirement is reflected in the
    design of the API here, specifically the install_token parameter for the
    InstallWebApp method. This token is to be acquired by the client of the
    D-Bus interface (Software) so that the installation can be achieved
    without any additional user interaction since the user would've already
    clicked "Install" in Software. Web app installation directly via
    Flatpak'd Epiphany's UI would involve a dialog created by the portal;
    this is because we don't sandboxed applications in general to be able to
    create desktop launchers without user interaction.
    
    The UninstallWebApp method by contrast doesn't require such a token,
    because the portal can ensure that only apps created by an application
    are deleted by that application.

 data/meson.build                                   |   7 +
 data/org.gnome.Epiphany.WebAppProvider.service.in  |   3 +
 lib/ephy-web-app-utils.c                           |  71 ++++--
 lib/ephy-web-app-utils.h                           |  13 +-
 src/meson.build                                    |  23 +-
 src/webapp-provider/ephy-webapp-provider-main.c    |  56 +++++
 src/webapp-provider/ephy-webapp-provider.c         | 278 +++++++++++++++++++++
 src/webapp-provider/ephy-webapp-provider.h         |  36 +++
 .../org.gnome.Epiphany.WebAppProvider.xml          | 124 +++++++++
 src/window-commands.c                              |   1 +
 tests/ephy-web-app-utils-test.c                    |   2 +-
 11 files changed, 591 insertions(+), 23 deletions(-)
---
diff --git a/data/meson.build b/data/meson.build
index eac6b8224..4d677fce7 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: '@0@.WebAppProvider.service'.format(application_id),
+  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..de411d239
--- /dev/null
+++ b/data/org.gnome.Epiphany.WebAppProvider.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=@appid@.WebAppProvider
+Exec=@libexecdir@/epiphany-webapp-provider
diff --git a/lib/ephy-web-app-utils.c b/lib/ephy-web-app-utils.c
index dd2291833..1b55c92e3 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)
 {
@@ -335,7 +350,7 @@ 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 +380,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 +414,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 +428,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 +467,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 +613,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,15 +640,22 @@ 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);
 }
 
+EphyWebApplication *
+ephy_web_application_for_desktop_path (const char *desktop_path)
+{
+  g_autofree char *profile_dir = g_path_get_dirname (desktop_path);
+  return ephy_web_application_for_profile_directory (profile_dir);
+}
+
 static GList *
 ephy_web_application_get_application_list_internal (gboolean only_legacy)
 {
diff --git a/lib/ephy-web-app-utils.h b/lib/ephy-web-app-utils.h
index 4f98fc678..4a11dd15e 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;
 
 /**
@@ -57,7 +58,13 @@ 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);
 
@@ -69,8 +76,12 @@ 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);
 
+EphyWebApplication *ephy_web_application_for_desktop_path (const char *desktop_path);
+
 void                ephy_web_application_free (EphyWebApplication *app);
 
 gboolean            ephy_web_application_exists (const char *id);
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..eb854141c
--- /dev/null
+++ b/src/webapp-provider/ephy-webapp-provider-main.c
@@ -0,0 +1,56 @@
+/* -*- 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>
+
+gint
+main (gint    argc,
+      gchar **argv)
+{
+  g_autoptr (EphyWebAppProviderService) webapp_provider = NULL;
+  int status;
+  GError *error = NULL;
+
+  /* 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 ();
+
+  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..c6329994c
--- /dev/null
+++ b/src/webapp-provider/ephy-webapp-provider.c
@@ -0,0 +1,278 @@
+/* -*- 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 */
+
+static gboolean
+handle_get_installed_web_apps (EphyWebAppProvider        *skeleton,
+                               GDBusMethodInvocation     *invocation,
+                               EphyWebAppProviderService *self)
+{
+  GVariantBuilder builder;
+  GList *apps;
+  GList *l;
+
+  g_application_hold (G_APPLICATION (self));
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
+
+  apps = ephy_web_application_get_application_list ();
+  for (l = apps; l; l = l->next) {
+    EphyWebApplication *app = (EphyWebApplication *)l->data;
+    g_autofree char *desktop_path = NULL;
+
+    desktop_path = ephy_web_application_get_desktop_path (app);
+    g_assert (desktop_path);
+
+    g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}"));
+    g_variant_builder_add (&builder, "{sv}", "desktop-path",
+                           g_variant_new_take_string (g_steal_pointer (&desktop_path)));
+    g_variant_builder_add (&builder, "{sv}", "name",
+                           g_variant_new_string (app->name));
+    g_variant_builder_add (&builder, "{sv}", "url",
+                           g_variant_new_string (app->url));
+    g_variant_builder_add (&builder, "{sv}", "icon-path",
+                           g_variant_new_string (app->icon_url));
+    g_variant_builder_add (&builder, "{sv}", "install-date",
+                           g_variant_new_uint64 (app->install_date_uint64));
+    g_variant_builder_close (&builder);
+  }
+
+  ephy_web_app_provider_complete_get_installed_web_apps (skeleton, invocation,
+                                                         g_variant_builder_end (&builder));
+
+  ephy_web_application_free_application_list (apps);
+
+  g_application_release (G_APPLICATION (self));
+
+  return TRUE;
+}
+
+static gboolean
+handle_install_web_app (EphyWebAppProvider        *skeleton,
+                        GDBusMethodInvocation     *invocation,
+                        char                      *url,
+                        char                      *name,
+                        char                      *icon_path,
+                        char                      *install_token,
+                        EphyWebAppProviderService *self)
+{
+  g_autofree char *id = NULL;
+  g_autofree char *desktop_path = NULL;
+
+  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).
+   */
+  if (ephy_is_running_inside_sandbox () &&
+      (!install_token || *install_token == '\0')) {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                                           "The install_token is required for sandboxed Epiphany");
+    goto out;
+  }
+  if (!g_uri_is_valid (url, G_URI_FLAGS_NONE, NULL)) {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                                           "The url passed was not valid");
+    goto out;
+  }
+  if (!name || *name == '\0') {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                                           "The name passed was not valid");
+    goto out;
+  }
+  if (!icon_path || !g_path_is_absolute (icon_path)) {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                                           "The icon_path passed was not valid");
+    goto out;
+  }
+
+  id = ephy_web_application_get_app_id_from_name (name);
+
+  /* Note: the icon_path is unlikely to be accessible to us from inside the
+   * Flatpak sandbox but that's okay because we only need to pass it to a
+   * portal.
+   */
+  desktop_path = ephy_web_application_create (id, url, name,
+                                              NULL, /* icon_pixbuf */
+                                              icon_path, install_token, EPHY_WEB_APPLICATION_NONE);
+  if (!desktop_path) {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+                                           "Installing the web application '%s' (%s) failed", name, url);
+    goto out;
+  }
+
+  ephy_web_app_provider_complete_install_web_app (skeleton, invocation);
+
+out:
+  g_application_release (G_APPLICATION (self));
+
+  return TRUE;
+}
+
+static gboolean
+handle_uninstall_web_app (EphyWebAppProvider        *skeleton,
+                          GDBusMethodInvocation     *invocation,
+                          char                      *desktop_path,
+                          EphyWebAppProviderService *self)
+{
+  g_autoptr (EphyWebApplication) app = NULL;
+
+  g_application_hold (G_APPLICATION (self));
+
+  if (!desktop_path || !g_path_is_absolute (desktop_path)) {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                                           "The desktop_path passed was not valid");
+    goto out;
+  }
+
+  app = ephy_web_application_for_desktop_path (desktop_path);
+  if (!app) {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+                                           "The desktop path '%s' does not correspond to an installed web 
app",
+                                           desktop_path);
+    goto out;
+  }
+
+  if (!ephy_web_application_delete (app->id)) {
+    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+                                           "The web application '%s' could not be deleted", app->id);
+    goto out;
+  }
+
+  ephy_web_app_provider_complete_uninstall_web_app (skeleton, invocation);
+
+out:
+  g_application_release (G_APPLICATION (self));
+
+  return TRUE;
+}
+
+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;
+
+  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);
+  ephy_web_app_provider_set_sandboxed (EPHY_WEB_APP_PROVIDER (self->skeleton),
+                                       ephy_is_running_inside_sandbox ());
+
+  g_signal_connect (self->skeleton, "handle-get-installed-web-apps",
+                    G_CALLBACK (handle_get_installed_web_apps), self);
+  g_signal_connect (self->skeleton, "handle-install-web-app",
+                    G_CALLBACK (handle_install_web_app), self);
+  g_signal_connect (self->skeleton, "handle-uninstall-web-app",
+                    G_CALLBACK (handle_uninstall_web_app), 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;
+
+  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_dispose (GObject *object)
+{
+  G_OBJECT_CLASS (ephy_web_app_provider_service_parent_class)->dispose (object);
+}
+
+static void
+ephy_web_app_provider_service_class_init (EphyWebAppProviderServiceClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GApplicationClass *application_class = G_APPLICATION_CLASS (klass);
+
+  object_class->dispose = ephy_web_app_provider_service_dispose;
+
+  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)
+{
+  g_autofree gchar *app_id = g_strconcat (APPLICATION_ID, ".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..67acd2ca5
--- /dev/null
+++ b/src/webapp-provider/org.gnome.Epiphany.WebAppProvider.xml
@@ -0,0 +1,124 @@
+<!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.
+
+      This documentation describes version 1 of this interface.
+  -->
+  <interface name="org.gnome.Epiphany.WebAppProvider">
+    <!--
+        version:
+
+        The API version number.
+    -->
+    <property name="version" type="u" access="read"/>
+
+    <!--
+        sandboxed:
+
+        This indicates whether Epiphany is running under Flatpak or a
+        Flatpak-compatible sandbox which means that portals will be necessary
+        for installing or removing web apps from the host system.
+    -->
+    <property name="sandboxed" type="b" access="read"/>
+
+    <!--
+        GetInstalledWebApps:
+        @webapps: An array of dictionaries, one for each installed Epiphany web
+          app.
+
+        Returns the set of installed Epiphany web applications.
+
+        The following information may be included in each @webapps dictionary:
+        <variablelist>
+          <varlistentry>
+            <term>desktop-path s</term>
+            <listitem><para>
+              The path to the .desktop file on the local filesystem.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>name s</term>
+            <listitem><para>
+              The human readable name of the application.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>url s</term>
+            <listitem><para>
+              The URL of the application.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>icon-path s</term>
+            <listitem><para>
+              The path to the icon on the local filesystem.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>install-date t</term>
+            <listitem><para>
+              The install time, in seconds since the UNIX epoch.
+            </para></listitem>
+          </varlistentry>
+        </variablelist>
+
+        Clients should gracefully handle unrecognized keys in the @webapps
+        dictionary, to allow extending it in the future without using new
+        interface and method names.
+    -->
+    <method name="GetInstalledWebApps">
+      <arg type="aa{sv}" name="webapps" direction="out" />
+    </method>
+
+    <!--
+        InstallWebApp:
+        @url: the URL of the web app
+        @name: the human readable name of the web app
+        @icon_path: the path to the icon on the local filesystem
+        @install_token: the token acquired via org.freedesktop.portal.InstallDynamicLauncher
+
+        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 by talking to the InstallDynamicLauncher portal. 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 can be the empty string if and only if Epiphany is
+        not running as a Flatpak (or similar sandbox, see the "sandboxed" property).
+
+        The @icon_path only needs to be valid for the duration of the method call,
+        since the icon will be copied elsewhere.
+    -->
+    <method name="InstallWebApp">
+      <arg type="s" name="url" direction="in" />
+      <arg type="s" name="name" direction="in" />
+      <arg type="s" name="icon_path" direction="in" />
+      <arg type="s" name="install_token" direction="in" />
+    </method>
+
+    <!--
+        UninstallWebApp:
+        @desktop_path: the path to the .desktop file of an installed web app,
+          as returned by GetInstalledWebApps()
+
+        Uninstalls a web app. Note that the @desktop_path is the target of a
+        symbolic link created in $XDG_DATA_DIRS, not the path of the symbolic
+        link itself. If you use the path returned by GetInstalledWebApps() you
+        don't have to worry about that distinction.
+
+        An error will be returned if the specified web app is not installed.
+    -->
+    <method name="UninstallWebApp">
+      <arg type="s" name="desktop_path" direction="in" />
+    </method>
+  </interface>
+</node>
diff --git a/src/window-commands.c b/src/window-commands.c
index 8b138257d..07ed22cea 100644
--- a/src/window-commands.c
+++ b/src/window-commands.c
@@ -1788,6 +1788,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)
diff --git a/tests/ephy-web-app-utils-test.c b/tests/ephy-web-app-utils-test.c
index 5026388db..2278b5f89 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));
 


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