[epiphany/pgriffis/web-extension/runtime-send-message: 5/15] WebExtensions: Implement messages replies to runtime.sendMessage()




commit 8cf0d4b577148fd24add62a5cf8d03decaac380f
Author: Patrick Griffis <pgriffis igalia com>
Date:   Sun May 29 12:18:55 2022 -0500

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

 .../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..baadab97b 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, 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));
+  /* 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)
 {
-  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 ();
+  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)
+{
+  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;
+  }
+
+  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]