[yelp/yelp-3-0] [libyelp] YelpView basic implementation, YelpSimpleDocument for (X)HTML



commit 2962b08821183df2f8d4dbfcc4f451e5c2ca89a1
Author: Shaun McCance <shaunm gnome org>
Date:   Wed Sep 23 23:45:34 2009 -0500

    [libyelp] YelpView basic implementation, YelpSimpleDocument for (X)HTML

 libyelp/Makefile.am            |    2 +
 libyelp/yelp-document.c        | 1072 ++++++++++++++++++++++++++++++++++++++++
 libyelp/yelp-document.h        |  151 ++++++
 libyelp/yelp-simple-document.c |  419 ++++++++++++++++
 libyelp/yelp-simple-document.h |   55 ++
 libyelp/yelp-uri.c             |   61 ++--
 libyelp/yelp-view.c            |  147 ++++--
 libyelp/yelp-view.h            |   25 +-
 src/yelp-document.c            |  931 ----------------------------------
 src/yelp-document.h            |  137 -----
 tests/Makefile.am              |    6 +-
 tests/test-view.c              |   61 +++
 12 files changed, 1912 insertions(+), 1155 deletions(-)
---
diff --git a/libyelp/Makefile.am b/libyelp/Makefile.am
index 7ee9080..ad40c35 100644
--- a/libyelp/Makefile.am
+++ b/libyelp/Makefile.am
@@ -2,7 +2,9 @@ noinst_LTLIBRARIES = libyelp.la
 
 libyelp_la_SOURCES =		\
 	yelp-debug.c		\
+	yelp-document.c		\
 	yelp-location-entry.c	\
+	yelp-simple-document.c	\
 	yelp-uri.c		\
 	yelp-view.c
 
