[epiphany] Add reader mode handler



commit cbf9765da58dd13b6ef219fcbcb7f02265c81572
Author: Jan-Michael Brummer <jan brummer tabos org>
Date:   Thu Jun 11 07:06:49 2020 +0200

    Add reader mode handler
    
    Fixes: https://gitlab.gnome.org/GNOME/epiphany/-/issues/50

 embed/ephy-embed-shell.c                          |  18 +
 embed/ephy-embed-utils.c                          |   4 +-
 embed/ephy-reader-handler.c                       | 393 ++++++++++++++++++++++
 embed/ephy-reader-handler.h                       |  37 ++
 embed/ephy-web-view.c                             | 148 ++------
 embed/meson.build                                 |   1 +
 src/ephy-session.c                                |   3 +-
 third-party/readability/README.epiphany           |  10 +-
 third-party/readability/Readability-readerable.js | 103 ++++++
 third-party/readability/readability.gresource.xml |   1 +
 10 files changed, 602 insertions(+), 116 deletions(-)
---
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index b7ec98e00..482fe7c83 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -36,6 +36,7 @@
 #include "ephy-history-service.h"
 #include "ephy-password-manager.h"
 #include "ephy-profile-utils.h"
+#include "ephy-reader-handler.h"
 #include "ephy-settings.h"
 #include "ephy-snapshot-service.h"
 #include "ephy-tabs-catalog.h"
