[epiphany/pgriffis/web-extension/unify-web-process-extensions: 1/2] WebExtensions: Unify web-process-extensions
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/pgriffis/web-extension/unify-web-process-extensions: 1/2] WebExtensions: Unify web-process-extensions
- Date: Sun, 17 Jul 2022 23:18:54 +0000 (UTC)
commit d71dfe35da668b5d24fec5de5fdc3536ebf6b849
Author: Patrick Griffis <pgriffis igalia com>
Date: Sun Jul 17 18:05:38 2022 -0500
WebExtensions: Unify web-process-extensions
Previously there were two web process extensions that tried to
not overlap with eachother however this design doesn't make sense.
Any tab can load web-extension pages. The only distinction is the URI they load.
So this combines them and depending on the URI loaded it will install the correct APIs.
embed/ephy-embed-shell.c | 19 +-
embed/ephy-embed-shell.h | 3 +
.../ephy-web-process-extension-main.c | 11 +-
.../ephy-web-process-extension.c | 488 ++++++++++++++-------
.../ephy-web-process-extension.h | 3 +-
.../web-process-extension/ephy-webextension-api.c | 319 --------------
.../web-process-extension/ephy-webextension-api.h | 32 --
embed/web-process-extension/meson.build | 33 --
.../resources/epiphany-web-extension.gresource.xml | 7 -
src/webextension/ephy-web-extension-manager.c | 91 +++-
10 files changed, 435 insertions(+), 571 deletions(-)
---
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index 53c47aaf6..a1eaba755 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -67,6 +67,7 @@ typedef struct {
EphyReaderHandler *reader_handler;
char *guid;
EphyFiltersManager *filters_manager;
+ GVariant *web_extension_initialization_data;
EphySearchEngineManager *search_engine_manager;
GCancellable *cancellable;
} EphyEmbedShellPrivate;
@@ -201,6 +202,7 @@ ephy_embed_shell_dispose (GObject *object)
g_clear_pointer (&priv->guid, g_free);
g_clear_object (&priv->filters_manager);
g_clear_object (&priv->search_engine_manager);
+ g_clear_pointer (&priv->web_extension_initialization_data, g_variant_unref);
G_OBJECT_CLASS (ephy_embed_shell_parent_class)->dispose (object);
}
@@ -770,13 +772,12 @@ initialize_web_process_extensions (WebKitWebContext *web_context,
#endif
private_profile = priv->mode == EPHY_EMBED_SHELL_MODE_PRIVATE || priv->mode ==
EPHY_EMBED_SHELL_MODE_INCOGNITO || priv->mode == EPHY_EMBED_SHELL_MODE_AUTOMATION;
- user_data = g_variant_new ("(smsbbbs)",
+ user_data = g_variant_new ("(smsbbv)",
priv->guid,
ephy_profile_dir_is_default () ? NULL : ephy_profile_dir (),
g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_REMEMBER_PASSWORDS),
private_profile,
- FALSE /* is_webextension */,
- "" /* webextension_translations */);
+ priv->web_extension_initialization_data);
webkit_web_context_set_web_extensions_initialization_user_data (web_context, g_steal_pointer (&user_data));
}
@@ -1057,6 +1058,8 @@ ephy_embed_shell_constructed (GObject *object)
priv->permissions_manager = ephy_permissions_manager_new ();
priv->filters_manager = ephy_filters_manager_new (NULL);
+
+ priv->web_extension_initialization_data = g_variant_new ("a{sv}", NULL);
}
static void
@@ -1406,6 +1409,16 @@ ephy_embed_shell_get_password_manager (EphyEmbedShell *shell)
return priv->password_manager;
}
+void
+ephy_embed_shell_set_web_extension_initialization_data (EphyEmbedShell *shell,
+ GVariant *data)
+{
+ EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
+
+ g_clear_pointer (&priv->web_extension_initialization_data, g_variant_unref);
+ priv->web_extension_initialization_data = g_variant_ref (data);
+}
+
void
ephy_embed_shell_register_ucm_handler (EphyEmbedShell *shell,
WebKitUserContentManager *ucm)
diff --git a/embed/ephy-embed-shell.h b/embed/ephy-embed-shell.h
index 85577c09d..534073337 100644
--- a/embed/ephy-embed-shell.h
+++ b/embed/ephy-embed-shell.h
@@ -90,5 +90,8 @@ void ephy_embed_shell_register_ucm_handler (EphyEmbedShell
WebKitUserContentManager *ucm);
void ephy_embed_shell_unregister_ucm_handler (EphyEmbedShell *shell,
WebKitUserContentManager *ucm);
+void ephy_embed_shell_set_web_extension_initialization_data
+ (EphyEmbedShell *shell,
+ GVariant *data);
G_END_DECLS
diff --git a/embed/web-process-extension/ephy-web-process-extension-main.c
b/embed/web-process-extension/ephy-web-process-extension-main.c
index cfb2584ec..c065fb8c4 100644
--- a/embed/web-process-extension/ephy-web-process-extension-main.c
+++ b/embed/web-process-extension/ephy-web-process-extension-main.c
@@ -37,13 +37,12 @@ webkit_web_extension_initialize_with_user_data (WebKitWebExtension *webkit_exten
{
const char *guid;
const char *profile_dir;
- const char *webextension_translations;
gboolean private_profile;
gboolean should_remember_passwords;
- gboolean is_webextension;
+ g_autoptr (GVariant) web_extensions = NULL;
g_autoptr (GError) error = NULL;
- g_variant_get (user_data, "(&sm&sbbb&s)", &guid, &profile_dir, &should_remember_passwords,
&private_profile, &is_webextension, &webextension_translations);
+ g_variant_get (user_data, "(&sm&sbbv)", &guid, &profile_dir, &should_remember_passwords, &private_profile,
&web_extensions);
if (!ephy_file_helpers_init (profile_dir, 0, &error))
g_warning ("Failed to initialize file helpers: %s", error->message);
@@ -55,14 +54,12 @@ webkit_web_extension_initialize_with_user_data (WebKitWebExtension *webkit_exten
extension = ephy_web_process_extension_get ();
- if (is_webextension)
- return;
-
ephy_web_process_extension_initialize (extension,
webkit_extension,
guid,
should_remember_passwords,
- private_profile);
+ private_profile,
+ web_extensions);
}
static void __attribute__((destructor))
diff --git a/embed/web-process-extension/ephy-web-process-extension.c
b/embed/web-process-extension/ephy-web-process-extension.c
index ad57ad331..9fbfa01db 100644
--- a/embed/web-process-extension/ephy-web-process-extension.c
+++ b/embed/web-process-extension/ephy-web-process-extension.c
@@ -20,7 +20,6 @@
#include "config.h"
#include "ephy-web-process-extension.h"
-#include "ephy-webextension-api.h"
#include "ephy-debug.h"
#include "ephy-file-helpers.h"
@@ -53,17 +52,24 @@ struct _EphyWebProcessExtension {
EphyPermissionsManager *permissions_manager;
WebKitScriptWorld *script_world;
- GHashTable *content_script_worlds;
gboolean should_remember_passwords;
gboolean is_private_profile;
GHashTable *frames_map;
- GHashTable *translation_table;
+
+ GHashTable *web_extensions;
};
G_DEFINE_TYPE (EphyWebProcessExtension, ephy_web_process_extension, G_TYPE_OBJECT)
+#define PAGE_IS_EXTENSION(web_page) (webkit_web_page_get_uri(web_page) && g_str_has_prefix
(webkit_web_page_get_uri(web_page), "ephy-webextension:"))
+
+static GHashTable *view_contexts;
+static JSCContext *background_context;
+
+/* ================ Private Ephy API ================ */
+
static void
web_page_will_submit_form (WebKitWebPage *web_page,
WebKitDOMHTMLFormElement *dom_form,
@@ -230,129 +236,6 @@ 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)
-{
- EphyWebProcessExtension *extension = user_data;
- g_autoptr (JSCContext) js_context = NULL;
- g_autoptr (JSCValue) js_browser = NULL;
- g_autoptr (JSCValue) result = NULL;
- g_autoptr (GBytes) bytes = NULL;
- JsonObject *translations;
- const char *guid;
- const char *data;
- gsize data_size;
-
- guid = webkit_script_world_get_name (world);
- js_context = webkit_frame_get_js_context_for_script_world (frame, world);
- translations = g_hash_table_lookup (extension->translation_table, 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-common.js",
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
- data = g_bytes_get_data (bytes, &data_size);
- result = jsc_context_evaluate_with_source_uri (js_context, data, data_size,
"resource:///org/gnome/epiphany-web-extension/js/webextensions-common.js", 1);
- g_clear_object (&result);
-
- ephy_webextension_install_common_apis (page,
- frame,
- js_context,
- guid,
- translations);
-}
-
-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),
- extension);
-}
-
-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)
-{
- g_autoptr (JSCContext) js_context = NULL;
-
- g_signal_connect (web_page, "context-menu",
- G_CALLBACK (web_page_context_menu),
- extension);
- g_signal_connect (web_page, "will-submit-form",
- G_CALLBACK (web_page_will_submit_form),
- 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
-ephy_web_process_extension_add_translations (GHashTable *translation_table,
- const char *guid,
- const char *data)
-{
- g_autoptr (JsonParser) parser = NULL;
- JsonNode *root;
- JsonObject *root_object;
- g_autoptr (GError) error = NULL;
-
- g_hash_table_remove (translation_table, guid);
-
- if (!data || !*data)
- return;
-
- parser = json_parser_new ();
- if (json_parser_load_from_data (parser, data, -1, &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 (translation_table, g_strdup (guid), json_object_ref (root_object));
- } else {
- g_warning ("Could not read translation json data: %s. '%s'", error->message, data);
- }
-}
-
static void
ephy_web_process_extension_user_message_received_cb (EphyWebProcessExtension *extension,
WebKitUserMessage *message)
@@ -441,17 +324,6 @@ ephy_web_process_extension_user_message_received_cb (EphyWebProcessExtension *ex
return;
g_variant_get (parameters, "b", &extension->should_remember_passwords);
- } else if (g_strcmp0 (name, "WebExtension.UpdateTranslations") == 0) {
- GVariant *parameters;
- const char *guid;
- const char *data;
-
- parameters = webkit_user_message_get_parameters (message);
- if (!parameters)
- return;
-
- g_variant_get (parameters, "(&s&s)", &guid, &data);
- ephy_web_process_extension_add_translations (extension->translation_table, guid, data);
}
}
@@ -473,6 +345,196 @@ get_password_manager (EphyWebProcessExtension *self,
return jsc_value_object_get_property (js_ephy, "passwordManager");
}
+/* ================ End Private Ephy API ================ */
+
+/* ================ Content Script API ================ */
+
+typedef struct {
+ char *manifest;
+ JsonObject *translations;
+ WebKitScriptWorld *script_world;
+ gboolean has_background_page;
+} WebExtensionData;
+
+static void
+content_script_window_object_cleared_cb (WebKitScriptWorld *world,
+ WebKitWebPage *page,
+ WebKitFrame *frame,
+ gpointer user_data)
+{
+ WebExtensionData *extension_data = user_data;
+ g_autoptr (JSCContext) js_context = NULL;
+ g_autoptr (JSCValue) js_browser = NULL;
+ g_autoptr (JSCValue) result = NULL;
+ g_autoptr (GBytes) bytes = NULL;
+ const char *guid;
+ const char *data;
+ gsize data_size;
+
+ if (PAGE_IS_EXTENSION (page))
+ return;
+
+ guid = webkit_script_world_get_name (world);
+ js_context = webkit_frame_get_js_context_for_script_world (frame, world);
+
+ 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-process-extension/js/webextensions-common.js",
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
+ data = g_bytes_get_data (bytes, &data_size);
+ result = jsc_context_evaluate_with_source_uri (js_context, data, data_size,
"resource:///org/gnome/epiphany-web-process-extension/js/webextensions-common.js", 1);
+ g_clear_object (&result);
+
+ ephy_webextension_install_common_apis (page,
+ frame,
+ js_context,
+ guid,
+ extension_data->translations);
+}
+
+static void
+web_extension_data_free (WebExtensionData *data)
+{
+ g_clear_pointer (&data->manifest, g_free);
+ g_clear_pointer (&data->translations, json_object_unref);
+ g_clear_object (&data->script_world);
+ g_free (data);
+}
+
+static WebExtensionData *
+create_web_extension_data (const char *extension_guid,
+ GVariantDict *dict)
+{
+ WebExtensionData *data = g_new (WebExtensionData, 1);
+ const char *translations_json;
+ g_autoptr (JsonNode) node = NULL;
+
+ g_assert (g_variant_dict_lookup (dict, "manifest", "s", &data->manifest));
+ g_assert (g_variant_dict_lookup (dict, "translations", "&s", &translations_json));
+ g_assert (g_variant_dict_lookup (dict, "has-background-page", "b", &data->has_background_page));
+
+ node = json_from_string (translations_json, NULL);
+ g_assert (node);
+ data->translations = json_object_ref (json_node_get_object (node));
+
+ data->script_world = webkit_script_world_new_with_name (extension_guid);
+ g_signal_connect (data->script_world,
+ "window-object-cleared",
+ G_CALLBACK (content_script_window_object_cleared_cb),
+ data);
+
+ return data;
+}
+
+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;
+ char *guid;
+ g_autoptr (GVariant) variant = NULL;
+ g_autoptr (GVariantDict) dict = NULL;
+
+ parameters = webkit_user_message_get_parameters (message);
+ if (!parameters)
+ return FALSE;
+
+ g_variant_get (parameters, "(sv)", &guid, &variant);
+ dict = g_variant_dict_new (variant);
+ g_hash_table_replace (extension->web_extensions, guid, create_web_extension_data (guid, dict));
+ } else {
+ g_warning ("Unhandled page message: %s", name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* ================ End Content Script API ================ */
+
+/* ================ WebExtension API ================ */
+
+static void
+ephy_web_extension_page_user_message_received_cb (WebKitWebPage *page,
+ WebKitUserMessage *message)
+{
+ const char *name = webkit_user_message_get_name (message);
+ WebKitFrame *frame = webkit_web_page_get_main_frame (page);
+ g_autoptr (JSCValue) value = NULL;
+
+ if (!PAGE_IS_EXTENSION (page)) {
+ g_warning ("Got user-messgae for non-extension page");
+ return;
+ }
+
+ if (strcmp (name, "executeScript") == 0) {
+ GVariant *parameters;
+ const char *guid;
+ 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);
+ if (!parameters)
+ return;
+
+ g_variant_get (parameters, "(&s&s&s)", &guid, &path, &code);
+ uri = g_strdup_printf ("ephy-webextension://%s/%s", guid, path);
+ value = jsc_context_evaluate_with_source_uri (context, code, -1, uri, 1);
+ g_clear_object (&value);
+ } else if (strcmp (name, "sendMessage") == 0) {
+ GVariant *parameters;
+ const char *script;
+ g_autofree char *uri = NULL;
+ JSCContext *context = webkit_frame_get_js_context (frame);
+
+ parameters = webkit_user_message_get_parameters (message);
+ if (!parameters)
+ return;
+
+ g_variant_get (parameters, "(&s)", &script);
+ value = jsc_context_evaluate (context, script, -1);
+ g_clear_object (&value);
+ }
+}
+
+static void
+ephy_web_process_extension_page_created_cb (EphyWebProcessExtension *extension,
+ WebKitWebPage *web_page)
+{
+ g_autoptr (JSCContext) js_context = NULL;
+
+ if (PAGE_IS_EXTENSION (web_page)) {
+ /* Enforce the creation of the script world global context in the main frame */
+ js_context = webkit_frame_get_js_context_for_script_world (webkit_web_page_get_main_frame (web_page),
webkit_script_world_get_default ());
+ (void)js_context;
+
+ g_signal_connect_swapped (web_page, "user-message-received",
+ G_CALLBACK (ephy_web_extension_page_user_message_received_cb),
+ web_page);
+ } else {
+ g_signal_connect (web_page, "context-menu",
+ G_CALLBACK (web_page_context_menu),
+ extension);
+ g_signal_connect (web_page, "will-submit-form",
+ G_CALLBACK (web_page_will_submit_form),
+ 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
drop_frame_weak_ref (gpointer key,
gpointer value,
@@ -502,8 +564,7 @@ ephy_web_process_extension_dispose (GObject *object)
g_clear_pointer (&extension->frames_map, g_hash_table_unref);
}
- g_clear_pointer (&extension->translation_table, g_hash_table_destroy);
- g_clear_pointer (&extension->content_script_worlds, g_hash_table_destroy);
+ /g_clear_pointer (&extension->web_extensions, g_hash_table_destroy);
G_OBJECT_CLASS (ephy_web_process_extension_parent_class)->dispose (object);
}
@@ -757,11 +818,117 @@ js_exception_handler (JSCContext *context,
jsc_context_throw_exception (context, exception);
}
+static JSCValue *
+ephy_get_view_objects (gpointer user_data)
+{
+ g_autoptr (GPtrArray) window_objects = g_ptr_array_new ();
+ GHashTableIter iter;
+ JSCContext *context;
+
+ g_hash_table_iter_init (&iter, view_contexts);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&context)) {
+ if (context == background_context)
+ g_ptr_array_insert (window_objects, 0, jsc_context_get_global_object (context));
+ else
+ g_ptr_array_add (window_objects, jsc_context_get_global_object (context));
+ }
+
+ return jsc_value_new_array_from_garray (jsc_context_get_current (), window_objects);
+}
+
+static void
+on_frame_destroyed (gpointer user_data,
+ GObject *object_location)
+{
+ g_hash_table_remove (view_contexts, user_data);
+}
+
+
+static void
+default_window_object_cleared_cb (WebKitScriptWorld *world,
+ WebKitWebPage *page,
+ WebKitFrame *frame,
+ EphyWebProcessExtension *extension)
+{
+ g_autoptr (JSCContext) js_context = NULL;
+ g_autoptr (JSCValue) js_browser = NULL;
+ g_autoptr (JSCValue) js_i18n = NULL;
+ g_autoptr (JSCValue) js_extension = NULL;
+ g_autoptr (JSCValue) js_function = NULL;
+ g_autoptr (GBytes) bytes = NULL;
+ g_autoptr (JSCValue) result = NULL;
+ g_autoptr (GError) error = NULL;
+ WebExtensionData *extension_data;
+ const char *data;
+ const char *guid;
+ GUri *parsed_uri;
+ gsize data_size;
+
+ if (!PAGE_IS_EXTENSION (page))
+ return;
+
+ parsed_uri = g_uri_parse (webkit_web_page_get_uri (page), G_URI_FLAGS_NON_DNS, &error);
+ if (!parsed_uri) {
+ g_warning ("Failed to parse URI of web page: %s", error->message);
+ return;
+ }
+
+ guid = g_uri_get_host (parsed_uri);
+ extension_data = g_hash_table_lookup (extension->web_extensions, guid);
+ if (!extension_data) {
+ g_warning ("Failed to find extension by guid: %s", guid);
+ return;
+ }
+
+ js_context = webkit_frame_get_js_context_for_script_world (frame, world);
+
+ /* The first context made is assumed to be the background page. */
+ if (!background_context && extension_data->has_background_page)
+ background_context = js_context;
+
+ if (!g_hash_table_contains (view_contexts, GUINT_TO_POINTER (webkit_frame_get_id (frame)))) {
+ g_hash_table_insert (view_contexts, GUINT_TO_POINTER (webkit_frame_get_id (frame)), g_object_ref
(js_context));
+ g_object_weak_ref (G_OBJECT (frame), on_frame_destroyed, GUINT_TO_POINTER (webkit_frame_get_id (frame)));
+ }
+
+ 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-process-extension/js/webextensions-common.js",
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
+ data = g_bytes_get_data (bytes, &data_size);
+ result = jsc_context_evaluate_with_source_uri (js_context, data, data_size,
"resource:///org/gnome/epiphany-web-process-extension/js/webextensions-common.js", 1);
+ g_bytes_unref (bytes);
+ g_clear_object (&result);
+
+ ephy_webextension_install_common_apis (page,
+ frame,
+ js_context,
+ guid,
+ extension_data->translations);
+
+ js_browser = jsc_context_get_value (js_context, "browser");
+ js_extension = jsc_value_object_get_property (js_browser, "extension");
+
+ js_function = jsc_value_new_function (js_context,
+ "ephy_get_view_objects",
+ G_CALLBACK (ephy_get_view_objects),
+ NULL,
+ NULL,
+ JSC_TYPE_VALUE, 0);
+ jsc_value_object_set_property (js_extension, "_ephy_get_view_objects", js_function);
+ g_clear_object (&js_function);
+
+ bytes = g_resources_lookup_data ("/org/gnome/epiphany-web-process-extension/js/webextensions.js",
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
+ data = g_bytes_get_data (bytes, &data_size);
+ result = jsc_context_evaluate_with_source_uri (js_context, data, data_size,
"resource:///org/gnome/epiphany-web-process-extension/js/webextensions.js", 1);
+ g_clear_object (&result);
+}
+
static void
-window_object_cleared_cb (WebKitScriptWorld *world,
- WebKitWebPage *page,
- WebKitFrame *frame,
- EphyWebProcessExtension *extension)
+private_window_object_cleared_cb (WebKitScriptWorld *world,
+ WebKitWebPage *page,
+ WebKitFrame *frame,
+ EphyWebProcessExtension *extension)
{
g_autoptr (JSCContext) js_context = NULL;
g_autoptr (GBytes) bytes = NULL;
@@ -771,6 +938,9 @@ window_object_cleared_cb (WebKitScriptWorld *world,
g_autoptr (JSCValue) js_function = NULL;
g_autoptr (JSCValue) result = NULL;
+ if (PAGE_IS_EXTENSION (page))
+ return;
+
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);
@@ -895,7 +1065,8 @@ ephy_web_process_extension_initialize (EphyWebProcessExtension *extension,
WebKitWebExtension *wk_extension,
const char *guid,
gboolean should_remember_passwords,
- gboolean is_private_profile)
+ gboolean is_private_profile,
+ GVariant *web_extensions)
{
g_assert (EPHY_IS_WEB_PROCESS_EXTENSION (extension));
@@ -906,11 +1077,17 @@ ephy_web_process_extension_initialize (EphyWebProcessExtension *extension,
g_assert (guid && *guid);
- extension->script_world = webkit_script_world_new_with_name (guid);
+ /* Default script world, used by WebExtensions. */
+ g_signal_connect (webkit_script_world_get_default (),
+ "window-object-cleared",
+ G_CALLBACK (default_window_object_cleared_cb),
+ extension);
+ /* Private script world, used by Epiphany on normal web pages. */
+ extension->script_world = webkit_script_world_new_with_name (guid);
g_signal_connect (extension->script_world,
"window-object-cleared",
- G_CALLBACK (window_object_cleared_cb),
+ G_CALLBACK (private_window_object_cleared_cb),
extension);
extension->extension = g_object_ref (wk_extension);
@@ -930,9 +1107,20 @@ ephy_web_process_extension_initialize (EphyWebProcessExtension *extension,
extension->frames_map = g_hash_table_new_full (g_int64_hash, g_int64_equal,
g_free, NULL);
- extension->translation_table = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, (GDestroyNotify)json_object_unref);
+ view_contexts = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
+ extension->web_extensions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify)web_extension_data_free);
+
- extension->content_script_worlds = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, g_object_unref);
+ for (guint i = 0; i < g_variant_n_children (web_extensions); i++) {
+ g_autoptr (GVariant) child = g_variant_get_child_value (web_extensions, i);
+ g_autoptr (GVariant) variant = NULL;
+ g_autoptr (GVariantDict) dict = NULL;
+ char *extension_guid;
+
+ g_variant_get (child, "{sv}", &extension_guid, &variant);
+ dict = g_variant_dict_new (variant);
+ g_hash_table_replace (extension->web_extensions, extension_guid,
+ create_web_extension_data (extension_guid, dict));
+ }
}
diff --git a/embed/web-process-extension/ephy-web-process-extension.h
b/embed/web-process-extension/ephy-web-process-extension.h
index faa577c6f..4370c74c4 100644
--- a/embed/web-process-extension/ephy-web-process-extension.h
+++ b/embed/web-process-extension/ephy-web-process-extension.h
@@ -34,7 +34,8 @@ void ephy_web_process_extension_initialize (EphyWebProcessEx
WebKitWebExtension *wk_extension,
const char *guid,
gboolean
should_remember_passwords,
- gboolean is_private_profile);
+ gboolean is_private_profile,
+ GVariant *web_extensions);
void ephy_web_process_extension_deinitialize (EphyWebProcessExtension *extension);
diff --git a/embed/web-process-extension/meson.build b/embed/web-process-extension/meson.build
index 0677a90c5..2c15c2c59 100644
--- a/embed/web-process-extension/meson.build
+++ b/embed/web-process-extension/meson.build
@@ -35,36 +35,3 @@ shared_module('ephywebprocessextension',
install_dir: webprocessextensionsdir,
install_rpath: pkglibdir
)
-
-#
-# WebExtensions
-#
-
-web_extension_resource_files = files('resources/epiphany-web-extension.gresource.xml')
-web_extension_resources = gnome.compile_resources('epiphany-web-extension-resources',
- web_extension_resource_files,
- c_name: 'epiphany_web_extension',
- source_dir: 'resources'
-)
-
-web_extension_sources = [
- 'ephy-webextension-api.c',
- web_extension_resources,
- web_extension_common_sources,
-]
-
-web_extension_deps = [
- config_h,
- ephymisc_dep,
- ephysync_dep,
- webkit2gtk_web_extension_dep
-]
-
-shared_module('ephywebextension',
- web_extension_sources,
- dependencies: web_extension_deps,
- install: true,
- install_dir: webprocessextensionsdir, # Same directory as above, single source
- install_rpath: pkglibdir
-)
-
diff --git a/src/webextension/ephy-web-extension-manager.c b/src/webextension/ephy-web-extension-manager.c
index 81d446983..16c6f91d4 100644
--- a/src/webextension/ephy-web-extension-manager.c
+++ b/src/webextension/ephy-web-extension-manager.c
@@ -51,6 +51,7 @@
static void handle_message_reply (EphyWebExtension *web_extension,
JsonArray *args);
+static char *get_translation_contents (EphyWebExtension *web_extension);
struct _EphyWebExtensionManager {
GObject parent_instance;
@@ -108,11 +109,48 @@ create_user_agent_overrides (void)
return overrides;
}
+static GVariant *
+create_extension_data_variant (EphyWebExtension *extension)
+{
+ g_auto (GVariantDict) dict = G_VARIANT_DICT_INIT (NULL);
+ g_autofree char *translations = get_translation_contents (extension);
+
+ g_variant_dict_insert (&dict, "manifest", "s", ephy_web_extension_get_manifest (extension));
+ g_variant_dict_insert (&dict, "translations", "s", translations);
+ g_variant_dict_insert (&dict, "has-background-page", "b", ephy_web_extension_has_background_web_view
(extension));
+
+ return g_variant_dict_end (&dict);
+}
+
+static GVariant *
+ephy_web_extension_manager_get_extension_initialization_data (EphyWebExtensionManager *self)
+{
+ g_auto (GVariantDict) dict = G_VARIANT_DICT_INIT (NULL);
+
+ for (guint i = 0; i < self->web_extensions->len; i++) {
+ EphyWebExtension *extension = g_ptr_array_index (self->web_extensions, i);
+
+ g_variant_dict_insert_value (&dict,
+ ephy_web_extension_get_guid (extension),
+ create_extension_data_variant (extension));
+ }
+
+ return g_variant_dict_end (&dict);
+}
+
+static void
+ephy_web_extension_manager_update_extension_initialization_data (EphyWebExtensionManager *self)
+{
+ EphyEmbedShell *embed = ephy_embed_shell_get_default ();
+ ephy_embed_shell_set_web_extension_initialization_data (embed,
ephy_web_extension_manager_get_extension_initialization_data (self));
+}
+
static void
ephy_web_extension_manager_add_to_list (EphyWebExtensionManager *self,
EphyWebExtension *web_extension)
{
g_ptr_array_add (self->web_extensions, g_object_ref (web_extension));
+ ephy_web_extension_manager_update_extension_initialization_data (self);
g_signal_emit (self, signals[CHANGED], 0);
}
@@ -121,6 +159,7 @@ ephy_web_extension_manager_remove_from_list (EphyWebExtensionManager *self,
EphyWebExtension *web_extension)
{
g_ptr_array_remove (self->web_extensions, web_extension);
+ ephy_web_extension_manager_update_extension_initialization_data (self);
g_signal_emit (self, signals[CHANGED], 0);
}
@@ -130,7 +169,7 @@ ephy_web_extension_manager_get_extension_by_guid (EphyWebExtensionManager *self,
{
for (guint i = 0; i < self->web_extensions->len; i++) {
EphyWebExtension *web_extension = g_ptr_array_index (self->web_extensions, i);
- if (strcmp (guid, ephy_web_extension_get_guid (web_extension)) == 0)
+ if (g_strcmp0 (guid, ephy_web_extension_get_guid (web_extension)) == 0)
return web_extension;
}
@@ -217,11 +256,15 @@ ephy_webextension_scheme_cb (WebKitURISchemeRequest *request,
gpointer user_data)
{
EphyWebExtensionManager *self = ephy_web_extension_manager_get_default ();
- EphyWebExtension *web_extension = user_data;
+ WebKitWebView *initiating_view;
+ EphyWebExtension *web_extension;
EphyWebExtension *target_web_extension;
g_autoptr (GInputStream) stream = NULL;
g_autoptr (GUri) uri = NULL;
+ const char *initiating_uri_string;
+ g_autoptr (GUri) initiating_uri = NULL;
g_autoptr (GError) error = NULL;
+ const char *initating_host;
const unsigned char *data;
gsize length;
@@ -240,6 +283,26 @@ ephy_webextension_scheme_cb (WebKitURISchemeRequest *request,
return;
}
+ /* Figure out the source of this request. */
+ initiating_view = webkit_uri_scheme_request_get_web_view (request);
+ if (EPHY_IS_WEB_VIEW (initiating_view))
+ initiating_uri_string = ephy_web_view_get_address (EPHY_WEB_VIEW (initiating_view));
+ else
+ initiating_uri_string = webkit_web_view_get_uri (initiating_view);
+ if (!initiating_uri_string) {
+ webkit_uri_scheme_request_finish_error (request, g_error_new (WEB_EXTENSION_ERROR,
WEB_EXTENSION_ERROR_INVALID_HOST, "Failed to determine initiating URI"));
+ return;
+ }
+
+ initiating_uri = g_uri_parse (initiating_uri_string, G_URI_FLAGS_NON_DNS, &error);
+ if (!initiating_uri) {
+ webkit_uri_scheme_request_finish_error (request, g_steal_pointer (&error));
+ return;
+ }
+
+ initating_host = g_uri_get_host (initiating_uri);
+ web_extension = ephy_web_extension_manager_get_extension_by_guid (self, initating_host);
+
/* If this is not originating from the same WebExtension view we must find it and filter it by
web_accessible_resources. */
if (web_extension != target_web_extension) {
if (!ephy_web_extension_has_web_accessible_resource (target_web_extension, g_uri_get_path (uri) + 1)) {
@@ -837,17 +900,7 @@ get_translation_contents (EphyWebExtension *web_extension)
g_autofree char *path = g_strdup_printf ("_locales/%s/messages.json", "en");
g_autofree char *data = ephy_web_extension_get_resource_as_string (web_extension, path);
- return data ? g_steal_pointer (&data) : g_strdup ("");
-}
-
-static void
-update_translations (EphyWebExtension *web_extension)
-{
- g_autofree char *data = get_translation_contents (web_extension);
-
- webkit_web_context_send_message_to_all_extensions (ephy_embed_shell_get_web_context
(ephy_embed_shell_get_default ()),
- webkit_user_message_new
("WebExtension.UpdateTranslations",
- g_variant_new ("(ss)",
ephy_web_extension_get_guid (web_extension), data)));
+ return data ? g_steal_pointer (&data) : g_strdup ("{}");
}
static void
@@ -857,7 +910,6 @@ send_to_page_ready_cb (WebKitWebView *web_view,
{
g_autoptr (WebKitUserMessage) response = webkit_web_view_send_message_to_page_finish (web_view, result,
NULL);
- update_translations (web_extension);
add_content_scripts (web_extension, EPHY_WEB_VIEW (web_view));
}
@@ -892,7 +944,8 @@ ephy_web_extension_manager_add_web_extension_to_webview (EphyWebExtensionManager
web_extension);
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))),
+ webkit_user_message_new ("WebExtension.Initialize",
+ g_variant_new ("(sv)", ephy_web_extension_get_guid (web_extension),
create_extension_data_variant (web_extension))),
NULL, (GAsyncReadyCallback)send_to_page_ready_cb, web_extension);
}
@@ -917,6 +970,7 @@ init_web_extension_api (WebKitWebContext *web_context,
EphyWebExtension *web_extension)
{
g_autoptr (GVariant) user_data = NULL;
+ EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
g_autofree char *translations = get_translation_contents (web_extension);
#if DEVELOPER_MODE
@@ -925,13 +979,12 @@ init_web_extension_api (WebKitWebContext *web_context,
webkit_web_context_set_web_extensions_directory (web_context, EPHY_WEB_PROCESS_EXTENSIONS_DIR);
#endif
- user_data = g_variant_new ("(smsbbbs)",
+ user_data = g_variant_new ("(smsbbv)",
ephy_web_extension_get_guid (web_extension),
ephy_profile_dir_is_default () ? NULL : ephy_profile_dir (),
FALSE /* should_remember_passwords */,
FALSE /* private_profile */,
- TRUE /* is_webextension */,
- translations);
+ ephy_web_extension_manager_get_extension_initialization_data (manager));
webkit_web_context_set_web_extensions_initialization_user_data (web_context, g_steal_pointer (&user_data));
}
@@ -1007,7 +1060,7 @@ ephy_web_extensions_manager_create_web_extensions_webview (EphyWebExtension *web
web_context = webkit_web_context_new_with_website_data_manager (data_manager);
webkit_web_context_set_sandbox_enabled (web_context, TRUE);
- webkit_web_context_register_uri_scheme (web_context, "ephy-webextension", ephy_webextension_scheme_cb,
web_extension, NULL);
+ webkit_web_context_register_uri_scheme (web_context, "ephy-webextension", ephy_webextension_scheme_cb,
NULL, NULL);
webkit_security_manager_register_uri_scheme_as_secure (webkit_web_context_get_security_manager
(web_context),
"ephy-webextension");
g_signal_connect_object (web_context, "initialize-web-extensions", G_CALLBACK (init_web_extension_api),
web_extension, 0);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]