[epiphany] Notify the web extension about history changes to update the overview
- From: Carlos Garcia Campos <carlosgc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany] Notify the web extension about history changes to update the overview
- Date: Tue, 18 Feb 2014 13:38:50 +0000 (UTC)
commit 59bc00280ce31e11b7056ea20dd339327892ff6e
Author: Carlos Garcia Campos <cgarcia igalia com>
Date: Mon Feb 17 14:32:50 2014 +0100
Notify the web extension about history changes to update the overview
And remove the automatic refresh when an overview item is removed via
javascript.
embed/ephy-embed-shell.c | 129 ++++++
embed/ephy-web-extension-proxy.c | 96 +++++
embed/ephy-web-extension-proxy.h | 13 +
embed/web-extension/Makefile.am | 4 +
embed/web-extension/ephy-web-extension.c | 165 +++++----
embed/web-extension/ephy-web-overview-model.c | 286 +++++++++++++
embed/web-extension/ephy-web-overview-model.h | 84 ++++
embed/web-extension/ephy-web-overview.c | 558 +++++++++++++++++++++++++
embed/web-extension/ephy-web-overview.h | 62 +++
src/resources/overview.html | 2 +-
10 files changed, 1327 insertions(+), 72 deletions(-)
---
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index 0ebfab1..0efa3c7 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -212,6 +212,117 @@ ephy_embed_shell_unwatch_web_extension (EphyWebExtensionProxy *web_extension,
g_object_weak_unref (G_OBJECT (web_extension), (GWeakNotify)web_extension_destroyed, shell);
}
+static void
+history_service_query_urls_cb (EphyHistoryService *service,
+ gboolean success,
+ GList *urls,
+ EphyEmbedShell *shell)
+{
+ GList *l;
+
+ if (!success)
+ return;
+
+ for (l = shell->priv->web_extensions; l; l = g_list_next (l)) {
+ EphyWebExtensionProxy *web_extension = (EphyWebExtensionProxy *)l->data;
+
+ ephy_web_extension_proxy_history_set_urls (web_extension, urls);
+ }
+}
+
+static void
+history_service_urls_visited_cb (EphyHistoryService *history,
+ EphyEmbedShell *shell)
+{
+ EphyHistoryQuery *query;
+
+ query = ephy_history_query_new ();
+ query->sort_type = EPHY_HISTORY_SORT_MOST_VISITED;
+ query->limit = ephy_frecent_store_get_history_length (ephy_embed_shell_get_frecent_store (shell));
+ query->ignore_hidden = TRUE;
+
+ ephy_history_service_query_urls (history, query, NULL,
+ (EphyHistoryJobCallback) history_service_query_urls_cb,
+ shell);
+ ephy_history_query_free (query);
+}
+
+static void
+history_service_url_title_changed_cb (EphyHistoryService *service,
+ const char *url,
+ const char *title,
+ EphyEmbedShell *shell)
+{
+ GList *l;
+
+ for (l = shell->priv->web_extensions; l; l = g_list_next (l)) {
+ EphyWebExtensionProxy *web_extension = (EphyWebExtensionProxy *)l->data;
+
+ ephy_web_extension_proxy_history_set_url_title (web_extension, url, title);
+ }
+}
+
+static void
+history_service_url_deleted_cb (EphyHistoryService *service,
+ const char *url,
+ EphyEmbedShell *shell)
+{
+ GList *l;
+
+ for (l = shell->priv->web_extensions; l; l = g_list_next (l)) {
+ EphyWebExtensionProxy *web_extension = (EphyWebExtensionProxy *)l->data;
+
+ ephy_web_extension_proxy_history_delete_url (web_extension, url);
+ }
+}
+
+static void
+history_service_host_deleted_cb (EphyHistoryService *service,
+ const char *deleted_url,
+ EphyEmbedShell *shell)
+{
+ GList *l;
+ SoupURI *deleted_uri;
+
+ deleted_uri = soup_uri_new (deleted_url);
+
+ for (l = shell->priv->web_extensions; l; l = g_list_next (l)) {
+ EphyWebExtensionProxy *web_extension = (EphyWebExtensionProxy *)l->data;
+
+ ephy_web_extension_proxy_history_delete_host (web_extension, soup_uri_get_host (deleted_uri));
+ }
+
+ soup_uri_free (deleted_uri);
+}
+
+static void
+history_service_cleared_cb (EphyHistoryService *service,
+ EphyEmbedShell *shell)
+{
+ GList *l;
+
+ for (l = shell->priv->web_extensions; l; l = g_list_next (l)) {
+ EphyWebExtensionProxy *web_extension = (EphyWebExtensionProxy *)l->data;
+
+ ephy_web_extension_proxy_history_clear (web_extension);
+ }
+}
+
+static void
+frecent_store_snapshot_saved_cb (EphyOverviewStore *store,
+ const char *url,
+ const char *path,
+ EphyEmbedShell *shell)
+{
+ GList *l;
+
+ for (l = shell->priv->web_extensions; l; l = g_list_next (l)) {
+ EphyWebExtensionProxy *web_extension = (EphyWebExtensionProxy *)l->data;
+
+ ephy_web_extension_proxy_history_set_url_thumbnail (web_extension, url, path);
+ }
+}
+
/**
* ephy_embed_shell_get_global_history_service:
* @shell: the #EphyEmbedShell
@@ -230,6 +341,24 @@ ephy_embed_shell_get_global_history_service (EphyEmbedShell *shell)
shell->priv->global_history_service = ephy_history_service_new (filename, FALSE);
g_free (filename);
g_return_val_if_fail (shell->priv->global_history_service, NULL);
+ g_signal_connect (shell->priv->global_history_service, "urls-visited",
+ G_CALLBACK (history_service_urls_visited_cb),
+ shell);
+ g_signal_connect (shell->priv->global_history_service, "url-title-changed",
+ G_CALLBACK (history_service_url_title_changed_cb),
+ shell);
+ g_signal_connect (shell->priv->global_history_service, "url-deleted",
+ G_CALLBACK (history_service_url_deleted_cb),
+ shell);
+ g_signal_connect (shell->priv->global_history_service, "host-deleted",
+ G_CALLBACK (history_service_host_deleted_cb),
+ shell);
+ g_signal_connect (shell->priv->global_history_service, "cleared",
+ G_CALLBACK (history_service_cleared_cb),
+ shell);
+ g_signal_connect (ephy_embed_shell_get_frecent_store (shell), "snapshot-saved",
+ G_CALLBACK (frecent_store_snapshot_saved_cb),
+ shell);
}
return G_OBJECT (shell->priv->global_history_service);
diff --git a/embed/ephy-web-extension-proxy.c b/embed/ephy-web-extension-proxy.c
index dc67723..2056a74 100644
--- a/embed/ephy-web-extension-proxy.c
+++ b/embed/ephy-web-extension-proxy.c
@@ -22,6 +22,7 @@
#include "ephy-web-extension-proxy.h"
#include "ephy-web-extension.h"
+#include "ephy-history-service.h"
struct _EphyWebExtensionProxyPrivate
{
@@ -398,3 +399,98 @@ ephy_web_extension_proxy_get_web_app_title_finish (EphyWebExtensionProxy *web_ex
return g_task_propagate_pointer (G_TASK (result), error);
}
+
+void
+ephy_web_extension_proxy_history_set_urls (EphyWebExtensionProxy *web_extension,
+ GList *urls)
+{
+ GList *l;
+ GVariantBuilder builder;
+
+ if (!web_extension->priv->proxy)
+ return;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)"));
+ for (l = urls; l; l = g_list_next (l)) {
+ EphyHistoryURL *url = (EphyHistoryURL *)l->data;
+
+ g_variant_builder_add (&builder, "(ss)", url->url, url->title);
+ }
+
+ g_dbus_proxy_call (web_extension->priv->proxy,
+ "HistorySetURLs",
+ g_variant_new ("(@a(ss))", g_variant_builder_end (&builder)),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL, NULL);
+}
+
+void
+ephy_web_extension_proxy_history_set_url_thumbnail (EphyWebExtensionProxy *web_extension,
+ const char *url,
+ const char *path)
+{
+ if (!web_extension->priv->proxy)
+ return;
+
+ g_dbus_proxy_call (web_extension->priv->proxy,
+ "HistorySetURLThumbnail",
+ g_variant_new ("(ss)", url, path),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL, NULL);
+}
+
+void
+ephy_web_extension_proxy_history_set_url_title (EphyWebExtensionProxy *web_extension,
+ const char *url,
+ const char *title)
+{
+ if (!web_extension->priv->proxy)
+ return;
+
+ g_dbus_proxy_call (web_extension->priv->proxy,
+ "HistorySetURLTitle",
+ g_variant_new ("(ss)", url, title),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL, NULL);
+}
+
+void
+ephy_web_extension_proxy_history_delete_url (EphyWebExtensionProxy *web_extension,
+ const char *url)
+{
+ if (!web_extension->priv->proxy)
+ return;
+
+ g_dbus_proxy_call (web_extension->priv->proxy,
+ "HistoryDeleteURL",
+ g_variant_new ("(s)", url),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL, NULL);
+}
+
+void
+ephy_web_extension_proxy_history_delete_host (EphyWebExtensionProxy *web_extension,
+ const char *host)
+{
+ if (!web_extension->priv->proxy)
+ return;
+
+ g_dbus_proxy_call (web_extension->priv->proxy,
+ "HistoryDeleteHost",
+ g_variant_new ("(s)", host),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL, NULL);
+}
+
+void
+ephy_web_extension_proxy_history_clear (EphyWebExtensionProxy *web_extension)
+{
+ if (!web_extension->priv->proxy)
+ return;
+
+ g_dbus_proxy_call (web_extension->priv->proxy,
+ "HistoryClear",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL, NULL);
+}
diff --git a/embed/ephy-web-extension-proxy.h b/embed/ephy-web-extension-proxy.h
index fef44d2..094a544 100644
--- a/embed/ephy-web-extension-proxy.h
+++ b/embed/ephy-web-extension-proxy.h
@@ -94,6 +94,19 @@ void ephy_web_extension_proxy_get_web_app_title
char *ephy_web_extension_proxy_get_web_app_title_finish
(EphyWebExtensionProxy *web_extension,
GAsyncResult
*result,
GError
**error);
+void ephy_web_extension_proxy_history_set_urls
(EphyWebExtensionProxy *web_extension,
+ GList
*urls);
+void ephy_web_extension_proxy_history_set_url_thumbnail
(EphyWebExtensionProxy *web_extension,
+ const char
*url,
+ const char
*path);
+void ephy_web_extension_proxy_history_set_url_title
(EphyWebExtensionProxy *web_extension,
+ const char
*url,
+ const char
*title);
+void ephy_web_extension_proxy_history_delete_url
(EphyWebExtensionProxy *web_extension,
+ const char
*url);
+void ephy_web_extension_proxy_history_delete_host
(EphyWebExtensionProxy *web_extension,
+ const char
*host);
+void ephy_web_extension_proxy_history_clear
(EphyWebExtensionProxy *web_extension);
G_END_DECLS
diff --git a/embed/web-extension/Makefile.am b/embed/web-extension/Makefile.am
index 5513d30..4063e15 100644
--- a/embed/web-extension/Makefile.am
+++ b/embed/web-extension/Makefile.am
@@ -8,6 +8,10 @@ libephywebextension_la_SOURCES = \
ephy-embed-form-auth.h \
ephy-web-extension.c \
ephy-web-extension.h \
+ ephy-web-overview.h \
+ ephy-web-overview.c \
+ ephy-web-overview-model.h \
+ ephy-web-overview-model.c \
$(top_srcdir)/embed/uri-tester.c \
$(top_srcdir)/embed/uri-tester.h \
$(top_srcdir)/lib/ephy-debug.c \
diff --git a/embed/web-extension/ephy-web-extension.c b/embed/web-extension/ephy-web-extension.c
index 9d3d7c7..d3ce630 100644
--- a/embed/web-extension/ephy-web-extension.c
+++ b/embed/web-extension/ephy-web-extension.c
@@ -30,6 +30,7 @@
#include "ephy-web-dom-utils.h"
#include "ephy-uri-helpers.h"
#include "uri-tester.h"
+#include "ephy-web-overview.h"
#include <gio/gio.h>
#include <gtk/gtk.h>
@@ -43,6 +44,7 @@ static UriTester *uri_tester;
static EphyFormAuthDataCache *form_auth_data_cache;
static GDBusConnection *dbus_connection;
static GArray *page_created_signals_pending;
+static EphyWebOverviewModel *overview_model;
static const char introspection_xml[] =
"<node>"
@@ -78,6 +80,24 @@ static const char introspection_xml[] =
" <arg type='u' name='request_id' direction='in'/>"
" <arg type='b' name='should_store' direction='in'/>"
" </method>"
+ " <method name='HistorySetURLs'>"
+ " <arg type='a(ss)' name='urls' direction='in'/>"
+ " </method>"
+ " <method name='HistorySetURLThumbnail'>"
+ " <arg type='s' name='url' direction='in'/>"
+ " <arg type='s' name='path' direction='in'/>"
+ " </method>"
+ " <method name='HistorySetURLTitle'>"
+ " <arg type='s' name='url' direction='in'/>"
+ " <arg type='s' name='title' direction='in'/>"
+ " </method>"
+ " <method name='HistoryDeleteURL'>"
+ " <arg type='s' name='url' direction='in'/>"
+ " </method>"
+ " <method name='HistoryDeleteHost'>"
+ " <arg type='s' name='host' direction='in'/>"
+ " </method>"
+ " <method name='HistoryClear'/>"
" </interface>"
"</node>";
@@ -255,7 +275,8 @@ request_decision_on_storing (EphyEmbedFormAuth *form_auth)
}
static void
-remove_from_overview_emit_signal (char *url)
+overview_item_removed (EphyWebOverview *overview,
+ const char *url)
{
GError *error = NULL;
@@ -1041,12 +1062,12 @@ web_page_created_callback (WebKitWebExtension *extension,
else
queue_page_created_signal_emission (page_id);
- g_signal_connect_object (web_page, "send-request",
- G_CALLBACK (web_page_send_request),
- NULL, 0);
- g_signal_connect_object (web_page, "document-loaded",
- G_CALLBACK (web_page_document_loaded),
- NULL, 0);
+ g_signal_connect (web_page, "send-request",
+ G_CALLBACK (web_page_send_request),
+ NULL);
+ g_signal_connect (web_page, "document-loaded",
+ G_CALLBACK (web_page_document_loaded),
+ NULL);
}
static WebKitWebPage *
@@ -1147,8 +1168,63 @@ handle_method_call (GDBusConnection *connection,
if (should_store)
store_password (form_auth);
g_hash_table_remove (requests, GINT_TO_POINTER (request_id));
- }
+ } else if (g_strcmp0 (method_name, "HistorySetURLs") == 0) {
+ if (overview_model) {
+ GVariantIter iter;
+ GVariant *array;
+ const char *url;
+ const char *title;
+ GList *items = NULL;
+
+ g_variant_get (parameters, "(@a(ss))", &array);
+ g_variant_iter_init (&iter, array);
+
+ while (g_variant_iter_loop (&iter, "(&s&s)", &url, &title))
+ items = g_list_prepend (items, ephy_web_overview_model_item_new (url, title));
+ g_variant_unref (array);
+
+ ephy_web_overview_model_set_urls (overview_model, g_list_reverse (items));
+ }
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ } else if (g_strcmp0 (method_name, "HistorySetURLThumbnail") == 0) {
+ if (overview_model) {
+ const char *url;
+ const char *path;
+
+ g_variant_get (parameters, "(&s&s)", &url, &path);
+ ephy_web_overview_model_set_url_thumbnail (overview_model, url, path);
+ }
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ } else if (g_strcmp0 (method_name, "HistorySetURLTitle") == 0) {
+ if (overview_model) {
+ const char *url;
+ const char *title;
+
+ g_variant_get (parameters, "(&s&s)", &url, &title);
+ ephy_web_overview_model_set_url_title (overview_model, url, title);
+ }
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ } else if (g_strcmp0 (method_name, "HistoryDeleteURL") == 0) {
+ if (overview_model) {
+ const char *url;
+
+ g_variant_get (parameters, "(&s)", &url);
+ ephy_web_overview_model_delete_url (overview_model, url);
+ }
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ } else if (g_strcmp0 (method_name, "HistoryDeleteHost") == 0) {
+ if (overview_model) {
+ const char *host;
+ g_variant_get (parameters, "(&s)", &host);
+ ephy_web_overview_model_delete_host (overview_model, host);
+ }
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ } else if (g_strcmp0 (method_name, "HistoryClear") == 0) {
+ if (overview_model)
+ ephy_web_overview_model_clear (overview_model);
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ }
}
static const GDBusInterfaceVTable interface_vtable = {
@@ -1186,59 +1262,6 @@ bus_acquired_cb (GDBusConnection *connection,
}
}
-
-static JSValueRef
-remove_from_overview_cb (JSContextRef context,
- JSObjectRef function,
- JSObjectRef this_object,
- size_t argument_count,
- const JSValueRef arguments[],
- JSValueRef *exception)
-{
- JSStringRef result_string_js;
- size_t max_size;
- char *result_string;
-
- result_string_js = JSValueToStringCopy (context, arguments[0], NULL);
- max_size = JSStringGetMaximumUTF8CStringSize (result_string_js);
-
- result_string = g_malloc (max_size);
- JSStringGetUTF8CString (result_string_js, result_string, max_size);
- remove_from_overview_emit_signal (result_string);
-
- JSStringRelease (result_string_js);
- g_free (result_string);
-
- return JSValueMakeUndefined (context);
-}
-
-static const JSStaticFunction overview_staticfuncs[] =
-{
- { "removeItemFromOverview", remove_from_overview_cb, kJSPropertyAttributeReadOnly |
kJSPropertyAttributeDontDelete },
- { NULL, NULL, 0 }
-};
-
-static const JSClassDefinition overview_notification_def =
-{
- 0, /* version */
- kJSClassAttributeNone, /* attributes */
- "Overview", /* className */
- NULL, /* parentClass */
- NULL, /* staticValues */
- overview_staticfuncs, /* staticFunctions */
- NULL, /* initialize */
- NULL, /* finalize */
- NULL, /* hasProperty */
- NULL, /* getProperty */
- NULL, /* setProperty */
- NULL, /* deleteProperty */
- NULL, /* getPropertyNames */
- NULL, /* callAsFunction */
- NULL, /* callAsConstructor */
- NULL, /* hasInstance */
- NULL /* convertToType */
-};
-
static void
window_object_cleared_cb (WebKitScriptWorld *world,
WebKitWebPage *web_page,
@@ -1246,18 +1269,17 @@ window_object_cleared_cb (WebKitScriptWorld *world,
gpointer user_data)
{
JSGlobalContextRef context;
- JSObjectRef global_object;
- JSClassRef class_def;
- JSObjectRef class_object;
- JSStringRef str;
+ EphyWebOverview *overview;
- context = webkit_frame_get_javascript_context_for_script_world (frame, world);
- global_object = JSContextGetGlobalObject (context);
+ if (g_strcmp0 (webkit_web_page_get_uri (web_page), "ephy-about:overview") != 0)
+ return;
- class_def = JSClassCreate (&overview_notification_def);
- class_object = JSObjectMake (context, class_def, context);
- str = JSStringCreateWithUTF8CString ("Overview");
- JSObjectSetProperty (context, global_object, str, class_object, kJSPropertyAttributeNone, NULL);
+ overview = ephy_web_overview_new (web_page, overview_model);
+ g_signal_connect (overview, "item-removed",
+ G_CALLBACK (overview_item_removed),
+ NULL);
+ context = webkit_frame_get_javascript_context_for_script_world (frame, world);
+ ephy_web_overview_init_js (overview, context);
}
G_MODULE_EXPORT void
@@ -1279,6 +1301,7 @@ webkit_web_extension_initialize_with_user_data (WebKitWebExtension *extension,
ephy_debug_init ();
uri_tester = uri_tester_new (dot_dir);
+ overview_model = ephy_web_overview_model_new ();
if (!private_profile)
form_auth_data_cache = ephy_form_auth_data_cache_new ();
diff --git a/embed/web-extension/ephy-web-overview-model.c b/embed/web-extension/ephy-web-overview-model.c
new file mode 100644
index 0000000..633e8bf
--- /dev/null
+++ b/embed/web-extension/ephy-web-overview-model.c
@@ -0,0 +1,286 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2014 Igalia S.L.
+ *
+ * This program 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, or (at your option)
+ * any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "ephy-web-overview-model.h"
+
+#include <libsoup/soup.h>
+
+struct _EphyWebOverviewModelPrivate
+{
+ GList *items;
+ GHashTable *thumbnails;
+};
+
+enum
+{
+ URLS_CHANGED,
+ THUMBNAIL_CHANGED,
+ TITLE_CHANGED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (EphyWebOverviewModel, ephy_web_overview_model, G_TYPE_OBJECT)
+
+static void
+ephy_web_overview_model_dispose (GObject *object)
+{
+ EphyWebOverviewModel *model = EPHY_WEB_OVERVIEW_MODEL (object);
+
+ if (model->priv->items) {
+ g_list_free_full (model->priv->items, (GDestroyNotify)ephy_web_overview_model_item_free);
+ model->priv->items = NULL;
+ }
+
+ if (model->priv->thumbnails) {
+ g_hash_table_destroy (model->priv->thumbnails);
+ model->priv->thumbnails = NULL;
+ }
+
+ G_OBJECT_CLASS (ephy_web_overview_model_parent_class)->dispose (object);
+}
+
+static void
+ephy_web_overview_model_class_init (EphyWebOverviewModelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = ephy_web_overview_model_dispose;
+
+ signals[URLS_CHANGED] =
+ g_signal_new ("urls-changed",
+ EPHY_TYPE_WEB_OVERVIEW_MODEL,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[THUMBNAIL_CHANGED] =
+ g_signal_new ("thumbnail-changed",
+ EPHY_TYPE_WEB_OVERVIEW_MODEL,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
+ signals[TITLE_CHANGED] =
+ g_signal_new ("title-changed",
+ EPHY_TYPE_WEB_OVERVIEW_MODEL,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
+ g_type_class_add_private (object_class, sizeof(EphyWebOverviewModelPrivate));
+}
+
+static void
+ephy_web_overview_model_init (EphyWebOverviewModel *model)
+{
+ model->priv = G_TYPE_INSTANCE_GET_PRIVATE (model, EPHY_TYPE_WEB_OVERVIEW_MODEL,
EphyWebOverviewModelPrivate);
+ model->priv->thumbnails = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_free);
+}
+
+EphyWebOverviewModel *
+ephy_web_overview_model_new (void)
+{
+ return g_object_new (EPHY_TYPE_WEB_OVERVIEW_MODEL, NULL);
+}
+
+void
+ephy_web_overview_model_set_urls (EphyWebOverviewModel *model,
+ GList *urls)
+{
+ g_return_if_fail (EPHY_IS_WEB_OVERVIEW_MODEL (model));
+
+ g_list_free_full (model->priv->items, (GDestroyNotify)ephy_web_overview_model_item_free);
+ model->priv->items = urls;
+ g_signal_emit (model, signals[URLS_CHANGED], 0);
+}
+
+GList *
+ephy_web_overview_model_get_urls (EphyWebOverviewModel *model)
+{
+ g_return_val_if_fail (EPHY_IS_WEB_OVERVIEW_MODEL (model), NULL);
+
+ return model->priv->items;
+}
+
+void
+ephy_web_overview_model_set_url_thumbnail (EphyWebOverviewModel *model,
+ const char *url,
+ const char *path)
+{
+ const char *thumbnail_path;
+ GList *l;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (EPHY_IS_WEB_OVERVIEW_MODEL (model));
+
+ thumbnail_path = ephy_web_overview_model_get_url_thumbnail (model, url);
+ if (g_strcmp0 (thumbnail_path, path) == 0)
+ return;
+
+ g_hash_table_insert (model->priv->thumbnails, g_strdup (url), g_strdup (path));
+ g_signal_emit (model, signals[THUMBNAIL_CHANGED], 0, url, path);
+}
+
+const char *
+ephy_web_overview_model_get_url_thumbnail (EphyWebOverviewModel *model,
+ const char *url)
+{
+ g_return_val_if_fail (EPHY_IS_WEB_OVERVIEW_MODEL (model), NULL);
+
+ return g_hash_table_lookup (model->priv->thumbnails, url);
+}
+
+void
+ephy_web_overview_model_set_url_title (EphyWebOverviewModel *model,
+ const char *url,
+ const char *title)
+{
+ GList *l;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (EPHY_IS_WEB_OVERVIEW_MODEL (model));
+
+ for (l = model->priv->items; l; l = g_list_next (l)) {
+ EphyWebOverviewModelItem *item = (EphyWebOverviewModelItem *)l->data;
+
+ if (g_strcmp0 (item->url, url) != 0)
+ continue;
+
+ if (g_strcmp0 (item->title, title) != 0) {
+ changed = TRUE;
+
+ g_free (item->title);
+ item->title = g_strdup (title);
+ }
+ }
+
+ if (changed)
+ g_signal_emit (model, signals[TITLE_CHANGED], 0, url, title);
+}
+
+void
+ephy_web_overview_model_delete_url (EphyWebOverviewModel *model,
+ const char *url)
+{
+ GList *l;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (EPHY_IS_WEB_OVERVIEW_MODEL (model));
+
+ l = model->priv->items;
+ while (l) {
+ EphyWebOverviewModelItem *item = (EphyWebOverviewModelItem *)l->data;
+ GList *next = l->next;
+
+ if (g_strcmp0 (item->url, url) == 0) {
+ changed = TRUE;
+
+ ephy_web_overview_model_item_free (item);
+ model->priv->items = g_list_delete_link (model->priv->items, l);
+ }
+
+ l = next;
+ }
+
+ if (changed)
+ g_signal_emit (model, signals[URLS_CHANGED], 0);
+}
+
+void
+ephy_web_overview_model_delete_host (EphyWebOverviewModel *model,
+ const char *host)
+{
+ GList *l;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (EPHY_IS_WEB_OVERVIEW_MODEL (model));
+
+ l = model->priv->items;
+ while (l) {
+ EphyWebOverviewModelItem *item = (EphyWebOverviewModelItem *)l->data;
+ SoupURI *uri = soup_uri_new (item->url);
+ GList *next = l->next;
+
+ if (g_strcmp0 (soup_uri_get_host (uri), host) == 0) {
+ changed = TRUE;
+
+ ephy_web_overview_model_item_free (item);
+ model->priv->items = g_list_delete_link (model->priv->items, l);
+ }
+
+ soup_uri_free (uri);
+ l = next;
+ }
+
+ if (changed)
+ g_signal_emit (model, signals[URLS_CHANGED], 0);
+}
+
+void
+ephy_web_overview_model_clear (EphyWebOverviewModel *model)
+{
+ g_return_if_fail (EPHY_IS_WEB_OVERVIEW_MODEL (model));
+
+ if (!model->priv->items)
+ return;
+
+ g_list_free_full (model->priv->items, (GDestroyNotify)ephy_web_overview_model_item_free);
+ model->priv->items = NULL;
+ g_signal_emit (model, signals[URLS_CHANGED], 0);
+}
+
+EphyWebOverviewModelItem *
+ephy_web_overview_model_item_new (const char *url,
+ const char *title)
+{
+ EphyWebOverviewModelItem *item;
+
+ item = g_slice_new0 (EphyWebOverviewModelItem);
+ item->url = g_strdup (url);
+ item->title = g_strdup (title);
+
+ return item;
+}
+
+void
+ephy_web_overview_model_item_free (EphyWebOverviewModelItem *item)
+{
+ if (G_UNLIKELY (!item))
+ return;
+
+ g_free (item->url);
+ g_free (item->title);
+
+ g_slice_free (EphyWebOverviewModelItem, item);
+}
diff --git a/embed/web-extension/ephy-web-overview-model.h b/embed/web-extension/ephy-web-overview-model.h
new file mode 100644
index 0000000..65838a0
--- /dev/null
+++ b/embed/web-extension/ephy-web-overview-model.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2014 Igalia S.L.
+ *
+ * This program 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, or (at your option)
+ * any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _EPHY_WEB_OVERVIEW_MODEL_H
+#define _EPHY_WEB_OVERVIEW_MODEL_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_WEB_OVERVIEW_MODEL (ephy_web_overview_model_get_type())
+#define EPHY_WEB_OVERVIEW_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
EPHY_TYPE_WEB_OVERVIEW_MODEL, EphyWebOverviewModel))
+#define EPHY_IS_WEB_OVERVIEW_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
EPHY_TYPE_WEB_OVERVIEW_MODEL))
+#define EPHY_WEB_OVERVIEW_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
EPHY_TYPE_WEB_OVERVIEW_MODEL, EphyWebOverviewModelClass))
+#define EPHY_IS_WEB_OVERVIEW_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
EPHY_TYPE_WEB_OVERVIEW_MODEL))
+#define EPHY_WEB_OVERVIEW_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
EPHY_TYPE_WEB_OVERVIEW_MODEL, EphyWebOverviewModelClass))
+
+typedef struct _EphyWebOverviewModel EphyWebOverviewModel;
+typedef struct _EphyWebOverviewModelClass EphyWebOverviewModelClass;
+typedef struct _EphyWebOverviewModelPrivate EphyWebOverviewModelPrivate;
+
+struct _EphyWebOverviewModel
+{
+ GObject parent;
+
+ EphyWebOverviewModelPrivate *priv;
+};
+
+struct _EphyWebOverviewModelClass
+{
+ GObjectClass parent_class;
+};
+
+GType ephy_web_overview_model_get_type (void) G_GNUC_CONST;
+EphyWebOverviewModel *ephy_web_overview_model_new (void);
+void ephy_web_overview_model_set_urls (EphyWebOverviewModel *model,
+ GList *urls);
+GList *ephy_web_overview_model_get_urls (EphyWebOverviewModel *model);
+void ephy_web_overview_model_set_url_thumbnail (EphyWebOverviewModel *model,
+ const char *url,
+ const char *path);
+const char *ephy_web_overview_model_get_url_thumbnail (EphyWebOverviewModel *model,
+ const char *url);
+void ephy_web_overview_model_set_url_title (EphyWebOverviewModel *model,
+ const char *url,
+ const char *title);
+void ephy_web_overview_model_delete_url (EphyWebOverviewModel *model,
+ const char *url);
+void ephy_web_overview_model_delete_host (EphyWebOverviewModel *model,
+ const char *host);
+void ephy_web_overview_model_clear (EphyWebOverviewModel *model);
+
+
+typedef struct _EphyWebOverviewModelItem EphyWebOverviewModelItem;
+struct _EphyWebOverviewModelItem
+{
+ char *url;
+ char *title;
+};
+
+EphyWebOverviewModelItem *ephy_web_overview_model_item_new (const char *url,
+ const char *title);
+void ephy_web_overview_model_item_free (EphyWebOverviewModelItem *item);
+
+G_END_DECLS
+
+#endif /* _EPHY_WEB_OVERVIEW_MODEL_H */
diff --git a/embed/web-extension/ephy-web-overview.c b/embed/web-extension/ephy-web-overview.c
new file mode 100644
index 0000000..118dbb9
--- /dev/null
+++ b/embed/web-extension/ephy-web-overview.c
@@ -0,0 +1,558 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2014 Igalia S.L.
+ *
+ * This program 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, or (at your option)
+ * any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "ephy-web-overview.h"
+
+#include <string.h>
+
+struct _EphyWebOverviewPrivate
+{
+ WebKitWebPage *web_page;
+ EphyWebOverviewModel *model;
+ GList *items;
+};
+
+enum
+{
+ PROP_0,
+ PROP_WEB_PAGE,
+ PROP_MODEL,
+};
+
+enum
+{
+ ITEM_REMOVED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (EphyWebOverview, ephy_web_overview, G_TYPE_OBJECT)
+
+typedef struct {
+ char *url;
+
+ WebKitDOMElement *anchor;
+ WebKitDOMElement *thumbnail;
+ WebKitDOMElement *title;
+} OverviewItem;
+
+static OverviewItem *
+overview_item_new (WebKitDOMElement *anchor)
+{
+ OverviewItem *item;
+ WebKitDOMNodeList *nodes;
+ int i, n_nodes;
+
+ item = g_slice_new0 (OverviewItem);
+ item->anchor = g_object_ref (anchor);
+ item->url = webkit_dom_element_get_attribute (anchor, "href");
+
+ nodes = webkit_dom_node_get_child_nodes (WEBKIT_DOM_NODE (anchor));
+ n_nodes = webkit_dom_node_list_get_length (nodes);
+ for (i = 0; i < n_nodes; i++) {
+ WebKitDOMNode* node = webkit_dom_node_list_item (nodes, i);
+ WebKitDOMElement *element;
+ char *tag;
+
+ if (!WEBKIT_DOM_IS_ELEMENT (node))
+ continue;
+
+ element = WEBKIT_DOM_ELEMENT (node);
+ tag = webkit_dom_element_get_tag_name (element);
+ if (g_strcmp0 (tag, "SPAN") == 0) {
+ char *class;
+
+ class = webkit_dom_element_get_class_name (element);
+ if (g_strcmp0 (class, "thumbnail") == 0)
+ item->thumbnail = g_object_ref (element);
+ else if (g_strcmp0 (class, "title") == 0)
+ item->title = g_object_ref (element);
+
+ g_free (class);
+ }
+
+ g_free (tag);
+ }
+ g_object_unref (nodes);
+
+ return item;
+}
+
+static void
+overview_item_free (OverviewItem *item)
+{
+ g_free (item->url);
+ g_clear_object (&item->anchor);
+ g_clear_object (&item->thumbnail);
+ g_clear_object (&item->title);
+
+ g_slice_free (OverviewItem, item);
+}
+
+static void
+ephy_web_overview_web_page_uri_changed (WebKitWebPage *web_page,
+ GParamSpec *spec,
+ EphyWebOverview *overview)
+{
+ if (g_strcmp0 (webkit_web_page_get_uri (web_page), "ephy-about:overview") != 0)
+ g_object_unref (overview);
+}
+
+static void
+ephy_web_overview_model_urls_changed (EphyWebOverviewModel *model,
+ EphyWebOverview *overview)
+{
+ GList *urls;
+ GList *l;
+ GList *items;
+ OverviewItem* item;
+
+ urls = ephy_web_overview_model_get_urls (model);
+
+ items = overview->priv->items;
+ for (l = urls; l; l = g_list_next (l)) {
+ EphyWebOverviewModelItem *url = (EphyWebOverviewModelItem *)l->data;
+ const char *thumbnail_path;
+
+ thumbnail_path = ephy_web_overview_model_get_url_thumbnail (model, url->url);
+
+ if (items) {
+ WebKitDOMDOMTokenList *class_list;
+
+ item = (OverviewItem *)items->data;
+
+ g_free (item->url);
+ item->url = g_strdup (url->url);
+
+ class_list = webkit_dom_element_get_class_list (webkit_dom_node_get_parent_element (WEBKIT_DOM_NODE
(item->anchor)));
+ if (class_list && webkit_dom_dom_token_list_contains (class_list, "removed", NULL))
+ webkit_dom_dom_token_list_remove (class_list, "removed", NULL);
+
+ webkit_dom_element_set_attribute (item->anchor, "href", url->url, NULL);
+ webkit_dom_element_set_attribute (item->anchor, "title", url->title, NULL);
+ webkit_dom_node_set_text_content (WEBKIT_DOM_NODE (item->title), url->title, NULL);
+
+ if (thumbnail_path) {
+ char *style;
+
+ style = g_strdup_printf ("background: url(ephy-about:/thumbnail-frame.png), url(file://%s) no-repeat
10px 9px;", thumbnail_path);
+ webkit_dom_element_set_attribute (item->thumbnail, "style", style, NULL);
+ g_free (style);
+ } else {
+ webkit_dom_element_remove_attribute (item->thumbnail, "style");
+ }
+ } else {
+ WebKitDOMDocument *document;
+ WebKitDOMElement *item_list, *anchor;
+ WebKitDOMNode *new_node;
+
+ item = g_slice_new0 (OverviewItem);
+ item->url = g_strdup (url->url);
+
+ document = webkit_web_page_get_dom_document (overview->priv->web_page);
+ item_list = webkit_dom_document_get_element_by_id (document, "item-list");
+
+ new_node = WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "LI", NULL));
+ webkit_dom_node_append_child (WEBKIT_DOM_NODE (item_list), WEBKIT_DOM_NODE (new_node), NULL);
+
+ anchor = webkit_dom_document_create_element (document, "A", NULL);
+ item->anchor = g_object_ref (anchor);
+ webkit_dom_element_set_class_name (WEBKIT_DOM_ELEMENT (anchor), "overview-item");
+ webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (anchor), "href", url->url, NULL);
+ webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (anchor), "title", url->title, NULL);
+ webkit_dom_node_append_child (WEBKIT_DOM_NODE (new_node), WEBKIT_DOM_NODE (anchor), NULL);
+
+ new_node = WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "DIV", NULL));
+ webkit_dom_element_set_class_name (WEBKIT_DOM_ELEMENT (new_node), "close-button");
+ webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (new_node), "onclick",
"removeFromOverview(this.parentNode,event)", NULL);
+ webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (new_node), "title", url->title, NULL);
+ webkit_dom_node_set_text_content (new_node, "✖", NULL);
+ webkit_dom_node_append_child (WEBKIT_DOM_NODE (anchor), new_node, NULL);
+
+ new_node = WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "SPAN", NULL));
+ item->thumbnail = g_object_ref (new_node);
+ webkit_dom_element_set_class_name (WEBKIT_DOM_ELEMENT (new_node), "thumbnail");
+ if (thumbnail_path) {
+ char *style;
+
+ style = g_strdup_printf ("background: url(ephy-about:/thumbnail-frame.png), url(file://%s) no-repeat
10px 9px;", thumbnail_path);
+ webkit_dom_element_set_attribute (WEBKIT_DOM_ELEMENT (new_node), "style", style, NULL);
+ g_free (style);
+ }
+ webkit_dom_node_append_child (WEBKIT_DOM_NODE (anchor), new_node, NULL);
+
+ new_node = WEBKIT_DOM_NODE (webkit_dom_document_create_element (document, "SPAN", NULL));
+ item->title = g_object_ref (new_node);
+ webkit_dom_element_set_class_name (WEBKIT_DOM_ELEMENT (new_node), "title");
+ webkit_dom_node_set_text_content (new_node, url->title, NULL);
+ webkit_dom_node_append_child (WEBKIT_DOM_NODE (anchor), new_node, NULL);
+
+ overview->priv->items = g_list_append (overview->priv->items, item);
+ }
+
+ items = g_list_next (items);
+ }
+
+ while (items) {
+ WebKitDOMNode *anchor;
+ GList *next = items->next;
+
+ item = (OverviewItem *)items->data;
+
+ anchor = WEBKIT_DOM_NODE (item->anchor);
+ webkit_dom_node_remove_child (webkit_dom_node_get_parent_node (anchor), anchor, NULL);
+
+ overview_item_free (item);
+ overview->priv->items = g_list_delete_link (overview->priv->items, items);
+
+ items = next;
+ }
+}
+
+static void
+ephy_web_overview_model_thumbnail_changed (EphyWebOverviewModel *model,
+ const char *url,
+ const char *path,
+ EphyWebOverview *overview)
+{
+ GList *l;
+
+ for (l = overview->priv->items; l; l = g_list_next (l)) {
+ OverviewItem *item = (OverviewItem *)l->data;
+ char *style;
+
+ if (g_strcmp0 (item->url, url) != 0)
+ continue;
+
+ style = g_strdup_printf ("background: url(ephy-about:/thumbnail-frame.png), url(file://%s) no-repeat
10px 9px;", path);
+ webkit_dom_element_set_attribute (item->thumbnail, "style", style, NULL);
+ g_free (style);
+ }
+}
+
+static void
+ephy_web_overview_model_title_changed (EphyWebOverviewModel *model,
+ const char *url,
+ const char *title,
+ EphyWebOverview *overview)
+{
+ GList *l;
+
+ for (l = overview->priv->items; l; l = g_list_next (l)) {
+ OverviewItem *item = (OverviewItem *)l->data;
+ char *style;
+
+ if (g_strcmp0 (item->url, url) != 0)
+ continue;
+
+ webkit_dom_element_set_attribute (item->anchor, "title", title, NULL);
+ webkit_dom_node_set_text_content (WEBKIT_DOM_NODE (item->title), title, NULL);
+ }
+}
+
+static void
+ephy_web_overview_update_thumbnail_in_model_from_element (EphyWebOverview *overview,
+ const char *url,
+ WebKitDOMElement *thumbnail)
+{
+ WebKitDOMCSSStyleDeclaration *style;
+ char *background;
+ char *thumbnail_path;
+
+ style = webkit_dom_element_get_style (thumbnail);
+ if (webkit_dom_css_style_declaration_is_property_implicit (style, "background"))
+ return;
+
+ background = webkit_dom_css_style_declaration_get_property_value (style, "background");
+ if (!background)
+ return;
+
+ thumbnail_path = g_strrstr (background, "file://");
+ if (thumbnail_path) {
+ char *p;
+
+ thumbnail_path += strlen ("file://");
+ p = g_strrstr (thumbnail_path, ")");
+ if (p) {
+ thumbnail_path = g_strndup (thumbnail_path, strlen (thumbnail_path) - strlen (p));
+ g_signal_handlers_block_by_func (overview->priv->model, G_CALLBACK
(ephy_web_overview_model_thumbnail_changed), overview);
+ ephy_web_overview_model_set_url_thumbnail (overview->priv->model, url, thumbnail_path);
+ g_signal_handlers_unblock_by_func (overview->priv->model, G_CALLBACK
(ephy_web_overview_model_thumbnail_changed), overview);
+ g_free (thumbnail_path);
+ }
+
+ }
+ g_free (background);
+}
+
+static void
+ephy_web_overview_document_loaded (WebKitWebPage *web_page,
+ EphyWebOverview *overview)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *nodes;
+ int i, n_nodes;
+
+ document = webkit_web_page_get_dom_document (web_page);
+ nodes = webkit_dom_document_get_elements_by_tag_name (document, "a");
+ n_nodes = webkit_dom_node_list_get_length (nodes);
+ for (i = 0; i < n_nodes; i++) {
+ WebKitDOMElement* element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (nodes, i));
+ char *class;
+
+ class = webkit_dom_element_get_class_name (element);
+ if (g_strcmp0 (class, "overview-item") == 0) {
+ OverviewItem *item = overview_item_new (element);
+
+ /* URLs and titles are always sent from the UI process, but thumbnails don't,
+ * so update the model with the thumbnail of there's one.
+ */
+ ephy_web_overview_update_thumbnail_in_model_from_element (overview, item->url, item->thumbnail);
+
+ overview->priv->items = g_list_prepend (overview->priv->items, item);
+ }
+ g_free (class);
+ }
+ g_object_unref (nodes);
+ overview->priv->items = g_list_reverse (overview->priv->items);
+}
+
+static void
+ephy_web_overview_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EphyWebOverview *overview = EPHY_WEB_OVERVIEW (object);
+
+ switch (prop_id)
+ {
+ case PROP_WEB_PAGE:
+ overview->priv->web_page = g_value_get_object (value);
+ break;
+ case PROP_MODEL:
+ overview->priv->model = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ephy_web_overview_dispose (GObject *object)
+{
+ EphyWebOverview *overview = EPHY_WEB_OVERVIEW (object);
+
+ if (overview->priv->items) {
+ g_list_free_full (overview->priv->items, (GDestroyNotify)overview_item_free);
+ overview->priv->items = NULL;
+ }
+
+ G_OBJECT_CLASS (ephy_web_overview_parent_class)->dispose (object);
+}
+
+static void
+ephy_web_overview_web_view_destroyed (EphyWebOverview *overview,
+ GObject *destroyed_web_view)
+{
+ overview->priv->web_page = NULL;
+ g_object_unref (overview);
+}
+
+void
+ephy_web_overview_finalize (GObject *object)
+{
+ EphyWebOverview *overview = EPHY_WEB_OVERVIEW (object);
+
+ if (overview->priv->web_page) {
+ g_object_weak_unref (G_OBJECT (overview->priv->web_page),
+ (GWeakNotify)ephy_web_overview_web_view_destroyed,
+ overview);
+ }
+
+ G_OBJECT_CLASS (ephy_web_overview_parent_class)->finalize (object);
+}
+
+static void
+ephy_web_overview_constructed (GObject *object)
+{
+ EphyWebOverview *overview = EPHY_WEB_OVERVIEW (object);
+
+ G_OBJECT_CLASS (ephy_web_overview_parent_class)->constructed (object);
+
+ g_object_weak_ref (G_OBJECT (overview->priv->web_page),
+ (GWeakNotify)ephy_web_overview_web_view_destroyed,
+ overview);
+
+ g_signal_connect_object (overview->priv->web_page, "notify::uri",
+ G_CALLBACK (ephy_web_overview_web_page_uri_changed),
+ overview, 0);
+ g_signal_connect_object (overview->priv->web_page, "document-loaded",
+ G_CALLBACK (ephy_web_overview_document_loaded),
+ overview, 0);
+
+ g_signal_connect_object (overview->priv->model, "urls-changed",
+ G_CALLBACK (ephy_web_overview_model_urls_changed),
+ overview, 0);
+ g_signal_connect_object (overview->priv->model, "thumbnail-changed",
+ G_CALLBACK (ephy_web_overview_model_thumbnail_changed),
+ overview, 0);
+ g_signal_connect_object (overview->priv->model, "title-changed",
+ G_CALLBACK (ephy_web_overview_model_title_changed),
+ overview, 0);
+}
+
+static void
+ephy_web_overview_class_init (EphyWebOverviewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = ephy_web_overview_dispose;
+ object_class->finalize = ephy_web_overview_finalize;
+ object_class->constructed = ephy_web_overview_constructed;
+ object_class->set_property = ephy_web_overview_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_WEB_PAGE,
+ g_param_spec_object ("web-page",
+ "WebPage",
+ "The overview WebPage",
+ WEBKIT_TYPE_WEB_PAGE,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
G_PARAM_STATIC_BLURB));
+ g_object_class_install_property (object_class,
+ PROP_MODEL,
+ g_param_spec_object ("model",
+ "Model",
+ "The overview model",
+ EPHY_TYPE_WEB_OVERVIEW_MODEL,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
G_PARAM_STATIC_BLURB));
+
+ signals[ITEM_REMOVED] =
+ g_signal_new ("item-removed",
+ EPHY_TYPE_WEB_OVERVIEW,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+
+ g_type_class_add_private (object_class, sizeof(EphyWebOverviewPrivate));
+}
+
+static void
+ephy_web_overview_init (EphyWebOverview *overview)
+{
+ overview->priv = G_TYPE_INSTANCE_GET_PRIVATE (overview, EPHY_TYPE_WEB_OVERVIEW, EphyWebOverviewPrivate);
+}
+
+EphyWebOverview *
+ephy_web_overview_new (WebKitWebPage *web_page,
+ EphyWebOverviewModel *model)
+{
+ g_return_val_if_fail (WEBKIT_IS_WEB_PAGE (web_page), NULL);
+ g_return_val_if_fail (EPHY_IS_WEB_OVERVIEW_MODEL (model), NULL);
+
+ return g_object_new (EPHY_TYPE_WEB_OVERVIEW,
+ "web-page", web_page,
+ "model", model,
+ NULL);
+}
+
+static JSValueRef
+remove_item_from_overview_cb (JSContextRef context,
+ JSObjectRef function,
+ JSObjectRef this_object,
+ size_t argument_count,
+ const JSValueRef arguments[],
+ JSValueRef *exception)
+{
+ EphyWebOverview *overview;
+ JSStringRef result_string_js;
+ size_t max_size;
+ char *result_string;
+
+ overview = EPHY_WEB_OVERVIEW (JSObjectGetPrivate (this_object));
+
+ result_string_js = JSValueToStringCopy (context, arguments[0], NULL);
+ max_size = JSStringGetMaximumUTF8CStringSize (result_string_js);
+
+ result_string = g_malloc (max_size);
+ JSStringGetUTF8CString (result_string_js, result_string, max_size);
+ g_signal_emit (overview, signals[ITEM_REMOVED], 0, result_string);
+ JSStringRelease (result_string_js);
+ g_free (result_string);
+
+ return JSValueMakeUndefined (context);
+}
+
+static const JSStaticFunction overview_static_funcs[] =
+{
+ { "removeItemFromOverview", remove_item_from_overview_cb, kJSPropertyAttributeReadOnly |
kJSPropertyAttributeDontDelete },
+ { NULL, NULL, 0 }
+};
+
+static const JSClassDefinition overview_class_def =
+{
+ 0, /* version */
+ kJSClassAttributeNone, /* attributes */
+ "Overview", /* className */
+ NULL, /* parentClass */
+ NULL, /* staticValues */
+ overview_static_funcs, /* staticFunctions */
+ NULL, /* initialize */
+ NULL, /* finalize */
+ NULL, /* hasProperty */
+ NULL, /* getProperty */
+ NULL, /* setProperty */
+ NULL, /* deleteProperty */
+ NULL, /* getPropertyNames */
+ NULL, /* callAsFunction */
+ NULL, /* callAsConstructor */
+ NULL, /* hasInstance */
+ NULL /* convertToType */
+};
+
+void
+ephy_web_overview_init_js (EphyWebOverview *overview,
+ JSGlobalContextRef context)
+{
+ JSObjectRef global_object;
+ JSClassRef class_def;
+ JSObjectRef class_object;
+ JSStringRef str;
+
+ global_object = JSContextGetGlobalObject (context);
+
+ class_def = JSClassCreate (&overview_class_def);
+ class_object = JSObjectMake (context, class_def, overview);
+ str = JSStringCreateWithUTF8CString ("Overview");
+ JSObjectSetProperty (context, global_object, str, class_object, kJSPropertyAttributeNone, NULL);
+ JSStringRelease (str);
+
+ /* FIXME: check leaks here */
+}
diff --git a/embed/web-extension/ephy-web-overview.h b/embed/web-extension/ephy-web-overview.h
new file mode 100644
index 0000000..b52f7a5
--- /dev/null
+++ b/embed/web-extension/ephy-web-overview.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2014 Igalia S.L.
+ *
+ * This program 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, or (at your option)
+ * any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _EPHY_WEB_OVERVIEW_H
+#define _EPHY_WEB_OVERVIEW_H
+
+#include "ephy-web-overview-model.h"
+#include <webkit2/webkit-web-extension.h>
+#include <JavaScriptCore/JavaScript.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_WEB_OVERVIEW (ephy_web_overview_get_type())
+#define EPHY_WEB_OVERVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EPHY_TYPE_WEB_OVERVIEW,
EphyWebOverview))
+#define EPHY_IS_WEB_OVERVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EPHY_TYPE_WEB_OVERVIEW))
+#define EPHY_WEB_OVERVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EPHY_TYPE_WEB_OVERVIEW,
EphyWebOverviewClass))
+#define EPHY_IS_WEB_OVERVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EPHY_TYPE_WEB_OVERVIEW))
+#define EPHY_WEB_OVERVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EPHY_TYPE_WEB_OVERVIEW,
EphyWebOverviewClass))
+
+typedef struct _EphyWebOverview EphyWebOverview;
+typedef struct _EphyWebOverviewClass EphyWebOverviewClass;
+typedef struct _EphyWebOverviewPrivate EphyWebOverviewPrivate;
+
+struct _EphyWebOverview
+{
+ GObject parent;
+
+ EphyWebOverviewPrivate *priv;
+};
+
+struct _EphyWebOverviewClass
+{
+ GObjectClass parent_class;
+};
+
+GType ephy_web_overview_get_type (void) G_GNUC_CONST;
+
+EphyWebOverview *ephy_web_overview_new (WebKitWebPage *web_page,
+ EphyWebOverviewModel *model);
+void ephy_web_overview_init_js (EphyWebOverview *overview,
+ JSGlobalContextRef context);
+
+G_END_DECLS
+
+#endif /* _EPHY_WEB_OVERVIEW_H */
diff --git a/src/resources/overview.html b/src/resources/overview.html
index 56d2f45..2cfd6f4 100755
--- a/src/resources/overview.html
+++ b/src/resources/overview.html
@@ -141,7 +141,7 @@
<div id="overview">
<div id="grid">
- <ul>
+ <ul id="item-list">
%s
</ul>
</div>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]