@@ -63,6 +64,7 @@ typedef struct {
   EphyPasswordManager *password_manager;
   EphyAboutHandler *about_handler;
   EphyViewSourceHandler *source_handler;
+  EphyReaderHandler *reader_handler;
   char *guid;
   EphyFiltersManager *filters_manager;
   EphySearchEngineManager *search_engine_manager;
@@ -188,6 +190,7 @@ ephy_embed_shell_dispose (GObject *object)
   g_clear_object (&priv->global_history_service);
   g_clear_object (&priv->global_gsb_service);
   g_clear_object (&priv->about_handler);
+  g_clear_object (&priv->reader_handler);
   g_clear_object (&priv->source_handler);
   g_clear_object (&priv->downloads_manager);
   g_clear_object (&priv->password_manager);
@@ -686,6 +689,15 @@ source_request_cb (WebKitURISchemeRequest *request,
   ephy_view_source_handler_handle_request (priv->source_handler, request);
 }
 
+static void
+reader_request_cb (WebKitURISchemeRequest *request,
+                   EphyEmbedShell         *shell)
+{
+  EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
+
+  ephy_reader_handler_handle_request (priv->reader_handler, request);
+}
+
 static void
 ephy_resource_request_cb (WebKitURISchemeRequest *request)
 {
@@ -887,6 +899,12 @@ ephy_embed_shell_startup (GApplication *application)
                                           (WebKitURISchemeRequestCallback)source_request_cb,
                                           shell, NULL);
 
+  /* reader mode handler */
+  priv->reader_handler = ephy_reader_handler_new ();
+  webkit_web_context_register_uri_scheme (priv->web_context, EPHY_READER_SCHEME,
+                                          (WebKitURISchemeRequestCallback)reader_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 e207a9ba0..edbe3d614 100644
--- a/embed/ephy-embed-utils.c
+++ b/embed/ephy-embed-utils.c
@@ -26,6 +26,7 @@
 
 #include "ephy-about-handler.h"
 #include "ephy-prefs.h"
+#include "ephy-reader-handler.h"
 #include "ephy-settings.h"
 #include "ephy-string.h"
 #include "ephy-view-source-handler.h"
@@ -132,6 +133,7 @@ ephy_embed_utils_address_has_web_scheme (const char *address)
                      g_ascii_strncasecmp (address, "ephy-about", colonpos) &&
                      g_ascii_strncasecmp (address, "ephy-resource", colonpos) &&
                      g_ascii_strncasecmp (address, "ephy-source", colonpos) &&
+                     g_ascii_strncasecmp (address, "ephy-reader", colonpos) &&
                      g_ascii_strncasecmp (address, "gopher", colonpos) &&
                      g_ascii_strncasecmp (address, "inspector", colonpos) &&
                      g_ascii_strncasecmp (address, "webkit", colonpos));
@@ -394,7 +396,7 @@ ephy_embed_utils_is_no_show_address (const char *address)
     if (!strcmp (address, do_not_show_address[i]))
       return TRUE;
 
-  if (strstr (address, EPHY_VIEW_SOURCE_SCHEME) == address)
+  if (g_str_has_prefix (address, EPHY_VIEW_SOURCE_SCHEME))
     return TRUE;
 
   return FALSE;
diff --git a/embed/ephy-reader-handler.c b/embed/ephy-reader-handler.c
new file mode 100644
index 000000000..f50f03de7
--- /dev/null
+++ b/embed/ephy-reader-handler.c
@@ -0,0 +1,393 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2020 Jan-Michael Brummer <jan brummer tabos org>
+ *
+ *  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 3 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-reader-handler.h"
+
+#include "ephy-embed-container.h"
+#include "ephy-embed-shell.h"
+#include "ephy-lib-type-builtins.h"
+#include "ephy-settings.h"
+#include "ephy-web-view.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+struct _EphyReaderHandler {
+  GObject parent_instance;
+
+  GList *outstanding_requests;
+};
+
+G_DEFINE_TYPE (EphyReaderHandler, ephy_reader_handler, G_TYPE_OBJECT)
+
+typedef struct {
+  EphyReaderHandler *source_handler;
+  WebKitURISchemeRequest *scheme_request;
+  WebKitWebView *web_view;
+  GCancellable *cancellable;
+  guint load_changed_id;
+} EphyReaderRequest;
+
+static EphyReaderRequest *
+ephy_reader_request_new (EphyReaderHandler      *handler,
+                         WebKitURISchemeRequest *request)
+{
+  EphyReaderRequest *reader_request;
+
+  reader_request = g_new (EphyReaderRequest, 1);
+  reader_request->source_handler = g_object_ref (handler);
+  reader_request->scheme_request = g_object_ref (request);
+  reader_request->web_view = NULL; /* created only if required */
+  reader_request->cancellable = g_cancellable_new ();
+  reader_request->load_changed_id = 0;
+
+  return reader_request;
+}
+
+static void
+ephy_reader_request_free (EphyReaderRequest *request)
+{
+  if (request->load_changed_id > 0)
+    g_signal_handler_disconnect (request->web_view, request->load_changed_id);
+
+  g_object_unref (request->source_handler);
+  g_object_unref (request->scheme_request);
+  g_clear_object (&request->web_view);
+
+  g_cancellable_cancel (request->cancellable);
+  g_object_unref (request->cancellable);
+
+  g_free (request);
+}
+
+static void
+finish_uri_scheme_request (EphyReaderRequest *request,
+                           gchar             *data,
+                           GError            *error)
+{
+  GInputStream *stream;
+  gssize data_length;
+
+  g_assert ((data && !error) || (!data && error));
+
+  if (error) {
+    webkit_uri_scheme_request_finish_error (request->scheme_request, error);
+  } else {
+    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");
+    g_object_unref (stream);
+  }
+
+  request->source_handler->outstanding_requests =
+    g_list_remove (request->source_handler->outstanding_requests,
+                   request);
+
+  ephy_reader_request_free (request);
+}
+
+static const char *
+enum_nick (GType enum_type,
+           int   value)
+{
+  GEnumClass *enum_class;
+  const GEnumValue *enum_value;
+  const char *nick = NULL;
+
+  enum_class = g_type_class_ref (enum_type);
+  enum_value = g_enum_get_value (enum_class, value);
+  if (enum_value)
+    nick = enum_value->value_nick;
+
+  g_type_class_unref (enum_class);
+  return nick;
+}
+
+static
+char *readability_get_property_string (WebKitJavascriptResult *js_result,
+                                       char                   *property)
+{
+  JSCValue *jsc_value;
+  char *result = NULL;
+
+  jsc_value = webkit_javascript_result_get_js_value (js_result);
+
+  if (!jsc_value_is_object (jsc_value))
+    return NULL;
+
+  if (jsc_value_object_has_property (jsc_value, property)) {
+    g_autoptr (JSCValue) jsc_content = jsc_value_object_get_property (jsc_value, property);
+
+    result = jsc_value_to_string (jsc_content);
+
+    if (result && strcmp (result, "null") == 0)
+      g_clear_pointer (&result, g_free);
+  }
+
+  return result;
+}
+
+static void
+readability_js_finish_cb (GObject      *object,
+                          GAsyncResult *result,
+                          gpointer      user_data)
+{
+  WebKitWebView *web_view = WEBKIT_WEB_VIEW (object);
+  EphyReaderRequest *request = user_data;
+  g_autoptr (WebKitJavascriptResult) js_result = NULL;
+  g_autoptr (GError) error = NULL;
+  g_autofree gchar *byline = NULL;
+  g_autofree gchar *content = NULL;
+  g_autoptr (GString) html = NULL;
+  g_autoptr (GBytes) style_css = NULL;
+  const gchar *title;
+  const gchar *font_style;
+  const gchar *color_scheme;
+
+  js_result = webkit_web_view_run_javascript_finish (web_view, result, &error);
+  if (!js_result) {
+    if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+      g_warning ("Error running javascript: %s", error->message);
+    g_error_free (error);
+    return;
+  }
+
+  byline = readability_get_property_string (js_result, "byline");
+  content = readability_get_property_string (js_result, "content");
+
+  html = g_string_new ("");
+  style_css = g_resources_lookup_data ("/org/gnome/epiphany/readability/reader.css", 
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
+  title = webkit_web_view_get_title (web_view);
+  font_style = enum_nick (EPHY_TYPE_PREFS_READER_FONT_STYLE,
+                          g_settings_get_enum (EPHY_SETTINGS_READER,
+                                               EPHY_PREFS_READER_FONT_STYLE));
+  color_scheme = enum_nick (EPHY_TYPE_PREFS_READER_COLOR_SCHEME,
+                            g_settings_get_enum (EPHY_SETTINGS_READER,
+                                                 EPHY_PREFS_READER_COLOR_SCHEME));
+
+  g_string_append_printf (html, "<style>%s</style>"
+                          "<title>%s</title>"
+                          "<meta http-equiv=\"Content-Type\" content=\"text/html;\" charset=\"UTF-8\">" \
+                          "<body class='%s %s'>"
+                          "<article>"
+                          "<h2>"
+                          "%s"
+                          "</h2>"
+                          "<i>"
+                          "%s"
+                          "</i>"
+                          "<hr>",
+                          (gchar *)g_bytes_get_data (style_css, NULL),
+                          title,
+                          font_style,
+                          color_scheme,
+                          title,
+                          byline != NULL ? byline : "");
+  g_string_append (html, content);
+  g_string_append (html, "</article>");
+
+  finish_uri_scheme_request (request, g_strdup (html->str), NULL);
+}
+
+static void
+ephy_reader_request_begin_get_source_from_web_view (EphyReaderRequest *request,
+                                                    WebKitWebView     *web_view)
+{
+  webkit_web_view_run_javascript_from_gresource (web_view,
+                                                 "/org/gnome/epiphany/readability/Readability.js",
+                                                 request->cancellable,
+                                                 readability_js_finish_cb,
+                                                 request);
+}
+
+static void
+load_changed_cb (WebKitWebView     *web_view,
+                 WebKitLoadEvent    load_event,
+                 EphyReaderRequest *request)
+{
+  if (load_event == WEBKIT_LOAD_FINISHED) {
+    g_signal_handler_disconnect (request->web_view, request->load_changed_id);
+    request->load_changed_id = 0;
+
+    ephy_reader_request_begin_get_source_from_web_view (request, web_view);
+  }
+}
+
+static void
+ephy_reader_request_begin_get_source_from_uri (EphyReaderRequest *request,
+                                               const char        *uri)
+{
+  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
+  WebKitWebContext *context = ephy_embed_shell_get_web_context (shell);
+
+  request->web_view = WEBKIT_WEB_VIEW (g_object_ref_sink (webkit_web_view_new_with_context (context)));
+
+  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, uri);
+}
+
+static gint
+embed_is_displaying_matching_uri (EphyEmbed *embed,
+                                  SoupURI   *uri)
+{
+  EphyWebView *web_view;
+  SoupURI *view_uri;
+  gint ret = -1;
+
+  if (ephy_embed_has_load_pending (embed))
+    return -1;
+
+  web_view = ephy_embed_get_web_view (embed);
+  if (ephy_web_view_is_loading (web_view))
+    return -1;
+
+  view_uri = soup_uri_new (ephy_web_view_get_address (web_view));
+  if (!view_uri)
+    return -1;
+
+  soup_uri_set_fragment (view_uri, NULL);
+  ret = soup_uri_equal (view_uri, uri) ? 0 : -1;
+
+  soup_uri_free (view_uri);
+
+  return ret;
+}
+
+static WebKitWebView *
+get_web_view_matching_uri (SoupURI *uri)
+{
+  EphyEmbedShell *shell;
+  GtkWindow *window;
+  GList *embeds = NULL;
+  GList *found;
+  EphyEmbed *embed = NULL;
+
+  shell = ephy_embed_shell_get_default ();
+  window = gtk_application_get_active_window (GTK_APPLICATION (shell));
+
+  if (!EPHY_IS_EMBED_CONTAINER (window))
+    goto out;
+
+  embeds = ephy_embed_container_get_children (EPHY_EMBED_CONTAINER (window));
+  found = g_list_find_custom (embeds, uri, (GCompareFunc)embed_is_displaying_matching_uri);
+
+  if (found)
+    embed = found->data;
+
+out:
+  g_list_free (embeds);
+
+  return embed ? WEBKIT_WEB_VIEW (ephy_embed_get_web_view (embed)) : NULL;
+}
+
+static void
+ephy_reader_request_start (EphyReaderRequest *request)
+{
+  g_autoptr (SoupURI) soup_uri = NULL;
+  const char *modified_uri;
+  const char *original_uri;
+  WebKitWebView *web_view;
+
+  original_uri = webkit_uri_scheme_request_get_uri (request->scheme_request);
+  soup_uri = soup_uri_new (original_uri);
+
+  if (!soup_uri) {
+    /* Can't assert because user could theoretically input something weird */
+    GError *error = g_error_new (WEBKIT_NETWORK_ERROR,
+                                 WEBKIT_NETWORK_ERROR_FAILED,
+                                 _("%s is not a valid URI"),
+                                 original_uri);
+    finish_uri_scheme_request (request, NULL, error);
+    return;
+  }
+
+  modified_uri = soup_uri_get_path (soup_uri);
+  g_assert (modified_uri);
+
+  web_view = webkit_uri_scheme_request_get_web_view (request->scheme_request);
+  if (web_view && !webkit_web_view_get_title (web_view))
+    web_view = NULL;
+
+  if (!web_view)
+    web_view = get_web_view_matching_uri (soup_uri);
+
+  if (web_view)
+    ephy_reader_request_begin_get_source_from_web_view (request, WEBKIT_WEB_VIEW (web_view));
+  else
+    ephy_reader_request_begin_get_source_from_uri (request, modified_uri);
+
+  request->source_handler->outstanding_requests =
+    g_list_prepend (request->source_handler->outstanding_requests, request);
+}
+
+static void
+cancel_outstanding_request (EphyReaderRequest *request)
+{
+  g_cancellable_cancel (request->cancellable);
+}
+
+static void
+ephy_reader_handler_dispose (GObject *object)
+{
+  EphyReaderHandler *handler = EPHY_READER_HANDLER (object);
+
+  if (handler->outstanding_requests) {
+    g_list_foreach (handler->outstanding_requests, (GFunc)cancel_outstanding_request, NULL);
+    g_list_free (handler->outstanding_requests);
+    handler->outstanding_requests = NULL;
+  }
+
+  G_OBJECT_CLASS (ephy_reader_handler_parent_class)->dispose (object);
+}
+
+static void
+ephy_reader_handler_init (EphyReaderHandler *handler)
+{
+}
+
+static void
+ephy_reader_handler_class_init (EphyReaderHandlerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = ephy_reader_handler_dispose;
+}
+
+EphyReaderHandler *
+ephy_reader_handler_new (void)
+{
+  return EPHY_READER_HANDLER (g_object_new (EPHY_TYPE_READER_HANDLER, NULL));
+}
+
+void
+ephy_reader_handler_handle_request (EphyReaderHandler      *handler,
+                                    WebKitURISchemeRequest *scheme_request)
+{
+  EphyReaderRequest *reader_request;
+
+  reader_request = ephy_reader_request_new (handler, scheme_request);
+  ephy_reader_request_start (reader_request);
+}
diff --git a/embed/ephy-reader-handler.h b/embed/ephy-reader-handler.h
new file mode 100644
index 000000000..eb806f1a1
--- /dev/null
+++ b/embed/ephy-reader-handler.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2020 Jan-Michael Brummer <jan brummer tabos org>
+ *
+ *  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 3 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_READER_HANDLER (ephy_reader_handler_get_type ())
+
+G_DECLARE_FINAL_TYPE (EphyReaderHandler, ephy_reader_handler, EPHY, READER_HANDLER, GObject)
+
+#define EPHY_READER_SCHEME "ephy-reader"
+
+EphyReaderHandler *ephy_reader_handler_new            (void);
+
+void               ephy_reader_handler_handle_request (EphyReaderHandler  *handler,
+                                                       WebKitURISchemeRequest *request);
+G_END_DECLS
diff --git a/embed/ephy-web-view.c b/embed/ephy-web-view.c
index c21e8553b..7e9094cdb 100644
--- a/embed/ephy-web-view.c
+++ b/embed/ephy-web-view.c
@@ -41,6 +41,7 @@
 #include "ephy-lib-type-builtins.h"
 #include "ephy-permissions-manager.h"
 #include "ephy-prefs.h"
