[gnome-software/mwleeds/pwa-plugin: 2/2] epiphany: Rewrite plugin (WIP)
- From: Phaedrus Leeds <mwleeds src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/mwleeds/pwa-plugin: 2/2] epiphany: Rewrite plugin (WIP)
- Date: Sat, 18 Dec 2021 02:31:21 +0000 (UTC)
commit 641ace5dd2cc1546bf2d39da0bd75ef33ec52dc7
Author: Phaedrus Leeds <mwleeds protonmail com>
Date: Thu Dec 9 16:34:55 2021 -0800
epiphany: Rewrite plugin (WIP)
lib/gs-plugin-loader.c | 4 +-
lib/gs-utils.c | 31 ++
lib/gs-utils.h | 1 +
plugins/epiphany/gs-plugin-epiphany.c | 540 +++++++++++----------
plugins/epiphany/gs-plugin-epiphany.h | 20 +
plugins/epiphany/gs-self-test.c | 33 +-
plugins/epiphany/meson.build | 8 +
.../epiphany/org.gnome.Epiphany.WebAppProvider.xml | 124 +++++
src/gs-installed-page.c | 6 +
9 files changed, 503 insertions(+), 264 deletions(-)
---
diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c
index 0d3ce396a..6031cae7d 100644
--- a/lib/gs-plugin-loader.c
+++ b/lib/gs-plugin-loader.c
@@ -1394,7 +1394,9 @@ gs_plugin_loader_app_is_valid (GsApp *app, gpointer user_data)
gs_plugin_loader_get_app_str (app));
return FALSE;
}
- if (gs_app_get_summary (app) == NULL) {
+ /* web apps usually don't have summaries */
+ if (gs_app_get_kind (app) != AS_COMPONENT_KIND_WEB_APP &&
+ gs_app_get_summary (app) == NULL) {
g_debug ("app invalid as no summary %s",
gs_plugin_loader_get_app_str (app));
return FALSE;
diff --git a/lib/gs-utils.c b/lib/gs-utils.c
index 8e81d11cf..6023b6fb6 100644
--- a/lib/gs-utils.c
+++ b/lib/gs-utils.c
@@ -41,6 +41,7 @@
#include "gs-app.h"
#include "gs-utils.h"
#include "gs-plugin.h"
+#include "gs-icon.h"
#define MB_IN_BYTES (1024 * 1024)
@@ -1427,6 +1428,36 @@ gs_utils_pixbuf_blur (GdkPixbuf *src, guint radius, guint iterations)
gs_pixbuf_blur_private (src, tmp, radius, div_kernel_size);
}
+/**
+ * gs_utils_file_icon_ensure_size:
+ * @icon: the #GIcon created with g_file_icon_new()
+ *
+ * This ensures that gs_icon_set_width() and gs_icon_set_height() have been
+ * called on @icon, by loading the width and height from the underlying file if
+ * needed.
+ **/
+void
+gs_utils_file_icon_ensure_size (GIcon *icon)
+{
+ GFile *file;
+ g_autofree char *file_path = NULL;
+ g_autoptr(GdkPixbuf) pixbuf = NULL;
+
+ if (gs_icon_get_width (icon) != 0)
+ return;
+
+ file = g_file_icon_get_file (G_FILE_ICON (icon));
+ g_assert (G_IS_FILE (file));
+ file_path = g_file_get_path (file);
+ pixbuf = gdk_pixbuf_new_from_file (file_path, NULL);
+ if (pixbuf == NULL)
+ g_warning ("%s: Failed to load pixbuf from %s", G_STRFUNC, file_path);
+ else {
+ gs_icon_set_width (icon, gdk_pixbuf_get_width (pixbuf));
+ gs_icon_set_height (icon, gdk_pixbuf_get_height (pixbuf));
+ }
+}
+
/**
* gs_utils_get_file_size:
* @filename: a file name to get the size of; it can be a file or a directory
diff --git a/lib/gs-utils.h b/lib/gs-utils.h
index b5f590df8..ba9898737 100644
--- a/lib/gs-utils.h
+++ b/lib/gs-utils.h
@@ -111,6 +111,7 @@ gchar *gs_utils_build_unique_id (AsComponentScope scope,
void gs_utils_pixbuf_blur (GdkPixbuf *src,
guint radius,
guint iterations);
+void gs_utils_file_icon_ensure_size (GIcon *icon);
/**
* GsFileSizeIncludeFunc:
diff --git a/plugins/epiphany/gs-plugin-epiphany.c b/plugins/epiphany/gs-plugin-epiphany.c
index 9a83e8004..6a97bcbfc 100644
--- a/plugins/epiphany/gs-plugin-epiphany.c
+++ b/plugins/epiphany/gs-plugin-epiphany.c
@@ -1,322 +1,360 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
*
- * Copyright (C) 2013-2014 Richard Hughes <richard hughsie com>
- * Copyright (C) 2015 Kalev Lember <klember redhat com>
+ * Copyright (C) 2021 Matthew Leeds <mwleeds protonmail com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
-
-#include <string.h>
-
+#include <glib/gi18n.h>
#include <gnome-software.h>
+#include "gs-epiphany-generated.h"
+#include "gs-plugin-epiphany.h"
+
/*
* SECTION:
- * Uses epiphany to launch web applications.
+ * This plugin uses Epiphany to install, launch, and uninstall web applications.
*
- * If the epiphany binary is not present then it self-disables.
+ * If the org.gnome.Epiphany.WebAppProvider D-Bus interface is not present then
+ * it self-disables. This should work with both Flatpak'd and not Flatpak'd
+ * Epiphany, for new enough versions of Epiphany.
*/
-void
-gs_plugin_initialize (GsPlugin *plugin)
+struct _GsPluginEpiphany
+{
+ GsPlugin parent;
+
+ GsEphyWebAppProvider *epiphany_proxy; /* (owned) */
+};
+
+G_DEFINE_TYPE (GsPluginEpiphany, gs_plugin_epiphany, GS_TYPE_PLUGIN)
+
+static void
+gs_epiphany_error_convert (GError **perror)
+{
+ GError *error = perror != NULL ? *perror : NULL;
+
+ /* not set */
+ if (error == NULL)
+ return;
+
+ /* parse remote epiphany-webapp-provider error */
+ if (g_dbus_error_is_remote_error (error)) {
+ g_autofree gchar *remote_error = g_dbus_error_get_remote_error (error);
+
+ g_dbus_error_strip_remote_error (error);
+
+ if (g_str_equal (remote_error, "org.freedesktop.DBus.Error.ServiceUnknown")) {
+ error->code = GS_PLUGIN_ERROR_NOT_SUPPORTED;
+ } else {
+ g_warning ("Can’t reliably fixup remote error ‘%s’", remote_error);
+ error->code = GS_PLUGIN_ERROR_FAILED;
+ }
+ error->domain = GS_PLUGIN_ERROR;
+ return;
+ }
+
+ /* this is allowed for low-level errors */
+ if (gs_utils_error_convert_gio (perror))
+ return;
+
+ /* this is allowed for low-level errors */
+ if (gs_utils_error_convert_gdbus (perror))
+ return;
+}
+
+static void
+proxy_new_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- g_autofree gchar *epiphany = NULL;
-
- /* we can only work with epiphany */
- epiphany = g_find_program_in_path ("epiphany");
- if (epiphany == NULL) {
- gs_plugin_set_enabled (plugin, FALSE);
- g_debug ("disabling '%s' as epiphany does not exist",
- gs_plugin_get_name (plugin));
+ g_autoptr(GTask) task = g_steal_pointer (&user_data);
+ GsPluginEpiphany *self = g_task_get_source_object (task);
+ g_autofree gchar *name_owner = NULL;
+ g_autoptr(GError) local_error = NULL;
+
+ self->epiphany_proxy = gs_ephy_web_app_provider_proxy_new_for_bus_finish (result, &local_error);
+ if (self->epiphany_proxy == NULL) {
+ gs_epiphany_error_convert (&local_error);
+ g_task_return_error (task, g_steal_pointer (&local_error));
+ return;
+ }
+
+ name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (self->epiphany_proxy));
+
+ if (name_owner == NULL) {
+ g_task_return_new_error (task, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED,
+ "Couldn’t create Epiphany WebAppProvider proxy: couldn’t get name
owner");
+ return;
}
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+gs_plugin_epiphany_setup_async (GsPlugin *plugin,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ task = g_task_new (plugin, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_plugin_epiphany_setup_async);
+
+ g_debug ("%s", G_STRFUNC);
+
+ /* Check that the proxy exists (and is owned; it should auto-start) so
+ * we can disable the plugin for systems which don’t have new enough
+ * Epiphany.
+ */
+ gs_ephy_web_app_provider_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.gnome.Epiphany.WebAppProvider",
+ "/org/gnome/Epiphany/WebAppProvider",
+ cancellable,
+ proxy_new_cb,
+ g_steal_pointer (&task));
+}
+
+static gboolean
+gs_plugin_epiphany_setup_finish (GsPlugin *plugin,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+gs_plugin_epiphany_init (GsPluginEpiphany *self)
+{
/* set name of MetaInfo file */
- gs_plugin_set_appstream_id (plugin, "org.gnome.Software.Plugin.Epiphany");
+ gs_plugin_set_appstream_id (GS_PLUGIN (self), "org.gnome.Software.Plugin.Epiphany");
/* need help from appstream */
- gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "appstream");
+ gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_RUN_AFTER, "appstream");
+}
+
+static void
+gs_plugin_epiphany_dispose (GObject *object)
+{
+ GsPluginEpiphany *self = GS_PLUGIN_EPIPHANY (object);
+
+ g_clear_object (&self->epiphany_proxy);
+
+ G_OBJECT_CLASS (gs_plugin_epiphany_parent_class)->dispose (object);
}
void
-gs_plugin_adopt_app (GsPlugin *plugin, GsApp *app)
+gs_plugin_adopt_app (GsPlugin *plugin,
+ GsApp *app)
{
- if (gs_app_get_kind (app) == AS_APP_KIND_WEB_APP &&
+ if (gs_app_get_kind (app) == AS_COMPONENT_KIND_WEB_APP &&
gs_app_get_bundle_kind (app) != AS_BUNDLE_KIND_PACKAGE) {
- gs_app_set_management_plugin (app, gs_plugin_get_name (plugin));
+ gs_app_set_management_plugin (app, plugin);
}
+
+ if (gs_app_get_state (app) == GS_APP_STATE_UNKNOWN)
+ gs_app_set_state (app, GS_APP_STATE_AVAILABLE);
}
-static gchar *
-_gs_app_get_id_nonfull (GsApp *app)
+gboolean
+gs_plugin_add_installed (GsPlugin *plugin,
+ GsAppList *list,
+ GCancellable *cancellable,
+ GError **error)
{
- gchar *id;
- gchar *tmp;
-
- id = g_strdup (gs_app_get_id (app));
- tmp = g_strrstr (id, ".desktop");
- if (tmp != NULL)
- *tmp = '\0';
- return id;
+ GsPluginEpiphany *self = GS_PLUGIN_EPIPHANY (plugin);
+ g_autoptr(GVariant) webapps_v = NULL;
+ gsize n_webapps;
+
+ if (!gs_ephy_web_app_provider_call_get_installed_web_apps_sync (self->epiphany_proxy,
+ &webapps_v,
+ cancellable,
+ error)) {
+ gs_epiphany_error_convert (error);
+ return FALSE;
+ }
+
+ g_assert (g_variant_is_of_type (webapps_v, G_VARIANT_TYPE ("aa{sv}")));
+ n_webapps = g_variant_n_children (webapps_v);
+ g_debug ("%s: epiphany-webapp-provider returned %" G_GSIZE_FORMAT " installed web apps", G_STRFUNC,
n_webapps);
+ for (gsize i = 0; i < n_webapps; i++) {
+ g_autoptr(GVariant) webapp_v = g_variant_get_child_value (webapps_v, i);
+ GVariantDict dict;
+ const gchar *desktop_path;
+ const gchar *name;
+ const gchar *url;
+ const gchar *icon_path = NULL;
+ guint64 install_date = 0;
+ g_autofree char *app_id = NULL;
+ g_autoptr(GsApp) app = NULL;
+
+ g_variant_dict_init (&dict, webapp_v);
+ if (!g_variant_dict_lookup (&dict, "desktop-path", "&s", &desktop_path)) {
+ g_warning ("%s: webapp missing desktop-path", G_STRFUNC);
+ continue;
+ }
+ if (!g_variant_dict_lookup (&dict, "name", "&s", &name)) {
+ g_warning ("%s: webapp missing name", G_STRFUNC);
+ continue;
+ }
+ if (!g_variant_dict_lookup (&dict, "url", "&s", &url)) {
+ g_warning ("%s: webapp missing url", G_STRFUNC);
+ continue;
+ }
+ g_variant_dict_lookup (&dict, "icon-path", "&s", &icon_path);
+ g_variant_dict_lookup (&dict, "install-date", "t", &install_date);
+
+ app_id = g_path_get_basename (desktop_path);
+ //TODO do we need to check a cache here, see if a GsApp already exists?
+ app = gs_app_new (app_id);
+ gs_app_set_management_plugin (app, plugin);
+ gs_app_set_state (app, GS_APP_STATE_INSTALLED);
+ gs_app_set_kind (app, AS_COMPONENT_KIND_WEB_APP);
+ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, name);
+ gs_app_set_url (app, AS_URL_KIND_HOMEPAGE, url);
+ gs_app_set_metadata (app, "epiphany::desktop-path", desktop_path);
+
+ if (icon_path) {
+ g_autoptr(GFile) icon_file = g_file_new_for_path (icon_path);
+ g_autoptr(GIcon) icon = g_file_icon_new (icon_file);
+ gs_utils_file_icon_ensure_size (icon);
+ gs_app_add_icon (app, icon);
+ }
+ if (install_date) {
+ gs_app_set_install_date (app, install_date);
+ }
+ gs_app_list_add (list, app);
+ }
+
+ return TRUE;
}
gboolean
-gs_plugin_app_install (GsPlugin *plugin, GsApp *app,
- GCancellable *cancellable, GError **error)
+gs_plugin_app_install (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error)
{
- AsIcon *icon;
+ GsPluginEpiphany *self = GS_PLUGIN_EPIPHANY (plugin);
+ const char *url;
+ const char *name;
GPtrArray *icons;
- gboolean ret = TRUE;
- gsize kf_length;
- g_autoptr(GError) error_local = NULL;
- g_autofree gchar *app_desktop = NULL;
- g_autofree gchar *epi_desktop = NULL;
- g_autofree gchar *epi_dir = NULL;
- g_autofree gchar *epi_icon = NULL;
- g_autofree gchar *exec = NULL;
- g_autofree gchar *hash = NULL;
- g_autofree gchar *id_nonfull = NULL;
- g_autofree gchar *kf_data = NULL;
- g_autofree gchar *wmclass = NULL;
- g_autoptr(GKeyFile) kf = NULL;
- g_autoptr(GFile) symlink_desktop = NULL;
- g_autoptr(GFile) symlink_icon = NULL;
- const gchar *url = NULL;
-
- /* only process this app if was created by this plugin */
- if (g_strcmp0 (gs_app_get_management_plugin (app),
- gs_plugin_get_name (plugin)) != 0)
+ g_autofree char *icon_path = NULL;
+
+ if (!gs_app_has_management_plugin (app, plugin))
return TRUE;
- /* create the correct directory */
- id_nonfull = _gs_app_get_id_nonfull (app);
- hash = g_compute_checksum_for_string (G_CHECKSUM_SHA1, gs_app_get_name (app), -1);
- epi_dir = g_strdup_printf ("%s/epiphany/app-%s-%s",
- g_get_user_config_dir (),
- id_nonfull,
- hash);
- g_mkdir_with_parents (epi_dir, 0755);
-
- /* symlink icon */
- epi_icon = g_build_filename (epi_dir, "app-icon.png", NULL);
- symlink_icon = g_file_new_for_path (epi_icon);
- icons = gs_app_get_icons (app);
- if (icons->len == 0) {
- g_set_error (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_NOT_SUPPORTED,
- "no icons for %s",
+ url = gs_app_get_url (app, AS_URL_KIND_HOMEPAGE);
+ if (url == NULL || *url == '\0') {
+ g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED,
+ "Can't install web app %s without url",
gs_app_get_id (app));
return FALSE;
}
- icon = g_ptr_array_index (icons, 0);
- if (as_icon_get_filename (icon) == NULL) {
- g_set_error (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_NOT_SUPPORTED,
- "no filename for icon %s",
- as_icon_get_name (icon));
+ name = gs_app_get_name (app);
+ if (name == NULL || *name == '\0') {
+ g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED,
+ "Can't install web app %s without name",
+ gs_app_get_id (app));
return FALSE;
}
- ret = g_file_make_symbolic_link (symlink_icon,
- as_icon_get_filename (icon),
- NULL,
- &error_local);
- if (!ret) {
- if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
- g_debug ("ignoring icon symlink failure: %s",
- error_local->message);
- } else {
- g_set_error (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_WRITE_FAILED,
- "Can't symlink icon: %s",
- error_local->message);
- return FALSE;
+ icons = gs_app_get_icons (app);
+ for (guint i = 0; icons != NULL && i < icons->len; i++) {
+ GIcon *icon = g_ptr_array_index (icons, i);
+ /* Note: GsRemoteIcon will work on this GFileIcon code path.
+ * The icons plugin should have called
+ * gs_app_ensure_icons_downloaded() for us
+ */
+ if (G_IS_FILE_ICON (icon)) {
+ icon_path = g_file_get_path (g_file_icon_get_file (G_FILE_ICON (icon)));
+ break;
}
}
-
- /* add desktop file */
- wmclass = g_strdup_printf ("%s-%s", id_nonfull, hash);
- kf = g_key_file_new ();
- g_key_file_set_string (kf,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_NAME,
- gs_app_get_name (app));
- g_key_file_set_string (kf,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_COMMENT,
- gs_app_get_summary (app));
- url = gs_app_get_launchable (app, AS_LAUNCHABLE_KIND_URL);
- if (url == NULL)
- url = gs_app_get_url (app, AS_URL_KIND_HOMEPAGE);
- exec = g_strdup_printf ("epiphany --application-mode --profile=\"%s\" %s",
- epi_dir,
- url);
- g_key_file_set_string (kf,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_EXEC,
- exec);
- g_key_file_set_boolean (kf,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY,
- TRUE);
- g_key_file_set_boolean (kf,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_TERMINAL,
- FALSE);
- g_key_file_set_boolean (kf,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
- FALSE);
- g_key_file_set_string (kf,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_TYPE,
- G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
- g_key_file_set_string (kf,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_ICON,
- epi_icon);
- g_key_file_set_string (kf,
- G_KEY_FILE_DESKTOP_GROUP,
- G_KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS,
- wmclass);
-
- /* save keyfile */
- kf_data = g_key_file_to_data (kf, &kf_length, error);
- if (kf_data == NULL)
- return FALSE;
- epi_desktop = g_strdup_printf ("%s/%s.desktop", epi_dir, wmclass);
- if (!g_file_set_contents (epi_desktop, kf_data, (gssize) kf_length, error))
- return FALSE;
-
- /* symlink it to somewhere the shell will notice */
- app_desktop = g_build_filename (g_get_user_data_dir (),
- "applications",
- gs_app_get_id (app),
- NULL);
- symlink_desktop = g_file_new_for_path (app_desktop);
- ret = g_file_make_symbolic_link (symlink_desktop,
- epi_desktop,
- NULL,
- error);
- if (!ret) {
- gs_utils_error_convert_gio (error);
+ if (icon_path == NULL) {
+ g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED,
+ "Can't install web app %s without icon",
+ gs_app_get_id (app));
return FALSE;
}
- /* update state */
- gs_app_set_state (app, AS_APP_STATE_INSTALLING);
- gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+ //TODO get install_token, do install
+
return TRUE;
}
gboolean
-gs_plugin_app_remove (GsPlugin *plugin, GsApp *app,
- GCancellable *cancellable, GError **error)
+gs_plugin_app_remove (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error)
{
- const gchar *epi_desktop;
- g_autofree gchar *app_desktop = NULL;
- g_autoptr(GFile) file_epi = NULL;
- g_autoptr(GFile) file_app = NULL;
-
- /* only process this app if was created by this plugin */
- if (g_strcmp0 (gs_app_get_management_plugin (app),
- gs_plugin_get_name (plugin)) != 0)
+ GsPluginEpiphany *self = GS_PLUGIN_EPIPHANY (plugin);
+ const char *desktop_path;
+
+ if (!gs_app_has_management_plugin (app, plugin))
return TRUE;
- /* remove the epi 'config' file */
- gs_app_set_state (app, AS_APP_STATE_REMOVING);
- epi_desktop = gs_app_get_source_id_default (app);
- file_epi = g_file_new_for_path (epi_desktop);
- if (!g_file_delete (file_epi, NULL, error))
+ desktop_path = gs_app_get_metadata_item (app, "epiphany::desktop-path");
+ if (desktop_path == NULL) {
+ g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED,
+ "Can't remove app %s without desktop file path",
+ gs_app_get_id (app));
return FALSE;
+ }
- /* remove the shared desktop file */
- app_desktop = g_build_filename (g_get_user_data_dir (),
- "applications",
- gs_app_get_id (app),
- NULL);
- file_app = g_file_new_for_path (app_desktop);
- if (!g_file_delete (file_app, NULL, error)) {
- gs_utils_error_convert_gio (error);
+ if (!gs_ephy_web_app_provider_call_uninstall_web_app_sync (self->epiphany_proxy,
+ desktop_path,
+ cancellable,
+ error)) {
+ gs_epiphany_error_convert (error);
return FALSE;
}
- gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
+
return TRUE;
}
gboolean
-gs_plugin_refine_app (GsPlugin *plugin,
- GsApp *app,
- GsPluginRefineFlags flags,
- GCancellable *cancellable,
- GError **error)
+gs_plugin_refine (GsPlugin *plugin,
+ GsAppList *list,
+ GsPluginRefineFlags flags,
+ GCancellable *cancellable,
+ GError **error)
{
- const gchar *name;
- g_autofree gchar *fn = NULL;
- g_autofree gchar *hash = NULL;
- g_autofree gchar *id_nonfull = NULL;
-
- /* only process this app if was created by this plugin */
- if (g_strcmp0 (gs_app_get_management_plugin (app),
- gs_plugin_get_name (plugin)) != 0)
- return TRUE;
-
- gs_app_set_size_installed (app, 4096);
-
- /* i guess this is technically true */
- gs_app_add_kudo (app, GS_APP_KUDO_SANDBOXED_SECURE);
+ //TODO
- name = gs_app_get_name (app);
- if (name == NULL) {
- g_set_error (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_INVALID_FORMAT,
- "name unset for %s",
- gs_app_get_id (app));
- return FALSE;
- }
- if (gs_app_get_summary (app) == NULL) {
- g_debug ("faking summary for %s", gs_app_get_id (app));
- gs_app_set_summary (app, GS_APP_QUALITY_LOWEST,
- "Web Application");
- }
- hash = g_compute_checksum_for_string (G_CHECKSUM_SHA1, name, -1);
- id_nonfull = _gs_app_get_id_nonfull (app);
- fn = g_strdup_printf ("%s/epiphany/app-%s-%s/%s-%s.desktop",
- g_get_user_config_dir (),
- id_nonfull,
- hash,
- id_nonfull,
- hash);
- /* try the new-style location */
- if (!g_file_test (fn, G_FILE_TEST_EXISTS)) {
- g_free (fn);
- fn = g_strdup_printf ("%s/epiphany/app-%s/%s.desktop",
- g_get_user_config_dir (),
- id_nonfull, id_nonfull);
- }
- if (g_file_test (fn, G_FILE_TEST_EXISTS)) {
- gs_app_set_state (app, AS_APP_STATE_INSTALLED);
- gs_app_add_source_id (app, fn);
- gs_app_set_management_plugin (app, gs_plugin_get_name (plugin));
- return TRUE;
- }
- gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
return TRUE;
}
gboolean
-gs_plugin_launch (GsPlugin *plugin,
- GsApp *app,
- GCancellable *cancellable,
- GError **error)
+gs_plugin_launch (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error)
{
- /* only process this app if was created by this plugin */
- if (g_strcmp0 (gs_app_get_management_plugin (app),
- gs_plugin_get_name (plugin)) != 0)
+ if (!gs_app_has_management_plugin (app, plugin))
return TRUE;
+
return gs_plugin_app_launch (plugin, app, error);
}
+
+static void
+gs_plugin_epiphany_class_init (GsPluginEpiphanyClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GsPluginClass *plugin_class = GS_PLUGIN_CLASS (klass);
+
+ object_class->dispose = gs_plugin_epiphany_dispose;
+
+ plugin_class->setup_async = gs_plugin_epiphany_setup_async;
+ plugin_class->setup_finish = gs_plugin_epiphany_setup_finish;
+}
+
+GType
+gs_plugin_query_type (void)
+{
+ return GS_TYPE_PLUGIN_EPIPHANY;
+}
diff --git a/plugins/epiphany/gs-plugin-epiphany.h b/plugins/epiphany/gs-plugin-epiphany.h
new file mode 100644
index 000000000..24f6565dd
--- /dev/null
+++ b/plugins/epiphany/gs-plugin-epiphany.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2021 Matthew Leeds <mwleeds>protonmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#pragma once
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_PLUGIN_EPIPHANY (gs_plugin_epiphany_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsPluginEpiphany, gs_plugin_epiphany, GS, PLUGIN_EPIPHANY, GsPlugin)
+
+G_END_DECLS
diff --git a/plugins/epiphany/gs-self-test.c b/plugins/epiphany/gs-self-test.c
index 2d2f47888..1c7b42c6e 100644
--- a/plugins/epiphany/gs-self-test.c
+++ b/plugins/epiphany/gs-self-test.c
@@ -1,4 +1,5 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
*
* Copyright (C) 2013-2017 Richard Hughes <richard hughsie com>
*
@@ -24,8 +25,8 @@ gs_plugins_epiphany_func (GsPluginLoader *plugin_loader)
return;
/* a webapp with a local icon */
- app = gs_app_new ("arachne.desktop");
- gs_app_set_kind (app, AS_APP_KIND_WEB_APP);
+ app = gs_app_new ("app.squoosh.webapp.desktop");
+ gs_app_set_kind (app, AS_COMPONENT_KIND_WEB_APP);
plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_REFINE,
"app", app,
"refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
@@ -33,10 +34,10 @@ gs_plugins_epiphany_func (GsPluginLoader *plugin_loader)
ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error);
gs_test_flush_main_context ();
g_assert_no_error (error);
- g_assert (ret);
+ g_assert_true (ret);
- g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_AVAILABLE);
- g_assert (gs_app_get_pixbuf (app) != NULL);
+ g_assert_cmpint (gs_app_get_state (app), ==, GS_APP_STATE_AVAILABLE);
+ g_assert_nonnull (gs_app_get_icons (app));
}
int
@@ -47,7 +48,7 @@ main (int argc, char **argv)
g_autofree gchar *xml = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GsPluginLoader) plugin_loader = NULL;
- const gchar *whitelist[] = {
+ const gchar *allowlist[] = {
"appstream",
"epiphany",
"icons",
@@ -58,15 +59,23 @@ main (int argc, char **argv)
g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
g_setenv ("GS_XMLB_VERBOSE", "1", TRUE);
+ /* Use an icon we already have locally */
fn = gs_test_get_filename (TESTDATADIR, "icons/hicolor/scalable/org.gnome.Software.svg");
g_assert (fn != NULL);
xml = g_strdup_printf ("<?xml version=\"1.0\"?>\n"
- "<components version=\"0.9\">\n"
+ "<components version=\"0.14\">\n"
" <component type=\"webapp\">\n"
- " <id>arachne.desktop</id>\n"
- " <name>test</name>\n"
- " <pkgname>test</pkgname>\n"
+ " <id>app.squoosh.webapp.desktop</id>\n"
+ " <metadata_license>CC0-1.0</metadata_license>\n"
+ " <project_license>Apache-2.0</project_license>\n"
+ " <name>Squoosh</name>\n"
+ " <summary>Compress and compare images with different codecs, right in your
browser</summary>\n"
+ " <launchable type=\"url\">https://squoosh.app/</launchable>\n"
" <icon type=\"remote\">file://%s</icon>\n"
+ " <categories>\n"
+ " <category>Utility</category>\n"
+ " </categories>\n"
+ " <pkgname>test</pkgname>\n"
" </component>\n"
" <info>\n"
" <scope>user</scope>\n"
@@ -82,12 +91,12 @@ main (int argc, char **argv)
gs_plugin_loader_add_location (plugin_loader, LOCALPLUGINDIR);
gs_plugin_loader_add_location (plugin_loader, LOCALPLUGINDIR_CORE);
ret = gs_plugin_loader_setup (plugin_loader,
- (gchar**) whitelist,
+ (gchar**) allowlist,
NULL,
NULL,
&error);
g_assert_no_error (error);
- g_assert (ret);
+ g_assert_true (ret);
/* plugin tests go here */
g_test_add_data_func ("/gnome-software/plugins/epiphany",
diff --git a/plugins/epiphany/meson.build b/plugins/epiphany/meson.build
index 9d57c4d16..f7447c276 100644
--- a/plugins/epiphany/meson.build
+++ b/plugins/epiphany/meson.build
@@ -1,7 +1,15 @@
cargs = ['-DG_LOG_DOMAIN="GsPluginEpiphany"']
+epiphany_generated = gnome.gdbus_codegen(
+ 'gs-epiphany-generated',
+ sources : ['org.gnome.Epiphany.WebAppProvider.xml'],
+ interface_prefix : 'org.gnome.Epiphany',
+ namespace : 'GsEphy',
+)
+
shared_module(
'gs_plugin_epiphany',
+ epiphany_generated,
sources : 'gs-plugin-epiphany.c',
include_directories : [
include_directories('../..'),
diff --git a/plugins/epiphany/org.gnome.Epiphany.WebAppProvider.xml
b/plugins/epiphany/org.gnome.Epiphany.WebAppProvider.xml
new file mode 100644
index 000000000..67acd2ca5
--- /dev/null
+++ b/plugins/epiphany/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/gs-installed-page.c b/src/gs-installed-page.c
index 6da76365a..78ae6d464 100644
--- a/src/gs-installed-page.c
+++ b/src/gs-installed-page.c
@@ -311,9 +311,15 @@ gs_installed_page_is_actual_app (GsApp *app)
{
if (gs_app_get_description (app) != NULL)
return TRUE;
+
/* special snowflake */
if (g_strcmp0 (gs_app_get_id (app), "google-chrome.desktop") == 0)
return TRUE;
+
+ /* web apps sometimes don't have descriptions */
+ if (gs_app_get_kind (app) == AS_COMPONENT_KIND_WEB_APP)
+ return TRUE;
+
g_debug ("%s is not an actual app", gs_app_get_unique_id (app));
return FALSE;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]