[evolution/gnome-3-38] I#1286 - WebView: Change how scheme handlers are registered



commit d03ab1c3148377100f0b6a695cf95b7becf059d1
Author: Milan Crha <mcrha redhat com>
Date:   Thu Jan 7 12:03:05 2021 +0100

    I#1286 - WebView: Change how scheme handlers are registered
    
    Closes https://gitlab.gnome.org/GNOME/evolution/-/issues/1286

 src/e-util/e-web-view.c                     | 270 +++++++++++++++++-----------
 src/modules/webkit-editor/e-webkit-editor.c |  78 +++++---
 2 files changed, 217 insertions(+), 131 deletions(-)
---
diff --git a/src/e-util/e-web-view.c b/src/e-util/e-web-view.c
index 9892dba1e6..1c34a578cd 100644
--- a/src/e-util/e-web-view.c
+++ b/src/e-util/e-web-view.c
@@ -75,6 +75,7 @@ struct _EWebViewPrivate {
        gulong font_name_changed_handler_id;
        gulong monospace_font_name_changed_handler_id;
 
+       GHashTable *scheme_handlers; /* gchar *scheme ~> EContentRequest */
        GHashTable *old_settings;
 
        WebKitFindController *find_controller;
@@ -83,8 +84,6 @@ struct _EWebViewPrivate {
 
        gboolean has_hover_link;
 
-       GSList *content_requests; /* EContentRequest * */
-
        GHashTable *element_clicked_cbs; /* gchar *element_class ~> GPtrArray {ElementClickedData} */
 
        gboolean has_selection;
@@ -876,6 +875,146 @@ find_property (guint n_properties,
        return NULL;
 }
 
+static void
+web_view_uri_request_done_cb (GObject *source_object,
+                             GAsyncResult *result,
+                             gpointer user_data)
+{
+       WebKitURISchemeRequest *request = user_data;
+       GInputStream *stream = NULL;
+       gint64 stream_length = -1;
+       gchar *mime_type = NULL;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_CONTENT_REQUEST (source_object));
+       g_return_if_fail (WEBKIT_IS_URI_SCHEME_REQUEST (request));
+
+       if (!e_content_request_process_finish (E_CONTENT_REQUEST (source_object),
+               result, &stream, &stream_length, &mime_type, &error)) {
+               webkit_uri_scheme_request_finish_error (request, error);
+               g_clear_error (&error);
+       } else {
+               webkit_uri_scheme_request_finish (request, stream, stream_length, mime_type);
+
+               g_clear_object (&stream);
+               g_free (mime_type);
+       }
+
+       g_object_unref (request);
+}
+
+static void
+e_web_view_process_uri_request (EWebView *web_view,
+                               WebKitURISchemeRequest *request)
+{
+       EContentRequest *content_request;
+       const gchar *scheme;
+       const gchar *uri;
+       gchar *redirect_to_uri = NULL;
+
+       g_return_if_fail (E_IS_WEB_VIEW (web_view));
+       g_return_if_fail (WEBKIT_IS_URI_SCHEME_REQUEST (request));
+
+       scheme = webkit_uri_scheme_request_get_scheme (request);
+       g_return_if_fail (scheme != NULL);
+
+       content_request = g_hash_table_lookup (web_view->priv->scheme_handlers, scheme);
+
+       if (!content_request) {
+               g_warning ("%s: Cannot find handler for scheme '%s'", G_STRFUNC, scheme);
+               return;
+       }
+
+       uri = webkit_uri_scheme_request_get_uri (request);
+
+       g_return_if_fail (e_content_request_can_process_uri (content_request, uri));
+
+       /* Expects an empty string to abandon the request,
+          or NULL to keep the passed-in uri,
+          or a new uri to load instead. */
+       g_signal_emit (web_view, signals[URI_REQUESTED], 0, uri, &redirect_to_uri);
+
+       if (redirect_to_uri && *redirect_to_uri) {
+               uri = redirect_to_uri;
+       } else if (redirect_to_uri) {
+               GError *error;
+
+               g_free (redirect_to_uri);
+
+               error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
+
+               webkit_uri_scheme_request_finish_error (request, error);
+               g_clear_error (&error);
+
+               return;
+       }
+
+       e_content_request_process (content_request, uri, G_OBJECT (web_view), web_view->priv->cancellable,
+               web_view_uri_request_done_cb, g_object_ref (request));
+
+       g_free (redirect_to_uri);
+}
+
+static void
+web_view_process_uri_request_cb (WebKitURISchemeRequest *request,
+                                gpointer user_data)
+{
+       WebKitWebView *web_view;
+
+       web_view = webkit_uri_scheme_request_get_web_view (request);
+
+       if (E_IS_WEB_VIEW (web_view)) {
+               e_web_view_process_uri_request (E_WEB_VIEW (web_view), request);
+       } else {
+               GError *error;
+
+               error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, "Unexpected WebView type");
+               webkit_uri_scheme_request_finish_error (request, error);
+               g_clear_error (&error);
+
+               g_warning ("%s: Unexpected WebView type '%s' received", G_STRFUNC, web_view ? 
G_OBJECT_TYPE_NAME (web_view) : "null");
+
+               return;
+       }
+}
+
+static GSList *known_schemes = NULL;
+
+static void
+web_view_web_context_gone (gpointer user_data,
+                          GObject *obj)
+{
+       gpointer *pweb_context = user_data;
+
+       g_return_if_fail (pweb_context != NULL);
+
+       *pweb_context = NULL;
+
+       g_slist_free_full (known_schemes, g_free);
+       known_schemes = NULL;
+}
+
+static void
+web_view_ensure_scheme_known (WebKitWebContext *web_context,
+                             const gchar *scheme)
+{
+       GSList *link;
+
+       g_return_if_fail (WEBKIT_IS_WEB_CONTEXT (web_context));
+       g_return_if_fail (scheme != NULL);
+
+       for (link = known_schemes; link; link = g_slist_next (link)) {
+               if (g_strcmp0 (scheme, link->data) == 0)
+                       break;
+       }
+
+       if (!link) {
+               known_schemes = g_slist_prepend (known_schemes, g_strdup (scheme));
+
+               webkit_web_context_register_uri_scheme (web_context, scheme, web_view_process_uri_request_cb, 
NULL, NULL);
+       }
+}
+
 static GObject*
 web_view_constructor (GType type,
                       guint n_construct_properties,
@@ -897,11 +1036,12 @@ web_view_constructor (GType type,
                        g_value_take_object (param->value, webkit_user_content_manager_new ());
                param_spec = g_object_class_find_property (object_class, "web-context");
                if ((param = find_property (n_construct_properties, construct_properties, param_spec))) {
-                       /* Share one web_context between all editors, thus there is one WebProcess
-                          for all the editors (and one for the preview). */
+                       /* Share one web_context between all previews. */
                        static gpointer web_context = NULL;
 
                        if (!web_context) {
+                               GSList *link;
+
                                web_context = webkit_web_context_new ();
 
                                webkit_web_context_set_cache_model (web_context, 
WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
@@ -912,7 +1052,13 @@ web_view_constructor (GType type,
                                webkit_web_context_add_path_to_sandbox (web_context, 
EVOLUTION_SOURCE_WEBKITDATADIR, TRUE);
                                #endif
 
-                               g_object_add_weak_pointer (G_OBJECT (web_context), &web_context);
+                               g_object_weak_ref (G_OBJECT (web_context), web_view_web_context_gone, 
&web_context);
+
+                               for (link = known_schemes; link; link = g_slist_next (link)) {
+                                       const gchar *scheme = link->data;
+
+                                       webkit_web_context_register_uri_scheme (web_context, scheme, 
web_view_process_uri_request_cb, NULL, NULL);
+                               }
                        } else {
                                g_object_ref (web_context);
                        }
@@ -1128,11 +1274,9 @@ web_view_dispose (GObject *object)
                priv->failed_to_find_text_handler_id = 0;
        }
 
+       g_hash_table_remove_all (priv->scheme_handlers);
        g_hash_table_remove_all (priv->element_clicked_cbs);
 
-       g_slist_free_full (priv->content_requests, g_object_unref);
-       priv->content_requests = NULL;
-
        g_clear_object (&priv->ui_manager);
        g_clear_object (&priv->open_proxy);
        g_clear_object (&priv->print_proxy);
@@ -1165,120 +1309,26 @@ web_view_finalize (GObject *object)
                priv->old_settings = NULL;
        }
 
+       g_hash_table_destroy (priv->scheme_handlers);
        g_hash_table_destroy (priv->element_clicked_cbs);
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_web_view_parent_class)->finalize (object);
 }
 
-static void
-web_view_uri_request_done_cb (GObject *source_object,
-                             GAsyncResult *result,
-                             gpointer user_data)
-{
-       WebKitURISchemeRequest *request = user_data;
-       GInputStream *stream = NULL;
-       gint64 stream_length = -1;
-       gchar *mime_type = NULL;
-       GError *error = NULL;
-
-       g_return_if_fail (E_IS_CONTENT_REQUEST (source_object));
-       g_return_if_fail (WEBKIT_IS_URI_SCHEME_REQUEST (request));
-
-       if (!e_content_request_process_finish (E_CONTENT_REQUEST (source_object),
-               result, &stream, &stream_length, &mime_type, &error)) {
-               webkit_uri_scheme_request_finish_error (request, error);
-               g_clear_error (&error);
-       } else {
-               webkit_uri_scheme_request_finish (request, stream, stream_length, mime_type);
-
-               g_clear_object (&stream);
-               g_free (mime_type);
-       }
-
-       g_object_unref (request);
-}
-
-static void
-web_view_process_uri_request_cb (WebKitURISchemeRequest *request,
-                                gpointer user_data)
-{
-       EWebView *web_view = NULL;
-       EContentRequest *content_request = user_data;
-       const gchar *uri;
-       gchar *redirect_to_uri = NULL;
-       GObject *requester;
-
-       g_return_if_fail (WEBKIT_IS_URI_SCHEME_REQUEST (request));
-       g_return_if_fail (E_IS_CONTENT_REQUEST (content_request));
-
-       uri = webkit_uri_scheme_request_get_uri (request);
-       requester = G_OBJECT (webkit_uri_scheme_request_get_web_view (request));
-
-       if (!requester) {
-               GError *error;
-
-               error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
-               webkit_uri_scheme_request_finish_error (request, error);
-               g_clear_error (&error);
-
-               return;
-       }
-
-       g_return_if_fail (e_content_request_can_process_uri (content_request, uri));
-
-       if (E_IS_WEB_VIEW (requester)) {
-               /* Expects an empty string to abandon the request,
-                  or NULL to keep the passed-in uri,
-                  or a new uri to load instead. */
-               g_signal_emit (requester, signals[URI_REQUESTED], 0, uri, &redirect_to_uri);
-
-               if (redirect_to_uri && *redirect_to_uri) {
-                       uri = redirect_to_uri;
-               } else if (redirect_to_uri) {
-                       GError *error;
-
-                       g_free (redirect_to_uri);
-
-                       error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
-
-                       webkit_uri_scheme_request_finish_error (request, error);
-                       g_clear_error (&error);
-
-                       return;
-               }
-
-               web_view = E_WEB_VIEW (requester);
-       }
-
-       e_content_request_process (content_request, uri, requester, web_view ? web_view->priv->cancellable : 
NULL,
-               web_view_uri_request_done_cb, g_object_ref (request));
-
-       g_free (redirect_to_uri);
-}
-
 /* 'scheme' is like "file", not "file:" */
 void
 e_web_view_register_content_request_for_scheme (EWebView *web_view,
                                                const gchar *scheme,
                                                EContentRequest *content_request)
 {
-       WebKitWebContext *web_context;
-
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
        g_return_if_fail (E_IS_CONTENT_REQUEST (content_request));
        g_return_if_fail (scheme != NULL);
 
-       web_context = webkit_web_view_get_context (WEBKIT_WEB_VIEW (web_view));
-
-       webkit_web_context_register_uri_scheme (web_context, scheme, web_view_process_uri_request_cb,
-               g_object_ref (content_request), g_object_unref);
+       g_hash_table_insert (web_view->priv->scheme_handlers, g_strdup (scheme), g_object_ref 
(content_request));
 
-       if (!g_slist_find (web_view->priv->content_requests, content_request)) {
-               web_view->priv->content_requests = g_slist_prepend (
-                       web_view->priv->content_requests,
-                       g_object_ref (content_request));
-       }
+       web_view_ensure_scheme_known (webkit_web_view_get_context (WEBKIT_WEB_VIEW (web_view)), scheme);
 }
 
 static void
@@ -2390,6 +2440,7 @@ e_web_view_init (EWebView *web_view)
 
        web_view->priv->highlights_enabled = TRUE;
        web_view->priv->old_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
(GDestroyNotify) g_variant_unref);
+       web_view->priv->scheme_handlers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
g_object_unref);
 
        g_signal_connect (
                web_view, "context-menu",
@@ -4031,15 +4082,18 @@ e_web_view_request (EWebView *web_view,
 {
        EContentRequest *content_request = NULL;
        AsyncContext *async_context;
-       GSList *link;
+       GHashTableIter iter;
        GTask *task;
        gboolean is_processed = FALSE;
+       gpointer value;
 
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
        g_return_if_fail (uri != NULL);
 
-       for (link = web_view->priv->content_requests; link; link = g_slist_next (link)) {
-               EContentRequest *adept = link->data;
+       g_hash_table_iter_init (&iter, web_view->priv->scheme_handlers);
+
+       while (g_hash_table_iter_next (&iter, NULL, &value)) {
+               EContentRequest *adept = value;
 
                if (!E_IS_CONTENT_REQUEST (adept) ||
                    !e_content_request_can_process_uri (adept, uri))
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index aa4a9aa129..648059b71a 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -78,6 +78,7 @@ struct _EWebKitEditorPrivate {
        EContentEditorInitializedCallback initialized_callback;
        gpointer initialized_user_data;
 
+       GHashTable *scheme_handlers; /* const gchar *scheme ~> EContentRequest */
        GCancellable *cancellable;
 
        gboolean html_mode;
@@ -3863,18 +3864,16 @@ static void
 webkit_editor_process_uri_request_cb (WebKitURISchemeRequest *request,
                                      gpointer user_data)
 {
+       WebKitWebView *web_view;
        EWebKitEditor *wk_editor;
-       EContentRequest *content_request = user_data;
-       const gchar *uri;
-       GObject *requester;
+       EContentRequest *content_request;
+       const gchar *uri, *scheme;
 
        g_return_if_fail (WEBKIT_IS_URI_SCHEME_REQUEST (request));
-       g_return_if_fail (E_IS_CONTENT_REQUEST (content_request));
 
-       uri = webkit_uri_scheme_request_get_uri (request);
-       requester = G_OBJECT (webkit_uri_scheme_request_get_web_view (request));
+       web_view = webkit_uri_scheme_request_get_web_view (request);
 
-       if (!requester) {
+       if (!web_view) {
                GError *error;
 
                error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
@@ -3884,11 +3883,35 @@ webkit_editor_process_uri_request_cb (WebKitURISchemeRequest *request,
                return;
        }
 
-       g_return_if_fail (e_content_request_can_process_uri (content_request, uri));
+       wk_editor = E_IS_WEBKIT_EDITOR (web_view) ? E_WEBKIT_EDITOR (web_view) : NULL;
 
-       wk_editor = E_IS_WEBKIT_EDITOR (requester) ? E_WEBKIT_EDITOR (requester) : NULL;
+       if (!wk_editor) {
+               GError *error;
+
+               error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, "Unexpected WebView type");
+               webkit_uri_scheme_request_finish_error (request, error);
+               g_clear_error (&error);
 
-       e_content_request_process (content_request, uri, requester, wk_editor ? wk_editor->priv->cancellable 
: NULL,
+               g_warning ("%s: Unexpected WebView type '%s' received", G_STRFUNC, web_view ? 
G_OBJECT_TYPE_NAME (web_view) : "null");
+
+               return;
+       }
+
+       scheme = webkit_uri_scheme_request_get_scheme (request);
+       g_return_if_fail (scheme != NULL);
+
+       content_request = g_hash_table_lookup (wk_editor->priv->scheme_handlers, scheme);
+
+       if (!content_request) {
+               g_warning ("%s: Cannot find handler for scheme '%s'", G_STRFUNC, scheme);
+               return;
+       }
+
+       uri = webkit_uri_scheme_request_get_uri (request);
+
+       g_return_if_fail (e_content_request_can_process_uri (content_request, uri));
+
+       e_content_request_process (content_request, uri, G_OBJECT (web_view), wk_editor ? 
wk_editor->priv->cancellable : NULL,
                webkit_editor_uri_request_done_cb, g_object_ref (request));
 }
 
@@ -4104,21 +4127,13 @@ webkit_editor_constructed (GObject *object)
        webkit_web_context_set_spell_checking_languages (web_context, (const gchar * const *) languages);
        g_strfreev (languages);
 
-       content_request = e_cid_request_new ();
-       webkit_web_context_register_uri_scheme (web_context, "cid", webkit_editor_process_uri_request_cb,
-               g_object_ref (content_request), g_object_unref);
-       g_object_unref (content_request);
-
-       content_request = e_file_request_new ();
-       webkit_web_context_register_uri_scheme (web_context, "evo-file", webkit_editor_process_uri_request_cb,
-               g_object_ref (content_request), g_object_unref);
-       g_object_unref (content_request);
+       /* When adding new scheme handlers add them also into webkit_editor_constructor() */
+       g_hash_table_insert (wk_editor->priv->scheme_handlers, (gpointer) "cid", e_cid_request_new ());
+       g_hash_table_insert (wk_editor->priv->scheme_handlers, (gpointer) "evo-file", e_file_request_new ());
 
        content_request = e_http_request_new ();
-       webkit_web_context_register_uri_scheme (web_context, "evo-http", webkit_editor_process_uri_request_cb,
-               g_object_ref (content_request), g_object_unref);
-       webkit_web_context_register_uri_scheme (web_context, "evo-https", 
webkit_editor_process_uri_request_cb,
-               g_object_ref (content_request), g_object_unref);
+       g_hash_table_insert (wk_editor->priv->scheme_handlers, (gpointer) "evo-http", g_object_ref 
(content_request));
+       g_hash_table_insert (wk_editor->priv->scheme_handlers, (gpointer) "evo-https", g_object_ref 
(content_request));
        g_object_unref (content_request);
 
        webkit_web_view_set_editable (web_view, TRUE);
@@ -4211,6 +4226,14 @@ webkit_editor_constructor (GType type,
                        static gpointer web_context = NULL;
 
                        if (!web_context) {
+                               const gchar *schemes[] = {
+                                       "cid",
+                                       "evo-file",
+                                       "evo-http",
+                                       "evo-https"
+                               };
+                               gint ii;
+
                                web_context = webkit_web_context_new ();
 
                                webkit_web_context_set_cache_model (web_context, 
WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
@@ -4222,6 +4245,10 @@ webkit_editor_constructor (GType type,
                                #endif
 
                                g_object_add_weak_pointer (G_OBJECT (web_context), &web_context);
+
+                               for (ii = 0; ii < G_N_ELEMENTS (schemes); ii++) {
+                                       webkit_web_context_register_uri_scheme (web_context, schemes[ii], 
webkit_editor_process_uri_request_cb, NULL, NULL);
+                               }
                        } else {
                                g_object_ref (web_context);
                        }
@@ -4278,6 +4305,8 @@ webkit_editor_dispose (GObject *object)
 
        webkit_editor_finish_search (E_WEBKIT_EDITOR (object));
 
+       g_hash_table_remove_all (priv->scheme_handlers);
+
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_webkit_editor_parent_class)->dispose (object);
 }
@@ -4319,6 +4348,8 @@ webkit_editor_finalize (GObject *object)
        g_free (priv->font_name);
        g_free (priv->context_menu_caret_word);
 
+       g_hash_table_destroy (priv->scheme_handlers);
+
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_webkit_editor_parent_class)->finalize (object);
 }
@@ -5557,6 +5588,7 @@ e_webkit_editor_init (EWebKitEditor *wk_editor)
 
        /* To be able to cancel any pending calls when 'dispose' is called. */
        wk_editor->priv->cancellable = g_cancellable_new ();
+       wk_editor->priv->scheme_handlers = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, 
g_object_unref);
        wk_editor->priv->is_malfunction = FALSE;
        wk_editor->priv->spell_check_enabled = TRUE;
        wk_editor->priv->spell_checker = e_spell_checker_new ();


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