[epiphany] Add reader mode handler
- From: Jan-Michael Brummer <jbrummer src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany] Add reader mode handler
- Date: Sun, 14 Jun 2020 14:41:04 +0000 (UTC)
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]