+#include "ephy-reader-handler.h"
 #include "ephy-settings.h"
 #include "ephy-snapshot-service.h"
 #include "ephy-string.h"
@@ -90,12 +91,8 @@ struct _EphyWebView {
   GdkPixbuf *icon;
 
   /* Reader mode */
-  char *reader_content;
-  char *reader_byline;
-  gboolean reader_loading;
-  gboolean reader_active;
+  gboolean reader_mode_available;
   guint reader_js_timeout;
-  char *reader_url;
 
   /* Local file watch. */
   EphyFileMonitor *file_monitor;
@@ -240,7 +237,7 @@ ephy_web_view_get_property (GObject    *object,
       g_value_set_boolean (value, view->is_blank);
       break;
     case PROP_READER_MODE:
-      g_value_set_boolean (value, view->reader_content != NULL);
+      g_value_set_boolean (value, view->reader_mode_available);
       break;
     case PROP_DISPLAY_ADDRESS:
       g_value_set_string (value, view->display_address);
@@ -669,55 +666,29 @@ _ephy_web_view_set_is_blank (EphyWebView *view,
   }
 }
 
-static
-char *readability_get_property_string (WebKitJavascriptResult *js_result,
-                                       char                   *property)
-{
-  JSCValue *jsc_value;
-  char *result = NULL;
-
-  jsc_value = webkit_javascript_result_get_js_value (js_result);
-
-  if (!jsc_value_is_object (jsc_value))
-    return NULL;
-
-  if (jsc_value_object_has_property (jsc_value, property)) {
-    JSCValue *jsc_content = jsc_value_object_get_property (jsc_value, property);
-
-    result = jsc_value_to_string (jsc_content);
-
-    if (result && strcmp (result, "null") == 0)
-      g_clear_pointer (&result, g_free);
-
-    g_object_unref (jsc_content);
-  }
-
-  return result;
-}
-
 static void
 readability_js_finish_cb (GObject      *object,
                           GAsyncResult *result,
                           gpointer      user_data)
 {
   EphyWebView *view = EPHY_WEB_VIEW (user_data);
-  WebKitJavascriptResult *js_result;
-  GError *error = NULL;
+  g_autoptr (WebKitJavascriptResult) js_result = NULL;
+  g_autoptr (GError) error = NULL;
+  JSCValue *jsc_value;
 
   js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error);
   if (!js_result) {
     if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
       g_warning ("Error running javascript: %s", error->message);
-    g_error_free (error);
     return;
   }
 
-  g_clear_pointer (&view->reader_byline, g_free);
-  g_clear_pointer (&view->reader_content, g_free);
+  jsc_value = webkit_javascript_result_get_js_value (js_result);
+  if (!jsc_value_is_boolean (jsc_value)) {
+    return;
+  }
 
-  view->reader_byline = readability_get_property_string (js_result, "byline");
-  view->reader_content = readability_get_property_string (js_result, "content");
-  webkit_javascript_result_unref (js_result);
+  view->reader_mode_available = jsc_value_to_boolean (jsc_value);
 
   g_object_notify_by_pspec (G_OBJECT (view), obj_properties[PROP_READER_MODE]);
 }
