[epiphany] Add URI handler for view source mode



commit d6d9d250121871e2e21271a2d88ecc585388c23c
Author: Michael Catanzaro <mcatanzaro gnome org>
Date:   Sun Sep 11 15:03:13 2016 -0500

    Add URI handler for view source mode
    
    So we don't have to open these in gedit anymore.
    
    Disadvantage: the URL that gets displayed is actually pulled from the
    server again, so it might not actually be what's displayed in your
    browser. This might be undesirable.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=738475

 embed/Makefile.am                |    4 +-
 embed/ephy-embed-shell.c         |   18 +++
 embed/ephy-embed-utils.c         |    4 +
 embed/ephy-view-source-handler.c |  230 ++++++++++++++++++++++++++++++++++++++
 embed/ephy-view-source-handler.h |   37 ++++++
 embed/ephy-web-view.c            |    5 +-
 src/window-commands.c            |   32 ++++--
 7 files changed, 318 insertions(+), 12 deletions(-)
---
diff --git a/embed/Makefile.am b/embed/Makefile.am
index 62bb1c9..7152d63 100644
--- a/embed/Makefile.am
+++ b/embed/Makefile.am
@@ -25,6 +25,7 @@ libephyembed_la_SOURCES = \
        ephy-embed-container.h          \
        ephy-embed-event.c              \
        ephy-embed-event.h              \
+       ephy-embed-prefs.c              \
        ephy-embed-prefs.h              \
        ephy-embed-private.h            \
        ephy-embed-shell.c              \
@@ -39,7 +40,8 @@ libephyembed_la_SOURCES = \
        ephy-file-monitor.h             \
        ephy-find-toolbar.c             \
        ephy-find-toolbar.h             \
-       ephy-embed-prefs.c              \
+       ephy-view-source-handler.c      \
+       ephy-view-source-handler.h      \
        ephy-web-view.c                 \
        ephy-web-view.h                 \
        ephy-web-extension-proxy.c      \
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index 2d86f32..70556a1 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -33,6 +33,7 @@
 #include "ephy-profile-utils.h"
 #include "ephy-settings.h"
 #include "ephy-snapshot-service.h"
+#include "ephy-view-source-handler.h"
 #include "ephy-web-app-utils.h"
 #include "ephy-web-extension-proxy.h"
 #include "ephy-web-extension-names.h"
