[epiphany/pgriffis/web-extension/commands: 2/4] WebExtensions: Add initial commands API
- From: Marge Bot <marge-bot src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/pgriffis/web-extension/commands: 2/4] WebExtensions: Add initial commands API
- Date: Sun, 10 Jul 2022 02:09:23 +0000 (UTC)
commit f3b9700b91cf81a086d072addcfcf60229c10abe
Author: Jamie Murphy <hello jamiethalacker dev>
Date: Thu Jun 30 19:50:33 2022 -0700
WebExtensions: Add initial commands API
Part-of: <https://gitlab.gnome.org/GNOME/epiphany/-/merge_requests/1165>
.../resources/js/webextensions.js | 7 +
src/webextension/api/commands.c | 296 +++++++++++++++++++++
src/webextension/api/commands.h | 36 +++
src/webextension/ephy-web-extension-manager.c | 2 +
src/webextension/ephy-web-extension.c | 136 ++++++++++
src/webextension/ephy-web-extension.h | 14 +
src/webextension/meson.build | 1 +
7 files changed, 492 insertions(+)
---
diff --git a/embed/web-process-extension/resources/js/webextensions.js
b/embed/web-process-extension/resources/js/webextensions.js
index de6454ab6..b63bb1078 100644
--- a/embed/web-process-extension/resources/js/webextensions.js
+++ b/embed/web-process-extension/resources/js/webextensions.js
@@ -13,6 +13,13 @@ window.browser.alarms = {
onAlarm: new EphyEventListener (),
};
+window.browser.commands = {
+ getAll: function (...args) { return ephy_message ('commands.getAll', args); },
+ reset: function (...args) { return ephy_message ('commands.reset', args); },
+ update: function (...args) { return ephy_message ('commands.update', args); },
+ onCommand: new EphyEventListener (),
+};
+
window.browser.windows = {
onRemoved: new EphyEventListener (),
};
diff --git a/src/webextension/api/commands.c b/src/webextension/api/commands.c
new file mode 100644
index 000000000..fde025f72
--- /dev/null
+++ b/src/webextension/api/commands.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright © 2022 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-shell.h"
+
+#include "api-utils.h"
+#include "commands.h"
+
+/* Local command struct. */
+typedef struct {
+ EphyWebExtension *web_extension; /* Parent object */
+ char *name;
+ char *accelerator;
+ char *description;
+} Command;
+
+static void
+command_destroy (Command *cmd)
+{
+ /* g_clear_pointer (&cmd->name); */
+ g_free (cmd);
+}
+
+static void
+on_command_activated (GAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
+ Command *cmd = user_data;
+ JsonNode *node;
+ node = json_node_init_string (json_node_alloc (), cmd->name);
+
+ ephy_web_extension_manager_emit_in_extension_views (manager,
+ cmd->web_extension,
+ "commands.onCommand",
+ json_to_string (node, FALSE));
+}
+
+static void
+setup_actions (Command *cmd)
+{
+ GSimpleAction *action = g_simple_action_new (cmd->name, NULL);
+ g_signal_connect (action, "activate", G_CALLBACK (on_command_activated), cmd);
+ g_action_map_add_action (G_ACTION_MAP (ephy_shell_get_default ()), (GAction *)action);
+ gtk_application_set_accels_for_action (GTK_APPLICATION (ephy_shell_get_default ()),
+ g_strdup_printf ("app.%s", cmd->name),
+ (const char *[]) {
+ cmd->accelerator,
+ NULL,
+ });
+}
+
+static Command *
+create_command (EphyWebExtension *self,
+ guint cmd)
+{
+ char *shortcut;
+ char *suggested_key;
+ char *description;
+ Command *command = g_new0 (Command, 1);
+
+ ephy_web_extension_get_command_data_from_index (self,
+ cmd,
+ &shortcut,
+ &suggested_key,
+ &description);
+
+ command->web_extension = self;
+ command->name = g_strdup (shortcut);
+ command->accelerator = g_strdup (suggested_key);
+ command->description = g_strdup (description);
+
+ setup_actions (command);
+
+ return command;
+}
+
+static GHashTable *
+get_commands (EphyWebExtension *self)
+{
+ GList *commands = ephy_web_extension_get_commands (self);
+ Command *cmd = NULL;
+
+ GHashTable *cmds = g_object_get_data (G_OBJECT (self), "commands");
+ if (cmds)
+ return cmds;
+
+ cmds = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)command_destroy);
+ g_object_set_data_full (G_OBJECT (self), "commands", cmds, (GDestroyNotify)g_hash_table_destroy);
+
+ for (GList *list = commands; list && list->data; list = list->next) {
+ cmd = create_command (self, g_list_index (list, list->data));
+
+ g_hash_table_replace (cmds, cmd->name, cmd);
+ }
+ return cmds;
+}
+
+static JsonNode *
+command_to_node (Command *cmd)
+{
+ JsonNode *node;
+ JsonObject *obj;
+
+ if (!cmd)
+ return NULL;
+
+ node = json_node_init_object (json_node_alloc (), json_object_new ());
+ obj = json_node_get_object (node);
+ json_object_set_string_member (obj, "name", cmd->name);
+ json_object_set_string_member (obj, "shortcut", cmd->accelerator);
+ json_object_set_string_member (obj, "description", cmd->description);
+
+ return node;
+}
+
+char *
+create_accelerator (char *orig_string)
+{
+ char **accelerator_keys = NULL;
+ char *accelerator = "";
+
+ if (strchr (orig_string, '<') != NULL || strchr (orig_string, '>') != NULL)
+ return orig_string;
+
+ accelerator_keys = g_strsplit ((const gchar *)orig_string, "+", 0);
+
+ for (int i = 0; accelerator_keys[i]; i++) {
+ /* We have to use 2 here, as F# keys are treated like normal keys. */
+ if (strlen (accelerator_keys[i]) > 3) {
+ accelerator = g_strdup_printf ("%s<%s>", accelerator, accelerator_keys[i]);
+ } else {
+ accelerator = g_strdup_printf ("%s%s", accelerator, accelerator_keys[i]);
+ }
+ }
+
+ return accelerator;
+}
+
+static char *
+commands_handler_get_all (EphyWebExtension *self,
+ char *name,
+ JSCValue *args,
+ WebKitWebView *web_view,
+ GError **error)
+{
+ GHashTable *commands = get_commands (self);
+ g_autoptr (JsonNode) node = json_node_init_array (json_node_alloc (), json_array_new ());
+ JsonArray *rel = json_node_get_array (node);
+ GHashTableIter iter;
+ Command *cmd;
+
+ g_hash_table_iter_init (&iter, commands);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&cmd))
+ json_array_add_element (rel, command_to_node (cmd));
+
+ return json_to_string (node, FALSE);
+}
+
+static char *
+commands_handler_reset (EphyWebExtension *self,
+ char *name,
+ JSCValue *args,
+ WebKitWebView *web_view,
+ GError **error)
+{
+ GHashTable *commands = get_commands (self);
+ g_autoptr (JSCValue) name_value = jsc_value_object_get_property_at_index (args, 0);
+ g_autofree char *name_str = NULL;
+ Command *cmd = NULL;
+ char *shortcut;
+ char *suggested_key;
+ char *description;
+
+ if (!jsc_value_is_string (name_value))
+ name_str = g_strdup ("");
+ else
+ name_str = jsc_value_to_string (name_value);
+
+ if (g_hash_table_lookup (commands, name_str)) {
+ cmd = g_hash_table_lookup (commands, name_str);
+ ephy_web_extension_get_command_data_from_name (self,
+ name_str,
+ &shortcut,
+ &suggested_key,
+ &description);
+
+ cmd->name = g_strdup (shortcut);
+ cmd->accelerator = g_strdup (suggested_key);
+ cmd->description = g_strdup (description);
+ }
+
+ return NULL;
+}
+
+static char *
+commands_handler_update (EphyWebExtension *self,
+ char *name,
+ JSCValue *args,
+ WebKitWebView *web_view,
+ GError **error)
+{
+ GHashTable *commands = get_commands (self);
+ g_autoptr (JSCValue) obj = jsc_value_object_get_property_at_index (args, 0);
+ Command *cmd = NULL;
+ g_autofree char *name_str = NULL;
+ g_autofree char *desc_str = NULL;
+ g_autofree char *shortcut_str = NULL;
+
+ if (!jsc_value_is_object (obj))
+ return NULL;
+ else {
+ if (!jsc_value_object_has_property (obj, "name"))
+ return NULL;
+ else
+ name_str = jsc_value_to_string (jsc_value_object_get_property (obj, "name"));
+
+ if (jsc_value_object_has_property (obj, "description"))
+ desc_str = jsc_value_to_string (jsc_value_object_get_property (obj, "description"));
+
+ if (jsc_value_object_has_property (obj, "shortcut")) {
+ shortcut_str = jsc_value_to_string (jsc_value_object_get_property (obj, "shortcut"));
+ shortcut_str = create_accelerator (shortcut_str);
+ }
+ }
+
+ if (g_hash_table_lookup (commands, name_str)) {
+ cmd = g_hash_table_lookup (commands, name_str);
+ cmd->name = g_strdup (name_str);
+ cmd->accelerator = g_strdup (shortcut_str);
+ cmd->description = g_strdup (desc_str);
+ gtk_application_set_accels_for_action (GTK_APPLICATION (ephy_shell_get_default ()),
+ g_strdup_printf ("app.%s", cmd->name),
+ (const char *[]) {
+ cmd->accelerator,
+ NULL,
+ });
+ }
+
+ return NULL;
+}
+
+static EphyWebExtensionSyncApiHandler commands_handlers[] = {
+ {"getAll", commands_handler_get_all},
+ {"reset", commands_handler_reset},
+ {"update", commands_handler_update}
+};
+
+void
+ephy_web_extension_api_commands_handler (EphyWebExtension *self,
+ char *name,
+ JSCValue *args,
+ WebKitWebView *web_view,
+ GTask *task)
+{
+ g_autoptr (GError) error = NULL;
+
+ for (guint idx = 0; idx < G_N_ELEMENTS (commands_handlers); idx++) {
+ EphyWebExtensionSyncApiHandler handler = commands_handlers[idx];
+ char *ret;
+
+ if (g_strcmp0 (handler.name, name) == 0) {
+ ret = handler.execute (self, name, args, web_view, &error);
+
+ if (error)
+ g_task_return_error (task, g_steal_pointer (&error));
+ else
+ g_task_return_pointer (task, ret, g_free);
+
+ return;
+ }
+ }
+
+ g_warning ("%s(): '%s' not implemented by Epiphany!", __FUNCTION__, name);
+ error = g_error_new_literal (WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "Not Implemented");
+ g_task_return_error (task, g_steal_pointer (&error));
+}
diff --git a/src/webextension/api/commands.h b/src/webextension/api/commands.h
new file mode 100644
index 000000000..0cb315883
--- /dev/null
+++ b/src/webextension/api/commands.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2022 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/>.
+ */
+
+
+#pragma once
+
+#include "ephy-web-extension.h"
+
+#include <webkit2/webkit2.h>
+
+G_BEGIN_DECLS
+
+void ephy_web_extension_api_commands_handler (EphyWebExtension *self,
+ char *name,
+ JSCValue *value,
+ WebKitWebView *web_view,
+ GTask *task);
+
+G_END_DECLS
diff --git a/src/webextension/ephy-web-extension-manager.c b/src/webextension/ephy-web-extension-manager.c
index 4ca8033d2..40ded52c9 100644
--- a/src/webextension/ephy-web-extension-manager.c
+++ b/src/webextension/ephy-web-extension-manager.c
@@ -36,6 +36,7 @@
#include "ephy-web-view.h"
#include "api/alarms.h"
+#include "api/commands.h"
#include "api/cookies.h"
#include "api/downloads.h"
#include "api/menus.h"
@@ -71,6 +72,7 @@ G_DEFINE_TYPE (EphyWebExtensionManager, ephy_web_extension_manager, G_TYPE_OBJEC
EphyWebExtensionApiHandler api_handlers[] = {
{"alarms", ephy_web_extension_api_alarms_handler},
+ {"commands", ephy_web_extension_api_commands_handler},
{"cookies", ephy_web_extension_api_cookies_handler},
{"downloads", ephy_web_extension_api_downloads_handler},
{"menus", ephy_web_extension_api_menus_handler},
diff --git a/src/webextension/ephy-web-extension.c b/src/webextension/ephy-web-extension.c
index 9ddb28081..6cc89f69a 100644
--- a/src/webextension/ephy-web-extension.c
+++ b/src/webextension/ephy-web-extension.c
@@ -75,6 +75,12 @@ typedef struct {
WebKitUserStyleSheet *style;
} WebExtensionCustomCSS;
+typedef struct {
+ char *shortcut;
+ char *suggested_key;
+ char *description;
+} WebExtensionCommand;
+
struct _EphyWebExtension {
GObject parent_instance;
@@ -105,6 +111,7 @@ struct _EphyWebExtension {
char *local_storage_path;
JsonNode *local_storage;
GHashTable *web_accessible_resources;
+ GList *commands;
};
G_DEFINE_QUARK (web - extension - error - quark, web_extension_error)
@@ -752,6 +759,131 @@ web_extension_add_web_accessible_resource (JsonArray *array,
g_hash_table_add (self->web_accessible_resources, g_strdup (web_accessible_resource));
}
+char *
+ephy_web_extension_get_command_accelerator (JsonObject *suggested_key_object)
+{
+ const char *orig_string = NULL;
+ char **accelerator_keys = NULL;
+ char *accelerator = "";
+
+ if (!ephy_json_object_get_string (suggested_key_object, "default") &&
+ !ephy_json_object_get_string (suggested_key_object, "linux")) {
+ return NULL;
+ } else if (ephy_json_object_get_string (suggested_key_object, "default")) {
+ orig_string = ephy_json_object_get_string (suggested_key_object, "default");
+ } else if (ephy_json_object_get_string (suggested_key_object, "linux")) {
+ orig_string = ephy_json_object_get_string (suggested_key_object, "linux");
+ }
+
+ accelerator_keys = g_strsplit ((const gchar *)orig_string, "+", 0);
+
+ for (int i = 0; accelerator_keys[i]; i++) {
+ /* We have to use 2 here, as F# keys are treated like normal keys. */
+ if (strlen (accelerator_keys[i]) > 2) {
+ accelerator = g_strdup_printf ("%s<%s>", accelerator, accelerator_keys[i]);
+ } else {
+ accelerator = g_strdup_printf ("%s%s", accelerator, accelerator_keys[i]);
+ }
+ }
+
+ return accelerator;
+}
+
+static void
+web_extension_parse_commands (EphyWebExtension *self,
+ JsonObject *object)
+{
+ JsonNode *node = NULL;
+ JsonObject *cmd_object = NULL;
+ JsonObject *key_object = NULL;
+ WebExtensionCommand *cmd = g_malloc0 (sizeof (WebExtensionCommand));
+ const char *description;
+ const char *shortcut;
+ const char *suggested_key;
+
+ for (GList *list = json_object_get_members (object); list && list->data; list = list->next) {
+ node = json_object_get_member (object, (gchar *)list->data);
+ cmd_object = ephy_json_node_get_object (node);
+ key_object = ephy_json_object_get_object (cmd_object, "suggested_key");
+
+ if (!cmd_object) {
+ LOG ("Skipping command as value is invalid");
+ return;
+ }
+
+ description = ephy_json_object_get_string (cmd_object, "description");
+
+ shortcut = (gchar *)list->data;
+ if (!shortcut) {
+ LOG ("Skipping command as value is invalid");
+ return;
+ }
+
+ cmd->description = g_strdup (description);
+ cmd->shortcut = g_strdup (shortcut);
+
+ suggested_key = ephy_web_extension_get_command_accelerator (key_object);
+
+ if (!g_strcmp0 (suggested_key, "") == 0)
+ cmd->suggested_key = g_strdup (suggested_key);
+
+ self->commands = g_list_append (self->commands, cmd);
+ }
+}
+
+void *
+ephy_web_extension_get_command_data_from_index (EphyWebExtension *self,
+ guint command,
+ char **shortcut,
+ char **suggested_key,
+ char **description)
+{
+ WebExtensionCommand *cmd = g_list_nth_data (self->commands, command);
+
+ *shortcut = strdup (cmd->shortcut);
+ *suggested_key = strdup (cmd->suggested_key);
+ *description = strdup (cmd->description);
+
+ return NULL;
+}
+
+void *
+ephy_web_extension_get_command_data_from_name (EphyWebExtension *self,
+ const char *name,
+ char **shortcut,
+ char **suggested_key,
+ char **description)
+{
+ WebExtensionCommand *cmd = NULL;
+
+ for (GList *list = self->commands; list && list->data; list = list->next) {
+ cmd = list->data;
+
+ if (strcmp (cmd->shortcut, name) == 0) {
+ *shortcut = strdup (cmd->shortcut);
+ *suggested_key = strdup (cmd->suggested_key);
+ *description = strdup (cmd->description);
+ }
+ }
+
+ return NULL;
+}
+
+GList *
+ephy_web_extension_get_commands (EphyWebExtension *self)
+{
+ return self->commands;
+}
+
+static void
+web_extension_commands_free (WebExtensionCommand *cmd)
+{
+ g_clear_pointer (&cmd->shortcut, g_free);
+ g_clear_pointer (&cmd->suggested_key, g_free);
+ g_clear_pointer (&cmd->description, g_free);
+ g_free (cmd);
+}
+
static void
ephy_web_extension_dispose (GObject *object)
{
@@ -781,6 +913,7 @@ ephy_web_extension_dispose (GObject *object)
g_clear_pointer (&self->page_action, web_extension_page_action_free);
g_clear_pointer (&self->browser_action, web_extension_browser_action_free);
g_clear_list (&self->custom_css, (GDestroyNotify)webkit_user_style_sheet_unref);
+ g_clear_list (&self->commands, (GDestroyNotify)web_extension_commands_free);
g_hash_table_destroy (self->page_action_map);
@@ -906,6 +1039,9 @@ ephy_web_extension_parse_manifest (EphyWebExtension *self,
if ((child_array = ephy_json_object_get_array (root_object, "web_accessible_resources")))
json_array_foreach_element (child_array, web_extension_add_web_accessible_resource, self);
+ if ((child_object = ephy_json_object_get_object (root_object, "commands")))
+ web_extension_parse_commands (self, child_object);
+
return TRUE;
}
diff --git a/src/webextension/ephy-web-extension.h b/src/webextension/ephy-web-extension.h
index 8fbd140bc..7b280fa8b 100644
--- a/src/webextension/ephy-web-extension.h
+++ b/src/webextension/ephy-web-extension.h
@@ -148,6 +148,20 @@ const char *ephy_web_extension_get_option_ui_page (EphyW
const char *ephy_web_extension_get_guid (EphyWebExtension *self);
+GList *ephy_web_extension_get_commands (EphyWebExtension *self);
+
+void *ephy_web_extension_get_command_data_from_index (EphyWebExtension *self,
+ guint command,
+ char **shortcut,
+ char
**suggested_key,
+ char **description);
+
+void *ephy_web_extension_get_command_data_from_name (EphyWebExtension *self,
+ const char *name,
+ char **shortcut,
+ char
**suggested_key,
+ char **description);
+
gboolean ephy_web_extension_has_tab_or_host_permission (EphyWebExtension *self,
EphyWebView *web_view,
gboolean
is_user_interaction);
diff --git a/src/webextension/meson.build b/src/webextension/meson.build
index 3d7fda2df..c4ad72c50 100644
--- a/src/webextension/meson.build
+++ b/src/webextension/meson.build
@@ -1,5 +1,6 @@
ephywebextension_src = [
'webextension/api/alarms.c',
+ 'webextension/api/commands.c',
'webextension/api/cookies.c',
'webextension/api/downloads.c',
'webextension/api/menus.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]