[evolution/gnome-41] Load JavaScript plugins for message preview and WebKit editor
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution/gnome-41] Load JavaScript plugins for message preview and WebKit editor
- Date: Thu, 25 Nov 2021 10:17:36 +0000 (UTC)
commit c715e5af6d58a974e9e49a1a969772c5ebafa43d
Author: Milan Crha <mcrha redhat com>
Date: Thu Nov 25 11:01:38 2021 +0100
Load JavaScript plugins for message preview and WebKit editor
This allows to save JavaScript plugins, files with .js extension,
into `$PREFIX/share/evolution/webkit/preview-plugins/` or into
`~/.local/share/evolution/preview-plugins/` for the message preview
and into `$PREFIX/share/evolution/webkit/webkit-editor-plugins/` or
into `~/.local/share/evolution/webkit-editor-plugins/` for
the WebKit composer.
The plugin is an object, which contains two members:
* `name` - a string with a name of the plugin
* `setup(doc)` - a setup function
The argument of the `setup` function is a `document`, which can
be either the main document or an iframe document. The function
can be called multiple times for the same document.
The preview plugin registers itself by calling `Evo.RegisterPlugin(plugin_instance)`.
The WebKit editor plugin registers itself by calling `EvoEditor.RegisterPlugin(plugin_instance)`.
data/webkit/e-editor.js | 42 ++++++
data/webkit/e-web-view.js | 44 ++++++-
.../web-extension/e-editor-web-extension.c | 145 ++++++++++++++++-----
src/web-extensions/e-web-extension.c | 145 ++++++++++++++++-----
4 files changed, 312 insertions(+), 64 deletions(-)
---
diff --git a/data/webkit/e-editor.js b/data/webkit/e-editor.js
index 5a3701519f..bf363edf7e 100644
--- a/data/webkit/e-editor.js
+++ b/data/webkit/e-editor.js
@@ -87,6 +87,7 @@ var EvoEditor = {
MODE_PLAIN_TEXT : 0,
MODE_HTML : 1,
+ plugins : null,
mode : 1, // one of the MODE constants
storedSelection : null,
propertiesSelection : null, // dedicated to Properties dialogs
@@ -117,6 +118,45 @@ var EvoEditor = {
}
};
+EvoEditor.RegisterPlugin = function(plugin)
+{
+ if (plugin == null)
+ return;
+
+ if (plugin.name === undefined) {
+ console.error("Evo.RegisterPlugin: Plugin '" + plugin + "' has missing 'name' member");
+ return;
+ }
+
+ if (plugin.setup === undefined) {
+ console.error("Evo.RegisterPlugin: Plugin '" + plugin.name + "' has missing 'setup'
function");
+ return;
+ }
+
+ if (EvoEditor.plugins == null)
+ EvoEditor.plugins = [];
+
+ EvoEditor.plugins.push(plugin);
+}
+
+EvoEditor.setupPlugins = function(doc)
+{
+ if (EvoEditor.plugins == null)
+ return;
+
+ var ii;
+
+ for (ii = 0; ii < EvoEditor.plugins.length; ii++) {
+ try {
+ if (EvoEditor.plugins[ii] != null)
+ EvoEditor.plugins[ii].setup(doc);
+ } catch (err) {
+ console.error("Failed to setup plugin '" + EvoEditor.plugins[ii].name + "': " +
err.name + ": " + err.message);
+ EvoEditor.plugins[ii] = null;
+ }
+ }
+}
+
EvoEditor.maybeUpdateFormattingState = function(force)
{
var anchorElem = null;
@@ -1778,6 +1818,8 @@ EvoEditor.initializeContent = function()
document.getSelection().setPosition(document.body.firstChild ?
document.body.firstChild : document.body, 0);
}
}
+
+ EvoEditor.setupPlugins(document);
}
EvoEditor.getNextNodeInHierarchy = function(node, upToNode)
diff --git a/data/webkit/e-web-view.js b/data/webkit/e-web-view.js
index 2e87eaed1d..73fce72680 100644
--- a/data/webkit/e-web-view.js
+++ b/data/webkit/e-web-view.js
@@ -24,9 +24,49 @@ var Evo = {
hasSelection : false,
blockquoteStyle : "margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex",
magicSpacebarState: -1,
- markCitationColor : null
+ markCitationColor : null,
+ plugins : null
};
+Evo.RegisterPlugin = function(plugin)
+{
+ if (plugin == null)
+ return;
+
+ if (plugin.name === undefined) {
+ console.error("Evo.RegisterPlugin: Plugin '" + plugin + "' has missing 'name' member");
+ return;
+ }
+
+ if (plugin.setup === undefined) {
+ console.error("Evo.RegisterPlugin: Plugin '" + plugin.name + "' has missing 'setup'
function");
+ return;
+ }
+
+ if (Evo.plugins == null)
+ Evo.plugins = [];
+
+ Evo.plugins.push(plugin);
+}
+
+Evo.setupPlugins = function(doc)
+{
+ if (Evo.plugins == null)
+ return;
+
+ var ii;
+
+ for (ii = 0; ii < Evo.plugins.length; ii++) {
+ try {
+ if (Evo.plugins[ii] != null)
+ Evo.plugins[ii].setup(doc);
+ } catch (err) {
+ console.error("Failed to setup plugin '" + Evo.plugins[ii].name + "': " + err.name +
": " + err.message);
+ Evo.plugins[ii] = null;
+ }
+ }
+}
+
/* The 'traversar_obj' is an object, which implements a callback function:
boolean exec(doc, iframe_id, level);
and it returns whether continue the traversar */
@@ -619,6 +659,8 @@ Evo.initialize = function(elem)
} else
doc = document;
+ Evo.setupPlugins(doc);
+
elems = doc.getElementsByTagName("iframe");
for (ii = 0; ii < elems.length; ii++) {
diff --git a/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
b/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
index 5b6cfb9bc5..cf8fdf90cf 100644
--- a/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
+++ b/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
@@ -31,6 +31,7 @@
struct _EEditorWebExtensionPrivate {
WebKitWebExtension *wk_extension;
ESpellChecker *spell_checker;
+ GSList *known_plugins; /* gchar * - full filename to known plugins */
};
G_DEFINE_TYPE_WITH_PRIVATE (EEditorWebExtension, e_editor_web_extension, G_TYPE_OBJECT)
@@ -43,6 +44,9 @@ e_editor_web_extension_dispose (GObject *object)
g_clear_object (&extension->priv->wk_extension);
g_clear_object (&extension->priv->spell_checker);
+ g_slist_free_full (extension->priv->known_plugins, g_free);
+ extension->priv->known_plugins = NULL;
+
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_editor_web_extension_parent_class)->dispose (object);
}
@@ -86,46 +90,26 @@ use_sources_js_file (void)
return res;
}
-static void
+static gboolean
load_javascript_file (JSCContext *jsc_context,
- const gchar *js_filename)
+ const gchar *js_filename,
+ const gchar *filename)
{
JSCValue *result;
JSCException *exception;
- gchar *content, *filename = NULL, *resource_uri;
+ gchar *content, *resource_uri;
gsize length = 0;
GError *error = NULL;
+ gboolean success = TRUE;
- g_return_if_fail (jsc_context != NULL);
-
- if (use_sources_js_file ()) {
- const gchar *source_webkitdatadir;
-
- source_webkitdatadir = g_getenv ("EVOLUTION_SOURCE_WEBKITDATADIR");
-
- if (source_webkitdatadir && *source_webkitdatadir) {
- filename = g_build_filename (source_webkitdatadir, js_filename, NULL);
-
- if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
- g_warning ("Cannot find '%s', using installed file '%s/%s' instead",
filename, EVOLUTION_WEBKITDATADIR, js_filename);
-
- g_clear_pointer (&filename, g_free);
- }
- } else {
- g_warning ("Environment variable 'EVOLUTION_SOURCE_WEBKITDATADIR' not set or invalid
value, using installed file '%s/%s' instead", EVOLUTION_WEBKITDATADIR, js_filename);
- }
- }
-
- if (!filename)
- filename = g_build_filename (EVOLUTION_WEBKITDATADIR, js_filename, NULL);
+ g_return_val_if_fail (jsc_context != NULL, FALSE);
if (!g_file_get_contents (filename, &content, &length, &error)) {
g_warning ("Failed to load '%s': %s", filename, error ? error->message : "Unknown error");
g_clear_error (&error);
- g_free (filename);
- return;
+ return FALSE;
}
resource_uri = g_strconcat ("resource:///", js_filename, NULL);
@@ -144,11 +128,89 @@ load_javascript_file (JSCContext *jsc_context,
jsc_exception_get_message (exception));
jsc_context_clear_exception (jsc_context);
+ success = FALSE;
}
g_clear_object (&result);
- g_free (filename);
g_free (content);
+
+ return success;
+}
+
+static void
+load_javascript_plugins (JSCContext *jsc_context,
+ const gchar *top_path,
+ GSList **out_loaded_plugins)
+{
+ const gchar *dirfile;
+ gchar *path;
+ GDir *dir;
+
+ g_return_if_fail (jsc_context != NULL);
+
+ /* Do not load plugins during unit tests */
+ if (use_sources_js_file ())
+ return;
+
+ path = g_build_filename (top_path, "webkit-editor-plugins", NULL);
+
+ dir = g_dir_open (path, 0, NULL);
+ if (!dir) {
+ g_free (path);
+ return;
+ }
+
+ while (dirfile = g_dir_read_name (dir), dirfile) {
+ if (g_str_has_suffix (dirfile, ".js") ||
+ g_str_has_suffix (dirfile, ".Js") ||
+ g_str_has_suffix (dirfile, ".jS") ||
+ g_str_has_suffix (dirfile, ".JS")) {
+ gchar *filename;
+
+ filename = g_build_filename (path, dirfile, NULL);
+ if (load_javascript_file (jsc_context, filename, filename))
+ *out_loaded_plugins = g_slist_prepend (*out_loaded_plugins, filename);
+ else
+ g_free (filename);
+ }
+ }
+
+ g_dir_close (dir);
+ g_free (path);
+}
+
+static void
+load_javascript_builtin_file (JSCContext *jsc_context,
+ const gchar *js_filename)
+{
+ gchar *filename = NULL;
+
+ g_return_if_fail (jsc_context != NULL);
+
+ if (use_sources_js_file ()) {
+ const gchar *source_webkitdatadir;
+
+ source_webkitdatadir = g_getenv ("EVOLUTION_SOURCE_WEBKITDATADIR");
+
+ if (source_webkitdatadir && *source_webkitdatadir) {
+ filename = g_build_filename (source_webkitdatadir, js_filename, NULL);
+
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ g_warning ("Cannot find '%s', using installed file '%s/%s' instead",
filename, EVOLUTION_WEBKITDATADIR, js_filename);
+
+ g_clear_pointer (&filename, g_free);
+ }
+ } else {
+ g_warning ("Environment variable 'EVOLUTION_SOURCE_WEBKITDATADIR' not set or invalid
value, using installed file '%s/%s' instead", EVOLUTION_WEBKITDATADIR, js_filename);
+ }
+ }
+
+ if (!filename)
+ filename = g_build_filename (EVOLUTION_WEBKITDATADIR, js_filename, NULL);
+
+ load_javascript_file (jsc_context, js_filename, filename);
+
+ g_free (filename);
}
static void
@@ -472,10 +534,10 @@ window_object_cleared_cb (WebKitScriptWorld *world,
jsc_context = webkit_frame_get_js_context (frame);
/* Read in order approximately as each other uses the previous */
- load_javascript_file (jsc_context, "e-convert.js");
- load_javascript_file (jsc_context, "e-selection.js");
- load_javascript_file (jsc_context, "e-undo-redo.js");
- load_javascript_file (jsc_context, "e-editor.js");
+ load_javascript_builtin_file (jsc_context, "e-convert.js");
+ load_javascript_builtin_file (jsc_context, "e-selection.js");
+ load_javascript_builtin_file (jsc_context, "e-undo-redo.js");
+ load_javascript_builtin_file (jsc_context, "e-editor.js");
jsc_editor = jsc_context_get_value (jsc_context, "EvoEditor");
@@ -525,6 +587,25 @@ window_object_cleared_cb (WebKitScriptWorld *world,
g_clear_object (&jsc_editor);
}
+ if (extension->priv->known_plugins) {
+ GSList *link;
+
+ for (link = extension->priv->known_plugins; link; link = g_slist_next (link)) {
+ const gchar *filename = link->data;
+
+ if (filename)
+ load_javascript_file (jsc_context, filename, filename);
+ }
+ } else {
+ load_javascript_plugins (jsc_context, EVOLUTION_WEBKITDATADIR,
&extension->priv->known_plugins);
+ load_javascript_plugins (jsc_context, e_get_user_data_dir (),
&extension->priv->known_plugins);
+
+ if (!extension->priv->known_plugins)
+ extension->priv->known_plugins = g_slist_prepend (extension->priv->known_plugins,
NULL);
+ else
+ extension->priv->known_plugins = g_slist_reverse (extension->priv->known_plugins);
+ }
+
g_clear_object (&jsc_context);
}
diff --git a/src/web-extensions/e-web-extension.c b/src/web-extensions/e-web-extension.c
index fe0123b789..57ac47042e 100644
--- a/src/web-extensions/e-web-extension.c
+++ b/src/web-extensions/e-web-extension.c
@@ -34,6 +34,7 @@
struct _EWebExtensionPrivate {
WebKitWebExtension *wk_extension;
+ GSList *known_plugins; /* gchar * - full filename to known plugins */
gboolean initialized;
};
@@ -56,6 +57,9 @@ e_web_extension_dispose (GObject *object)
g_clear_object (&extension->priv->wk_extension);
+ g_slist_free_full (extension->priv->known_plugins, g_free);
+ extension->priv->known_plugins = NULL;
+
G_OBJECT_CLASS (e_web_extension_parent_class)->dispose (object);
}
@@ -159,46 +163,26 @@ use_sources_js_file (void)
return res;
}
-static void
+static gboolean
load_javascript_file (JSCContext *jsc_context,
- const gchar *js_filename)
+ const gchar *js_filename,
+ const gchar *filename)
{
JSCValue *result;
JSCException *exception;
- gchar *content, *filename = NULL, *resource_uri;
+ gchar *content, *resource_uri;
gsize length = 0;
GError *error = NULL;
+ gboolean success = TRUE;
- g_return_if_fail (jsc_context != NULL);
-
- if (use_sources_js_file ()) {
- const gchar *source_webkitdatadir;
-
- source_webkitdatadir = g_getenv ("EVOLUTION_SOURCE_WEBKITDATADIR");
-
- if (source_webkitdatadir && *source_webkitdatadir) {
- filename = g_build_filename (source_webkitdatadir, js_filename, NULL);
-
- if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
- g_warning ("Cannot find '%s', using installed file '%s/%s' instead",
filename, EVOLUTION_WEBKITDATADIR, js_filename);
-
- g_clear_pointer (&filename, g_free);
- }
- } else {
- g_warning ("Environment variable 'EVOLUTION_SOURCE_WEBKITDATADIR' not set or invalid
value, using installed file '%s/%s' instead", EVOLUTION_WEBKITDATADIR, js_filename);
- }
- }
-
- if (!filename)
- filename = g_build_filename (EVOLUTION_WEBKITDATADIR, js_filename, NULL);
+ g_return_val_if_fail (jsc_context != NULL, FALSE);
if (!g_file_get_contents (filename, &content, &length, &error)) {
g_warning ("Failed to load '%s': %s", filename, error ? error->message : "Unknown error");
g_clear_error (&error);
- g_free (filename);
- return;
+ return FALSE;
}
resource_uri = g_strconcat ("resource:///", js_filename, NULL);
@@ -217,11 +201,89 @@ load_javascript_file (JSCContext *jsc_context,
jsc_exception_get_message (exception));
jsc_context_clear_exception (jsc_context);
+ success = FALSE;
}
g_clear_object (&result);
- g_free (filename);
g_free (content);
+
+ return success;
+}
+
+static void
+load_javascript_plugins (JSCContext *jsc_context,
+ const gchar *top_path,
+ GSList **out_loaded_plugins)
+{
+ const gchar *dirfile;
+ gchar *path;
+ GDir *dir;
+
+ g_return_if_fail (jsc_context != NULL);
+
+ /* Do not load plugins during unit tests */
+ if (use_sources_js_file ())
+ return;
+
+ path = g_build_filename (top_path, "preview-plugins", NULL);
+
+ dir = g_dir_open (path, 0, NULL);
+ if (!dir) {
+ g_free (path);
+ return;
+ }
+
+ while (dirfile = g_dir_read_name (dir), dirfile) {
+ if (g_str_has_suffix (dirfile, ".js") ||
+ g_str_has_suffix (dirfile, ".Js") ||
+ g_str_has_suffix (dirfile, ".jS") ||
+ g_str_has_suffix (dirfile, ".JS")) {
+ gchar *filename;
+
+ filename = g_build_filename (path, dirfile, NULL);
+ if (load_javascript_file (jsc_context, filename, filename))
+ *out_loaded_plugins = g_slist_prepend (*out_loaded_plugins, filename);
+ else
+ g_free (filename);
+ }
+ }
+
+ g_dir_close (dir);
+ g_free (path);
+}
+
+static void
+load_javascript_builtin_file (JSCContext *jsc_context,
+ const gchar *js_filename)
+{
+ gchar *filename = NULL;
+
+ g_return_if_fail (jsc_context != NULL);
+
+ if (use_sources_js_file ()) {
+ const gchar *source_webkitdatadir;
+
+ source_webkitdatadir = g_getenv ("EVOLUTION_SOURCE_WEBKITDATADIR");
+
+ if (source_webkitdatadir && *source_webkitdatadir) {
+ filename = g_build_filename (source_webkitdatadir, js_filename, NULL);
+
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ g_warning ("Cannot find '%s', using installed file '%s/%s' instead",
filename, EVOLUTION_WEBKITDATADIR, js_filename);
+
+ g_clear_pointer (&filename, g_free);
+ }
+ } else {
+ g_warning ("Environment variable 'EVOLUTION_SOURCE_WEBKITDATADIR' not set or invalid
value, using installed file '%s/%s' instead", EVOLUTION_WEBKITDATADIR, js_filename);
+ }
+ }
+
+ if (!filename)
+ filename = g_build_filename (EVOLUTION_WEBKITDATADIR, js_filename, NULL);
+
+ load_javascript_file (jsc_context, js_filename, filename);
+
+ g_free (filename);
}
static void
@@ -230,6 +292,7 @@ window_object_cleared_cb (WebKitScriptWorld *world,
WebKitFrame *frame,
gpointer user_data)
{
+ EWebExtension *extension = user_data;
JSCContext *jsc_context;
JSCValue *jsc_evo_object;
@@ -240,8 +303,8 @@ window_object_cleared_cb (WebKitScriptWorld *world,
jsc_context = webkit_frame_get_js_context (frame);
/* Read e-convert.js first, because e-web-view.js uses it */
- load_javascript_file (jsc_context, "e-convert.js");
- load_javascript_file (jsc_context, "e-web-view.js");
+ load_javascript_builtin_file (jsc_context, "e-convert.js");
+ load_javascript_builtin_file (jsc_context, "e-web-view.js");
jsc_evo_object = jsc_context_get_value (jsc_context, "Evo");
@@ -262,6 +325,26 @@ window_object_cleared_cb (WebKitScriptWorld *world,
}
g_clear_object (&jsc_evo_object);
+
+ if (extension->priv->known_plugins) {
+ GSList *link;
+
+ for (link = extension->priv->known_plugins; link; link = g_slist_next (link)) {
+ const gchar *filename = link->data;
+
+ if (filename)
+ load_javascript_file (jsc_context, filename, filename);
+ }
+ } else {
+ load_javascript_plugins (jsc_context, EVOLUTION_WEBKITDATADIR,
&extension->priv->known_plugins);
+ load_javascript_plugins (jsc_context, e_get_user_data_dir (),
&extension->priv->known_plugins);
+
+ if (!extension->priv->known_plugins)
+ extension->priv->known_plugins = g_slist_prepend (extension->priv->known_plugins,
NULL);
+ else
+ extension->priv->known_plugins = g_slist_reverse (extension->priv->known_plugins);
+ }
+
g_clear_object (&jsc_context);
}
@@ -287,7 +370,7 @@ e_web_extension_initialize (EWebExtension *extension,
script_world = webkit_script_world_get_default ();
g_signal_connect (script_world, "window-object-cleared",
- G_CALLBACK (window_object_cleared_cb), NULL);
+ G_CALLBACK (window_object_cleared_cb), extension);
}
WebKitWebExtension *
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]