[epiphany/history-rewrite: 2/45] Initial implementation of EphyHistoryService.
- From: Claudio Saavedra <csaavedra src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/history-rewrite: 2/45] Initial implementation of EphyHistoryService.
- Date: Wed, 24 Aug 2011 19:39:29 +0000 (UTC)
commit 4fb4c5834ef787032666064b7dc6a6fc96b5d28e
Author: Martin Robinson <mrobinson igalia com>
Date: Wed Apr 20 18:14:38 2011 -0700
Initial implementation of EphyHistoryService.
This implementation includes basic functionality for importing history
artifacts from the old EphyNode representation.
.gitignore | 1 +
embed/Makefile.am | 4 +
embed/history/ephy-history-service-private.h | 45 +++
embed/history/ephy-history-service-urls-table.c | 169 ++++++++
embed/history/ephy-history-service-visits-table.c | 90 +++++
embed/history/ephy-history-service.c | 431 +++++++++++++++++++++
embed/history/ephy-history-service.h | 63 +++
embed/history/ephy-history-types.c | 85 ++++
embed/history/ephy-history-types.h | 68 ++++
tests/Makefile.am | 5 +
tests/ephy-history.c | 103 +++++
11 files changed, 1064 insertions(+), 0 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 58f13d1..8d5de39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -67,6 +67,7 @@ src/epiphany.h
tests/test-ephy-embed-persist
tests/test-ephy-embed-single
+tests/test-ephy-history
tests/test-ephy-location-entry
tests/test-ephy-search-entry
tests/test-ephy-sqlite
diff --git a/embed/Makefile.am b/embed/Makefile.am
index 46905f7..45ccf3f 100644
--- a/embed/Makefile.am
+++ b/embed/Makefile.am
@@ -49,6 +49,10 @@ libephyembed_la_SOURCES = \
ephy-permission-manager.c \
ephy-embed-prefs.c \
ephy-web-view.c \
+ history/ephy-history-service.c \
+ history/ephy-history-service-urls-table.c \
+ history/ephy-history-service-visits-table.c \
+ history/ephy-history-types.c \
sqlite/ephy-sqlite-connection.c \
sqlite/ephy-sqlite-statement.c \
$(INST_H_FILES) \
diff --git a/embed/history/ephy-history-service-private.h b/embed/history/ephy-history-service-private.h
new file mode 100644
index 0000000..b6c0667
--- /dev/null
+++ b/embed/history/ephy-history-service-private.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright  2011 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.
+ *
+ * $Id$
+ */
+
+#ifndef EPHY_HISTORY_SERVICE_PRIVATE_H
+#define EPHY_HISTORY_SERVICE_PRIVATE_H
+
+#include "../sqlite/ephy-sqlite-connection.h"
+
+struct _EphyHistoryServicePrivate {
+ char *history_filename;
+ EphySQLiteConnection *history_database;
+ GThread *history_thread;
+ GMutex *history_thread_mutex;
+ GMainLoop *history_thread_main_loop;
+ GMainContext *history_thread_main_context;
+ gboolean active;
+};
+
+void ephy_history_service_schedule_commit (EphyHistoryService *self);
+gboolean ephy_history_service_initialize_urls_table (EphyHistoryService *self);
+EphyHistoryURL * ephy_history_service_get_url_row (EphyHistoryService *self, const char *url);
+void ephy_history_service_add_url_row (EphyHistoryService *self, EphyHistoryURL *url);
+void ephy_history_service_update_url_row (EphyHistoryService *self, EphyHistoryURL *url);
+
+gboolean ephy_history_service_initialize_visits_table (EphyHistoryService *self);
+void ephy_history_service_add_visit_row (EphyHistoryService *self, EphyHistoryPageVisit *visit);
+
+#endif /* EPHY_HISTORY_SERVICE_PRIVATE_H */
diff --git a/embed/history/ephy-history-service-urls-table.c b/embed/history/ephy-history-service-urls-table.c
new file mode 100644
index 0000000..65bb9f6
--- /dev/null
+++ b/embed/history/ephy-history-service-urls-table.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright  2011 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.
+ *
+ * $Id$
+ */
+
+#include "config.h"
+
+#include "ephy-history-service.h"
+#include "ephy-history-service-private.h"
+
+gboolean
+ephy_history_service_initialize_urls_table (EphyHistoryService *self)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+ GError *error = NULL;
+
+ if (ephy_sqlite_connection_table_exists (priv->history_database, "visits")) {
+ return TRUE;
+ }
+ ephy_sqlite_connection_execute (priv->history_database,
+ "CREATE TABLE urls ("
+ "id INTEGER PRIMARY KEY,"
+ "url LONGVARCAR,"
+ "title LONGVARCAR,"
+ "visit_count INTEGER DEFAULT 0 NOT NULL,"
+ "typed_count INTEGER DEFAULT 0 NOT NULL,"
+ "last_visit_time INTEGER,"
+ "favicon_id INTEGER DEFAULT 0 NOT NULL)", &error);
+
+ if (NULL != error) {
+ g_error("Could not create urls table: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+ ephy_history_service_schedule_commit (self);
+ return TRUE;
+}
+
+EphyHistoryURL *
+ephy_history_service_get_url_row (EphyHistoryService *self, const char *url_string)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+ EphySQLiteStatement *statement = NULL;
+ EphyHistoryURL *url = NULL;
+ GError *error = NULL;
+
+ g_assert (priv->history_thread == g_thread_self ());
+ g_assert (priv->history_database != NULL);
+
+ statement = ephy_sqlite_connection_create_statement (priv->history_database,
+ "SELECT id, url, title, visit_count, typed_count, last_visit_time, favicon_id FROM urls "
+ "WHERE url=?", &error);
+ if (NULL != error) {
+ g_error ("Could not build urls table query statement: %s", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ ephy_sqlite_statement_bind_string (statement, 0, url_string, &error);
+ if (NULL != error) {
+ g_error ("Could not build urls table query statement: %s", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ if (ephy_sqlite_statement_step (statement, &error)) {
+ url = ephy_history_url_new (ephy_sqlite_statement_get_column_as_string (statement, 1),
+ ephy_sqlite_statement_get_column_as_string (statement, 2),
+ ephy_sqlite_statement_get_column_as_int (statement, 3),
+ ephy_sqlite_statement_get_column_as_int (statement, 4),
+ ephy_sqlite_statement_get_column_as_int (statement, 5));
+ url->id = ephy_sqlite_statement_get_column_as_int (statement, 0);
+ }
+
+ g_object_unref (statement);
+ return url;
+}
+
+void
+ephy_history_service_add_url_row (EphyHistoryService *self, EphyHistoryURL *url)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+ EphySQLiteStatement *statement = NULL;
+ GError *error = NULL;
+
+ g_assert (priv->history_thread == g_thread_self ());
+ g_assert (priv->history_database != NULL);
+
+ statement = ephy_sqlite_connection_create_statement (priv->history_database,
+ "INSERT INTO urls (url, title, visit_count, typed_count, last_visit_time) "
+ " VALUES (?, ?, ?, ?, ?)", &error);
+ if (NULL != error) {
+ g_error ("Could not build urls table addition statement: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ if (FALSE == ephy_sqlite_statement_bind_string (statement, 0, url->url, &error) ||
+ FALSE == ephy_sqlite_statement_bind_string (statement, 1, url->title, &error) ||
+ FALSE == ephy_sqlite_statement_bind_int (statement, 2, url->visit_count, &error) ||
+ FALSE == ephy_sqlite_statement_bind_int (statement, 3, url->typed_count, &error) ||
+ FALSE == ephy_sqlite_statement_bind_int (statement, 4, url->last_visit_time, &error)) {
+ g_error ("Could not insert URL into urls table: %s", error->message);
+ g_object_unref (error);
+ return;
+ }
+
+ ephy_sqlite_statement_step (statement, &error);
+ if (NULL != error) {
+ g_error ("Could not insert URL into urls table: %s", error->message);
+ g_object_unref (error);
+ } else {
+ url->id = ephy_sqlite_connection_get_last_insert_id (priv->history_database);
+ }
+
+ g_object_unref (statement);
+}
+
+void
+ephy_history_service_update_url_row (EphyHistoryService *self, EphyHistoryURL *url)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+ EphySQLiteStatement *statement;
+ GError *error = NULL;
+
+ g_assert (priv->history_thread == g_thread_self ());
+ g_assert (priv->history_database != NULL);
+
+ statement = ephy_sqlite_connection_create_statement (priv->history_database,
+ "UPDATE urls SET title=?, visit_count=?, typed_count=?, last_visit_time=? WHERE id=?"
+ " VALUES (?, ?, ?, ?, ?, ?)", &error);
+ if (NULL != error) {
+ g_error ("Could not build urls table modification statement: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ if (FALSE == ephy_sqlite_statement_bind_string (statement, 0, url->title, &error) ||
+ FALSE == ephy_sqlite_statement_bind_int (statement, 1, url->visit_count, &error) ||
+ FALSE == ephy_sqlite_statement_bind_int (statement, 2, url->typed_count, &error) ||
+ FALSE == ephy_sqlite_statement_bind_int (statement, 3, url->last_visit_time, &error) ||
+ FALSE == ephy_sqlite_statement_bind_int (statement, 4, url->id, &error)) {
+ g_error ("Could not modify URL in urls table: %s", error->message);
+ g_object_unref (error);
+ return;
+ }
+
+ ephy_sqlite_statement_step (statement, &error);
+ if (NULL != error) {
+ g_error ("Could not modify URL in urls table: %s", error->message);
+ g_object_unref (error);
+ }
+ g_object_unref (statement);
+}
diff --git a/embed/history/ephy-history-service-visits-table.c b/embed/history/ephy-history-service-visits-table.c
new file mode 100644
index 0000000..f2a9e27
--- /dev/null
+++ b/embed/history/ephy-history-service-visits-table.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright  2011 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.
+ *
+ * $Id$
+ */
+
+#include "config.h"
+
+#include "ephy-history-service.h"
+#include "ephy-history-service-private.h"
+
+gboolean
+ephy_history_service_initialize_visits_table (EphyHistoryService *self)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+ GError *error = NULL;
+
+ if (ephy_sqlite_connection_table_exists (priv->history_database, "visits")) {
+ return TRUE;
+ }
+ ephy_sqlite_connection_execute (priv->history_database,
+ "CREATE TABLE visits ("
+ "id INTEGER PRIMARY KEY,"
+ "url INTEGER NOT NULL,"
+ "visit_time INTEGER NOT NULL,"
+ "visit_type INTEGER NOT NULL,"
+ "referring_visit INTEGER)", &error);
+
+ if (NULL != error) {
+ g_error("Could not create visits table: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+ ephy_history_service_schedule_commit (self);
+ return TRUE;
+}
+
+void
+ephy_history_service_add_visit_row (EphyHistoryService *self, EphyHistoryPageVisit *visit)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+ EphySQLiteStatement *statement;
+ GError *error = NULL;
+
+ g_assert (priv->history_thread == g_thread_self ());
+ g_assert (priv->history_database != NULL);
+
+ statement = ephy_sqlite_connection_create_statement (
+ priv->history_database,
+ "INSERT INTO visits (url, visit_time, visit_type) "
+ " VALUES (?, ?, ?) ", &error);
+ if (NULL != error) {
+ g_error ("Could not build visits table addition statement: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ if (FALSE == ephy_sqlite_statement_bind_int (statement, 0, visit->url_id, &error) ||
+ FALSE == ephy_sqlite_statement_bind_int (statement, 1, visit->visit_time, &error) ||
+ FALSE == ephy_sqlite_statement_bind_int (statement, 2, visit->visit_type, &error)) {
+ g_error ("Could not build visits table addition statement: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ ephy_sqlite_statement_step (statement, &error);
+ if (NULL != error) {
+ g_error ("Could not insert URL into visits table: %s", error->message);
+ g_object_unref (error);
+ } else {
+ visit->id = ephy_sqlite_connection_get_last_insert_id (priv->history_database);
+ }
+
+ ephy_history_service_schedule_commit (self);
+ g_object_unref (statement);
+}
diff --git a/embed/history/ephy-history-service.c b/embed/history/ephy-history-service.c
new file mode 100644
index 0000000..9d02895
--- /dev/null
+++ b/embed/history/ephy-history-service.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright  2011 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.
+ *
+ * $Id$
+ */
+
+#include "config.h"
+
+#include "ephy-history-service.h"
+#include "ephy-history-service-private.h"
+#include "ephy-history-types.h"
+#include "../sqlite/ephy-sqlite-connection.h"
+
+typedef gpointer (*EphyHistoryJobMethod) (EphyHistoryService *self, gpointer data, gboolean *result);
+
+static void ephy_history_service_class_init (EphyHistoryServiceClass *klass);
+static void ephy_history_service_init (EphyHistoryService *self);
+static void ephy_history_service_finalize (GObject *self);
+static void ephy_history_service_dispose (GObject *self);
+static gpointer run_history_service_thread (EphyHistoryService *self);
+static gboolean ephy_history_service_queue_stop_history_service_thread (EphyHistoryService *self);
+static void ephy_history_service_schedule_idle (EphyHistoryService *self, int priority, GSourceFunc callback, gpointer data);
+static void ephy_history_service_schedule_source (EphyHistoryService *self, int priority, GSource *source, GSourceFunc callback, gpointer data);
+
+enum
+{
+ PROP_0,
+ PROP_HISTORY_FILENAME,
+};
+
+enum {
+ LAST_SIGNAL
+};
+
+#define EPHY_HISTORY_SERVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), EPHY_TYPE_HISTORY_SERVICE, EphyHistoryServicePrivate))
+
+G_DEFINE_TYPE (EphyHistoryService, ephy_history_service, G_TYPE_OBJECT);
+
+static void
+ephy_history_service_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ EphyHistoryService *self = EPHY_HISTORY_SERVICE (object);
+
+ switch (property_id) {
+ case PROP_HISTORY_FILENAME:
+ g_free (self->priv->history_filename);
+ self->priv->history_filename = g_strdup (g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
+ break;
+ }
+}
+
+static void
+ephy_history_service_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ EphyHistoryService *self = EPHY_HISTORY_SERVICE (object);
+ switch (property_id) {
+ case PROP_HISTORY_FILENAME:
+ g_value_set_string (value, self->priv->history_filename);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+ephy_history_service_class_init (EphyHistoryServiceClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = ephy_history_service_finalize;
+ gobject_class->dispose = ephy_history_service_dispose;
+ gobject_class->get_property = ephy_history_service_get_property;
+ gobject_class->set_property = ephy_history_service_set_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_HISTORY_FILENAME,
+ g_param_spec_string ("history-filename",
+ "History filename",
+ "The filename of the SQLite file holding containing history",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private (gobject_class, sizeof (EphyHistoryServicePrivate));
+}
+
+static void
+ephy_history_service_init (EphyHistoryService *self)
+{
+ GError *error = NULL;
+ self->priv = EPHY_HISTORY_SERVICE_GET_PRIVATE (self);
+
+ self->priv->history_thread_mutex = g_mutex_new ();
+ self->priv->history_thread_main_context = g_main_context_new ();
+ self->priv->history_database = NULL;
+ self->priv->active = TRUE;
+
+ self->priv->history_thread = g_thread_create ((GThreadFunc) run_history_service_thread, self, TRUE, &error);
+ if (NULL != error) {
+ g_error("Could not start history thread: %s", error->message);
+ g_error_free (error);
+ self->priv->history_thread = NULL;
+ }
+}
+
+static void
+ephy_history_service_dispose (GObject *self)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+
+ if (NULL == priv->history_thread_mutex) {
+ return;
+ }
+
+ {
+ g_mutex_lock (priv->history_thread_mutex);
+ priv->active = FALSE;
+
+ /* This will be a no-op if the main thread is no longer active or in the process of shutting down. */
+ if (priv->history_thread_main_loop) {
+ ephy_history_service_schedule_source (EPHY_HISTORY_SERVICE (self),
+ G_PRIORITY_HIGH, g_timeout_source_new (0),
+ (GSourceFunc) ephy_history_service_queue_stop_history_service_thread, self);
+ }
+ g_mutex_unlock (priv->history_thread_mutex);
+ }
+
+ G_OBJECT_CLASS (ephy_history_service_parent_class)->dispose (self);
+}
+
+static void
+ephy_history_service_finalize (GObject *self)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+ if (NULL != priv->history_thread) {
+ g_thread_join (priv->history_thread);
+ }
+
+ if (NULL != priv->history_thread_mutex) {
+ g_mutex_free (priv->history_thread_mutex);
+ }
+
+ if (NULL != priv->history_thread_main_context) {
+ g_main_context_unref (priv->history_thread_main_context);
+ }
+
+ G_OBJECT_CLASS (ephy_history_service_parent_class)->finalize (self);
+}
+
+EphyHistoryService *
+ephy_history_service_new (const char *history_filename)
+{
+ return EPHY_HISTORY_SERVICE (g_object_new (EPHY_TYPE_HISTORY_SERVICE,
+ "history-filename", history_filename,
+ NULL));
+}
+
+static void
+ephy_history_service_schedule_source (EphyHistoryService *self, int priority, GSource *source, GSourceFunc callback, gpointer data)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+
+ g_source_set_priority (source, priority);
+ g_source_set_callback (source, callback, data, NULL);
+ g_source_attach (source, priv->history_thread_main_context);
+ g_source_unref (source);
+}
+
+static void
+ephy_history_service_schedule_idle (EphyHistoryService *self, int priority, GSourceFunc callback, gpointer data)
+{
+ ephy_history_service_schedule_source (self, priority, g_idle_source_new (), callback, data);
+}
+
+static gboolean
+ephy_history_service_commit (EphyHistoryService *self)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+ GError *error = NULL;
+ g_assert (priv->history_thread == g_thread_self ());
+
+ if (NULL == priv->history_database) {
+ return FALSE;
+ }
+
+ ephy_sqlite_connection_commit_transaction (priv->history_database, &error);
+ if (NULL != error) {
+ g_error ("Could not commit idle history database transaction: %s", error->message);
+ g_error_free (error);
+ }
+ ephy_sqlite_connection_begin_transaction (priv->history_database, &error);
+ if (NULL != error) {
+ g_error ("Could not start long-running history database transaction: %s", error->message);
+ g_error_free (error);
+ }
+ return FALSE;
+}
+
+void
+ephy_history_service_schedule_commit (EphyHistoryService *self)
+{
+ ephy_history_service_schedule_idle (self, G_PRIORITY_DEFAULT, (GSourceFunc) ephy_history_service_commit, self);
+}
+
+static gboolean
+ephy_history_service_open_database_connections (EphyHistoryService *self)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+ GError *error = NULL;
+
+ g_assert (priv->history_thread == g_thread_self ());
+
+ priv->history_database = ephy_sqlite_connection_new ();
+ ephy_sqlite_connection_open (priv->history_database, priv->history_filename, &error);
+ if (NULL != error) {
+ g_object_unref (priv->history_database);
+ priv->history_database = NULL;
+ g_error ("Could not open history database: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ ephy_sqlite_connection_begin_transaction (priv->history_database, &error);
+ if (NULL != error) {
+ g_error ("Could not begin long running transaction in history database: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ if ((FALSE == ephy_history_service_initialize_urls_table (self)) ||
+ (FALSE == ephy_history_service_initialize_visits_table (self))) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+ephy_history_service_close_database_connections (EphyHistoryService *self)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+
+ g_assert (priv->history_thread == g_thread_self ());
+
+ ephy_sqlite_connection_close (priv->history_database);
+ g_object_unref (priv->history_database);
+ priv->history_database = NULL;
+}
+
+static gpointer
+run_history_service_thread (EphyHistoryService *self)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+
+ g_assert (priv->history_thread == g_thread_self ());
+ g_main_context_push_thread_default (priv->history_thread_main_context);
+
+ {
+ g_mutex_lock (priv->history_thread_mutex);
+ if (FALSE == priv->active) {
+ g_mutex_unlock (priv->history_thread_mutex);
+ return NULL;
+ }
+ priv->history_thread_main_loop = g_main_loop_new (priv->history_thread_main_context, FALSE);
+ g_mutex_unlock (priv->history_thread_mutex);
+ }
+
+ if (FALSE == ephy_history_service_open_database_connections (self)) {
+ g_object_unref (priv->history_thread_main_loop);
+ return NULL;
+ }
+
+ g_main_loop_run (priv->history_thread_main_loop);
+
+ /* We want to avoid the situation where you check if the history thread
+ exists on another thread and then have it dereffed out of existence on
+ this thread, so we guard this operation with the mutex. */
+ {
+ g_mutex_lock (priv->history_thread_mutex);
+ g_main_loop_unref (priv->history_thread_main_loop);
+ priv->history_thread_main_loop = NULL;
+ g_mutex_unlock (priv->history_thread_mutex);
+ }
+
+ ephy_history_service_close_database_connections (self);
+
+ return NULL;
+}
+
+static gboolean
+ephy_history_service_queue_stop_history_service_thread (EphyHistoryService *self)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+ g_assert (priv->history_thread == g_thread_self ());
+
+ // TODO: Remove all main thread sources here.
+
+ ephy_history_service_commit (self);
+ g_main_loop_quit (priv->history_thread_main_loop);
+ return FALSE;
+}
+
+typedef struct _EphyHistoryThreadJobDetails
+{
+ EphyHistoryService *service;
+ EphyHistoryJobMethod method;
+ gpointer *method_argument;
+ gboolean success;
+ gpointer result;
+ GDestroyNotify method_argument_cleanup;
+ EphyHistoryJobCallback callback;
+} EphyHistoryThreadJobDetails;
+
+static EphyHistoryThreadJobDetails *
+ephy_history_thread_job_details_new (EphyHistoryService *service,
+ EphyHistoryJobMethod method,
+ gpointer method_argument,
+ GDestroyNotify method_argument_cleanup,
+ EphyHistoryJobCallback callback)
+{
+ EphyHistoryThreadJobDetails *details = g_slice_alloc0 (sizeof (EphyHistoryThreadJobDetails));
+ details->service = service;
+ details->method = method;
+ details->method_argument = method_argument;
+ details->callback = callback;
+ return details;
+}
+
+static void
+ephy_history_thread_job_details_free (EphyHistoryThreadJobDetails *details)
+{
+ if (details->method_argument_cleanup) {
+ details->method_argument_cleanup (details->method_argument);
+ }
+ g_slice_free1 (sizeof (EphyHistoryThreadJobDetails), details);
+}
+
+static gboolean
+ephy_history_service_execute_job_callback (gpointer data)
+{
+ EphyHistoryThreadJobDetails *details = (EphyHistoryThreadJobDetails*) data;
+ g_assert (details->callback);
+ details->callback (details->service, details->success, details->result);
+ ephy_history_thread_job_details_free (details);
+
+ return FALSE;
+}
+
+static gboolean
+ephy_history_service_execute_job_on_history_thread (gpointer data)
+{
+ EphyHistoryThreadJobDetails *details = (EphyHistoryThreadJobDetails*) data;
+
+ /* TODO: Here we must check if the history thread is shutting down
+ in which case we need to free the details and abort */
+
+ details->success = TRUE; /* Successful by default */
+ details->result = details->method (details->service, details->method_argument, &details->success);
+
+ if (details->callback) {
+ g_idle_add (ephy_history_service_execute_job_callback, details);
+ } else {
+ ephy_history_thread_job_details_free (details);
+ }
+
+ return FALSE;
+}
+
+static gpointer
+ephy_history_service_execute_add_visit (EphyHistoryService *self, EphyHistoryPageVisit *visit, gboolean *success)
+{
+ EphyHistoryServicePrivate *priv = EPHY_HISTORY_SERVICE (self)->priv;
+ EphyHistoryURL *url = NULL;
+
+ g_assert (priv->history_thread == g_thread_self ());
+
+ url = ephy_history_service_get_url_row (self, visit->url);
+ if (NULL == url) { /* This URL does not yet exist in the history table */
+ url = ephy_history_url_new (visit->url, "", 1, 0, visit->visit_time);
+ ephy_history_service_add_url_row (self, url);
+
+ if (url->id == -1) {
+ g_error ("Adding visit failed after failed URL addition.");
+ *success = FALSE;
+ return NULL;
+ }
+ visit->url_id = url->id;
+
+ } else {
+ url->visit_count++;
+ if (visit->visit_time > url->last_visit_time) {
+ url->last_visit_time = visit->visit_time;
+ }
+ ephy_history_service_update_url_row (self, url);
+ }
+
+ ephy_history_service_add_visit_row (self, visit);
+ ephy_history_url_free (url);
+ *success = visit->id != -1;
+ return NULL;
+}
+
+void
+ephy_history_service_add_visit (EphyHistoryService *self, EphyHistoryPageVisit *visit, EphyHistoryJobCallback callback)
+{
+ EphyHistoryThreadJobDetails *details =
+ ephy_history_thread_job_details_new (self,
+ (EphyHistoryJobMethod) ephy_history_service_execute_add_visit,
+ ephy_history_page_visit_copy (visit),
+ (GDestroyNotify) ephy_history_page_visit_free,
+ callback);
+ ephy_history_service_schedule_idle (self, G_PRIORITY_DEFAULT,
+ ephy_history_service_execute_job_on_history_thread,
+ details);
+}
diff --git a/embed/history/ephy-history-service.h b/embed/history/ephy-history-service.h
new file mode 100644
index 0000000..d4db2bb
--- /dev/null
+++ b/embed/history/ephy-history-service.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright  2011 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.
+ *
+ * $Id$
+ */
+
+#ifndef EPHY_HISTORY_SERVICE_H
+#define EPHY_HISTORY_SERVICE_H
+
+#include <glib-object.h>
+#include "ephy-history-types.h"
+
+G_BEGIN_DECLS
+
+/* convenience macros */
+#define EPHY_TYPE_HISTORY_SERVICE (ephy_history_service_get_type())
+#define EPHY_HISTORY_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),EPHY_TYPE_HISTORY_SERVICE,EphyHistoryService))
+#define EPHY_HISTORY_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),EPHY_TYPE_HISTORY_SERVICE,EphyHistoryServiceClass))
+#define EPHY_IS_HISTORY_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),EPHY_TYPE_HISTORY_SERVICE))
+#define EPHY_IS_HISTORY_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),EPHY_TYPE_HISTORY_SERVICE))
+#define EPHY_HISTORY_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),EPHY_TYPE_HISTORY_SERVICE,EphyHistoryServiceClass))
+
+typedef struct _EphyHistoryService EphyHistoryService;
+typedef struct _EphyHistoryServiceClass EphyHistoryServiceClass;
+typedef struct _EphyHistoryServicePrivate EphyHistoryServicePrivate;
+
+typedef void (*EphyHistoryJobCallback) (EphyHistoryService *service, gboolean result, gpointer data);
+
+struct _EphyHistoryService {
+ GObject parent;
+
+ /* private */
+ EphyHistoryServicePrivate *priv;
+};
+
+struct _EphyHistoryServiceClass {
+ GObjectClass parent_class;
+};
+
+GType ephy_history_service_get_type (void);
+EphyHistoryService * ephy_history_service_new (const char *history_filename);
+
+void ephy_history_service_add_visit (EphyHistoryService *self, EphyHistoryPageVisit *visit, EphyHistoryJobCallback callback);
+
+
+G_END_DECLS
+
+#endif /* EPHY_HISTORY_SERVICE_H */
+
diff --git a/embed/history/ephy-history-types.c b/embed/history/ephy-history-types.c
new file mode 100644
index 0000000..5954f48
--- /dev/null
+++ b/embed/history/ephy-history-types.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright  2011 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.
+ *
+ * $Id$
+ */
+
+#include <glib.h>
+
+#include "ephy-history-types.h"
+
+EphyHistoryPageVisit *
+ephy_history_page_visit_new (const char *url, gint64 visit_time, EphyHistoryPageVisitType visit_type)
+{
+ EphyHistoryPageVisit *visit = g_slice_alloc0 (sizeof (EphyHistoryPageVisit));
+ visit->id = -1;
+ visit->url = g_strdup (url);
+ visit->url_id = -1;
+ visit->visit_time = visit_time;
+ visit->visit_type = visit_type;
+ return visit;
+}
+
+void
+ephy_history_page_visit_free (EphyHistoryPageVisit *visit)
+{
+ g_free (visit->url);
+ g_slice_free1 (sizeof (EphyHistoryPageVisit), visit);
+}
+
+EphyHistoryPageVisit *
+ephy_history_page_visit_copy (EphyHistoryPageVisit *visit)
+{
+ EphyHistoryPageVisit *copy = ephy_history_page_visit_new (visit->url, visit->visit_time, visit->visit_type);
+ copy->id = visit->id;
+ copy->url_id = visit->url_id;
+ return copy;
+}
+
+EphyHistoryURL *
+ephy_history_url_new (const char *url, const char *title, int visit_count, int typed_count, int last_visit_time)
+{
+ EphyHistoryURL *history_url = g_slice_alloc0 (sizeof (EphyHistoryURL));
+ history_url->id = -1;
+ history_url->url = g_strdup (url);
+ history_url->title = g_strdup (title);
+ history_url->visit_count = visit_count;
+ history_url->typed_count = typed_count;
+ history_url->last_visit_time = last_visit_time;
+ return history_url;
+}
+
+EphyHistoryURL *
+ephy_history_url_copy (EphyHistoryURL *url)
+{
+ EphyHistoryURL *copy = ephy_history_url_new (url->url,
+ url->title,
+ url->visit_count,
+ url->typed_count,
+ url->last_visit_time);
+ copy->id = url->id;
+ return copy;
+}
+
+void
+ephy_history_url_free (EphyHistoryURL *url)
+{
+ g_free (url->url);
+ g_free (url->title);
+ g_slice_free1 (sizeof (EphyHistoryURL), url);
+}
+
diff --git a/embed/history/ephy-history-types.h b/embed/history/ephy-history-types.h
new file mode 100644
index 0000000..3d52aa0
--- /dev/null
+++ b/embed/history/ephy-history-types.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright  2011 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.
+ *
+ * $Id$
+ */
+
+#ifndef EPHY_HISTORY_TYPES_H
+#define EPHY_HISTORY_TYPES_H
+
+G_BEGIN_DECLS
+
+/*
+ * Page transition types heavily inspired by those used in Chromium. See:
+ * src/chrome/common/page_transition_types.h in the Chromium source code.
+ */
+typedef enum {
+ EphyPageVisitTypeLink,
+ EphyPageVisitTypeTyped,
+ EphyPageVisitTypeManualSubframe,
+ EphyPageVisitTypeAutoSubframe,
+ EphyPageVisitTypeStartup,
+ EphyPageVisitTypeFormSubmission,
+ EphyPageVisitTypeFormReload,
+} EphyHistoryPageVisitType;
+
+typedef struct _EphyHistoryPageVisit
+{
+ int id;
+ char* url;
+ int url_id;
+ gint64 visit_time;
+ EphyHistoryPageVisitType visit_type;
+} EphyHistoryPageVisit;
+
+typedef struct _EphyHistoryURL
+{
+ int id;
+ char* url;
+ char* title;
+ int visit_count;
+ int typed_count;
+ int last_visit_time;
+} EphyHistoryURL;
+
+EphyHistoryPageVisit * ephy_history_page_visit_new (const char *url, gint64 visit_time, EphyHistoryPageVisitType visit_type);
+EphyHistoryPageVisit * ephy_history_page_visit_copy (EphyHistoryPageVisit *visit);
+void ephy_history_page_visit_free (EphyHistoryPageVisit *visit);
+EphyHistoryURL * ephy_history_url_new (const char *url, const char* title, int visit_count, int typed_count, int last_visit_time);
+EphyHistoryURL * ephy_history_url_copy (EphyHistoryURL *url);
+void ephy_history_url_free (EphyHistoryURL *url);
+
+G_END_DECLS
+
+#endif /* EPHY_HISTORY_TYPES_H */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 3f693bd..c97d40e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,6 +1,7 @@
noinst_PROGRAMS = \
test-ephy-download \
test-ephy-embed-single \
+ test-ephy-history \
test-ephy-location-entry \
test-ephy-search-entry \
test-ephy-sqlite \
@@ -9,6 +10,7 @@ noinst_PROGRAMS = \
INCLUDES = \
-I$(top_srcdir)/embed \
-I$(top_srcdir)/embed/sqlite \
+ -I$(top_srcdir)/embed/history \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/lib/widgets \
-I$(top_srcdir)/src \
@@ -41,6 +43,9 @@ test_ephy_download_SOURCES = \
test_ephy_embed_single_SOURCES = \
ephy-embed-single.c
+test_ephy_history_SOURCES = \
+ ephy-history.c
+
test_ephy_location_entry_SOURCES = \
ephy-location-entry.c
diff --git a/tests/ephy-history.c b/tests/ephy-history.c
new file mode 100644
index 0000000..7e1d4a5
--- /dev/null
+++ b/tests/ephy-history.c
@@ -0,0 +1,103 @@
+/* vim: set sw=2 ts=2 sts=2 et: */
+/*
+ * ephy-sqlite-statement.c
+ * This file is part of Epiphany
+ *
+ * Copyright  2010 Igalia S.L.
+ *
+ * Epiphany is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Epiphany; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include "ephy-history-service.h"
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+
+static EphyHistoryService *
+ensure_empty_history (const char* filename)
+{
+ if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
+ g_unlink (filename);
+ }
+
+ return ephy_history_service_new (filename);
+}
+
+static void
+test_create_history_service (void)
+{
+ gchar *temporary_file = g_build_filename (g_get_tmp_dir (), "epiphany-history-test.db", NULL);
+ EphyHistoryService *service = ensure_empty_history (temporary_file);
+
+ g_free (temporary_file);
+ g_object_unref (service);
+}
+
+static gboolean
+destroy_history_service_and_end_main_loop (EphyHistoryService *service)
+{
+ g_object_unref (service);
+ g_assert (TRUE);
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static void
+test_create_history_service_and_destroy_later (void)
+{
+ gchar *temporary_file = g_build_filename (g_get_tmp_dir (), "epiphany-history-test.db", NULL);
+ EphyHistoryService *service = ensure_empty_history(temporary_file);
+ g_free (temporary_file);
+ g_timeout_add (100, (GSourceFunc) destroy_history_service_and_end_main_loop, service);
+
+ gtk_main ();
+}
+
+static void
+page_vist_created (EphyHistoryService *service, gboolean success, gpointer result)
+{
+ g_object_unref (service);
+ g_assert (result == NULL);
+ g_assert (success);
+ gtk_main_quit ();
+}
+
+static void
+test_create_history_entry (void)
+{
+ gchar *temporary_file = g_build_filename (g_get_tmp_dir (), "epiphany-history-test.db", NULL);
+ EphyHistoryService *service = ensure_empty_history(temporary_file);
+
+ EphyHistoryPageVisit *visit = ephy_history_page_visit_new ("http://www.gnome.org", 0, EphyPageVisitTypeTyped);
+ ephy_history_service_add_visit (service, visit, page_vist_created);
+
+ gtk_main ();
+}
+
+int
+main (int argc, char *argv[])
+{
+ g_thread_init (NULL);
+ gtk_test_init (&argc, &argv);
+
+ g_test_add_func ("/embed/history/test_create_history_service", test_create_history_service);
+ g_test_add_func ("/embed/history/test_create_history_service_and_destroy_later", test_create_history_service_and_destroy_later);
+ g_test_add_func ("/embed/history/test_create_history_entry", test_create_history_entry);
+
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]