[epiphany/pgriffis/web-extension/content-script-replies: 2/4] WebExtensions: Implement replies to tabs.sendMessage()
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/pgriffis/web-extension/content-script-replies: 2/4] WebExtensions: Implement replies to tabs.sendMessage()
- Date: Mon, 6 Jun 2022 20:01:20 +0000 (UTC)
commit 76e1893680616e67b8089078164dd4f3bd994e44
Author: Patrick Griffis <pgriffis igalia com>
Date: Mon Jun 6 12:07:04 2022 -0500
WebExtensions: Implement replies to tabs.sendMessage()
.../resources/js/webextensions-common.js | 5 +
src/webextension/api/runtime.c | 15 ---
src/webextension/api/tabs.c | 50 +++++-----
src/webextension/ephy-web-extension-manager.c | 104 ++++++++++++++++++---
src/webextension/ephy-web-extension-manager.h | 7 ++
src/webextension/ephy-web-extension.c | 15 +++
src/webextension/ephy-web-extension.h | 3 +
7 files changed, 145 insertions(+), 54 deletions(-)
---
diff --git a/embed/web-process-extension/resources/js/webextensions-common.js
b/embed/web-process-extension/resources/js/webextensions-common.js
index 0cba51763..fca23caed 100644
--- a/embed/web-process-extension/resources/js/webextensions-common.js
+++ b/embed/web-process-extension/resources/js/webextensions-common.js
@@ -38,6 +38,11 @@ class EphyEventListener {
for (const listener of this._listeners) {
const ret = listener.callback (message, sender, reply_callback);
if (typeof ret === 'object' && typeof ret.then === 'function') {
+ /* FIXME: I'm very unsure about this behavior. Extensions such as Dark Reader
+ * will have multiple handlers and by listening to extra promises they will
+ * complete the response early. */
+ if (handled)
+ continue;
ret.then(x => { reply_callback(x); }).catch(x => { reply_callback(); });
handled = true;
} else if (ret === true) {
diff --git a/src/webextension/api/runtime.c b/src/webextension/api/runtime.c
index 19438c034..ffaee8d2a 100644
--- a/src/webextension/api/runtime.c
+++ b/src/webextension/api/runtime.c
@@ -58,20 +58,6 @@ is_empty_object (JSCValue *value)
return FALSE;
}
-static char *
-create_sender_object (EphyWebExtension *web_extension,
- WebKitWebView *web_view)
-{
- g_autoptr (JsonNode) node = json_node_init_object (json_node_alloc (), json_object_new ());
- JsonObject *obj = json_node_get_object (node);
-
- json_object_set_string_member (obj, "id", ephy_web_extension_get_guid (web_extension));
- if (web_view)
- json_object_set_string_member (obj, "url", webkit_web_view_get_uri (web_view));
-
- return json_to_string (node, FALSE);
-}
-
static void
runtime_handler_send_message (EphyWebExtension *self,
char *name,
@@ -109,7 +95,6 @@ runtime_handler_send_message (EphyWebExtension *self,
ephy_web_extension_manager_emit_in_extension_views_with_reply (manager, self, "runtime.onMessage",
json,
web_view,
- create_sender_object (self, web_view),
task);
return;
diff --git a/src/webextension/api/tabs.c b/src/webextension/api/tabs.c
index 60438323a..e47816804 100644
--- a/src/webextension/api/tabs.c
+++ b/src/webextension/api/tabs.c
@@ -496,13 +496,14 @@ tabs_handler_execute_script (EphyWebExtension *self,
}
}
-static char *
-tabs_handler_send_message (EphyWebExtension *self,
- char *name,
- JSCValue *args,
- WebKitWebView *web_view,
- GError **error)
+static void
+tabs_handler_send_message (EphyWebExtension *self,
+ char *name,
+ JSCValue *args,
+ WebKitWebView *web_view,
+ GTask *task)
{
+ EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
g_autoptr (JSCValue) tab_id_value = NULL;
g_autoptr (JSCValue) message_value = NULL;
g_autofree char *serialized_message = NULL;
@@ -512,37 +513,38 @@ tabs_handler_send_message (EphyWebExtension *self,
tab_id_value = jsc_value_object_get_property_at_index (args, 0);
if (!jsc_value_is_number (tab_id_value)) {
- g_set_error_literal (error, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid
Arguments");
- return NULL;
+ g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT,
"tabs.sendMessage(): Invalid tabId");
+ return;
}
message_value = jsc_value_object_get_property_at_index (args, 1);
if (jsc_value_is_undefined (message_value)) {
- g_set_error_literal (error, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid
Arguments");
- return NULL;
+ g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT,
"tabs.sendMessage(): Message argument missing");
+ return;
}
serialized_message = jsc_value_to_json (message_value, 0);
code = g_strdup_printf ("window.browser.runtime.onMessage._emit(JSON.parse('%s'));", serialized_message);
target_web_view = get_web_view_for_tab_id (shell, jsc_value_to_int32 (tab_id_value), NULL);
+ if (!target_web_view) {
+ g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT,
"tabs.sendMessage(): Failed to find tabId");
+ return;
+ }
- if (target_web_view) {
- if (!ephy_web_extension_has_host_or_active_permission (self, EPHY_WEB_VIEW (target_web_view), TRUE)) {
- g_set_error_literal (error, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_PERMISSION_DENIED, "Permission
Denied");
- return NULL;
- }
- webkit_web_view_run_javascript_in_world (target_web_view,
- code,
- ephy_web_extension_get_guid (self),
- NULL,
- NULL,
- NULL);
+ if (!ephy_web_extension_has_host_or_active_permission (self, EPHY_WEB_VIEW (target_web_view), TRUE)) {
+ g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_PERMISSION_DENIED,
"tabs.sendMessage(): Permission Denied");
+ return;
}
- /* FIXME: Return message response. */
- return NULL;
+ ephy_web_extension_manager_emit_in_tab_with_reply (manager,
+ self,
+ "runtime.onMessage",
+ serialized_message,
+ target_web_view,
+ ephy_web_extension_create_sender_object (self,
web_view),
+ task);
}
static char *
@@ -837,7 +839,6 @@ static EphyWebExtensionSyncApiHandler tabs_sync_handlers[] = {
{"remove", tabs_handler_remove},
{"removeCSS", tabs_handler_remove_css},
{"get", tabs_handler_get},
- {"sendMessage", tabs_handler_send_message},
{"getZoom", tabs_handler_get_zoom},
{"setZoom", tabs_handler_set_zoom},
{"update", tabs_handler_update},
@@ -845,6 +846,7 @@ static EphyWebExtensionSyncApiHandler tabs_sync_handlers[] = {
static EphyWebExtensionAsyncApiHandler tab_async_handlers[] = {
{"executeScript", tabs_handler_execute_script},
+ {"sendMessage", tabs_handler_send_message},
};
void
diff --git a/src/webextension/ephy-web-extension-manager.c b/src/webextension/ephy-web-extension-manager.c
index 47571e784..523e3b7b8 100644
--- a/src/webextension/ephy-web-extension-manager.c
+++ b/src/webextension/ephy-web-extension-manager.c
@@ -1306,6 +1306,84 @@ typedef struct {
gboolean handled;
} PendingMessageReplyTracker;
+static void
+tab_emit_ready_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ 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;
+ GTask *pending_task;
+
+ js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (source),
+ result,
+ &error);
+
+ /* If it returned true it will be asynchronously handled later. Otherwise we
+ * complete it now with undefined. */
+ if (error || !jsc_value_to_boolean (webkit_javascript_result_get_js_value (js_result))) {
+ 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);
+ }
+
+ if (error)
+ g_warning ("Emitting in tab errored: %s", error->message);
+
+ g_free (tracker);
+}
+
+void
+ephy_web_extension_manager_emit_in_tab_with_reply (EphyWebExtensionManager *self,
+ EphyWebExtension *web_extension,
+ const char *name,
+ const char *message_json,
+ WebKitWebView *target_web_view,
+ const char *sender_json,
+ GTask *reply_task)
+{
+ g_autofree char *script = NULL;
+ PendingMessageReplyTracker *tracker = NULL;
+ GHashTable *pending_messages;
+ g_autofree char *message_guid = NULL;
+
+ g_assert (reply_task);
+ g_assert (target_web_view);
+
+ /* This is similar to ephy_web_extension_manager_emit_in_extension_views_internal()
+ * except it only emits in a single view in a private script world. */
+
+ message_guid = g_dbus_generate_guid ();
+ script = g_strdup_printf ("window.browser.%s._emit_with_reply(%s, %s, '%s');", name, message_json,
sender_json, message_guid);
+
+ tracker = g_new0 (PendingMessageReplyTracker, 1);
+ tracker->web_extension = web_extension;
+ tracker->message_guid = message_guid;
+ webkit_web_view_run_javascript_in_world (target_web_view,
+ script,
+ ephy_web_extension_get_guid (web_extension),
+ NULL,
+ tab_emit_ready_cb,
+ tracker);
+
+ 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");
+}
+
static void
on_extension_emit_ready (GObject *source,
GAsyncResult *result,
@@ -1321,12 +1399,7 @@ on_extension_emit_ready (GObject *source,
result,
&error);
- if (error) {
- g_warning ("%s", error->message);
- return;
- }
-
- if (jsc_value_to_boolean (webkit_javascript_result_get_js_value (js_result)))
+ if (!error && 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
@@ -1349,6 +1422,9 @@ on_extension_emit_ready (GObject *source,
}
g_free (tracker);
}
+
+ if (error)
+ g_warning ("Emitting in view errored: %s", error->message);
}
static void
@@ -1356,8 +1432,7 @@ ephy_web_extension_manager_emit_in_extension_views_internal (EphyWebExtensionMan
EphyWebExtension *web_extension,
const char *name,
const char *message_json,
- WebKitWebView *web_view_exception,
- const char *sender_json,
+ WebKitWebView *own_web_view,
GTask *reply_task)
{
WebKitWebView *background_view = ephy_web_extension_manager_get_background_web_view (self, web_extension);
@@ -1378,6 +1453,7 @@ ephy_web_extension_manager_emit_in_extension_views_internal (EphyWebExtensionMan
* - The first `runtime._sendMessageReply` call wins and completes the GTask with its data.
*/
if (reply_task) {
+ g_autofree char *sender_json = ephy_web_extension_create_sender_object (web_extension, own_web_view);
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);
@@ -1385,7 +1461,7 @@ ephy_web_extension_manager_emit_in_extension_views_internal (EphyWebExtensionMan
script = g_strdup_printf ("window.browser.%s._emit(%s);", name, message_json);
if (background_view) {
- if (web_view_exception != background_view) {
+ if (own_web_view != background_view) {
webkit_web_view_run_javascript (background_view,
script,
NULL,
@@ -1398,7 +1474,7 @@ ephy_web_extension_manager_emit_in_extension_views_internal (EphyWebExtensionMan
if (popup_views) {
for (guint i = 0; i < popup_views->len; i++) {
WebKitWebView *popup_view = g_ptr_array_index (popup_views, i);
- if (web_view_exception == popup_view)
+ if (own_web_view == popup_view)
continue;
webkit_web_view_run_javascript (popup_view,
@@ -1439,7 +1515,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, NULL,
NULL);
+ ephy_web_extension_manager_emit_in_extension_views_internal (self, web_extension, name, json, NULL, NULL);
}
void
@@ -1449,7 +1525,7 @@ ephy_web_extension_manager_emit_in_extension_views_except_self (EphyWebExtension
const char *json,
WebKitWebView *own_webview)
{
- ephy_web_extension_manager_emit_in_extension_views_internal (self, web_extension, name, json, own_webview,
NULL, NULL);
+ ephy_web_extension_manager_emit_in_extension_views_internal (self, web_extension, name, json, own_webview,
NULL);
}
void
@@ -1458,11 +1534,9 @@ ephy_web_extension_manager_emit_in_extension_views_with_reply (EphyWebExtensionM
const char *name,
const char *json,
WebKitWebView *own_web_view,
- const char *sender_json,
GTask *reply_task)
{
- g_assert (sender_json);
g_assert (reply_task);
g_assert (own_web_view);
- ephy_web_extension_manager_emit_in_extension_views_internal (self, web_extension, name, json,
own_web_view, sender_json, reply_task);
+ ephy_web_extension_manager_emit_in_extension_views_internal (self, web_extension, name, json,
own_web_view, reply_task);
}
diff --git a/src/webextension/ephy-web-extension-manager.h b/src/webextension/ephy-web-extension-manager.h
index 5593da6b1..3e639c8af 100644
--- a/src/webextension/ephy-web-extension-manager.h
+++ b/src/webextension/ephy-web-extension-manager.h
@@ -87,6 +87,13 @@ void ephy_web_extension_manager_emit_in_extension_views_with
const char
*name,
const char
*json,
WebKitWebView
*own_web_view,
+ GTask
*reply_task);
+
+void ephy_web_extension_manager_emit_in_tab_with_reply (EphyWebExtensionManager
*self,
+ EphyWebExtension
*web_extension,
+ const char
*name,
+ const char
*message_json,
+ WebKitWebView
*target_web_view,
const char
*sender_json,
GTask
*reply_task);
diff --git a/src/webextension/ephy-web-extension.c b/src/webextension/ephy-web-extension.c
index bcbf3f93a..a360d8b6d 100644
--- a/src/webextension/ephy-web-extension.c
+++ b/src/webextension/ephy-web-extension.c
@@ -1499,3 +1499,18 @@ ephy_web_extension_clear_local_storage (EphyWebExtension *self)
{
self->local_storage = json_node_init_object (self->local_storage, json_object_new ());
}
+
+char *
+ephy_web_extension_create_sender_object (EphyWebExtension *self,
+ WebKitWebView *web_view)
+{
+ g_autoptr (JsonNode) node = json_node_init_object (json_node_alloc (), json_object_new ());
+ JsonObject *obj = json_node_get_object (node);
+
+ json_object_set_string_member (obj, "id", ephy_web_extension_get_guid (self));
+ if (web_view) {
+ json_object_set_string_member (obj, "url", webkit_web_view_get_uri (web_view));
+ }
+
+ return json_to_string (node, FALSE);
+}
diff --git a/src/webextension/ephy-web-extension.h b/src/webextension/ephy-web-extension.h
index 59b9ec839..53d20de56 100644
--- a/src/webextension/ephy-web-extension.h
+++ b/src/webextension/ephy-web-extension.h
@@ -167,5 +167,8 @@ void ephy_web_extension_save_local_storage (EphyW
void ephy_web_extension_clear_local_storage (EphyWebExtension *self);
+char *ephy_web_extension_create_sender_object (EphyWebExtension *self,
+ WebKitWebView *web_view);
+
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]