@@ -730,7 +701,7 @@ run_readability_js_if_needed (gpointer data)
   /* Internal pages should never receive reader mode. */
   if (!ephy_embed_utils_is_no_show_address (web_view->address)) {
     webkit_web_view_run_javascript_from_gresource (WEBKIT_WEB_VIEW (web_view),
-                                                   "/org/gnome/epiphany/readability/Readability.js",
+                                                   
"/org/gnome/epiphany/readability/Readability-readerable.js",
                                                    web_view->cancellable,
                                                    readability_js_finish_cb,
                                                    web_view);
@@ -1313,6 +1284,7 @@ update_security_status_for_committed_load (EphyWebView *view,
 
   if (!soup_uri ||
       strcmp (soup_uri->scheme, EPHY_VIEW_SOURCE_SCHEME) == 0 ||
+      strcmp (soup_uri->scheme, EPHY_READER_SCHEME) == 0 ||
       g_strcmp0 (tld, "127.0.0.1") == 0 ||
       g_strcmp0 (tld, "::1") == 0 ||
       g_strcmp0 (tld, "localhost") == 0 || /* We trust localhost to be local since glib!616. */
@@ -1396,14 +1368,6 @@ load_changed_cb (WebKitWebView   *web_view,
 
       ephy_web_view_set_loading_message (view, NULL);
 
-      if (!view->reader_loading) {
-        g_clear_pointer (&view->reader_byline, g_free);
-        g_clear_pointer (&view->reader_content, g_free);
-        view->reader_active = FALSE;
-      }
-
-      g_object_notify_by_pspec (G_OBJECT (view), obj_properties[PROP_READER_MODE]);
-
       break;
     }
     case WEBKIT_LOAD_REDIRECTED:
@@ -1449,10 +1413,6 @@ load_changed_cb (WebKitWebView   *web_view,
       /* Zoom level. */
       restore_zoom_level (view, uri);
 
-      /* Reset reader content */
-      if (!view->reader_active)
-        g_clear_pointer (&view->reader_content, g_free);
-
       /* We have to reset the background color here because we set a
        * nonwhite background in constructed.
        */
@@ -1489,8 +1449,7 @@ load_changed_cb (WebKitWebView   *web_view,
 
       g_clear_handle_id (&view->reader_js_timeout, g_source_remove);
 
-      view->reader_loading = FALSE;
-      if (!view->reader_active)
+      if (!ephy_embed_utils_is_no_show_address (view->address))
         view->reader_js_timeout = g_idle_add (run_readability_js_if_needed, web_view);
 
       break;
@@ -1535,7 +1494,6 @@ ephy_web_view_set_placeholder (EphyWebView *view,
   ephy_web_view_set_address (view, uri);
 }
 
-
 static char *
 get_style_sheet (void)
 {
@@ -2326,7 +2284,7 @@ reader_setting_changed_cb (GSettings   *settings,
   const gchar *color_scheme;
   gchar *js_snippet;
 
-  if (!web_view->reader_active)
+  if (!g_str_has_prefix (web_view->address, EPHY_READER_SCHEME))
     return;
 
   font_style = enum_nick (EPHY_TYPE_PREFS_READER_FONT_STYLE,
@@ -2556,7 +2514,6 @@ ephy_web_view_load_url (EphyWebView *view,
   g_assert (EPHY_IS_WEB_VIEW (view));
   g_assert (url);
 
-  view->reader_active = FALSE;
   effective_url = ephy_embed_utils_normalize_address (url);
   if (g_str_has_prefix (effective_url, "javascript:")) {
     char *decoded_url;
@@ -2607,7 +2564,14 @@ ephy_web_view_is_overview (EphyWebView *view)
 const char *
 ephy_web_view_get_address (EphyWebView *view)
 {
-  return view->address ? view->address : "about:blank";
+  if (view->address) {
+    if (g_str_has_prefix (view->address, EPHY_READER_SCHEME))
+      return view->address + strlen (EPHY_READER_SCHEME) + 1;
+
+    return view->address;
+  }
+
+  return "about:blank";
 }
 
 /**
@@ -3428,75 +3392,39 @@ ephy_web_view_toggle_reader_mode (EphyWebView *view,
                                   gboolean     active)
 {
   WebKitWebView *web_view = WEBKIT_WEB_VIEW (view);
-  GString *html;
-  GBytes *style_css;
-  const gchar *title;
-  const gchar *font_style;
-  const gchar *color_scheme;
+  char *reader_uri = NULL;
+  const gchar *address;
+  gboolean view_active = g_str_has_prefix (view->address, EPHY_READER_SCHEME);
 
-  if (view->reader_active == active)
+  if (view_active == active)
     return;
 
-  if (view->reader_active) {
+  address = ephy_web_view_get_address (view);
+
+  if (view_active) {
     ephy_web_view_freeze_history (view);
-    webkit_web_view_load_uri (web_view, view->reader_url);
-    view->reader_active = FALSE;
+    webkit_web_view_load_uri (web_view, address);
     return;
   }
 
-  if (!ephy_web_view_is_reader_mode_available (view)) {
-    view->reader_active = FALSE;
+  if (!ephy_web_view_is_reader_mode_available (view))
     return;
-  }
 
-  view->reader_url = g_strdup (ephy_web_view_get_address (view));
-  html = g_string_new ("");
-  style_css = g_resources_lookup_data ("/org/gnome/epiphany/readability/reader.css", 
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
-  title = webkit_web_view_get_title (web_view);
-  font_style = enum_nick (EPHY_TYPE_PREFS_READER_FONT_STYLE,
-                          g_settings_get_enum (EPHY_SETTINGS_READER,
-                                               EPHY_PREFS_READER_FONT_STYLE));
-  color_scheme = enum_nick (EPHY_TYPE_PREFS_READER_COLOR_SCHEME,
-                            g_settings_get_enum (EPHY_SETTINGS_READER,
-                                                 EPHY_PREFS_READER_COLOR_SCHEME));
-  g_string_append_printf (html, "<style>%s</style>"
-                          "<title>%s</title>"
-                          "<body class='%s %s'>"
-                          "<article>"
-                          "<h2>"
-                          "%s"
-                          "</h2>"
-                          "<i>"
-                          "%s"
-                          "</i>"
-                          "<hr>",
-                          (gchar *)g_bytes_get_data (style_css, NULL),
-                          title,
-                          font_style,
-                          color_scheme,
-                          title,
-                          view->reader_byline != NULL ? view->reader_byline : "");
-  g_string_append (html, view->reader_content);
-  g_string_append (html, "</article>");
+  reader_uri = g_strconcat (EPHY_READER_SCHEME, ":", address, NULL);
 
-  ephy_web_view_freeze_history (view);
-  view->reader_loading = TRUE;
-  webkit_web_view_load_alternate_html (web_view, html->str, view->reader_url, NULL);
-  view->reader_active = TRUE;
-  g_string_free (html, TRUE);
+  webkit_web_view_load_uri (web_view, reader_uri);
 }
 
-
 gboolean
 ephy_web_view_is_reader_mode_available (EphyWebView *view)
 {
-  return view->reader_content != NULL && strlen (view->reader_content) > 0;
+  return view->reader_mode_available;
 }
 
 gboolean
 ephy_web_view_get_reader_mode_state (EphyWebView *view)
 {
-  return view->reader_active;
+  return g_str_has_prefix (view->address, EPHY_READER_SCHEME);
 }
 
 gboolean
@@ -3550,10 +3478,6 @@ ephy_web_view_finalize (GObject *object)
   g_free (view->tls_error_failing_uri);
   g_free (view->pending_snapshot_uri);
 
-  g_free (view->reader_content);
-  g_free (view->reader_byline);
-  g_free (view->reader_url);
-
   G_OBJECT_CLASS (ephy_web_view_parent_class)->finalize (object);
 }
 
diff --git a/embed/meson.build b/embed/meson.build
index d78c261c4..72188aae5 100644
--- a/embed/meson.build
+++ b/embed/meson.build
@@ -27,6 +27,7 @@ libephyembed_sources = [
   'ephy-file-monitor.c',
   'ephy-filters-manager.c',
   'ephy-find-toolbar.c',
+  'ephy-reader-handler.c',
   'ephy-view-source-handler.c',
   'ephy-web-view.c',
   enums
diff --git a/src/ephy-session.c b/src/ephy-session.c
index 988294ec2..337d0740e 100644
--- a/src/ephy-session.c
+++ b/src/ephy-session.c
@@ -875,7 +875,8 @@ session_seems_sane (GList *windows)
       if (uri) {
         if (uri->host != NULL ||
             uri->scheme == SOUP_URI_SCHEME_DATA ||
-            uri->scheme == SOUP_URI_SCHEME_FILE)
+            uri->scheme == SOUP_URI_SCHEME_FILE ||
+            strcmp (uri->scheme, "ephy-reader") == 0)
           sane = TRUE;
         soup_uri_free (uri);
       }
diff --git a/third-party/readability/README.epiphany b/third-party/readability/README.epiphany
index 731426a81..1273a0529 100644
--- a/third-party/readability/README.epiphany
+++ b/third-party/readability/README.epiphany
@@ -5,14 +5,20 @@ This directory contains an official readability.js release version, distributed
 ## Update process
 
 $ wget https://raw.githubusercontent.com/mozilla/readability/master/Readability.js
+$ wget https://raw.githubusercontent.com/mozilla/readability/master/Readability-readerable.js
 
-Copy Readability.js to <epiphany-source>/third-party/readability/
+Copy Readability.js and Readability-readerable.js to <epiphany-source>/third-party/readability/
 
-# Added the following to the bottom of the js file:
+# Added the following to the bottom of the Readability.js file:
 
 // Added for Epiphany
 var documentClone = document.cloneNode(true);
 reader = new Readability(documentClone);
 reader.parse();
 
+# Added the following to the bottom of the Readability-readerable.js file:
+
+// Added for Epiphany
+isProbablyReaderable(document, false);
+
 # Documentation created by Jan-Michael Brummer <jan brummer tabos org>
\ No newline at end of file
diff --git a/third-party/readability/Readability-readerable.js 
b/third-party/readability/Readability-readerable.js
new file mode 100644
index 000000000..4d4001c6e
--- /dev/null
+++ b/third-party/readability/Readability-readerable.js
@@ -0,0 +1,103 @@
+/* eslint-env es6:false */
+/* globals exports */
+/*
+ * Copyright (c) 2010 Arc90 Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This code is heavily based on Arc90's readability.js (1.7.1) script
+ * available at: http://code.google.com/p/arc90labs-readability
+ */
+
+var REGEXPS = {
+  // NOTE: These two regular expressions are duplicated in
+  // Readability.js. Please keep both copies in sync.
+  unlikelyCandidates: 
/-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i,
+  okMaybeItsACandidate: /and|article|body|column|content|main|shadow/i,
+};
+
+function isNodeVisible(node) {
+  // Have to null-check node.style and node.className.indexOf to deal with SVG and MathML nodes.
+  return (!node.style || node.style.display != "none")
+    && !node.hasAttribute("hidden")
+    //check for "fallback-image" so that wikimedia math images are displayed
+    && (!node.hasAttribute("aria-hidden") || node.getAttribute("aria-hidden") != "true" || (node.className 
&& node.className.indexOf && node.className.indexOf("fallback-image") !== -1));
+}
+
+/**
+ * Decides whether or not the document is reader-able without parsing the whole thing.
+ *
+ * @return boolean Whether or not we suspect Readability.parse() will suceeed at returning an article object.
+ */
+function isProbablyReaderable(doc, isVisible) {
+  if (!isVisible) {
+    isVisible = isNodeVisible;
+  }
+
+  var nodes = doc.querySelectorAll("p, pre");
+
+  // Get <div> nodes which have <br> node(s) and append them into the `nodes` variable.
+  // Some articles' DOM structures might look like
+  // <div>
+  //   Sentences<br>
+  //   <br>
+  //   Sentences<br>
+  // </div>
+  var brNodes = doc.querySelectorAll("div > br");
+  if (brNodes.length) {
+    var set = new Set(nodes);
+    [].forEach.call(brNodes, function(node) {
+      set.add(node.parentNode);
+    });
+    nodes = Array.from(set);
+  }
+
+  var score = 0;
+  // This is a little cheeky, we use the accumulator 'score' to decide what to return from
+  // this callback:
+  return [].some.call(nodes, function(node) {
+    if (!isVisible(node))
+      return false;
+
+    var matchString = node.className + " " + node.id;
+    if (REGEXPS.unlikelyCandidates.test(matchString) &&
+        !REGEXPS.okMaybeItsACandidate.test(matchString)) {
+      return false;
+    }
+
+    if (node.matches("li p")) {
+      return false;
+    }
+
+    var textContentLength = node.textContent.trim().length;
+    if (textContentLength < 140) {
+      return false;
+    }
+
+    score += Math.sqrt(textContentLength - 140);
+
+    if (score > 20) {
+      return true;
+    }
+    return false;
+  });
+}
+
+if (typeof exports === "object") {
+  exports.isProbablyReaderable = isProbablyReaderable;
+}
+
+// Added for Epiphany
+isProbablyReaderable(document, false);
diff --git a/third-party/readability/readability.gresource.xml 
b/third-party/readability/readability.gresource.xml
index b3216fd5f..ff4dc2b2d 100644
--- a/third-party/readability/readability.gresource.xml
+++ b/third-party/readability/readability.gresource.xml
@@ -2,6 +2,7 @@
 <gresources>
   <gresource prefix="/org/gnome/epiphany/readability">
     <file compressed="true">Readability.js</file>
+    <file compressed="true">Readability-readerable.js</file>
     <file compressed="true">reader.css</file>
   </gresource>
 </gresources>


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