diff --git a/libyelp/yelp-document.c b/libyelp/yelp-document.c
new file mode 100644
index 0000000..2f9df0d
--- /dev/null
+++ b/libyelp/yelp-document.c
@@ -0,0 +1,1072 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-2009 Shaun McCance  <shaunm gnome org>
+ *
+ * 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 of the
+ * License, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance  <shaunm gnome org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "yelp-document.h"
+
+typedef struct _Request Request;
+struct _Request {
+    YelpDocument         *document;
+    gchar                *page_id;
+    GCancellable         *cancellable;
+    YelpDocumentCallback  callback;
+    gpointer              user_data;
+
+    gint                  idle_funcs;
+};
+
+struct _YelpDocumentPriv {
+    GMutex       *mutex;
+
+    GSList       *reqs_all;         /* Holds canonical refs, only free from here */
+    GHashTable   *reqs_by_page_id;  /* Indexed by page ID, contains GSList */
+    GSList       *reqs_pending;     /* List of requests that need a page */
+
+    /* Real page IDs map to themselves, so this list doubles
+     * as a list of all valid page IDs.
+     */
+    GHashTable   *page_ids;      /* Mapping of fragment IDs to real page IDs */
+    GHashTable   *titles;        /* Mapping of page IDs to titles */
+    GHashTable   *mime_types;    /* Mapping of page IDs to mime types */
+    GHashTable   *contents;      /* Mapping of page IDs to string content */
+
+    GHashTable   *root_ids;      /* Mapping of page IDs to "previous page" IDs */
+    GHashTable   *prev_ids;      /* Mapping of page IDs to "previous page" IDs */
+    GHashTable   *next_ids;      /* Mapping of page IDs to "next page" IDs */
+    GHashTable   *up_ids;        /* Mapping of page IDs to "up page" IDs */
+
+    GMutex       *str_mutex;
+    GHashTable   *str_refs;
+};
+
+G_DEFINE_TYPE (YelpDocument, yelp_document, G_TYPE_OBJECT);
+
+#define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_DOCUMENT, YelpDocumentPriv))
+
+static void           yelp_document_class_init  (YelpDocumentClass    *klass);
+static void           yelp_document_init        (YelpDocument         *document);
+static void           yelp_document_dispose     (GObject              *object);
+static void           yelp_document_finalize    (GObject              *object);
+
+static gboolean       document_request_page     (YelpDocument         *document,
+						 const gchar          *page_id,
+						 GCancellable         *cancellable,
+						 YelpDocumentCallback  callback,
+						 gpointer              user_data);
+static const gchar *  document_read_contents    (YelpDocument         *document,
+						 const gchar          *page_id);
+static void           document_finish_read      (YelpDocument         *document,
+						 const gchar          *contents);
+static gchar *        document_get_mime_type    (YelpDocument         *document,
+						 const gchar          *mime_type);
+
+static void           request_cancel            (Request              *request);
+static gboolean       request_idle_contents     (Request              *request);
+static gboolean       request_idle_info         (Request              *request);
+static void           request_try_free          (Request              *request);
+static void           request_free              (Request              *request);
+
+static const gchar *  str_ref                   (const gchar          *str);
+static void           str_unref                 (const gchar          *str);
+static void           hash_slist_insert         (GHashTable           *hash,
+					         const gchar          *key,
+						 gpointer              value);
+static void           hash_slist_remove         (GHashTable           *hash,
+						 const gchar          *key,
+						 gpointer              value);
+
+#if 0
+static gboolean       request_idle_error        (Request             *request);
+static gboolean       request_idle_final        (YelpDocument        *document);
+
+#endif
+
+GStaticMutex str_mutex = G_STATIC_MUTEX_INIT;
+GHashTable  *str_refs  = NULL;
+
+static void
+yelp_document_class_init (YelpDocumentClass *klass)
+{
+    GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->dispose  = yelp_document_finalize;
+    object_class->finalize = yelp_document_finalize;
+
+    klass->request_page =   document_request_page;
+    klass->read_contents =  document_read_contents;
+    klass->finish_read =    document_finish_read;
+    klass->get_mime_type =  document_get_mime_type;
+
+    g_type_class_add_private (klass, sizeof (YelpDocumentPriv));
+}
+
+static void
+yelp_document_init (YelpDocument *document)
+{
+    YelpDocumentPriv *priv;
+
+    document->priv = priv = GET_PRIV (document);
+
+    priv->mutex = g_mutex_new ();
+
+    priv->reqs_by_page_id =
+	g_hash_table_new_full (g_str_hash, g_str_equal,
+			       g_free,
+			       (GDestroyNotify) g_slist_free);
+    priv->reqs_all = NULL;
+    priv->reqs_pending = NULL;
+
+    priv->page_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+    priv->titles = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+    priv->mime_types = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+    priv->contents = g_hash_table_new_full (g_str_hash, g_str_equal,
+					    g_free,
+					    (GDestroyNotify) str_unref);
+
+    priv->root_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+    priv->prev_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+    priv->next_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+    priv->up_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+}
+
+static void
+yelp_document_dispose (GObject *object)
+{
+    YelpDocument *document = YELP_DOCUMENT (object);
+
+    if (document->priv->reqs_all) {
+	g_slist_foreach (document->priv->reqs_all, request_try_free, NULL);
+	g_slist_free (document->priv->reqs_all);
+	document->priv->reqs_all = NULL;
+    }
+
+    G_OBJECT_CLASS (yelp_document_parent_class)->dispose (object);
+}
+
+static void
+yelp_document_finalize (GObject *object)
+{
+    YelpDocument *document = YELP_DOCUMENT (object);
+
+    g_slist_free (document->priv->reqs_pending);
+    g_hash_table_destroy (document->priv->reqs_by_page_id);
+
+    g_hash_table_destroy (document->priv->page_ids);
+    g_hash_table_destroy (document->priv->titles);
+    g_hash_table_destroy (document->priv->mime_types);
+
+    g_hash_table_destroy (document->priv->contents);
+
+    g_hash_table_destroy (document->priv->root_ids);
+    g_hash_table_destroy (document->priv->prev_ids);
+    g_hash_table_destroy (document->priv->next_ids);
+    g_hash_table_destroy (document->priv->up_ids);
+
+    g_mutex_free (document->priv->mutex);
+
+    G_OBJECT_CLASS (yelp_document_parent_class)->finalize (object);
+}
+
+/******************************************************************************/
+
+gchar *
+yelp_document_get_page_id (YelpDocument *document,
+			   const gchar  *id)
+{
+    gchar *ret = NULL;
+
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+    ret = g_hash_table_lookup (document->priv->page_ids, id);
+    if (ret)
+	ret = g_strdup (ret);
+
+    g_mutex_unlock (document->priv->mutex);
+
+    return ret;
+}
+
+void
+yelp_document_set_page_id (YelpDocument *document,
+			   const gchar  *id,
+			   const gchar  *page_id)
+{
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+
+    g_hash_table_replace (document->priv->root_ids, g_strdup (id), g_strdup (page_id));
+
+    if (!g_str_equal (id, page_id)) {
+	GSList *reqs, *cur;
+	reqs = g_hash_table_lookup (document->priv->reqs_by_page_id, id);
+	for (cur = reqs; cur != NULL; cur = cur->next) {
+	    Request *request = (Request *) cur->data;
+	    g_free (request->page_id);
+	    request->page_id = g_strdup (page_id);
+	    hash_slist_insert (document->priv->reqs_by_page_id, page_id, request);
+	}
+	if (reqs)
+	    g_hash_table_remove (document->priv->reqs_by_page_id, id);
+    }
+
+    g_mutex_unlock (document->priv->mutex);
+}
+
+gchar *
+yelp_document_get_root_id (YelpDocument *document,
+			   const gchar  *page_id)
+{
+    gchar *real, *ret = NULL;
+
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+    real = g_hash_table_lookup (document->priv->page_ids, page_id);
+    if (real) {
+	ret = g_hash_table_lookup (document->priv->root_ids, real);
+	if (ret)
+	    ret = g_strdup (ret);
+    }
+    g_mutex_unlock (document->priv->mutex);
+
+    return ret;
+}
+
+void
+yelp_document_set_root_id (YelpDocument *document,
+			   const gchar  *page_id,
+			   const gchar  *root_id)
+{
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+    g_hash_table_replace (document->priv->root_ids, g_strdup (page_id), g_strdup (root_id));
+    g_mutex_unlock (document->priv->mutex);
+}
+
+gchar *
+yelp_document_get_prev_id (YelpDocument *document,
+			   const gchar  *page_id)
+{
+    gchar *real, *ret = NULL;
+
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+    real = g_hash_table_lookup (document->priv->page_ids, page_id);
+    if (real) {
+	ret = g_hash_table_lookup (document->priv->prev_ids, real);
+	if (ret)
+	    ret = g_strdup (ret);
+    }
+    g_mutex_unlock (document->priv->mutex);
+
+    return ret;
+}
+
+void
+yelp_document_set_prev_id (YelpDocument *document,
+			   const gchar  *page_id,
+			   const gchar  *prev_id)
+{
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+    g_hash_table_replace (document->priv->prev_ids, g_strdup (page_id), g_strdup (prev_id));
+    g_mutex_unlock (document->priv->mutex);
+}
+
+gchar *
+yelp_document_get_next_id (YelpDocument *document,
+			   const gchar  *page_id)
+{
+    gchar *real, *ret = NULL;
+
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+    real = g_hash_table_lookup (document->priv->page_ids, page_id);
+    if (real) {
+	ret = g_hash_table_lookup (document->priv->next_ids, real);
+	if (ret)
+	    ret = g_strdup (ret);
+    }
+    g_mutex_unlock (document->priv->mutex);
+
+    return ret;
+}
+
+void
+yelp_document_set_next_id (YelpDocument *document,
+			   const gchar  *page_id,
+			   const gchar  *next_id)
+{
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+    g_hash_table_replace (document->priv->next_ids, g_strdup (page_id), g_strdup (next_id));
+    g_mutex_unlock (document->priv->mutex);
+}
+
+gchar *
+yelp_document_get_up_id (YelpDocument *document,
+			 const gchar  *page_id)
+{
+    gchar *real, *ret = NULL;
+
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+    real = g_hash_table_lookup (document->priv->page_ids, page_id);
+    if (real) {
+	ret = g_hash_table_lookup (document->priv->up_ids, real);
+	if (ret)
+	    ret = g_strdup (ret);
+    }
+    g_mutex_unlock (document->priv->mutex);
+
+    return ret;
+}
+
+void
+yelp_document_set_up_id (YelpDocument *document,
+			 const gchar  *page_id,
+			 const gchar  *up_id)
+{
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+    g_hash_table_replace (document->priv->up_ids, g_strdup (page_id), g_strdup (up_id));
+    g_mutex_unlock (document->priv->mutex);
+}
+
+gchar *
+yelp_document_get_title (YelpDocument *document,
+			 const gchar  *page_id)
+{
+    gchar *real, *ret = NULL;
+
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+    real = g_hash_table_lookup (document->priv->page_ids, page_id);
+    if (real) {
+	ret = g_hash_table_lookup (document->priv->titles, real);
+	if (ret)
+	    ret = g_strdup (ret);
+    }
+    g_mutex_unlock (document->priv->mutex);
+
+    return ret;
+}
+
+void
+yelp_document_set_title (YelpDocument *document,
+			 const gchar  *page_id,
+			 const gchar  *title)
+{
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+    g_hash_table_replace (document->priv->titles, g_strdup (page_id), g_strdup (title));
+    g_mutex_unlock (document->priv->mutex);
+}
+
+/******************************************************************************/
+
+gboolean
+yelp_document_request_page (YelpDocument         *document,
+			    const gchar          *page_id,
+			    GCancellable         *cancellable,
+			    YelpDocumentCallback  callback,
+			    gpointer              user_data)
+{
+    g_return_if_fail (YELP_IS_DOCUMENT (document));
+    g_return_if_fail (YELP_DOCUMENT_GET_CLASS (document)->request_page != NULL);
+
+    return YELP_DOCUMENT_GET_CLASS (document)->request_page (document,
+							     page_id,
+							     cancellable,
+							     callback,
+							     user_data);
+}
+
+static gboolean
+document_request_page (YelpDocument         *document,
+		       const gchar          *page_id,
+		       GCancellable         *cancellable,
+		       YelpDocumentCallback  callback,
+		       gpointer              user_data)
+{
+    Request *request;
+    gchar *real_id;
+    gboolean ret = FALSE;
+
+    request = g_slice_new0 (Request);
+    request->document = g_object_ref (document);
+
+    real_id = g_hash_table_lookup (document->priv->page_ids, page_id);
+    if (real_id)
+	request->page_id = g_strdup (real_id);
+    else
+	request->page_id = g_strdup (page_id);
+
+    request->cancellable = g_object_ref (cancellable);
+    g_signal_connect (cancellable, "cancelled",
+		      G_CALLBACK (request_cancel), request);
+
+    request->callback = callback;
+    request->user_data = user_data;
+    request->idle_funcs = 0;
+
+    g_mutex_lock (document->priv->mutex);
+
+    hash_slist_insert (document->priv->reqs_by_page_id,
+		       request->page_id,
+		       request);
+
+    document->priv->reqs_all = g_slist_prepend (document->priv->reqs_all, request);
+    document->priv->reqs_pending = g_slist_prepend (document->priv->reqs_pending, request);
+
+    if (g_hash_table_lookup (document->priv->titles, request->page_id)) {
+	request->idle_funcs++;
+	g_idle_add ((GSourceFunc) request_idle_info, request);
+    }
+
+    if (g_hash_table_lookup (document->priv->contents, request->page_id)) {
+	request->idle_funcs++;
+	g_idle_add ((GSourceFunc) request_idle_contents, request);
+	ret = TRUE;
+    }
+
+    g_mutex_unlock (document->priv->mutex);
+
+    return ret;
+}
+
+/******************************************************************************/
+
+const gchar *
+yelp_document_read_contents (YelpDocument *document,
+			     const gchar  *page_id)
+{
+    g_return_val_if_fail (YELP_IS_DOCUMENT (document), NULL);
+    g_return_val_if_fail (YELP_DOCUMENT_GET_CLASS (document)->read_contents != NULL, NULL);
+
+    YELP_DOCUMENT_GET_CLASS (document)->read_contents (document, page_id);
+}
+
+static const gchar *
+document_read_contents (YelpDocument *document,
+			const gchar  *page_id)
+{
+    gchar *real, *str;
+
+    g_mutex_lock (document->priv->mutex);
+
+    real = g_hash_table_lookup (document->priv->page_ids, page_id);
+    str = g_hash_table_lookup (document->priv->contents, real);
+    if (str)
+	str_ref (str);
+
+    g_mutex_unlock (document->priv->mutex);
+
+    return (const gchar *) str;
+}
+
+void
+yelp_document_finish_read (YelpDocument *document,
+			   const gchar  *contents)
+{
+    g_return_if_fail (YELP_IS_DOCUMENT (document));
+    g_return_if_fail (YELP_DOCUMENT_GET_CLASS (document)->finish_read != NULL);
+
+    YELP_DOCUMENT_GET_CLASS (document)->finish_read (document, contents);
+}
+
+static void
+document_finish_read (YelpDocument *document,
+		      const gchar  *contents)
+{
+    str_unref (contents);
+}
+
+void
+yelp_document_take_contents (YelpDocument *document,
+			     const gchar  *page_id,
+			     gchar        *contents,
+			     const gchar  *mime)
+{
+    g_return_if_fail (YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+
+    g_hash_table_replace (document->priv->contents,
+			  g_strdup (page_id),
+			  (gpointer) str_ref (contents));
+   
+    g_hash_table_replace (document->priv->mime_types,
+			  g_strdup (page_id),
+			  g_strdup (mime));
+
+    g_mutex_unlock (document->priv->mutex);
+}
+
+gchar *
+yelp_document_get_mime_type (YelpDocument *document,
+			     const gchar  *page_id)
+{
+    g_return_if_fail (YELP_IS_DOCUMENT (document));
+    g_return_if_fail (YELP_DOCUMENT_GET_CLASS (document)->get_mime_type != NULL);
+
+    YELP_DOCUMENT_GET_CLASS (document)->get_mime_type (document, page_id);
+}
+
+static gchar *
+document_get_mime_type (YelpDocument *document,
+			const gchar  *page_id)
+{
+    gchar *real, *ret = NULL;
+
+    g_mutex_lock (document->priv->mutex);
+    real = g_hash_table_lookup (document->priv->page_ids, page_id);
+    if (real) {
+	ret = g_hash_table_lookup (document->priv->mime_types, real);
+	if (ret)
+	    ret = g_strdup (ret);
+    }
+    g_mutex_unlock (document->priv->mutex);
+
+    return ret;
+}
+
+/******************************************************************************/
+
+void
+yelp_document_signal (YelpDocument       *document,
+		      const gchar        *page_id,
+		      YelpDocumentSignal  signal,
+		      GError             *error)
+{
+    GSList *reqs, *cur;
+
+    g_return_if_fail (YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+
+    reqs = g_hash_table_lookup (document->priv->reqs_by_page_id, page_id);
+    for (cur = reqs; cur != NULL; cur = cur->next) {
+	Request *request = (Request *) cur->data;
+	if (!request)
+	    continue;
+	switch (signal) {
+	case YELP_DOCUMENT_SIGNAL_CONTENTS:
+	    request->idle_funcs++;
+	    g_idle_add ((GSourceFunc) request_idle_contents, request);
+	    break;
+	case YELP_DOCUMENT_SIGNAL_INFO:
+	    request->idle_funcs++;
+	    g_idle_add ((GSourceFunc) request_idle_info, request);
+	    break;
+	case YELP_DOCUMENT_SIGNAL_ERROR:
+	    /* FIXME */
+	default:
+	    break;
+	}
+    }
+
+    g_mutex_unlock (document->priv->mutex);
+}
+
+static void
+request_cancel (Request *request)
+{
+    GSList *cur;
+    YelpDocument *document = request->document;
+
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    g_mutex_lock (document->priv->mutex);
+
+    document->priv->reqs_pending = g_slist_remove (document->priv->reqs_pending,
+						   (gconstpointer) request);
+    hash_slist_remove (document->priv->reqs_by_page_id,
+		       request->page_id,
+		       request);
+    for (cur = document->priv->reqs_all; cur != NULL; cur = cur->next) {
+	if (cur->data == request) {
+	    document->priv->reqs_all = g_slist_delete_link (document->priv->reqs_all, cur);
+	    break;
+	}
+    }
+    request_try_free (request);
+
+    g_mutex_unlock (document->priv->mutex);
+}
+
+static gboolean
+request_idle_contents (Request *request)
+{
+    YelpDocument *document;
+    YelpDocumentCallback callback = NULL;
+    gpointer user_data = user_data;
+
+    g_assert (request != NULL && YELP_IS_DOCUMENT (request->document));
+
+    if (g_cancellable_is_cancelled (request->cancellable)) {
+	request->idle_funcs--;
+	return FALSE;
+    }
+
+    document = g_object_ref (request->document);
+
+    g_mutex_lock (document->priv->mutex);
+
+    callback = request->callback;
+    user_data = request->user_data;
+    request->idle_funcs--;
+
+    g_mutex_unlock (document->priv->mutex);
+
+    if (callback)
+	callback (document, YELP_DOCUMENT_SIGNAL_CONTENTS, user_data, NULL);
+
+    g_object_unref (document);
+    return FALSE;
+}
+
+static gboolean
+request_idle_info (Request *request)
+{
+    YelpDocument *document;
+    YelpDocumentCallback callback = NULL;
+    gpointer user_data = user_data;
+
+    g_assert (request != NULL && YELP_IS_DOCUMENT (request->document));
+
+    if (g_cancellable_is_cancelled (request->cancellable)) {
+	request->idle_funcs--;
+	return FALSE;
+    }
+
+    document = g_object_ref (request->document);
+
+    g_mutex_lock (document->priv->mutex);
+
+    callback = request->callback;
+    user_data = request->user_data;
+    request->idle_funcs--;
+
+    g_mutex_unlock (document->priv->mutex);
+
+    if (callback)
+	callback (document, YELP_DOCUMENT_SIGNAL_INFO, user_data, NULL);
+
+    g_object_unref (document);
+    return FALSE;
+}
+
+static void
+request_try_free (Request *request)
+{
+    if (!g_cancellable_is_cancelled (request->cancellable))
+	g_cancellable_cancel (request->cancellable);
+
+    if (request->idle_funcs == 0)
+	request_free (request);
+    else
+	g_idle_add ((GSourceFunc) request_try_free, request);
+}
+
+static void
+request_free (Request *request)
+{
+    g_object_unref (request->document);
+    g_free (request->page_id);
+    g_object_unref (request->cancellable);
+
+    g_slice_free (Request, request);
+}
+
+/******************************************************************************/
+
+static const gchar *
+str_ref (const gchar *str)
+{
+    gpointer p;
+    guint i;
+
+    g_static_mutex_lock (&str_mutex);
+    if (str_refs == NULL)
+	str_refs = g_hash_table_new (g_direct_hash, g_direct_equal);
+    p = g_hash_table_lookup (str_refs, str);
+
+    i = GPOINTER_TO_UINT (p);
+    i++;
+    p = GUINT_TO_POINTER (i);
+
+    g_hash_table_insert (str_refs, (gpointer) str, p);
+    g_static_mutex_unlock (&str_mutex);
+
+    return str;
+}
+
+static void
+str_unref (const gchar *str)
+{
+    gpointer p;
+    guint i;
+
+    g_static_mutex_lock (&str_mutex);
+    p = g_hash_table_lookup (str_refs, str);
+
+    i = GPOINTER_TO_UINT (p);
+    i--;
+    p = GUINT_TO_POINTER (i);
+
+    if (i > 0) {
+	g_hash_table_insert (str_refs, (gpointer) str, p);
+    }
+    else {
+	g_hash_table_remove (str_refs, (gpointer) str);
+	g_free ((gchar *) str);
+    }
+
+    g_static_mutex_unlock (&str_mutex);
+}
+
+static void
+hash_slist_insert (GHashTable  *hash,
+		   const gchar *key,
+		   gpointer     value)
+{
+    GSList *list;
+    list = g_hash_table_lookup (hash, key);
+    if (list) {
+	list->next = g_slist_prepend (list->next, value);
+    } else {
+	list = g_slist_prepend (NULL, value);
+	list = g_slist_prepend (list, NULL);
+	g_hash_table_insert (hash, g_strdup (key), list);
+    }
+}
+
+static void
+hash_slist_remove (GHashTable  *hash,
+		   const gchar *key,
+		   gpointer     value)
+{
+    GSList *list;
+    list = g_hash_table_lookup (hash, key);
+    if (list) {
+	list = g_slist_remove (list, value);
+	if (list->next == NULL)
+	    g_hash_table_remove (hash, key);
+    }
+}
+
+#if 0
+
+void
+yelp_document_add_page (YelpDocument *document, gchar *page_id, const gchar *contents)
+{
+    GSList *reqs, *cur;
+    Request *request;
+    YelpDocumentPriv *priv;
+
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    debug_print (DB_FUNCTION, "entering\n");
+    debug_print (DB_ARG, "  page_id = \"%s\"\n", page_id);
+    priv = document->priv;
+
+    g_mutex_lock (priv->mutex);
+
+    g_hash_table_replace (priv->contents,
+			  g_strdup (page_id),
+			  str_ref ((gchar *) contents));
+
+    reqs = g_hash_table_lookup (priv->reqs_by_page_id, page_id);
+    for (cur = reqs; cur != NULL; cur = cur->next) {
+	if (cur->data) {
+	    request = (Request *) cur->data;
+	    request->idle_funcs++;
+	    g_idle_add ((GSourceFunc) request_idle_page, request);
+	}
+    }
+
+    g_mutex_unlock (priv->mutex);
+}
+
+gboolean
+yelp_document_has_page (YelpDocument *document, gchar *page_id)
+{
+    gchar *content;
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+    content = g_hash_table_lookup (document->priv->contents, page_id);
+    return !(content == NULL);
+}
+
+void
+yelp_document_error_request (YelpDocument *document, gint req_id, YelpError *error)
+{
+    Request *request;
+    YelpDocumentPriv *priv;
+
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    debug_print (DB_FUNCTION, "entering\n");
+    priv = document->priv;
+
+    g_mutex_lock (priv->mutex);
+
+    request = g_hash_table_lookup (priv->reqs_by_req_id,
+				   GINT_TO_POINTER (req_id));
+    if (request) {
+	request->error = error;
+	request->idle_funcs++;
+	g_idle_add ((GSourceFunc) request_idle_error, request);
+    } else {
+	yelp_error_free (error);
+    }
+
+    g_mutex_unlock (priv->mutex);
+}
+
+void
+yelp_document_error_page (YelpDocument *document, gchar *page_id, YelpError *error)
+{
+    GSList *requests;
+    Request *request = NULL;
+    YelpDocumentPriv *priv;
+
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    debug_print (DB_FUNCTION, "entering\n");
+    priv = document->priv;
+    g_mutex_lock (priv->mutex);
+
+    requests = g_hash_table_lookup (priv->reqs_by_page_id, page_id);
+    while (requests) {
+	request = (Request *) requests->data;
+	if (request && request->error == NULL) {
+	    request->error = yelp_error_copy (error);
+	    request->idle_funcs++;
+	    g_idle_add ((GSourceFunc) request_idle_error, request);
+	}
+	requests = requests->next;
+    }
+
+    yelp_error_free (error);
+
+    g_mutex_unlock (priv->mutex);
+}
+
+void
+yelp_document_error_pending (YelpDocument *document, YelpError *error)
+{
+    GSList *cur;
+    Request *request;
+    YelpDocumentPriv *priv;
+
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    debug_print (DB_FUNCTION, "entering\n");
+    priv = document->priv;
+
+    g_mutex_lock (priv->mutex);
+
+    if (priv->reqs_pending) {
+	for (cur = priv->reqs_pending; cur; cur = cur->next) {
+	    request = cur->data;
+	    if (cur->next)
+		request->error = yelp_error_copy (error);
+	    else
+		request->error = error;
+	    request->idle_funcs++;
+	    g_idle_add ((GSourceFunc) request_idle_error, request);
+	}
+
+	g_slist_free (priv->reqs_pending);
+	priv->reqs_pending = NULL;
+    } else {
+	yelp_error_free (error);
+    }
+
+    g_mutex_unlock (priv->mutex);
+}
+
+void
+yelp_document_final_pending (YelpDocument *document, YelpError *error)
+{
+    YelpDocumentPriv *priv;
+
+    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+    debug_print (DB_FUNCTION, "entering\n");
+    priv = document->priv;
+
+    g_mutex_lock (priv->mutex);
+    if (priv->reqs_pending) {
+	priv->final_error = error;
+	g_idle_add ((GSourceFunc) request_idle_final, document);
+    } else {
+	yelp_error_free (error);
+    }
+
+    g_mutex_unlock (priv->mutex);
+}
+
+
+/******************************************************************************/
+
+static gboolean
+request_idle_error (Request *request)
+{
+    YelpDocument *document;
+    YelpDocumentPriv *priv;
+    YelpDocumentFunc  func = NULL;
+    YelpError *error = NULL;
+    gint req_id = 0;
+    gpointer user_data = user_data;
+
+    g_assert (request != NULL && YELP_IS_DOCUMENT (request->document));
+
+    if (request->cancel) {
+	request->idle_funcs--;
+	return FALSE;
+    }
+
+    debug_print (DB_FUNCTION, "entering\n");
+
+    document = g_object_ref (request->document);
+    priv = document->priv;
+
+    g_mutex_lock (priv->mutex);
+
+    if (request->error) {
+	func = request->func;
+	req_id = request->req_id;
+	user_data = request->user_data;
+	error = request->error;
+	request->error = NULL;
+
+	priv->reqs_pending = g_slist_remove (priv->reqs_pending, request);
+    }
+
+    request->idle_funcs--;
+    g_mutex_unlock (priv->mutex);
+
+    if (func)
+	func (document,
+	      YELP_DOCUMENT_SIGNAL_ERROR,
+	      req_id,
+	      error,
+	      user_data);
+
+    g_object_unref (document);
+    return FALSE;
+}
+
+
+static gboolean
+request_idle_final (YelpDocument *document)
+{
+    YelpDocumentPriv *priv;
+    YelpDocumentFunc  func = NULL;
+    YelpError *error = NULL;
+    gint req_id = 0;
+    gpointer user_data = user_data;
+    Request *request = NULL;
+    GSList *cur = NULL;
+
+    debug_print (DB_FUNCTION, "entering\n");
+
+    priv = document->priv;
+
+    g_mutex_lock (priv->mutex);
+
+    if (priv->reqs_pending == NULL) {
+	/*
+	  Time to bail as we shouldn't be here anyway.
+	*/
+	g_mutex_unlock (priv->mutex);
+	return FALSE;
+    }
+    
+    for (cur = priv->reqs_pending; cur; cur = cur->next) {
+	request = cur->data;
+	if (request->idle_funcs != 0) {
+	    /* 
+	       While there are outstanding requests, we should wait for them
+	       to complete before signalling the error
+	    */
+	    request->idle_funcs++;
+	    g_mutex_unlock (priv->mutex);
+	    return TRUE;
+	}
+    }
+
+    for (cur = priv->reqs_pending; cur; cur = cur->next) {
+	request = cur->data;
+	
+	if (cur->next)
+	    request->error = yelp_error_copy (priv->final_error);
+	else
+	    request->error = error;
+	
+	if (request->error) {
+	    func = request->func;
+	    req_id = request->req_id;
+	    user_data = request->user_data;
+	    error = request->error;
+	    request->error = NULL;
+	    
+	    priv->reqs_pending = g_slist_remove (priv->reqs_pending, request);
+	}
+	
+	
+	if (func)
+	    func (document,
+		  YELP_DOCUMENT_SIGNAL_ERROR,
+		  req_id,
+		  error,
+		  user_data);
+    }
+    g_mutex_unlock (priv->mutex);
+    
+    g_object_unref (document);
+    return FALSE;
+}
+
+/******************************************************************************/
+
+
+#endif
diff --git a/libyelp/yelp-document.h b/libyelp/yelp-document.h
new file mode 100644
index 0000000..e86d4f3
--- /dev/null
+++ b/libyelp/yelp-document.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-2009 Shaun McCance  <shaunm gnome org>
+ *
+ * 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 of the
+ * License, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance  <shaunm gnome org>
+ */
+
+#ifndef __YELP_DOCUMENT_H__
+#define __YELP_DOCUMENT_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define YELP_TYPE_DOCUMENT         (yelp_document_get_type ())
+#define YELP_DOCUMENT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_DOCUMENT, YelpDocument))
+#define YELP_DOCUMENT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_DOCUMENT, YelpDocumentClass))
+#define YELP_IS_DOCUMENT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_DOCUMENT))
+#define YELP_IS_DOCUMENT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_DOCUMENT))
+#define YELP_DOCUMENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_DOCUMENT, YelpDocumentClass))
+
+typedef struct _YelpDocument      YelpDocument;
+typedef struct _YelpDocumentClass YelpDocumentClass;
+typedef struct _YelpDocumentPriv  YelpDocumentPriv;
+
+typedef enum {
+    YELP_DOCUMENT_SIGNAL_CONTENTS,
+    YELP_DOCUMENT_SIGNAL_INFO,
+    YELP_DOCUMENT_SIGNAL_ERROR
+} YelpDocumentSignal;
+
+typedef void      (*YelpDocumentCallback)      (YelpDocument         *document,
+						YelpDocumentSignal    signal,
+						gpointer              user_data,
+						GError               *error);
+
+struct _YelpDocument {
+    GObject           parent;
+    YelpDocumentPriv *priv;
+};
+
+struct _YelpDocumentClass {
+    GObjectClass      parent_class;
+
+    /* Virtual Functions */
+    gboolean      (*request_page)              (YelpDocument         *document,
+						const gchar          *page_id,
+						GCancellable         *cancellable,
+						YelpDocumentCallback  callback,
+						gpointer              user_data);
+    const gchar * (*read_contents)             (YelpDocument         *document,
+						const gchar          *page_id);
+    void          (*finish_read)               (YelpDocument         *document,
+						const gchar          *contents);
+    gchar *       (*get_mime_type)             (YelpDocument         *document,
+						const gchar          *mime_type);
+
+};
+
+GType             yelp_document_get_type       (void);
+
+gboolean          yelp_document_request_page   (YelpDocument         *document,
+					        const gchar          *page_id,
+					        GCancellable         *cancellable,
+						YelpDocumentCallback  callback,
+					        gpointer              user_data);
+
+void              yelp_document_take_contents  (YelpDocument         *document,
+						const gchar          *page_id,
+						gchar                *contents,
+						const gchar          *mime);
+gchar *           yelp_document_get_mime_type  (YelpDocument         *document,
+						const gchar          *page_id);
+const gchar *     yelp_document_read_contents  (YelpDocument         *document,
+						const gchar          *page_id);
+void              yelp_document_finish_read    (YelpDocument         *document,
+						const gchar          *contents);
+
+gchar *           yelp_document_get_page_id    (YelpDocument        *document,
+						const gchar         *id);
+void              yelp_document_set_page_id    (YelpDocument        *document,
+						const gchar         *id,
+						const gchar         *page_id);
+
+gchar *           yelp_document_get_root_id    (YelpDocument        *document,
+						const gchar         *page_id);
+void              yelp_document_set_root_id    (YelpDocument        *document,
+						const gchar         *page_id,
+						const gchar         *root_id);
+
+gchar *           yelp_document_get_prev_id    (YelpDocument        *document,
+						const gchar         *page_id);
+void              yelp_document_set_prev_id    (YelpDocument        *document,
+						const gchar         *page_id,
+						const gchar         *prev_id);
+
+char *            yelp_document_get_next_id    (YelpDocument        *document,
+						const gchar         *page_id);
+void              yelp_document_set_next_id    (YelpDocument        *document,
+						const gchar         *page_id,
+						const gchar         *next_id);
+
+gchar *           yelp_document_get_up_id      (YelpDocument        *document,
+						const gchar         *page_id);
+void              yelp_document_set_up_id      (YelpDocument        *document,
+						const gchar         *page_id,
+						const gchar         *up_id);
+
+gchar *           yelp_document_get_title      (YelpDocument        *document,
+						const gchar         *page_id);
+void              yelp_document_set_title      (YelpDocument        *document,
+						const gchar         *page_id,
+						const gchar         *title);
+
+gboolean          yelp_document_has_page       (YelpDocument        *document,
+						const gchar         *page_id);
+
+void              yelp_document_signal         (YelpDocument        *document,
+						const gchar         *page_id,
+						YelpDocumentSignal   signal,
+						GError              *error);
+/* FIXME */
+/*
+void              yelp_document_error_request  (YelpDocument        *document,
+						gint                 req_id,
+						YelpError           *error);
+void              yelp_document_error_page     (YelpDocument        *document,
+						gchar               *page_id,
+						YelpError           *error);
+void              yelp_document_error_pending  (YelpDocument        *document,
+						YelpError           *error);
+void              yelp_document_final_pending  (YelpDocument        *document,
+						YelpError           *error);
+*/
+
+
+#endif /* __YELP_DOCUMENT_H__ */
diff --git a/libyelp/yelp-simple-document.c b/libyelp/yelp-simple-document.c
new file mode 100644
index 0000000..099d456
--- /dev/null
+++ b/libyelp/yelp-simple-document.c
@@ -0,0 +1,419 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-2009 Shaun McCance  <shaunm gnome org>
+ *
+ * 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 of the
+ * License, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance  <shaunm gnome org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "yelp-document.h"
+#include "yelp-simple-document.h"
+
+typedef struct _Request Request;
+struct _Request {
+    YelpDocument         *document;
+    GCancellable         *cancellable;
+    YelpDocumentCallback  callback;
+    gpointer              user_data;
+
+    gint                  idle_funcs;
+};
+
+struct _YelpSimpleDocumentPriv {
+    gchar        *base_uri;
+
+    GFile        *file;
+    GInputStream *stream;
+
+    gchar        *contents;
+    gssize        contents_len;
+    gssize        contents_read;
+    gchar        *mime_type;
+    gboolean      finished;
+
+    GSList       *reqs;
+};
+
+#define BUFFER_SIZE 4096
+
+G_DEFINE_TYPE (YelpSimpleDocument, yelp_simple_document, YELP_TYPE_DOCUMENT);
+#define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_SIMPLE_DOCUMENT, YelpSimpleDocumentPriv))
+
+static void           yelp_simple_document_class_init  (YelpSimpleDocumentClass *klass);
+static void           yelp_simple_document_init        (YelpSimpleDocument      *document);
+static void           yelp_simple_document_dispose     (GObject                 *object);
+static void           yelp_simple_document_finalize    (GObject                 *object);
+
+static gboolean       document_request_page            (YelpDocument            *document,
+							const gchar             *page_id,
+							GCancellable            *cancellable,
+							YelpDocumentCallback     callback,
+							gpointer                 user_data);
+static const gchar *  document_read_contents           (YelpDocument            *document,
+							const gchar             *page_id);
+static void           document_finish_read             (YelpDocument            *document,
+							const gchar             *contents);
+static gchar *        document_get_mime_type           (YelpDocument            *document,
+							const gchar             *mime_type);
+static void           document_signal_all              (YelpSimpleDocument      *document);
+
+static void           file_info_cb                     (GFile                   *file,
+							GAsyncResult            *result,
+							YelpSimpleDocument      *document);
+static void           file_read_cb                     (GFile                   *file,
+							GAsyncResult            *result,
+							YelpSimpleDocument      *document);
+static void           stream_read_cb                   (GInputStream            *stream,
+							GAsyncResult            *result,
+							YelpSimpleDocument      *document);
+static void           stream_close_cb                  (GInputStream            *stream,
+							GAsyncResult            *result,
+							YelpSimpleDocument      *document);
+
+static void           request_cancel                   (Request                 *request);
+static void           request_try_free                 (Request                 *request);
+static void           request_free                     (Request                 *request);
+
+static void
+yelp_simple_document_class_init (YelpSimpleDocumentClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass);
+
+    object_class->dispose  = yelp_simple_document_dispose;
+    object_class->finalize = yelp_simple_document_finalize;
+
+    document_class->request_page = document_request_page;
+    document_class->read_contents = document_read_contents;
+    document_class->finish_read = document_finish_read;
+    document_class->get_mime_type = document_get_mime_type;
+
+    g_type_class_add_private (klass, sizeof (YelpSimpleDocumentPriv));
+}
+
+static void
+yelp_simple_document_init (YelpSimpleDocument *document)
+{
+    document->priv = GET_PRIV (document);
+
+    document->priv->file = NULL;
+    document->priv->stream = NULL;
+
+    document->priv->finished = FALSE;
+    document->priv->contents = NULL;
+    document->priv->mime_type = NULL;
+}
+
+static void
+yelp_simple_document_dispose (GObject *object)
+{
+    YelpSimpleDocument *document = YELP_SIMPLE_DOCUMENT (object);
+
+    if (document->priv->reqs) {
+	g_slist_foreach (document->priv->reqs, (GFunc) request_try_free, NULL);
+	g_slist_free (document->priv->reqs);
+	document->priv->reqs = NULL;
+    }
+
+    if (document->priv->file) {
+	g_object_unref (document->priv->file);
+	document->priv->file = NULL;
+    }
+
+    if (document->priv->stream) {
+	g_object_unref (document->priv->stream);
+	document->priv->stream = NULL;
+    }
+
+    G_OBJECT_CLASS (yelp_simple_document_parent_class)->dispose (object);
+}
+
+static void
+yelp_simple_document_finalize (GObject *object)
+{
+    YelpSimpleDocument *document = YELP_SIMPLE_DOCUMENT (object);
+
+    g_free (document->priv->base_uri);
+
+    g_free (document->priv->contents);
+    g_free (document->priv->mime_type);
+
+    G_OBJECT_CLASS (yelp_simple_document_parent_class)->finalize (object);
+}
+
+YelpDocument *
+yelp_simple_document_new (YelpUri *uri)
+{
+    YelpSimpleDocument *document;
+
+    document = (YelpSimpleDocument *) g_object_new (YELP_TYPE_SIMPLE_DOCUMENT, NULL);
+
+    document->priv->base_uri = yelp_uri_get_base_uri (uri);
+
+    return (YelpDocument *) document;
+}
+
+/******************************************************************************/
+
+static gboolean
+document_request_page (YelpDocument         *document,
+		       const gchar          *page_id,
+		       GCancellable         *cancellable,
+		       YelpDocumentCallback  callback,
+		       gpointer              user_data)
+{
+    YelpSimpleDocument *simple = YELP_SIMPLE_DOCUMENT (document);
+    Request *request;
+    gboolean ret = FALSE;
+
+    request = g_slice_new0 (Request);
+
+    request->document = g_object_ref (document);
+    request->callback = callback;
+    request->user_data = user_data;
+
+    request->cancellable = g_object_ref (cancellable);
+    g_signal_connect (cancellable, "cancelled",
+		      G_CALLBACK (request_cancel), request);
+
+    simple->priv->reqs = g_slist_prepend (simple->priv->reqs, request);
+
+    if (simple->priv->finished) {
+	g_idle_add ((GSourceFunc) document_signal_all, simple);
+	ret = TRUE;
+    }
+    else if (simple->priv->file == NULL) {
+	simple->priv->file = g_file_new_for_uri (simple->priv->base_uri);
+	g_file_query_info_async (simple->priv->file,
+				 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+				 G_FILE_QUERY_INFO_NONE,
+				 G_PRIORITY_DEFAULT,
+				 NULL,
+				 (GAsyncReadyCallback) file_info_cb,
+				 document);
+    }
+
+    return ret;
+}
+
+static const gchar *
+document_read_contents (YelpDocument *document,
+			const gchar  *page_id)
+{
+    YelpSimpleDocument *simple = YELP_SIMPLE_DOCUMENT (document);
+
+    return (const gchar*) simple->priv->contents;
+}
+
+static void
+document_finish_read (YelpDocument *document,
+		      const gchar  *contents)
+{
+    YelpSimpleDocument *simple = YELP_SIMPLE_DOCUMENT (document);
+
+    if (simple->priv->reqs == NULL) {
+	g_free (simple->priv->contents);
+	simple->priv->contents = NULL;
+
+	g_free (simple->priv->mime_type);
+	simple->priv->mime_type = NULL;
+    }
+}
+
+static gchar *
+document_get_mime_type (YelpDocument *document,
+			const gchar  *mime_type)
+{
+    YelpSimpleDocument *simple = YELP_SIMPLE_DOCUMENT (document);
+
+    if (simple->priv->mime_type)
+	return g_strdup (simple->priv->mime_type);
+    else
+	return NULL;
+}
+
+static void
+document_signal_all (YelpSimpleDocument *document)
+{
+    GSList *cur;
+    for (cur = document->priv->reqs; cur != NULL; cur = cur->next) {
+	Request *request = (Request *) cur->data;
+	if (request->callback)
+	    request->callback (request->document,
+			       YELP_DOCUMENT_SIGNAL_CONTENTS,
+			       request->user_data,
+			       NULL);
+    }
+}
+
+/******************************************************************************/
+
+static void
+file_info_cb (GFile              *file,
+	      GAsyncResult       *result,
+	      YelpSimpleDocument *document)
+{
+    GFileInfo *info = g_file_query_info_finish (file, result, NULL);
+    const gchar *type = g_file_info_get_attribute_string (info,
+							  G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
+    document->priv->mime_type = g_strdup (type);
+    g_object_unref (info);
+    g_file_read_async (document->priv->file,
+		       G_PRIORITY_DEFAULT,
+		       NULL,
+		       (GAsyncReadyCallback) file_read_cb,
+		       document);
+}
+
+static void
+file_read_cb (GFile              *file,
+	      GAsyncResult       *result,
+	      YelpSimpleDocument *document)
+{
+    GError *error = NULL;
+
+    document->priv->stream = (GInputStream *) g_file_read_finish (file, result, &error);
+
+    if (document->priv->stream == NULL) {
+	GSList *cur;
+	for (cur = document->priv->reqs; cur != NULL; cur = cur->next) {
+	    Request *request = (Request *) cur->data;
+	    if (request->callback) {
+		GError *err = g_error_copy (error);
+		request->callback (request->document,
+				   YELP_DOCUMENT_SIGNAL_ERROR,
+				   request->user_data,
+				   err);
+		g_error_free (err);
+	    }
+	}
+	g_error_free (error);
+	return;
+    }
+
+    g_assert (document->priv->contents == NULL);
+    document->priv->contents_len = BUFFER_SIZE;
+    document->priv->contents = g_realloc (document->priv->contents,
+					  document->priv->contents_len);
+    document->priv->contents[0] = '\0';
+    document->priv->contents_read = 0;
+    g_input_stream_read_async (document->priv->stream,
+			       document->priv->contents,
+			       BUFFER_SIZE,
+			       G_PRIORITY_DEFAULT,
+			       NULL,
+			       (GAsyncReadyCallback) stream_read_cb,
+			       document);
+}
+
+static void
+stream_read_cb (GInputStream       *stream,
+		GAsyncResult       *result,
+		YelpSimpleDocument *document)
+{
+    gssize bytes;
+
+    bytes = g_input_stream_read_finish (stream, result, NULL);
+    document->priv->contents_read += bytes;
+
+    if (bytes == 0) {
+	/* If the preceding read filled contents, it was extended before the
+	   read that gave us zero bytes.  Otherwise, there's room for this
+	   byte.  I'm 99.99% certain I'm right.
+	 */
+	g_assert (document->priv->contents_read < document->priv->contents_len);
+	document->priv->contents[document->priv->contents_read + 1] = '\0';
+	g_input_stream_close_async (document->priv->stream,
+				    G_PRIORITY_DEFAULT,
+				    NULL,
+				    (GAsyncReadyCallback) stream_close_cb,
+				    document);
+	return;
+    }
+
+    if (document->priv->contents_read == document->priv->contents_len) {
+	document->priv->contents_len = document->priv->contents_read + BUFFER_SIZE;
+	document->priv->contents = g_realloc (document->priv->contents,
+					      document->priv->contents_len);
+    }
+    g_input_stream_read_async (document->priv->stream,
+			       document->priv->contents + document->priv->contents_read,
+			       document->priv->contents_len - document->priv->contents_read,
+			       G_PRIORITY_DEFAULT,
+			       NULL,
+			       (GAsyncReadyCallback) stream_read_cb,
+			       document);
+}
+
+static void
+stream_close_cb (GInputStream       *stream,
+		 GAsyncResult       *result,
+		 YelpSimpleDocument *document)
+{
+    GSList *cur;
+
+    document->priv->finished = TRUE;
+    document_signal_all (document);
+}
+
+/******************************************************************************/
+
+static void
+request_cancel (Request *request)
+{
+    GSList *cur;
+    YelpSimpleDocument *document = request->document;
+
+    g_assert (document != NULL && YELP_IS_SIMPLE_DOCUMENT (document));
+
+    for (cur = document->priv->reqs; cur != NULL; cur = cur->next) {
+	if (cur->data == request) {
+	    document->priv->reqs = g_slist_delete_link (document->priv->reqs, cur);
+	    break;
+	}
+    }
+    request_try_free (request);
+}
+
+static void
+request_try_free (Request *request)
+{
+    if (!g_cancellable_is_cancelled (request->cancellable))
+	g_cancellable_cancel (request->cancellable);
+
+    if (request->idle_funcs == 0)
+	request_free (request);
+    else
+	g_idle_add ((GSourceFunc) request_try_free, request);
+}
+
+static void
+request_free (Request *request)
+{
+    g_object_unref (request->document);
+    g_object_unref (request->cancellable);
+
+    g_slice_free (Request, request);
+}
diff --git a/libyelp/yelp-simple-document.h b/libyelp/yelp-simple-document.h
new file mode 100644
index 0000000..aec9182
--- /dev/null
+++ b/libyelp/yelp-simple-document.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-2009 Shaun McCance  <shaunm gnome org>
+ *
+ * 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 of the
+ * License, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance  <shaunm gnome org>
+ */
+
+#ifndef __YELP_SIMPLE_DOCUMENT_H__
+#define __YELP_SIMPLE_DOCUMENT_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "yelp-document.h"
+#include "yelp-uri.h"
+
+#define YELP_TYPE_SIMPLE_DOCUMENT         (yelp_simple_document_get_type ())
+#define YELP_SIMPLE_DOCUMENT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_SIMPLE_DOCUMENT, YelpSimpleDocument))
+#define YELP_SIMPLE_DOCUMENT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_SIMPLE_DOCUMENT, YelpSimpleDocumentClass))
+#define YELP_IS_SIMPLE_DOCUMENT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_SIMPLE_DOCUMENT))
+#define YELP_IS_SIMPLE_DOCUMENT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_SIMPLE_DOCUMENT))
+#define YELP_SIMPLE_DOCUMENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_SIMPLE_DOCUMENT, YelpSimpleDocumentClass))
+
+typedef struct _YelpSimpleDocument      YelpSimpleDocument;
+typedef struct _YelpSimpleDocumentClass YelpSimpleDocumentClass;
+typedef struct _YelpSimpleDocumentPriv  YelpSimpleDocumentPriv;
+
+struct _YelpSimpleDocument {
+    YelpDocument parent;
+    YelpSimpleDocumentPriv *priv;
+};
+
+struct _YelpSimpleDocumentClass {
+    YelpDocumentClass parent_class;
+};
+
+GType             yelp_simple_document_get_type       (void);
+YelpDocument *    yelp_simple_document_new            (YelpUri  *uri);
+
+#endif /* __YELP_SIMPLE_DOCUMENT_H__ */
diff --git a/libyelp/yelp-uri.c b/libyelp/yelp-uri.c
index a2f0ddb..580e533 100644
--- a/libyelp/yelp-uri.c
+++ b/libyelp/yelp-uri.c
@@ -32,6 +32,7 @@
 #include "yelp-uri.h"
 #include "yelp-debug.h"
 
+G_DEFINE_TYPE (YelpUri, yelp_uri, G_TYPE_OBJECT);
 #define YELP_URI_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_URI, YelpUriPriv))
 
 struct _YelpUriPriv {
@@ -42,9 +43,10 @@ struct _YelpUriPriv {
     gchar                *frag_id;
 };
 
-static void           uri_class_init        (YelpUriClass   *klass);
-static void           uri_init              (YelpUri        *uri);
-static void           uri_dispose           (GObject        *object);
+static void           yelp_uri_class_init   (YelpUriClass   *klass);
+static void           yelp_uri_init         (YelpUri        *uri);
+static void           yelp_uri_dispose      (GObject        *object);
+static void           yelp_uri_finalize     (GObject        *object);
 
 static void           resolve_file_uri      (YelpUri        *ret,
                                              gchar          *arg);
@@ -88,54 +90,49 @@ static const gchar *mancats[] = {
 
 /******************************************************************************/
 
-GType
-yelp_uri_get_type (void)
-{
-    static GType type = 0;
-    if (!type) {
-        static const GTypeInfo info = {
-            sizeof (YelpUriClass),
-            NULL, NULL,
-            (GClassInitFunc) uri_class_init,
-            NULL, NULL,
-            sizeof (YelpUri),
-            0,
-            (GInstanceInitFunc) uri_init,
-        };
-        type = g_type_register_static (G_TYPE_OBJECT,
-                                       "YelpUri", 
-                                       &info, 0);
-    }
-    return type;
-}
-
 static void
-uri_class_init (YelpUriClass *klass)
+yelp_uri_class_init (YelpUriClass *klass)
 {
-    GObjectClass   *object_class = G_OBJECT_CLASS (klass);
-    parent_class = g_type_class_peek_parent (klass);
-    object_class->dispose      = uri_dispose;
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->dispose  = yelp_uri_dispose;
+    object_class->finalize = yelp_uri_finalize;
+
     g_type_class_add_private (klass, sizeof (YelpUriPriv));
 }
 
 static void
-uri_init (YelpUri *uri)
+yelp_uri_init (YelpUri *uri)
 {
     uri->priv = YELP_URI_GET_PRIVATE (uri);
 }
 
 static void
-uri_dispose (GObject *object)
+yelp_uri_dispose (GObject *object)
 {
     YelpUri *uri = YELP_URI (object);
 
-    if (uri->priv->gfile)
+    if (uri->priv->gfile) {
         g_object_unref (uri->priv->gfile);
+        uri->priv->gfile = NULL;
+    }
+    g_strfreev (uri->priv->search_path);
+    g_free (uri->priv->page_id);
+    g_free (uri->priv->frag_id);
+
+    G_OBJECT_CLASS (yelp_uri_parent_class)->dispose (object);
+}
+
+static void
+yelp_uri_finalize (GObject *object)
+{
+    YelpUri *uri = YELP_URI (object);
+
     g_strfreev (uri->priv->search_path);
     g_free (uri->priv->page_id);
     g_free (uri->priv->frag_id);
 
-    parent_class->dispose (object);
+    G_OBJECT_CLASS (yelp_uri_parent_class)->finalize (object);
 }
 
 /******************************************************************************/
diff --git a/libyelp/yelp-view.c b/libyelp/yelp-view.c
index 2cc83cf..4403388 100644
--- a/libyelp/yelp-view.c
+++ b/libyelp/yelp-view.c
@@ -31,20 +31,28 @@
 
 #include "yelp-view.h"
 
-static void        view_init		          (YelpView        *view);
-static void        view_class_init	          (YelpViewClass   *klass);
+static void        yelp_view_init                 (YelpView           *view);
+static void        yelp_view_class_init           (YelpViewClass      *klass);
+static void        yelp_view_dispose              (GObject            *object);
+static void        yelp_view_finalize             (GObject            *object);
+
+static void        document_callback              (YelpDocument       *document,
+                                                   YelpDocumentSignal  signal,
+                                                   YelpView           *view,
+                                                   GError             *error);
 
 enum {
     NEW_VIEW_REQUESTED,
     LAST_SIGNAL
 };
 static gint signals[LAST_SIGNAL] = { 0 };
-static GObjectClass *parent_class = NULL;
 
-#define YELP_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_VIEW, YelpViewPriv))
+G_DEFINE_TYPE (YelpView, yelp_view, WEBKIT_TYPE_WEB_VIEW);
+#define GET_PRIV(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_VIEW, YelpViewPriv))
 
 struct _YelpViewPriv {
-    WebKitWebView  *webview;
+    YelpUri       *uri;
+    GCancellable  *cancellable;
 };
 
 #define TARGET_TYPE_URI_LIST     "text/uri-list"
@@ -52,64 +60,48 @@ enum {
     TARGET_URI_LIST
 };
 
-GType
-yelp_view_get_type (void)
-{
-    static GType view_type = 0;
-
-    if (!view_type) {
-	static const GTypeInfo view_info = {
-	    sizeof (YelpViewClass),
-	    NULL,
-	    NULL,
-	    (GClassInitFunc) view_class_init,
-	    NULL,
-	    NULL,
-	    sizeof (YelpView),
-	    0,
-	    (GInstanceInitFunc) view_init,
-	};
-	view_type = g_type_register_static (YELP_TYPE_VIEW,
-                                            "YelpView",
-                                            &view_info, 0);
-    }
-    return view_type;
-}
-
 static void
-view_init (YelpView *view)
+yelp_view_init (YelpView *view)
 {
-    view->priv = YELP_VIEW_GET_PRIVATE (view);
-    view->priv->webview = (WebKitWebView *) webkit_web_view_new ();
-    gtk_container_add (GTK_CONTAINER (view), (GtkWidget *) view->priv->webview);
+    view->priv = GET_PRIV (view);
+
+    view->priv->cancellable = NULL;
 }
 
 static void
-view_dispose (GObject *object)
+yelp_view_dispose (GObject *object)
 {
-    parent_class->dispose (object);
+    YelpView *view = YELP_VIEW (object);
+
+    if (view->priv->uri) {
+        g_object_unref (view->priv->uri);
+        view->priv->uri = NULL;
+    }
+
+    if (view->priv->cancellable) {
+        g_cancellable_cancel (view->priv->cancellable);
+        g_object_unref (view->priv->cancellable);
+        view->priv->cancellable = NULL;
+    }
+
+    G_OBJECT_CLASS (yelp_view_parent_class)->dispose (object);
 }
 
 static void
-view_finalize (GObject *object)
+yelp_view_finalize (GObject *object)
 {
     YelpView *view = YELP_VIEW (object);
-    YelpViewPriv *priv = view->priv;
-
-    /* FIXME Free stuff */
 
-    parent_class->finalize (object);
+    G_OBJECT_CLASS (yelp_view_parent_class)->finalize (object);
 }
 
 static void
-view_class_init (YelpViewClass *klass)
+yelp_view_class_init (YelpViewClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-    parent_class = (GObjectClass *) g_type_class_peek_parent (klass);
-
-    object_class->dispose = view_dispose;
-    object_class->finalize = view_finalize;
+    object_class->dispose = yelp_view_dispose;
+    object_class->finalize = yelp_view_finalize;
 
     signals[NEW_VIEW_REQUESTED] =
 	g_signal_new ("new_view_requested",
@@ -121,3 +113,68 @@ view_class_init (YelpViewClass *klass)
 
     g_type_class_add_private (klass, sizeof (YelpViewPriv));
 }
+
+/******************************************************************************/
+
+GtkWidget *
+yelp_view_new (void)
+{
+    return (GtkWidget *) g_object_new (YELP_TYPE_VIEW, NULL);
+}
+
+void
+yelp_view_load (YelpView *view,
+                YelpUri  *uri)
+{
+    /* FIXME: get a document and call load_document */
+}
+
+void
+yelp_view_load_document (YelpView     *view,
+                         YelpUri      *uri,
+                         YelpDocument *document)
+{
+    gchar *page_id;
+
+    /* FIXME: unset previous load */
+
+    page_id = yelp_uri_get_page_id (uri);
+    view->priv->uri = g_object_ref (uri);
+    view->priv->cancellable = g_cancellable_new ();
+    yelp_document_request_page (document,
+                                page_id,
+                                view->priv->cancellable,
+                                (YelpDocumentCallback) document_callback,
+                                view);
+
+    g_free (page_id);
+}
+
+static void
+document_callback (YelpDocument       *document,
+                   YelpDocumentSignal  signal,
+                   YelpView           *view,
+                   GError             *error)
+{
+    if (signal == YELP_DOCUMENT_SIGNAL_INFO) {
+    }
+    else if (signal == YELP_DOCUMENT_SIGNAL_CONTENTS) {
+	const gchar *contents = yelp_document_read_contents (document, NULL);
+        gchar *base_uri, *mime_type, *page_id;
+        base_uri = yelp_uri_get_base_uri (view->priv->uri);
+        page_id = yelp_uri_get_page_id (view->priv->uri);
+        mime_type = yelp_document_get_mime_type (document, page_id);
+        webkit_web_view_load_string (WEBKIT_WEB_VIEW (view),
+                                     contents,
+                                     mime_type,
+                                     "UTF-8",
+                                     base_uri);
+        g_free (page_id);
+        g_free (mime_type);
+        g_free (base_uri);
+	yelp_document_finish_read (document, contents);
+    }
+    else if (signal == YELP_DOCUMENT_SIGNAL_ERROR) {
+        printf ("ERROR: %s\n", error->message);
+    }
+}
diff --git a/libyelp/yelp-view.h b/libyelp/yelp-view.h
index 0b1e3df..a5bafd8 100644
--- a/libyelp/yelp-view.h
+++ b/libyelp/yelp-view.h
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /*
- * Copyright (C) 2001-2002 Mikael Hallendal <micke imendio com>
+ * Copyright (C) 2009 Shaun McCance <shaunm gnome org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -17,13 +17,17 @@
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  *
- * Author: Mikael Hallendal <micke imendio com>
+ * Author: Shaun McCance <shaunm gnome org>
  */
 
 #ifndef __YELP_VIEW_H__
 #define __YELP_VIEW_H__
 
 #include <gtk/gtk.h>
+#include <webkit/webkit.h>
+
+#include "yelp-document.h"
+#include "yelp-uri.h"
 
 #define YELP_TYPE_VIEW            (yelp_view_get_type ())
 #define YELP_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), YELP_TYPE_VIEW, YelpView))
@@ -37,19 +41,22 @@ typedef struct _YelpViewPriv   YelpViewPriv;
 
 struct _YelpView
 {
-    GtkContainer  parent;
-    YelpViewPriv *priv;
+    WebKitWebView       parent;
+    YelpViewPriv       *priv;
 };
 
 struct _YelpViewClass
 {
-    GtkContainerClass  parent_class;
+    WebKitWebViewClass  parent_class;
 };
 
 GType            yelp_view_get_type        (void);
-GtkWidget *      yelp_view_new             (GNode         *doc_tree,
-					    GList         *index);
-void             yelp_view_load            (YelpView      *view,
-					    const gchar   *uri);
+GtkWidget *      yelp_view_new             (void);
+
+void             yelp_view_load            (YelpView     *view,
+					    YelpUri      *uri);
+void             yelp_view_load_document   (YelpView     *view,
+					    YelpUri      *uri,
+					    YelpDocument *document);
 
 #endif /* __YELP_VIEW_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b1efc6a..d9cfd0f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -9,10 +9,14 @@ YELP_COMMON_LDADD =				\
 
 check_PROGRAMS =				\
 	test-location-entry			\
-	test-uri
+	test-uri				\
+	test-view
 
 test_location_entry_CFLAGS = $(YELP_COMMON_CFLAGS)
 test_location_entry_LDADD = $(YELP_COMMON_LDADD)
 
 test_uri_CFLAGS = $(YELP_COMMON_CFLAGS)
 test_uri_LDADD = $(YELP_COMMON_LDADD)
+
+test_view_CFLAGS = $(YELP_COMMON_CFLAGS)
+test_view_LDADD = $(YELP_COMMON_LDADD)
diff --git a/tests/test-view.c b/tests/test-view.c
new file mode 100644
index 0000000..99a70f0
--- /dev/null
+++ b/tests/test-view.c
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-2009 Shaun McCance  <shaunm gnome org>
+ *
+ * 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 of the
+ * License, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance  <shaunm gnome org>
+ */
+
+#include <gtk/gtk.h>
+#include <webkit/webkit.h>
+#include "yelp-view.h"
+#include "yelp-uri.h"
+#include "yelp-simple-document.h"
+
+int
+main (int argc, char **argv)
+{
+    GtkWidget *window, *scroll, *view;
+    YelpUri *uri;
+    YelpDocument *document;
+    GCancellable *cancellable;
+
+    gtk_init (&argc, &argv);
+
+    g_thread_init (NULL);
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
+
+    scroll = gtk_scrolled_window_new (NULL, NULL);
+    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
+				    GTK_POLICY_AUTOMATIC,
+				    GTK_POLICY_AUTOMATIC);
+    gtk_container_add (GTK_CONTAINER (window), scroll);
+
+    view = yelp_view_new ();
+    gtk_container_add (GTK_CONTAINER (scroll), view);
+
+    g_assert (argc >= 2);
+    uri = yelp_uri_resolve (argv[1]);
+    document = yelp_simple_document_new (uri);
+    yelp_view_load_document (view, uri, document);
+
+    gtk_widget_show_all (GTK_WIDGET (window));
+
+    gtk_main ();
+}



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