[epiphany/pgriffis/web-extension/unify-web-process-extensions: 1/2] WebExtensions: Unify web-process-extensions




commit d71dfe35da668b5d24fec5de5fdc3536ebf6b849
Author: Patrick Griffis <pgriffis igalia com>
Date:   Sun Jul 17 18:05:38 2022 -0500

    WebExtensions: Unify web-process-extensions
    
    Previously there were two web process extensions that tried to
    not overlap with eachother however this design doesn't make sense.
    
    Any tab can load web-extension pages. The only distinction is the URI they load.
    
    So this combines them and depending on the URI loaded it will install the correct APIs.

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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]