[epiphany/pgriffis/web-extension/runtime-send-message: 1/11] WebExtensions: Implement messages replies to runtime.sendMessage()
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/pgriffis/web-extension/runtime-send-message: 1/11] WebExtensions: Implement messages replies to runtime.sendMessage()
- Date: Thu, 2 Jun 2022 18:44:28 +0000 (UTC)
commit 2e9947316c8c11dbdf2efa99cf8fdf93f71f8768
Author: Patrick Griffis <pgriffis igalia com>
Date: Sun May 29 12:18:55 2022 -0500
WebExtensions: Implement messages replies to runtime.sendMessage()
.../web-process-extension/ephy-webextension-api.c | 12 +-
.../resources/js/webextensions-common.js | 20 +++
src/webextension/api/alarms.c | 14 +-
src/webextension/api/alarms.h | 2 +-
src/webextension/api/notifications.c | 6 +-
src/webextension/api/notifications.h | 2 +-
src/webextension/api/pageaction.c | 14 +-
src/webextension/api/pageaction.h | 2 +-
src/webextension/api/runtime.c | 56 +++---
src/webextension/api/runtime.h | 2 +-
src/webextension/api/storage.c | 12 +-
src/webextension/api/storage.h | 2 +-
src/webextension/api/tabs.c | 16 +-
src/webextension/api/tabs.h | 2 +-
src/webextension/ephy-web-extension-manager.c | 199 ++++++++++++++++++---
src/webextension/ephy-web-extension-manager.h | 11 +-
src/webextension/ephy-web-extension.h | 4 +-
17 files changed, 287 insertions(+), 89 deletions(-)
---
diff --git a/embed/web-process-extension/ephy-webextension-api.c
b/embed/web-process-extension/ephy-webextension-api.c
index 42f4330af..a9f4d0261 100644
--- a/embed/web-process-extension/ephy-webextension-api.c
+++ b/embed/web-process-extension/ephy-webextension-api.c
@@ -203,20 +203,22 @@ ephy_send_message (const char *function_name,
JSCValue *reject_callback,
gpointer user_data)
{
- EphyWebExtensionExtension *extension = user_data;
+ EphyWebExtensionExtension *extension = ephy_web_extension_extension_get ();
+ guint page_id = GPOINTER_TO_UINT (user_data);
WebKitUserMessage *message;
- char *args_json;
+ g_autofree char *args_json;
if (!jsc_value_is_function (reject_callback))
return; /* Can't reject in this case. */
if (!jsc_value_is_array (function_args) || !jsc_value_is_function (resolve_callback)) {
- g_autoptr (JSCValue) ret = jsc_value_function_call (reject_callback, G_TYPE_STRING, "Invalid Arguments",
G_TYPE_NONE);
+ g_autoptr (JSCValue) ret = jsc_value_function_call (reject_callback, G_TYPE_STRING,
"ephy_send_message(): Invalid Arguments", G_TYPE_NONE);
return;
}
args_json = jsc_value_to_json (function_args, 0);
- message = webkit_user_message_new (function_name, g_variant_new_take_string (args_json));
+ message = webkit_user_message_new (function_name,
+ g_variant_new ("(us)", page_id, args_json));
webkit_web_extension_send_message_to_context (extension->extension, message, NULL,
(GAsyncReadyCallback)on_send_message_finish,
@@ -282,7 +284,7 @@ window_object_cleared_cb (WebKitScriptWorld *world,
js_function = jsc_value_new_function (js_context,
"ephy_send_message",
G_CALLBACK (ephy_send_message),
- extension, NULL,
+ GUINT_TO_POINTER (webkit_web_page_get_id (page)), NULL,
G_TYPE_NONE,
4,
G_TYPE_STRING,
diff --git a/embed/web-process-extension/resources/js/webextensions-common.js
b/embed/web-process-extension/resources/js/webextensions-common.js
index c8194fd26..8aaf54819 100644
--- a/embed/web-process-extension/resources/js/webextensions-common.js
+++ b/embed/web-process-extension/resources/js/webextensions-common.js
@@ -26,6 +26,26 @@ class EphyEventListener {
for (const listener of this._listeners)
listener.callback (data);
}
+
+ _emit_with_reply (message, sender, message_guid) {
+ let handled = false;
+ const reply_callback = function (reply_message) {
+ ephy_message ('runtime._sendMessageReply', [message_guid, reply_message]);
+ };
+
+ for (const listener of this._listeners) {
+ const ret = listener.callback (message, sender, reply_callback);
+ if (typeof ret === 'object' && typeof ret.then === 'function') {
+ ret.then(x => { reply_callback(x); }).catch(x => { reply_callback(); })
+ handled = true;
+ } else if (ret === true) {
+ // We expect listener.callback to call `reply_callback`.
+ handled = true;
+ }
+ }
+
+ return handled;
+ }
}
const ephy_message = function (fn, args) {
diff --git a/src/webextension/api/alarms.c b/src/webextension/api/alarms.c
index 675faa546..8924c1154 100644
--- a/src/webextension/api/alarms.c
+++ b/src/webextension/api/alarms.c
@@ -166,7 +166,7 @@ static char *
alarms_handler_create (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
g_autoptr (JSCValue) alarm_name = NULL;
@@ -224,7 +224,7 @@ static char *
alarms_handler_clear (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
GHashTable *alarms = get_alarms (self);
@@ -246,7 +246,7 @@ static char *
alarms_handler_clear_all (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
GHashTable *alarms = get_alarms (self);
@@ -262,7 +262,7 @@ static char *
alarms_handler_get (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
GHashTable *alarms = get_alarms (self);
@@ -283,7 +283,7 @@ static char *
alarms_handler_get_all (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
GHashTable *alarms = get_alarms (self);
@@ -311,7 +311,7 @@ void
ephy_web_extension_api_alarms_handler (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GTask *task)
{
g_autoptr (GError) error = NULL;
@@ -328,7 +328,7 @@ ephy_web_extension_api_alarms_handler (EphyWebExtension *self,
char *ret;
if (g_strcmp0 (handler.name, name) == 0) {
- ret = handler.execute (self, name, args, context_guid, &error);
+ ret = handler.execute (self, name, args, extension_page_id, &error);
if (error)
g_task_return_error (task, g_steal_pointer (&error));
diff --git a/src/webextension/api/alarms.h b/src/webextension/api/alarms.h
index 32936ebb1..7409aa43c 100644
--- a/src/webextension/api/alarms.h
+++ b/src/webextension/api/alarms.h
@@ -30,7 +30,7 @@ G_BEGIN_DECLS
void ephy_web_extension_api_alarms_handler (EphyWebExtension *self,
char *name,
JSCValue *value,
- const char *context_guid,
+ gint64 extension_page_id,
GTask *task);
G_END_DECLS
diff --git a/src/webextension/api/notifications.c b/src/webextension/api/notifications.c
index 0cf5b7ac7..00b55bfb0 100644
--- a/src/webextension/api/notifications.c
+++ b/src/webextension/api/notifications.c
@@ -29,7 +29,7 @@ static char *
notifications_handler_create (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
@@ -64,7 +64,7 @@ void
ephy_web_extension_api_notifications_handler (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GTask *task)
{
g_autoptr (GError) error = NULL;
@@ -75,7 +75,7 @@ ephy_web_extension_api_notifications_handler (EphyWebExtension *self,
char *ret;
if (g_strcmp0 (handler.name, name) == 0) {
- ret = handler.execute (self, name, args, context_guid, &error);
+ ret = handler.execute (self, name, args, extension_page_id, &error);
if (error)
g_task_return_error (task, g_steal_pointer (&error));
diff --git a/src/webextension/api/notifications.h b/src/webextension/api/notifications.h
index 80a31382a..0f456810e 100644
--- a/src/webextension/api/notifications.h
+++ b/src/webextension/api/notifications.h
@@ -28,7 +28,7 @@ G_BEGIN_DECLS
void ephy_web_extension_api_notifications_handler (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GTask *task);
G_END_DECLS
diff --git a/src/webextension/api/pageaction.c b/src/webextension/api/pageaction.c
index f93d54a6c..2fd5b335e 100644
--- a/src/webextension/api/pageaction.c
+++ b/src/webextension/api/pageaction.c
@@ -57,7 +57,7 @@ static char *
pageaction_handler_seticon (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
GtkWidget *action;
@@ -83,7 +83,7 @@ static char *
pageaction_handler_settitle (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
GtkWidget *action;
@@ -106,7 +106,7 @@ static char *
pageaction_handler_gettitle (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
@@ -128,7 +128,7 @@ static char *
pageaction_handler_show (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
@@ -149,7 +149,7 @@ static char *
pageaction_handler_hide (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
@@ -178,7 +178,7 @@ void
ephy_web_extension_api_pageaction_handler (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GTask *task)
{
g_autoptr (GError) error = NULL;
@@ -189,7 +189,7 @@ ephy_web_extension_api_pageaction_handler (EphyWebExtension *self,
char *ret;
if (g_strcmp0 (handler.name, name) == 0) {
- ret = handler.execute (self, name, args, context_guid, &error);
+ ret = handler.execute (self, name, args, extension_page_id, &error);
if (error)
g_task_return_error (task, g_steal_pointer (&error));
diff --git a/src/webextension/api/pageaction.h b/src/webextension/api/pageaction.h
index e1531db28..1830339da 100644
--- a/src/webextension/api/pageaction.h
+++ b/src/webextension/api/pageaction.h
@@ -28,7 +28,7 @@ G_BEGIN_DECLS
void ephy_web_extension_api_pageaction_handler (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GTask *task);
G_END_DECLS
diff --git a/src/webextension/api/runtime.c b/src/webextension/api/runtime.c
index e6295542d..19cb22afd 100644
--- a/src/webextension/api/runtime.c
+++ b/src/webextension/api/runtime.c
@@ -31,7 +31,7 @@ static char *
runtime_handler_get_browser_info (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
g_autoptr (JsonBuilder) builder = json_builder_new ();
@@ -58,14 +58,15 @@ is_empty_object (JSCValue *value)
return FALSE;
}
-static char *
-runtime_handler_send_message (EphyWebExtension *self,
- char *name,
- JSCValue *args,
- const char *context_guid,
- GError **error)
+static void
+runtime_handler_send_message (EphyWebExtension *self,
+ char *name,
+ JSCValue *args,
+ gint64 extension_page_id,
+ GTask *task)
{
EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
+ g_autoptr (GError) error = NULL;
g_autoptr (JSCValue) last_value = NULL;
g_autoptr (JSCValue) message = NULL;
g_autofree char *json = NULL;
@@ -75,29 +76,32 @@ runtime_handler_send_message (EphyWebExtension *self,
last_value = jsc_value_object_get_property_at_index (args, 2);
if (!jsc_value_is_undefined (last_value)) {
/* We don't actually support sending to external extensions yet. */
- g_set_error_literal (error, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "extensionId is
not supported");
- return NULL;
+ error = g_error_new_literal (WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "extensionId is
not supported");
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
}
last_value = jsc_value_object_get_property_at_index (args, 1);
if (jsc_value_is_undefined (last_value) || jsc_value_is_null (last_value) || is_empty_object (last_value))
{
message = jsc_value_object_get_property_at_index (args, 0);
} else {
- g_set_error_literal (error, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "extensionId is
not supported");
- return NULL;
+ error = g_error_new_literal (WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "extensionId is
not supported");
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
}
json = jsc_value_to_json (message, 0);
- ephy_web_extension_manager_emit_in_extension_views_except_self (manager, self, "runtime.onMessage", json,
context_guid);
+ g_message ("Sending message with %s", json);
+ ephy_web_extension_manager_emit_in_extension_views_with_reply (manager, self, "runtime.onMessage", json,
extension_page_id, "{}", task);
- return NULL;
+ return;
}
static char *
runtime_handler_open_options_page (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
const char *data = ephy_web_extension_get_option_ui_page (self);
@@ -120,28 +124,31 @@ runtime_handler_open_options_page (EphyWebExtension *self,
return NULL;
}
-static EphyWebExtensionSyncApiHandler runtime_handlers[] = {
+static EphyWebExtensionSyncApiHandler runtime_sync_handlers[] = {
{"getBrowserInfo", runtime_handler_get_browser_info},
- {"sendMessage", runtime_handler_send_message},
{"openOptionsPage", runtime_handler_open_options_page},
};
+static EphyWebExtensionAsyncApiHandler runtime_async_handlers[] = {
+ {"sendMessage", runtime_handler_send_message},
+};
+
void
ephy_web_extension_api_runtime_handler (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GTask *task)
{
g_autoptr (GError) error = NULL;
guint idx;
- for (idx = 0; idx < G_N_ELEMENTS (runtime_handlers); idx++) {
- EphyWebExtensionSyncApiHandler handler = runtime_handlers[idx];
+ for (idx = 0; idx < G_N_ELEMENTS (runtime_sync_handlers); idx++) {
+ EphyWebExtensionSyncApiHandler handler = runtime_sync_handlers[idx];
char *ret;
if (g_strcmp0 (handler.name, name) == 0) {
- ret = handler.execute (self, name, args, context_guid, &error);
+ ret = handler.execute (self, name, args, extension_page_id, &error);
if (error)
g_task_return_error (task, g_steal_pointer (&error));
@@ -152,6 +159,15 @@ ephy_web_extension_api_runtime_handler (EphyWebExtension *self,
}
}
+ for (idx = 0; idx < G_N_ELEMENTS (runtime_async_handlers); idx++) {
+ EphyWebExtensionAsyncApiHandler handler = runtime_async_handlers[idx];
+
+ if (g_strcmp0 (handler.name, name) == 0) {
+ handler.execute (self, name, args, extension_page_id, task);
+ 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/runtime.h b/src/webextension/api/runtime.h
index 3ee3aefa6..7ab12e61b 100644
--- a/src/webextension/api/runtime.h
+++ b/src/webextension/api/runtime.h
@@ -28,7 +28,7 @@ G_BEGIN_DECLS
void ephy_web_extension_api_runtime_handler (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GTask *task);
G_END_DECLS
diff --git a/src/webextension/api/storage.c b/src/webextension/api/storage.c
index a70da76d1..3f43e87f1 100644
--- a/src/webextension/api/storage.c
+++ b/src/webextension/api/storage.c
@@ -61,7 +61,7 @@ static char *
storage_handler_local_set (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
JsonNode *local_storage = ephy_web_extension_get_local_storage (self);
@@ -90,7 +90,7 @@ static char *
storage_handler_local_get (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
JsonNode *local_storage = ephy_web_extension_get_local_storage (self);
@@ -150,7 +150,7 @@ static char *
storage_handler_local_remove (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
JsonNode *local_storage = ephy_web_extension_get_local_storage (self);
@@ -180,7 +180,7 @@ static char *
storage_handler_local_clear (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
ephy_web_extension_clear_local_storage (self);
@@ -199,7 +199,7 @@ void
ephy_web_extension_api_storage_handler (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GTask *task)
{
g_autoptr (GError) error = NULL;
@@ -217,7 +217,7 @@ ephy_web_extension_api_storage_handler (EphyWebExtension *self,
char *ret;
if (g_strcmp0 (handler.name, name) == 0) {
- ret = handler.execute (self, name, args, context_guid, &error);
+ ret = handler.execute (self, name, args, extension_page_id, &error);
if (error)
g_task_return_error (task, g_steal_pointer (&error));
diff --git a/src/webextension/api/storage.h b/src/webextension/api/storage.h
index 20bdfb64f..3c8e10e8a 100644
--- a/src/webextension/api/storage.h
+++ b/src/webextension/api/storage.h
@@ -31,7 +31,7 @@ G_BEGIN_DECLS
void ephy_web_extension_api_storage_handler (EphyWebExtension *self,
char *name,
JSCValue *value,
- const char *context_guid,
+ gint64 extension_page_id,
GTask *task);
G_END_DECLS
diff --git a/src/webextension/api/tabs.c b/src/webextension/api/tabs.c
index 8bcb46fe8..2ee89d46e 100644
--- a/src/webextension/api/tabs.c
+++ b/src/webextension/api/tabs.c
@@ -150,7 +150,7 @@ static char *
tabs_handler_query (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
g_autoptr (JsonBuilder) builder = json_builder_new ();
@@ -228,7 +228,7 @@ static char *
tabs_handler_insert_css (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
EphyShell *shell = ephy_shell_get_default ();
@@ -283,7 +283,7 @@ static char *
tabs_handler_remove_css (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
EphyShell *shell = ephy_shell_get_default ();
@@ -337,7 +337,7 @@ static char *
tabs_handler_get (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
EphyShell *shell = ephy_shell_get_default ();
@@ -370,7 +370,7 @@ static char *
tabs_handler_execute_script (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
g_autoptr (JSCValue) code_value = NULL;
@@ -434,7 +434,7 @@ static char *
tabs_handler_send_message (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error)
{
g_autoptr (JSCValue) tab_id_value = NULL;
@@ -492,7 +492,7 @@ void
ephy_web_extension_api_tabs_handler (EphyWebExtension *self,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GTask *task)
{
g_autoptr (GError) error = NULL;
@@ -503,7 +503,7 @@ ephy_web_extension_api_tabs_handler (EphyWebExtension *self,
char *ret;
if (g_strcmp0 (handler.name, name) == 0) {
- ret = handler.execute (self, name, args, context_guid, &error);
+ ret = handler.execute (self, name, args, extension_page_id, &error);
if (error)
g_task_return_error (task, g_steal_pointer (&error));
diff --git a/src/webextension/api/tabs.h b/src/webextension/api/tabs.h
index 50f9c2e46..367857ace 100644
--- a/src/webextension/api/tabs.h
+++ b/src/webextension/api/tabs.h
@@ -30,7 +30,7 @@ G_BEGIN_DECLS
void ephy_web_extension_api_tabs_handler (EphyWebExtension *self,
char *name,
JSCValue *value,
- const char *context_guid,
+ gint64 extension_page_id,
GTask *task);
G_END_DECLS
diff --git a/src/webextension/ephy-web-extension-manager.c b/src/webextension/ephy-web-extension-manager.c
index 61e7aca69..18d1fa0ed 100644
--- a/src/webextension/ephy-web-extension-manager.c
+++ b/src/webextension/ephy-web-extension-manager.c
@@ -44,6 +44,9 @@
#include <json-glib/json-glib.h>
+static void handle_message_reply (EphyWebExtension *web_extension,
+ JSCValue *args);
+
struct _EphyWebExtensionManager {
GObject parent_instance;
@@ -54,6 +57,8 @@ struct _EphyWebExtensionManager {
GHashTable *background_web_views;
GHashTable *popup_web_views;
+
+ GHashTable *pending_messages;
};
G_DEFINE_TYPE (EphyWebExtensionManager, ephy_web_extension_manager, G_TYPE_OBJECT)
@@ -209,6 +214,7 @@ ephy_web_extension_manager_constructed (GObject *object)
self->popup_web_views = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
(GDestroyNotify)g_ptr_array_free);
self->page_action_map = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_hash_table_destroy);
self->browser_action_map = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)destroy_widget_list);
+ self->pending_messages = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
(GDestroyNotify)g_hash_table_destroy);
self->web_extensions = NULL;
ephy_web_extension_manager_scan_directory (self, dir);
@@ -222,6 +228,7 @@ ephy_web_extension_manager_dispose (GObject *object)
g_clear_pointer (&self->background_web_views, g_hash_table_destroy);
g_clear_pointer (&self->popup_web_views, g_hash_table_destroy);
g_clear_pointer (&self->page_action_map, g_hash_table_destroy);
+ g_clear_pointer (&self->pending_messages, g_hash_table_destroy);
g_list_free_full (g_steal_pointer (&self->web_extensions), g_object_unref);
}
@@ -511,12 +518,21 @@ ephy_web_extension_handle_user_message (WebKitWebContext *context,
g_autoptr (JSCValue) args = NULL;
const char *name = webkit_user_message_get_name (message);
g_auto (GStrv) split = NULL;
- const char *context_guid = g_object_get_data (G_OBJECT (context), "guid");
+ guint page_id;
+ const char *json_args;
+
+ g_variant_get (webkit_user_message_get_parameters (message), "(u&s)", &page_id, &json_args);
js_context = jsc_context_new ();
- args = jsc_value_new_from_json (js_context, g_variant_get_string (webkit_user_message_get_parameters
(message), NULL));
+ args = jsc_value_new_from_json (js_context, json_args);
- LOG ("%s(): Called for %s, function %s (%s)\n", __FUNCTION__, ephy_web_extension_get_name (web_extension),
name, g_variant_get_string (webkit_user_message_get_parameters (message), NULL));
+ LOG ("%s(): Called for %s, function %s (%s)\n", __FUNCTION__, ephy_web_extension_get_name (web_extension),
name, json_args);
+
+ /* Private API for message replies handled by the manager. */
+ if (strcmp (name, "runtime._sendMessageReply") == 0) {
+ handle_message_reply (web_extension, args);
+ return TRUE;
+ }
split = g_strsplit (name, ".", 2);
if (g_strv_length (split) != 2) {
@@ -532,7 +548,7 @@ ephy_web_extension_handle_user_message (WebKitWebContext *context,
GTask *task = g_task_new (web_extension, NULL,
(GAsyncReadyCallback)on_web_extension_api_handler_finish, NULL);
g_task_set_task_data (task, api_handler_data_new (message, args),
(GDestroyNotify)api_handler_data_free);
- handler.execute (web_extension, split[1], args, context_guid, task);
+ handler.execute (web_extension, split[1], args, page_id, task);
return TRUE;
}
}
@@ -737,9 +753,6 @@ create_web_extensions_webview (EphyWebExtension *web_extension)
web_context = webkit_web_context_new ();
- /* Assign a temporary GUID that is used to distinguish WebContexts on received messages. */
- g_object_set_data_full (G_OBJECT (web_context), "guid", g_dbus_generate_guid (), g_free);
-
webkit_web_context_register_uri_scheme (web_context, "ephy-webextension", web_extension_cb, web_extension,
NULL);
webkit_security_manager_register_uri_scheme_as_secure (webkit_web_context_get_security_manager
(web_context),
"ephy-webextension");
@@ -1146,46 +1159,167 @@ ephy_web_extension_manager_get_page_action (EphyWebExtensionManager *self,
return ret;
}
-static const char *
-get_guid_for_view (WebKitWebView *view)
+static void
+handle_message_reply (EphyWebExtension *web_extension,
+ JSCValue *args)
+{
+ EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
+ GHashTable *pending_messages = g_hash_table_lookup (manager->pending_messages, web_extension);
+ GTask *pending_task;
+ g_autofree char *message_guid = NULL;
+ g_autoptr (JSCValue) message_guid_value = NULL;
+ g_autoptr (JSCValue) reply_value = NULL;
+
+ g_message ("handle_message_reply");
+
+ message_guid_value = jsc_value_object_get_property_at_index (args, 0);
+ if (!jsc_value_is_string (message_guid_value)) {
+ g_debug ("Received invalid message reply");
+ return;
+ }
+
+ message_guid = jsc_value_to_string (message_guid_value);
+ pending_task = g_hash_table_lookup (pending_messages, message_guid);
+ if (!pending_task) {
+ g_debug ("Received message not found in pending replies");
+ return;
+ }
+
+ reply_value = jsc_value_object_get_property_at_index (args, 1);
+ g_hash_table_steal (pending_messages, message_guid);
+ g_task_return_pointer (pending_task, jsc_value_to_json (reply_value, 0), g_free);
+}
+
+typedef struct {
+ EphyWebExtension *web_extension;
+ char *message_guid; /* Owned by manager->pending_messages. */
+ guint pending_view_responses;
+ gboolean handled;
+} PendingMessageReplyTracker;
+
+static void
+on_extension_emit_ready (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
- WebKitWebContext *context = webkit_web_view_get_context (view);
- return g_object_get_data (G_OBJECT (context), "guid");
+ EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
+ PendingMessageReplyTracker *tracker = user_data;
+ GHashTable *pending_messages;
+ g_autoptr (GError) error = NULL;
+ g_autoptr (WebKitJavascriptResult) js_result = NULL;
+
+ js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (source),
+ result,
+ &error);
+
+ if (error) {
+ g_warning ("%s", error->message);
+ return;
+ } else {
+ if (jsc_value_to_boolean (webkit_javascript_result_get_js_value (js_result)))
+ tracker->handled = TRUE;
+ }
+
+ /* Once all views have been notified it will either be handled by one of them, in which case
+ * handle_message_reply() finishes the task, or we finish it here with an empty response. */
+ /* FIXME: A race condition is possible where a view is destroyed before it responds. */
+ tracker->pending_view_responses--;
+ if (tracker->pending_view_responses == 0) {
+ if (!tracker->handled) {
+ GTask *pending_task;
+
+ pending_messages = g_hash_table_lookup (manager->pending_messages, tracker->web_extension);
+ pending_task = g_hash_table_lookup (pending_messages, tracker->message_guid);
+ g_assert (pending_task);
+ g_assert (g_hash_table_steal (pending_messages, tracker->message_guid));
+ g_clear_pointer (&tracker->message_guid, g_free);
+
+ g_task_return_pointer (pending_task, NULL, NULL);
+ }
+ g_free (tracker);
+ }
}
static void
ephy_web_extension_manager_emit_in_extension_views_internal (EphyWebExtensionManager *self,
EphyWebExtension *web_extension,
const char *name,
- const char *json,
- const char *exception_guid)
+ const char *message_json,
+ gint64 page_id_exception,
+ const char *sender_json,
+ GTask *reply_task)
{
WebKitWebView *background_view = ephy_web_extension_manager_get_background_web_view (self, web_extension);
GPtrArray *popup_views = g_hash_table_lookup (self->popup_web_views, web_extension);
- g_autofree char *script = g_strdup_printf ("window.browser.%s._emit(%s);", name, json);
+ g_autofree char *script = NULL;
+ PendingMessageReplyTracker *tracker = NULL;
+ guint pending_views = 0;
+ GHashTable *pending_messages;
+ g_autofree char *message_guid = NULL;
+
+ /* The `runtime.sendMessage()` API emits `runtime.onMessage` and waits for a reply.
+ * The way this is implemented is:
+ * - All API handlers can be async: Returning a Promise backed by GTask (@reply_task).
+ * - Instead of completing the GTask we store it for each message waiting on replies.
+ * - We then call every extension view and track if any of them handled it (see webextensions-common.js).
+ * - If none handled it we complete with an empty message.
+ * - Otherwise we wait for our private `runtime._sendMessageReply` API call.
+ * - The first `runtime._sendMessageReply` call wins and completes the GTask with its data.
+ */
+ if (reply_task) {
+ message_guid = g_dbus_generate_guid ();
+ tracker = g_new0 (PendingMessageReplyTracker, 1);
+ script = g_strdup_printf ("window.browser.%s._emit_with_reply(%s, %s, '%s');", name, message_json,
sender_json, message_guid);
+ } else
+ script = g_strdup_printf ("window.browser.%s._emit(%s);", name, message_json);
if (background_view) {
- if (g_strcmp0 (get_guid_for_view (background_view), exception_guid) != 0)
+ if ((gint64)webkit_web_view_get_page_id (background_view) != page_id_exception) {
webkit_web_view_run_javascript (background_view,
script,
NULL,
- NULL,
- NULL);
+ reply_task ? on_extension_emit_ready : NULL,
+ tracker);
+ pending_views++;
+ }
}
if (popup_views) {
for (guint i = 0; i < popup_views->len; i++) {
WebKitWebView *popup_view = g_ptr_array_index (popup_views, i);
- if (g_strcmp0 (get_guid_for_view (popup_view), exception_guid) == 0)
+ if ((gint64)webkit_web_view_get_page_id (popup_view) == page_id_exception)
continue;
webkit_web_view_run_javascript (popup_view,
script,
NULL,
- NULL,
- NULL);
+ reply_task ? on_extension_emit_ready : NULL,
+ tracker);
+ pending_views++;
}
}
+
+ if (!reply_task)
+ return;
+
+ if (!pending_views) {
+ g_task_return_pointer (reply_task, NULL, NULL);
+ g_free (tracker);
+ return;
+ }
+
+ tracker->web_extension = web_extension;
+ tracker->pending_view_responses = pending_views;
+ tracker->message_guid = message_guid;
+
+ pending_messages = g_hash_table_lookup (self->pending_messages, web_extension);
+ if (!pending_messages) {
+ pending_messages = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
(GDestroyNotify)g_object_unref);
+ g_hash_table_insert (self->pending_messages, web_extension, pending_messages);
+ }
+
+ if (!g_hash_table_replace (pending_messages, g_steal_pointer (&message_guid), reply_task))
+ g_warning ("Duplicate message GUID");
}
void
@@ -1194,7 +1328,7 @@ ephy_web_extension_manager_emit_in_extension_views (EphyWebExtensionManager *sel
const char *name,
const char *json)
{
- ephy_web_extension_manager_emit_in_extension_views_internal (self, web_extension, name, json, NULL);
+ ephy_web_extension_manager_emit_in_extension_views_internal (self, web_extension, name, json, -1, NULL,
NULL);
}
void
@@ -1202,8 +1336,25 @@ ephy_web_extension_manager_emit_in_extension_views_except_self (EphyWebExtension
EphyWebExtension *web_extension,
const char *name,
const char *json,
- const char *context_guid)
+ gint64 extension_page_id)
{
- g_assert (context_guid);
- ephy_web_extension_manager_emit_in_extension_views_internal (self, web_extension, name, json,
context_guid);
+ g_assert (extension_page_id > 0);
+ ephy_web_extension_manager_emit_in_extension_views_internal (self, web_extension, name, json,
extension_page_id, NULL, NULL);
+}
+
+void
+ephy_web_extension_manager_emit_in_extension_views_with_reply (EphyWebExtensionManager *self,
+ EphyWebExtension *web_extension,
+ const char *name,
+ const char *json,
+ gint64 extension_page_id,
+ const char *sender_json,
+ GTask *reply_task)
+{
+ g_assert (sender_json);
+ g_assert (reply_task);
+ g_assert (extension_page_id > 0);
+ ephy_web_extension_manager_emit_in_extension_views_internal (self, web_extension, name, json,
extension_page_id, sender_json, reply_task);
+}
+
}
diff --git a/src/webextension/ephy-web-extension-manager.h b/src/webextension/ephy-web-extension-manager.h
index 70bbf7e07..b202e38a0 100644
--- a/src/webextension/ephy-web-extension-manager.h
+++ b/src/webextension/ephy-web-extension-manager.h
@@ -79,6 +79,15 @@ void ephy_web_extension_manager_emit_in_extension_views_exce
EphyWebExtension
*web_extension,
const char
*name,
const char
*json,
- const char
*context_guid);
+ gint64
extension_page_id);
+
+void ephy_web_extension_manager_emit_in_extension_views_with_reply
+ (EphyWebExtensionManager
*self,
+ EphyWebExtension
*web_extension,
+ const char
*name,
+ const char
*json,
+ gint64
extension_page_id,
+ const char
*sender_json,
+ GTask
*reply_task);
G_END_DECLS
diff --git a/src/webextension/ephy-web-extension.h b/src/webextension/ephy-web-extension.h
index 3fa446d9a..c20ab964c 100644
--- a/src/webextension/ephy-web-extension.h
+++ b/src/webextension/ephy-web-extension.h
@@ -38,13 +38,13 @@ G_DECLARE_FINAL_TYPE (EphyWebExtension, ephy_web_extension, EPHY, WEB_EXTENSION,
typedef void (*executeTaskHandler)(EphyWebExtension *web_extension,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GTask *task);
typedef char *(*executeHandler)(EphyWebExtension *web_extension,
char *name,
JSCValue *args,
- const char *context_guid,
+ gint64 extension_page_id,
GError **error);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]