[epiphany/pgriffis/web-extension-fixes-2] WebExtensions: Run content-scripts in the right world with WebExtension APIs available
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/pgriffis/web-extension-fixes-2] WebExtensions: Run content-scripts in the right world with WebExtension APIs available
- Date: Tue, 17 May 2022 21:33:11 +0000 (UTC)
commit ebe53366f69fd0ee96bbef177ff92332d33616b3
Author: Patrick Griffis <pgriffis igalia com>
Date: Tue May 17 16:10:27 2022 -0500
WebExtensions: Run content-scripts in the right world with WebExtension APIs available
Fixes #1754
.../ephy-web-process-extension.c | 63 +++++++++++
.../web-process-extension/ephy-webextension-api.c | 125 ++-------------------
.../ephy-webextension-common.c | 124 ++++++++++++++++++++
.../ephy-webextension-common.h | 30 +++++
embed/web-process-extension/meson.build | 10 +-
.../resources/js/webextensions.js | 33 +++---
src/webextension/ephy-web-extension-manager.c | 9 +-
src/webextension/ephy-web-extension.c | 2 +-
8 files changed, 251 insertions(+), 145 deletions(-)
---
diff --git a/embed/web-process-extension/ephy-web-process-extension.c
b/embed/web-process-extension/ephy-web-process-extension.c
index f739c1236..fe216a589 100644
--- a/embed/web-process-extension/ephy-web-process-extension.c
+++ b/embed/web-process-extension/ephy-web-process-extension.c
@@ -29,6 +29,7 @@
#include "ephy-settings.h"
#include "ephy-uri-helpers.h"
#include "ephy-web-overview-model.h"
+#include "ephy-webextension-common.h"
#include <gio/gio.h>
#include <glib/gi18n.h>
@@ -52,6 +53,7 @@ struct _EphyWebProcessExtension {
EphyPermissionsManager *permissions_manager;
WebKitScriptWorld *script_world;
+ GHashTable *content_script_worlds;
gboolean should_remember_passwords;
gboolean is_private_profile;
@@ -225,6 +227,58 @@ web_page_context_menu (WebKitWebPage *web_page,
return TRUE;
}
+static void
+content_script_window_object_cleared_cb (WebKitScriptWorld *world,
+ WebKitWebPage *page,
+ WebKitFrame *frame,
+ gpointer user_data)
+{
+ g_autoptr (JSCContext) js_context = NULL;
+
+ js_context = webkit_frame_get_js_context_for_script_world (frame, world);
+ ephy_webextension_install_common_apis (js_context, webkit_script_world_get_name (world));
+}
+
+static void
+create_content_script_world (EphyWebProcessExtension *extension,
+ const char *guid)
+{
+ WebKitScriptWorld *world = webkit_script_world_new_with_name (guid);
+
+ g_hash_table_insert (extension->content_script_worlds, g_strdup (guid), world);
+
+ g_signal_connect (world,
+ "window-object-cleared",
+ G_CALLBACK (content_script_window_object_cleared_cb),
+ NULL);
+}
+
+static gboolean
+web_page_received_message (WebKitWebPage *web_page,
+ WebKitUserMessage *message,
+ gpointer user_data)
+{
+ EphyWebProcessExtension *extension = user_data;
+ const char *name = webkit_user_message_get_name (message);
+
+ if (g_strcmp0 (name, "WebExtension.Initialize") == 0) {
+ GVariant *parameters;
+ const char *guid;
+
+ parameters = webkit_user_message_get_parameters (message);
+ if (!parameters)
+ return FALSE;
+
+ g_variant_get (parameters, "&s", &guid);
+ create_content_script_world (extension, guid);
+ } else {
+ g_warning ("Unhandled page message: %s", name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static void
ephy_web_process_extension_page_created_cb (EphyWebProcessExtension *extension,
WebKitWebPage *web_page)
@@ -240,6 +294,9 @@ ephy_web_process_extension_page_created_cb (EphyWebProcessExtension *extension,
g_signal_connect (web_page, "form-controls-associated-for-frame",
G_CALLBACK (web_page_form_controls_associated),
extension);
+ g_signal_connect (web_page, "user-message-received",
+ G_CALLBACK (web_page_received_message),
+ extension);
}
static void
@@ -330,6 +387,8 @@ ephy_web_process_extension_user_message_received_cb (EphyWebProcessExtension *ex
return;
g_variant_get (parameters, "b", &extension->should_remember_passwords);
+ } else {
+ g_warning ("Unhandled user-message: %s", name);
}
}
@@ -381,6 +440,7 @@ ephy_web_process_extension_dispose (GObject *object)
}
g_clear_pointer (&extension->translation_table, g_hash_table_destroy);
+ g_clear_pointer (&extension->content_script_worlds, g_hash_table_destroy);
G_OBJECT_CLASS (ephy_web_process_extension_parent_class)->dispose (object);
}
@@ -808,4 +868,7 @@ ephy_web_process_extension_initialize (EphyWebProcessExtension *extension,
g_free, NULL);
extension->translation_table = g_hash_table_new (g_str_hash, NULL);
+
+ extension->content_script_worlds = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
}
diff --git a/embed/web-process-extension/ephy-webextension-api.c
b/embed/web-process-extension/ephy-webextension-api.c
index 5cf5a6d8e..9499899f6 100644
--- a/embed/web-process-extension/ephy-webextension-api.c
+++ b/embed/web-process-extension/ephy-webextension-api.c
@@ -20,8 +20,8 @@
#include "config.h"
#include "ephy-webextension-api.h"
+#include "ephy-webextension-common.h"
-#include <locale.h>
#include <json-glib/json-glib.h>
#include <webkit2/webkit-web-extension.h>
#include <JavaScriptCore/JavaScript.h>
@@ -49,53 +49,6 @@ ephy_web_extension_extension_get_translations (EphyWebExtensionExtension *extens
return extension->translation_table;
}
-static void
-js_exception_handler (JSCContext *context,
- JSCException *exception)
-{
- g_autoptr (JSCValue) js_console = NULL;
- g_autoptr (JSCValue) js_result = NULL;
- g_autofree char *report = NULL;
-
- js_console = jsc_context_get_value (context, "console");
- js_result = jsc_value_object_invoke_method (js_console, "error", JSC_TYPE_EXCEPTION, exception,
G_TYPE_NONE);
- (void)js_result;
- report = jsc_exception_report (exception);
- g_warning ("%s", report);
-
- jsc_context_throw_exception (context, exception);
-}
-
-static char *
-js_getmessage (const char *message,
- gpointer user_data)
-{
- return g_strdup (message);
-}
-
-static char *
-js_getuilanguage (void)
-{
- char *locale = setlocale (LC_MESSAGES, NULL);
-
- if (locale) {
- locale[2] = '\0';
-
- return g_strdup (locale);
- }
-
- return g_strdup ("en");
-}
-
-static char *
-js_geturl (const char *path,
- gpointer user_data)
-{
- EphyWebExtensionExtension *extension = EPHY_WEB_EXTENSION_EXTENSION (user_data);
-
- return g_strdup_printf ("ephy-webextension://%s/%s", extension->guid, path);
-}
-
static void
ephy_web_extension_page_user_message_received_cb (WebKitWebPage *page,
WebKitUserMessage *message)
@@ -110,6 +63,7 @@ ephy_web_extension_page_user_message_received_cb (WebKitWebPage *page,
const char *path;
const char *code;
g_autofree char *uri = NULL;
+ /* FIXME: This should run in content-script world of the target tab. */
JSCContext *context = webkit_frame_get_js_context (frame);
parameters = webkit_user_message_get_parameters (message);
@@ -136,36 +90,6 @@ ephy_web_extension_page_user_message_received_cb (WebKitWebPage *page,
}
}
-void
-webextensions_add_translation (EphyWebExtensionExtension *extension,
- const char *name,
- const char *data,
- guint64 length)
-{
- GHashTable *translations = ephy_web_extension_extension_get_translations (extension);
- JsonParser *parser = NULL;
- JsonNode *root;
- JsonObject *root_object;
- g_autoptr (GError) error = NULL;
-
- g_hash_table_remove (translations, name);
-
- if (!data || strlen (data) == 0)
- return;
-
- parser = json_parser_new ();
- if (json_parser_load_from_data (parser, data, length, &error)) {
- root = json_parser_get_root (parser);
- g_assert (root);
- root_object = json_node_get_object (root);
- g_assert (root_object);
-
- g_hash_table_insert (translations, (char *)name, json_object_ref (root_object));
- } else {
- g_warning ("Could not read translation json data: %s. '%s'", error->message, data);
- }
-}
-
static void
ephy_web_extension_extension_page_created_cb (EphyWebExtensionExtension *extension,
WebKitWebPage *web_page)
@@ -238,46 +162,11 @@ window_object_cleared_cb (WebKitScriptWorld *world,
gsize data_size;
js_context = webkit_frame_get_js_context_for_script_world (frame, world);
- jsc_context_push_exception_handler (js_context, (JSCExceptionHandler)js_exception_handler, NULL, NULL);
-
- result = jsc_context_get_value (js_context, "browser");
- g_assert (jsc_value_is_undefined (result));
-
- js_browser = jsc_value_new_object (js_context, NULL, NULL);
- jsc_context_set_value (js_context, "browser", js_browser);
-
- /* i18n */
- js_i18n = jsc_value_new_object (js_context, NULL, NULL);
- jsc_value_object_set_property (js_browser, "i18n", js_i18n);
-
- js_function = jsc_value_new_function (js_context,
- "getUILanguage",
- G_CALLBACK (js_getuilanguage), extension, NULL,
- G_TYPE_STRING,
- 0);
- jsc_value_object_set_property (js_i18n, "getUILanguage", js_function);
- g_clear_object (&js_function);
-
- js_function = jsc_value_new_function (js_context,
- "getMessage",
- G_CALLBACK (js_getmessage), extension, NULL,
- G_TYPE_STRING, 1,
- G_TYPE_STRING);
- jsc_value_object_set_property (js_i18n, "getMessage", js_function);
- g_clear_object (&js_function);
-
- /* extension */
- js_extension = jsc_value_new_object (js_context, NULL, NULL);
- jsc_value_object_set_property (js_browser, "extension", js_extension);
-
- js_function = jsc_value_new_function (js_context,
- "getURL",
- G_CALLBACK (js_geturl), extension, NULL,
- G_TYPE_STRING,
- 1,
- G_TYPE_STRING);
- jsc_value_object_set_property (js_extension, "getURL", js_function);
- g_clear_object (&js_function);
+
+ ephy_webextension_install_common_apis (js_context, extension->guid);
+
+ js_browser = jsc_context_get_value (js_context, "browser");
+ g_assert (jsc_value_is_object (js_browser));
bytes = g_resources_lookup_data ("/org/gnome/epiphany-web-extension/js/webextensions.js",
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
data = g_bytes_get_data (bytes, &data_size);
diff --git a/embed/web-process-extension/ephy-webextension-common.c
b/embed/web-process-extension/ephy-webextension-common.c
new file mode 100644
index 000000000..0854641bb
--- /dev/null
+++ b/embed/web-process-extension/ephy-webextension-common.c
@@ -0,0 +1,124 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2020 Jan-Michael Brummer <jan brummer tabos org>
+ *
+ * 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 "ephy-webextension-common.h"
+
+#include <locale.h>
+
+static char *
+js_getmessage (const char *message,
+ gpointer user_data)
+{
+ return g_strdup (message);
+}
+
+static char *
+js_getuilanguage (void)
+{
+ char *locale = setlocale (LC_MESSAGES, NULL);
+
+ if (locale) {
+ locale[2] = '\0';
+
+ return g_strdup (locale);
+ }
+
+ return g_strdup ("en");
+}
+
+static char *
+js_geturl (const char *path,
+ gpointer user_data)
+{
+ const char *guid = user_data;
+
+ return g_strdup_printf ("ephy-webextension://%s/%s", guid, path);
+}
+
+static void
+js_exception_handler (JSCContext *context,
+ JSCException *exception)
+{
+ g_autoptr (JSCValue) js_console = NULL;
+ g_autoptr (JSCValue) js_result = NULL;
+ g_autofree char *report = NULL;
+
+ js_console = jsc_context_get_value (context, "console");
+ js_result = jsc_value_object_invoke_method (js_console, "error", JSC_TYPE_EXCEPTION, exception,
G_TYPE_NONE);
+ (void)js_result;
+ report = jsc_exception_report (exception);
+ g_warning ("%s", report);
+
+ jsc_context_throw_exception (context, exception);
+}
+
+void
+ephy_webextension_install_common_apis (JSCContext *js_context,
+ const char *guid)
+{
+ g_autoptr (JSCValue) result = NULL;
+ g_autoptr (JSCValue) js_browser = NULL;
+ g_autoptr (JSCValue) js_i18n = NULL;
+ g_autoptr (JSCValue) js_runtime = NULL;
+ g_autoptr (JSCValue) js_function = NULL;
+
+ jsc_context_push_exception_handler (js_context, (JSCExceptionHandler)js_exception_handler, NULL, NULL);
+
+ /* APIs available in content scripts: https://developer.chrome.com/docs/extensions/mv3/content_scripts/ */
+
+ result = jsc_context_get_value (js_context, "browser");
+ g_assert (jsc_value_is_undefined (result));
+
+ js_browser = jsc_value_new_object (js_context, NULL, NULL);
+ jsc_context_set_value (js_context, "browser", js_browser);
+
+ /* i18n */
+ js_i18n = jsc_value_new_object (js_context, NULL, NULL);
+ jsc_value_object_set_property (js_browser, "i18n", js_i18n);
+
+ js_function = jsc_value_new_function (js_context,
+ "getUILanguage",
+ G_CALLBACK (js_getuilanguage), NULL, NULL,
+ G_TYPE_STRING,
+ 0);
+ jsc_value_object_set_property (js_i18n, "getUILanguage", js_function);
+ g_clear_object (&js_function);
+
+ js_function = jsc_value_new_function (js_context,
+ "getMessage",
+ G_CALLBACK (js_getmessage), NULL, NULL,
+ G_TYPE_STRING, 1,
+ G_TYPE_STRING);
+ jsc_value_object_set_property (js_i18n, "getMessage", js_function);
+ g_clear_object (&js_function);
+
+ /* runtime */
+ js_runtime = jsc_value_new_object (js_context, NULL, NULL);
+ jsc_value_object_set_property (js_browser, "runtime", js_runtime);
+
+ js_function = jsc_value_new_function (js_context,
+ "getURL",
+ G_CALLBACK (js_geturl), g_strdup (guid), g_free,
+ G_TYPE_STRING,
+ 1,
+ G_TYPE_STRING);
+ jsc_value_object_set_property (js_runtime, "getURL", js_function);
+ g_clear_object (&js_function);
+}
diff --git a/embed/web-process-extension/ephy-webextension-common.h
b/embed/web-process-extension/ephy-webextension-common.h
new file mode 100644
index 000000000..d16cebab6
--- /dev/null
+++ b/embed/web-process-extension/ephy-webextension-common.h
@@ -0,0 +1,30 @@
+ /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2020 Jan-Michael Brummer <jan brummer tabos org>
+ *
+ * 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 <glib-object.h>
+#include <jsc/jsc.h>
+
+G_BEGIN_DECLS
+
+void ephy_webextension_install_common_apis (JSCContext *js_context, const char *guid);
+
+G_END_DECLS
diff --git a/embed/web-process-extension/meson.build b/embed/web-process-extension/meson.build
index d77fa2af0..0677a90c5 100644
--- a/embed/web-process-extension/meson.build
+++ b/embed/web-process-extension/meson.build
@@ -9,11 +9,16 @@ web_process_extension_resources = gnome.compile_resources('epiphany-web-process-
source_dir: 'resources'
)
+web_extension_common_sources = [
+ 'ephy-webextension-common.c'
+]
+
web_process_extension_sources = [
'ephy-web-process-extension.c',
'ephy-web-process-extension-main.c',
'ephy-web-overview-model.c',
- web_process_extension_resources
+ web_process_extension_resources,
+ web_extension_common_sources,
]
web_process_extension_deps = [
@@ -44,7 +49,8 @@ web_extension_resources = gnome.compile_resources('epiphany-web-extension-resour
web_extension_sources = [
'ephy-webextension-api.c',
- web_extension_resources
+ web_extension_resources,
+ web_extension_common_sources,
]
web_extension_deps = [
diff --git a/embed/web-process-extension/resources/js/webextensions.js
b/embed/web-process-extension/resources/js/webextensions.js
index 56a37d8df..e234494b8 100644
--- a/embed/web-process-extension/resources/js/webextensions.js
+++ b/embed/web-process-extension/resources/js/webextensions.js
@@ -74,28 +74,21 @@ window.browser.notifications = {
create: function (args, cb) { return ephy_message ('notifications.create', args, cb); },
};
-window.browser.runtime = {
- getURL: function (args, cb) { return window.browser.extension.getURL(args, cb); },
- getManifest: function (args, cb) { return '[]'; },
- getBrowserInfo: function (args, cb) { return ephy_message ('runtime.getBrowserInfo', args, cb); },
- onInstalled: {
- addListener: function (cb) { runtime_listeners.push({callback: cb}); }
- },
- onMessage: {
- addListener: function (cb) { runtime_onmessage_listeners.push({callback: cb}); }
- },
- onMessageExternal: {
- addListener: function (cb) { runtime_onmessageexternal_listeners.push({callback: cb}); }
- },
- onConnect: {
- addListener: function (cb) { runtime_onconnect_listeners.push({callback: cb}); }
- },
- connectNative: function (args, cb) { return ephy_message ('runtime.connectNative', args, cb); },
- sendMessage: function (args, cb) { return ephy_message ('runtime.sendMessage', args, cb); },
- openOptionsPage: function (args, cb) { return ephy_message ('runtime.openOptionsPage', args, cb); },
- setUninstallURL: function (args, cb) { return ephy_message ('runtime.setUninstallURL', args, cb); },
+window.browser.extension = {
+ getURL: function (args, cb) { return window.browser.runtime.getURL(args, cb); },
};
+window.browser.runtime.getManifest = function (args, cb) { return '[]'; };
+window.browser.runtime.getBrowserInfo = function (args, cb) { return ephy_message ('runtime.getBrowserInfo',
args, cb); },
+window.browser.runtime.onInstalled = { addListener: function (cb) { runtime_listeners.push({callback: cb});
} };
+window.browser.runtime.onMessage = { addListener: function (cb) {
runtime_onmessage_listeners.push({callback: cb}); } };
+window.browser.runtime.onMessageExternal = { addListener: function (cb) {
runtime_onmessageexternal_listeners.push({callback: cb}); } };
+window.browser.runtime.onConnect = { addListener: function (cb) {
runtime_onconnect_listeners.push({callback: cb}); } };
+window.browser.runtime.connectNative = function (args, cb) { return ephy_message ('runtime.connectNative',
args, cb); },
+window.browser.runtime.sendMessage = function (args, cb) { return ephy_message ('runtime.sendMessage', args,
cb); },
+window.browser.runtime.openOptionsPage = function (args, cb) { return ephy_message
('runtime.openOptionsPage', args, cb); },
+window.browser.runtime.setUninstallURL = function (args, cb) { return ephy_message
('runtime.setUninstallURL', args, cb); },
+
window.browser.pageAction = {
setIcon: function (args, cb) { return ephy_message ('pageAction.setIcon', args, cb); },
setTitle: function (args, cb) { return ephy_message ('pageAction.setTitle', args, cb); },
diff --git a/src/webextension/ephy-web-extension-manager.c b/src/webextension/ephy-web-extension-manager.c
index 86056df5e..3c859b37f 100644
--- a/src/webextension/ephy-web-extension-manager.c
+++ b/src/webextension/ephy-web-extension-manager.c
@@ -461,8 +461,7 @@ add_content_scripts (EphyWebExtension *web_extension,
return;
ucm = webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (web_view));
- g_signal_connect_object (ucm, "script-message-received", G_CALLBACK
(ephy_web_extension_handle_background_script_message), web_extension, 0);
- webkit_user_content_manager_register_script_message_handler (ucm, "epiphany");
+ /* NOTE: This will have to connect/disconnect script-message-recieved once we implement content-script
APIs using this. */
for (GList *list = content_scripts; list && list->data; list = list->next) {
GList *js_list = ephy_web_extension_get_content_script_js (web_extension, list->data);
@@ -491,8 +490,6 @@ remove_content_scripts (EphyWebExtension *self,
for (GList *tmp_list = js_list; tmp_list && tmp_list->data; tmp_list = tmp_list->next)
webkit_user_content_manager_remove_script (WEBKIT_USER_CONTENT_MANAGER (ucm), tmp_list->data);
}
-
- g_signal_handlers_disconnect_by_func (ucm, G_CALLBACK
(ephy_web_extension_handle_background_script_message), self);
}
static void
@@ -555,6 +552,10 @@ ephy_web_extension_manager_add_web_extension_to_webview (EphyWebExtensionManager
}
}
+ webkit_web_view_send_message_to_page (WEBKIT_WEB_VIEW (web_view),
+ webkit_user_message_new ("WebExtension.Initialize",
g_variant_new_string (ephy_web_extension_get_guid (web_extension))),
+ NULL, NULL, NULL);
+
update_translations (web_extension);
add_content_scripts (web_extension, web_view);
}
diff --git a/src/webextension/ephy-web-extension.c b/src/webextension/ephy-web-extension.c
index 6da0035c9..878de29b9 100644
--- a/src/webextension/ephy-web-extension.c
+++ b/src/webextension/ephy-web-extension.c
@@ -427,7 +427,7 @@ web_extension_content_script_build (EphyWebExtension *self,
user_script = webkit_user_script_new_for_world (js_data,
content_script->injected_frames,
content_script->injection_time,
- ephy_embed_shell_get_guid (ephy_embed_shell_get_default
()),
+ ephy_web_extension_get_guid (self),
(const char * const *)content_script->allow_list->pdata,
(const char * const *)content_script->block_list->pdata);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]