[epiphany/pgriffis/web-extension/content-script-replies: 3/5] WebExtensions: Implement replies to tabs.sendMessage()




commit 946558d2d27b3922861ac6b5ba517868db7ef6be
Author: Patrick Griffis <pgriffis igalia com>
Date:   Mon Jun 6 12:07:04 2022 -0500

    WebExtensions: Implement replies to tabs.sendMessage()
    
    Part-of: <https://gitlab.gnome.org/GNOME/epiphany/-/merge_requests/1136>

 .../resources/js/webextensions-common.js           |   5 +
 src/webextension/api/runtime.c                     |  15 ---
 src/webextension/api/tabs.c                        |  55 ++++++-----
 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, 146 insertions(+), 58 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 473dcb45a..42289072d 100644
--- a/src/webextension/api/tabs.c
+++ b/src/webextension/api/tabs.c
@@ -496,53 +496,52 @@ 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;
-  g_autofree char *code = NULL;
   EphyShell *shell = ephy_shell_get_default ();
   WebKitWebView *target_web_view;
 
   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;
+  serialized_message = jsc_value_to_json (message_value, 0);
+  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 *
@@ -845,7 +844,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},
@@ -853,6 +851,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]