@@ -55,6 +56,7 @@ typedef struct {
   WebKitUserContentManager *user_content;
   EphyDownloadsManager *downloads_manager;
   EphyAboutHandler *about_handler;
+  EphyViewSourceHandler *source_handler;
   guint update_overview_timeout_id;
   guint hiding_overview_item;
   GDBusServer *dbus_server;
@@ -100,6 +102,7 @@ ephy_embed_shell_dispose (GObject *object)
   g_clear_object (&priv->print_settings);
   g_clear_object (&priv->global_history_service);
   g_clear_object (&priv->about_handler);
+  g_clear_object (&priv->source_handler);
   g_clear_object (&priv->user_content);
   g_clear_object (&priv->downloads_manager);
   g_clear_object (&priv->web_context);
@@ -474,6 +477,15 @@ about_request_cb (WebKitURISchemeRequest *request,
 }
 
 static void
+source_request_cb (WebKitURISchemeRequest *request,
+                   EphyEmbedShell         *shell)
+{
+  EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
+
+  ephy_view_source_handler_handle_request (priv->source_handler, request);
+}
+
+static void
 ephy_resource_request_cb (WebKitURISchemeRequest *request)
 {
   const char *path;
@@ -778,6 +790,12 @@ ephy_embed_shell_startup (GApplication *application)
   webkit_security_manager_register_uri_scheme_as_local (webkit_web_context_get_security_manager 
(priv->web_context),
                                                         EPHY_ABOUT_SCHEME);
 
+  /* view source handler */
+  priv->source_handler = ephy_view_source_handler_new ();
+  webkit_web_context_register_uri_scheme (priv->web_context, EPHY_VIEW_SOURCE_SCHEME,
+                                          (WebKitURISchemeRequestCallback)source_request_cb,
+                                          shell, NULL);
+
   /* ephy-resource handler */
   webkit_web_context_register_uri_scheme (priv->web_context, "ephy-resource",
                                           (WebKitURISchemeRequestCallback)ephy_resource_request_cb,
diff --git a/embed/ephy-embed-utils.c b/embed/ephy-embed-utils.c
index cb87080..bdd14f7 100644
--- a/embed/ephy-embed-utils.c
+++ b/embed/ephy-embed-utils.c
@@ -26,6 +26,7 @@
 #include "ephy-embed-private.h"
 #include "ephy-settings.h"
 #include "ephy-string.h"
+#include "ephy-view-source-handler.h"
 
 #include <string.h>
 #include <glib/gi18n.h>
@@ -294,6 +295,9 @@ ephy_embed_utils_is_no_show_address (const char *address)
     if (g_str_equal (address, do_not_show_address[i]))
       return TRUE;
 
+  if (strstr (address, EPHY_VIEW_SOURCE_SCHEME) == address)
+    return TRUE;
+
   return FALSE;
 }
 
diff --git a/embed/ephy-view-source-handler.c b/embed/ephy-view-source-handler.c
new file mode 100644
index 0000000..8be34b7
--- /dev/null
+++ b/embed/ephy-view-source-handler.c
@@ -0,0 +1,230 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2016 Igalia S.L.
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-view-source-handler.h"
+
+#include "ephy-embed-shell.h"
+
+#include <gio/gio.h>
+#include <string.h>
+
+struct _EphyViewSourceHandler {
+  GObject parent_instance;
+
+  GList *outstanding_requests;
+};
+
+G_DEFINE_TYPE (EphyViewSourceHandler, ephy_view_source_handler, G_TYPE_OBJECT)
+
+typedef struct {
+  EphyViewSourceHandler *source_handler;
+  WebKitURISchemeRequest *scheme_request;
+  WebKitWebView *web_view;
+  GCancellable *cancellable;
+  guint load_changed_id;
+} EphyViewSourceRequest;
+
+static EphyViewSourceRequest *
+ephy_view_source_request_new (EphyViewSourceHandler  *handler,
+                              WebKitURISchemeRequest *request)
+{
+  EphyViewSourceRequest *view_source_request;
+  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
+  WebKitWebContext *context = ephy_embed_shell_get_web_context (shell);
+
+  view_source_request = g_slice_new (EphyViewSourceRequest);
+  view_source_request->source_handler = handler;
+  view_source_request->scheme_request = g_object_ref (request);
+  view_source_request->web_view = g_object_ref_sink (webkit_web_view_new_with_context (context));
+  view_source_request->cancellable = g_cancellable_new ();
+  view_source_request->load_changed_id = 0;
+
+  return view_source_request;
+}
+
+static void
+ephy_view_source_request_free (EphyViewSourceRequest *request)
+{
+  if (request->load_changed_id > 0)
+    g_signal_handler_disconnect (request->web_view, request->load_changed_id);
+
+  g_object_unref (request->scheme_request);
+  g_object_unref (request->web_view);
+
+  g_cancellable_cancel (request->cancellable);
+  g_object_unref (request->cancellable);
+
+  g_slice_free (EphyViewSourceRequest, request);
+}
+
+static void
+finish_uri_scheme_request (EphyViewSourceRequest *request,
+                           gchar                 *data)
+{
+  GInputStream *stream;
+  gssize data_length;
+
+  data_length = MIN (strlen (data), G_MAXSSIZE);
+  stream = g_memory_input_stream_new_from_data (data, data_length, g_free);
+  webkit_uri_scheme_request_finish (request->scheme_request, stream, data_length, "text/html");
+
+  request->source_handler->outstanding_requests =
+      g_list_remove (request->source_handler->outstanding_requests,
+                     request);
+
+  ephy_view_source_request_free (request);
+  g_object_unref (stream);
+}
+
+static void
+web_resource_data_cb (WebKitWebResource     *resource,
+                      GAsyncResult          *result,
+                      EphyViewSourceRequest *request)
+{
+  guchar *data;
+  char *data_str;
+  char *escaped_str;
+  char *html;
+  gsize length;
+  GError *error = NULL;
+
+  data = webkit_web_resource_get_data_finish (resource, result, &length, &error);
+  if (error) {
+    html = g_strdup (error->message);
+    length = strlen (html);
+    g_error_free (error);
+    finish_uri_scheme_request (request, html);
+    return;
+  }
+
+  data_str = g_malloc (length + 1);
+  strncpy (data_str, (const char *)data, length);
+  data_str[length] = '\0';
+  g_free (data);
+
+  escaped_str = g_markup_escape_text (data_str, -1);
+  g_free (data_str);
+
+  html = g_strdup_printf ("<body>"
+                            "<pre>"
+                              "<code class=\"language-html\">%s</code>"
+                            "</pre>"
+                          "</body>",
+                          escaped_str);
+  g_free (escaped_str);
+
+  finish_uri_scheme_request (request, html);
+}
+
+static void
+load_changed_cb (WebKitWebView         *web_view,
+                 WebKitLoadEvent        load_event,
+                 EphyViewSourceRequest *request)
+{
+  if (load_event == WEBKIT_LOAD_FINISHED) {
+    WebKitWebResource *resource = webkit_web_view_get_main_resource (web_view);
+    webkit_web_resource_get_data (resource,
+                                  request->cancellable,
+                                  (GAsyncReadyCallback)(web_resource_data_cb),
+                                  request);
+  }
+}
+
+static void
+ephy_view_source_request_start (EphyViewSourceRequest *request)
+{
+  SoupURI *soup_uri;
+  char *modified_uri;
+  char *decoded_fragment;
+  const char *original_uri;
+
+  request->source_handler->outstanding_requests =
+      g_list_prepend (request->source_handler->outstanding_requests, request);
+
+  original_uri = webkit_uri_scheme_request_get_uri (request->scheme_request);
+  soup_uri = soup_uri_new (original_uri);
+
+  if (!soup_uri) {
+    g_critical ("Failed to construct SoupURI for %s", original_uri);
+    finish_uri_scheme_request (request, g_strdup (""));
+    return;
+  }
+
+  /* Convert e.g. ephy-source://gnome.org#https to https://gnome.org */
+  g_assert (soup_uri->fragment);
+  decoded_fragment = soup_uri_decode (soup_uri->fragment);
+  soup_uri_set_scheme (soup_uri, decoded_fragment);
+  soup_uri_set_fragment (soup_uri, NULL);
+  modified_uri = soup_uri_to_string (soup_uri, FALSE);
+
+  g_assert(request->load_changed_id == 0);
+  request->load_changed_id = g_signal_connect (request->web_view, "load-changed",
+                                               G_CALLBACK (load_changed_cb),
+                                               request);
+
+  webkit_web_view_load_uri (request->web_view, modified_uri);
+
+  g_free (decoded_fragment);
+  g_free (modified_uri);
+  soup_uri_free (soup_uri);
+}
+
+static void
+ephy_view_source_handler_dispose (GObject *object)
+{
+  EphyViewSourceHandler *handler = EPHY_VIEW_SOURCE_HANDLER (object);
+
+  if (handler->outstanding_requests) {
+    g_list_free_full (handler->outstanding_requests, (GDestroyNotify)ephy_view_source_request_free);
+    handler->outstanding_requests = NULL;
+  }
+
+  G_OBJECT_CLASS (ephy_view_source_handler_parent_class)->dispose (object);
+}
+
+static void
+ephy_view_source_handler_init (EphyViewSourceHandler *handler)
+{
+}
+
+static void
+ephy_view_source_handler_class_init (EphyViewSourceHandlerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = ephy_view_source_handler_dispose;
+}
+
+EphyViewSourceHandler *
+ephy_view_source_handler_new (void)
+{
+  return EPHY_VIEW_SOURCE_HANDLER (g_object_new (EPHY_TYPE_VIEW_SOURCE_HANDLER, NULL));
+}
+
+void
+ephy_view_source_handler_handle_request (EphyViewSourceHandler  *handler,
+                                         WebKitURISchemeRequest *scheme_request)
+{
+  EphyViewSourceRequest *view_source_request;
+
+  view_source_request = ephy_view_source_request_new (handler, scheme_request);
+  ephy_view_source_request_start (view_source_request);
+}
diff --git a/embed/ephy-view-source-handler.h b/embed/ephy-view-source-handler.h
new file mode 100644
index 0000000..9c268a3
--- /dev/null
+++ b/embed/ephy-view-source-handler.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2016 Igalia S.L.
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <webkit2/webkit2.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_VIEW_SOURCE_HANDLER (ephy_view_source_handler_get_type ())
+
+G_DECLARE_FINAL_TYPE (EphyViewSourceHandler, ephy_view_source_handler, EPHY, VIEW_SOURCE_HANDLER, GObject)
+
+#define EPHY_VIEW_SOURCE_SCHEME "ephy-source"
+
+EphyViewSourceHandler *ephy_view_source_handler_new            (void);
+
+void                   ephy_view_source_handler_handle_request (EphyViewSourceHandler  *handler,
+                                                                WebKitURISchemeRequest *request);
+G_END_DECLS
diff --git a/embed/ephy-web-view.c b/embed/ephy-web-view.c
index e21982c..5777656 100644
--- a/embed/ephy-web-view.c
+++ b/embed/ephy-web-view.c
@@ -40,6 +40,7 @@
 #include "ephy-snapshot-service.h"
 #include "ephy-string.h"
 #include "ephy-uri-helpers.h"
+#include "ephy-view-source-handler.h"
 #include "ephy-web-app-utils.h"
 #include "ephy-web-dom-utils.h"
 #include "ephy-web-extension-proxy.h"
@@ -1457,7 +1458,9 @@ update_security_status_for_committed_load (EphyWebView *view,
   g_clear_object (&view->certificate);
   g_clear_pointer (&view->tls_error_failing_uri, g_free);
 
-  if (!soup_uri || webkit_security_manager_uri_scheme_is_local (security_manager, soup_uri->scheme)) {
+  if (!soup_uri ||
+      strcmp (soup_uri_get_scheme (soup_uri), EPHY_VIEW_SOURCE_SCHEME) == 0 ||
+      webkit_security_manager_uri_scheme_is_local (security_manager, soup_uri->scheme)) {
     security_level = EPHY_SECURITY_LEVEL_LOCAL_PAGE;
   } else if (webkit_web_view_get_tls_info (WEBKIT_WEB_VIEW (view), &view->certificate, &view->tls_errors)) {
     g_object_ref (view->certificate);
diff --git a/src/window-commands.c b/src/window-commands.c
index feb7a88..ab007d1 100644
--- a/src/window-commands.c
+++ b/src/window-commands.c
@@ -46,6 +46,7 @@
 #include "ephy-settings.h"
 #include "ephy-shell.h"
 #include "ephy-string.h"
+#include "ephy-view-source-handler.h"
 #include "ephy-web-app-utils.h"
 #include "ephy-zoom.h"
 
@@ -1369,6 +1370,23 @@ static void
 view_source_embedded (const char *uri, EphyEmbed *embed)
 {
   EphyEmbed *new_embed;
+  SoupURI *soup_uri;
+  char *source_uri;
+
+  /* Abort if we're already in view source mode */
+  if (strstr (uri, EPHY_VIEW_SOURCE_SCHEME) == uri)
+    return;
+
+  soup_uri = soup_uri_new (uri);
+  if (!soup_uri) {
+    g_critical ("Failed to construct SoupURI for %s", uri);
+    return;
+  }
+
+  /* Convert e.g. https://gnome.org to ephy-source://gnome.org#https */
+  soup_uri_set_fragment (soup_uri, soup_uri->scheme);
+  soup_uri_set_scheme (soup_uri, EPHY_VIEW_SOURCE_SCHEME);
+  source_uri = soup_uri_to_string (soup_uri, FALSE);
 
   new_embed = ephy_shell_new_tab
                 (ephy_shell_get_default (),
@@ -1376,13 +1394,11 @@ view_source_embedded (const char *uri, EphyEmbed *embed)
                 embed,
                 EPHY_NEW_TAB_JUMP | EPHY_NEW_TAB_APPEND_AFTER);
 
-  /* FIXME: Implement embedded view source mode using a custom URI handler and a
-   * javascript library for the syntax highlighting.
-   * https://bugzilla.gnome.org/show_bug.cgi?id=731558
-   */
-  webkit_web_view_load_uri
-    (EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (new_embed), uri);
+  webkit_web_view_load_uri (EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (new_embed), source_uri);
   gtk_widget_grab_focus (GTK_WIDGET (new_embed));
+
+  g_free (source_uri);
+  soup_uri_free (soup_uri);
 }
 
 static void
@@ -1569,15 +1585,11 @@ window_cmd_page_source (GSimpleAction *action,
 
   address = ephy_web_view_get_address (ephy_embed_get_web_view (embed));
 
-#if 0
- FIXME: Disabled due to bug #738475
-
   if (g_settings_get_boolean (EPHY_SETTINGS_MAIN,
                               EPHY_PREFS_INTERNAL_VIEW_SOURCE)) {
     view_source_embedded (address, embed);
     return;
   }
-#endif
 
   user_time = gtk_get_current_event_time ();
 


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