[yelp/yelp-3-0] [yelp-uri] Resolve URIs asynchronously, no blocking IO



commit 0708e72ace823babfe0bc46c19221981baac70ab
Author: Shaun McCance <shaunm gnome org>
Date:   Tue Oct 6 23:10:53 2009 -0500

    [yelp-uri] Resolve URIs asynchronously, no blocking IO

 libyelp/yelp-document.c |    8 +-
 libyelp/yelp-settings.h |    6 +-
 libyelp/yelp-uri.c      |  334 ++++++++++++++++++++++++++++++++---------------
 libyelp/yelp-uri.h      |   23 ++--
 libyelp/yelp-view.c     |  109 +++++++++++++---
 tests/test-uri.c        |   41 +++++--
 6 files changed, 377 insertions(+), 144 deletions(-)
---
diff --git a/libyelp/yelp-document.c b/libyelp/yelp-document.c
index 1efcb90..d464d44 100644
--- a/libyelp/yelp-document.c
+++ b/libyelp/yelp-document.c
@@ -117,13 +117,18 @@ yelp_document_get_for_uri (YelpUri *uri)
 {
     static GHashTable *documents = NULL;
     gchar *base_uri;
-    YelpDocument *document;
+    YelpDocument *document = NULL;
 
     if (documents == NULL)
 	documents = g_hash_table_new_full (g_str_hash, g_str_equal,
 					   g_free, g_object_unref);
 
+    g_return_val_if_fail (yelp_uri_is_resolved (uri), NULL);
+
     base_uri = yelp_uri_get_base_uri (uri);
+    if (base_uri == NULL)
+	return NULL;
+
     document = g_hash_table_lookup (documents, base_uri);
 
     if (document != NULL) {
@@ -155,7 +160,6 @@ yelp_document_get_for_uri (YelpUri *uri)
     case YELP_URI_DOCUMENT_TYPE_SEARCH:
 	/* FIXME */
 	break;
-    case YELP_URI_DOCUMENT_TYPE_UNKNOWN:
     case YELP_URI_DOCUMENT_TYPE_NOT_FOUND:
     case YELP_URI_DOCUMENT_TYPE_EXTERNAL:
     case YELP_URI_DOCUMENT_TYPE_ERROR:
diff --git a/libyelp/yelp-settings.h b/libyelp/yelp-settings.h
index 91a8e32..000f5f8 100644
--- a/libyelp/yelp-settings.h
+++ b/libyelp/yelp-settings.h
@@ -48,7 +48,7 @@ struct _YelpSettingsClass {
 };
 
 typedef enum {
-    YELP_SETTINGS_COLOR_BASE = 0,
+    YELP_SETTINGS_COLOR_BASE,
     YELP_SETTINGS_COLOR_TEXT,
     YELP_SETTINGS_COLOR_TEXT_LIGHT,
     YELP_SETTINGS_COLOR_LINK,
@@ -65,13 +65,13 @@ typedef enum {
 } YelpSettingsColor;
 
 typedef enum {
-    YELP_SETTINGS_FONT_VARIABLE = 0,
+    YELP_SETTINGS_FONT_VARIABLE,
     YELP_SETTINGS_FONT_FIXED,
     YELP_SETTINGS_NUM_FONTS
 } YelpSettingsFont;
 
 typedef enum {
-    YELP_SETTINGS_ICON_BUG = 0,
+    YELP_SETTINGS_ICON_BUG,
     YELP_SETTINGS_ICON_CAUTION,
     YELP_SETTINGS_ICON_IMPORTANT,
     YELP_SETTINGS_ICON_NOTE,
diff --git a/libyelp/yelp-uri.c b/libyelp/yelp-uri.c
index 5b6de6e..b8d157a 100644
--- a/libyelp/yelp-uri.c
+++ b/libyelp/yelp-uri.c
@@ -37,24 +37,22 @@ 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,
-                                             const gchar    *arg);
-static void           resolve_file_path     (YelpUri        *ret,
-                                             YelpUri        *base,
-                                             const gchar    *arg);
-static void           resolve_data_dirs     (YelpUri        *ret,
+static void           resolve_async         (YelpUri        *uri);
+static gboolean       resolve_final         (YelpUri        *uri);
+
+static void           resolve_file_uri      (YelpUri        *uri);
+static void           resolve_file_path     (YelpUri        *uri);
+static void           resolve_data_dirs     (YelpUri        *uri,
                                              const gchar   **subdirs,
                                              const gchar    *docid,
                                              const gchar    *pageid);
-static void           resolve_ghelp_uri     (YelpUri        *ret,
-                                             const gchar    *arg);
-static void           resolve_man_uri       (YelpUri        *ret,
-                                             const gchar    *arg);
-static void           resolve_info_uri      (YelpUri        *ret,
-                                             const gchar    *arg);
-static void           resolve_page_and_frag (YelpUri        *ret,
+static void           resolve_ghelp_uri     (YelpUri        *uri);
+static void           resolve_man_uri       (YelpUri        *uri);
+static void           resolve_info_uri      (YelpUri        *uri);
+static void           resolve_page_and_frag (YelpUri        *uri,
                                              const gchar    *arg);
-static void           resolve_common        (YelpUri        *ret);
+static void           resolve_common        (YelpUri        *uri);
+
 static gboolean       is_man_path           (const gchar    *uri,
                                              const gchar    *encoding);
 
@@ -63,13 +61,27 @@ G_DEFINE_TYPE (YelpUri, yelp_uri, G_TYPE_OBJECT);
 
 typedef struct _YelpUriPrivate YelpUriPrivate;
 struct _YelpUriPrivate {
+    GThread              *resolver;
+
     YelpUriDocumentType   doctype;
+    YelpUriDocumentType   tmptype;
+
     GFile                *gfile;
     gchar               **search_path;
     gchar                *page_id;
     gchar                *frag_id;
+
+    /* Unresolved */
+    YelpUri              *res_base;
+    gchar                *res_arg;
 };
 
+enum {
+    RESOLVED,
+    LAST_SIGNAL
+};
+static guint uri_signals[LAST_SIGNAL] = {0,};
+
 /******************************************************************************/
 
 static const gchar *mancats[] = {
@@ -96,6 +108,14 @@ yelp_uri_class_init (YelpUriClass *klass)
     object_class->dispose  = yelp_uri_dispose;
     object_class->finalize = yelp_uri_finalize;
 
+    uri_signals[RESOLVED] =
+        g_signal_new ("resolved",
+                      G_OBJECT_CLASS_TYPE (klass),
+                      G_SIGNAL_RUN_LAST,
+                      0, NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
+
     g_type_class_add_private (klass, sizeof (YelpUriPrivate));
 }
 
@@ -115,6 +135,11 @@ yelp_uri_dispose (GObject *object)
         priv->gfile = NULL;
     }
 
+    if (priv->res_base) {
+        g_object_unref (priv->res_base);
+        priv->res_base = NULL;
+    }
+
     G_OBJECT_CLASS (yelp_uri_parent_class)->dispose (object);
 }
 
@@ -126,6 +151,7 @@ yelp_uri_finalize (GObject *object)
     g_strfreev (priv->search_path);
     g_free (priv->page_id);
     g_free (priv->frag_id);
+    g_free (priv->res_arg);
 
     G_OBJECT_CLASS (yelp_uri_parent_class)->finalize (object);
 }
@@ -133,11 +159,104 @@ yelp_uri_finalize (GObject *object)
 /******************************************************************************/
 
 YelpUri *
-yelp_uri_resolve (const gchar *arg)
+yelp_uri_new (const gchar *arg)
+{
+    return yelp_uri_new_relative (NULL, arg);
+}
+
+YelpUri *
+yelp_uri_new_relative (YelpUri *base, const gchar *arg)
+{
+    YelpUri *uri;
+    YelpUriPrivate *priv;
+
+    uri = (YelpUri *) g_object_new (YELP_TYPE_URI, NULL);
+
+    priv = GET_PRIV (uri);
+    priv->doctype = YELP_URI_DOCUMENT_TYPE_UNRESOLVED;
+    if (base)
+        priv->res_base = g_object_ref (base);
+    priv->res_arg = g_strdup (arg);
+
+    return uri;
+}
+
+/******************************************************************************/
+
+void
+yelp_uri_resolve (YelpUri *uri)
+{
+    YelpUriPrivate *priv = GET_PRIV (uri);
+    if (priv->resolver == NULL)
+        priv->resolver = g_thread_create ((GThreadFunc) resolve_async,
+                                          uri, FALSE, NULL);
+}
+
+/* We want code to be able to do something like this:
+ *
+ *   if (yelp_uri_get_document_type (uri) != YELP_URI_DOCUMENT_TYPE_UNRESOLVED) {
+ *     g_signal_connect (uri, "resolve", callback, data);
+ *     yelp_uri_resolve (uri);
+ *   }
+ *
+ * Resolving happens in a separate thread, though, so if that thread can change
+ * the document type, we have a race condition.  So here's the rules we play by:
+ *
+ * 1) None of the getters except the document type getter can return real data
+ *    while the URI is unresolved.  They all do a resolved check first, and
+ *    return NULL if the URI is not resolved.
+ *
+ * 2) The threaded resolver functions can modify anything but the document
+ *    type.  They are the only things that are allowed to modify that data.
+ *
+ * 3) The resolver thread is not allowed to modify the document type.  When
+ *    it's done, it queues an async function to set the document type and
+ *    emit "resolved" in the main thread.
+ *
+ * 4) Once a URI is resolved, it is immutable.
+ */
+static void
+resolve_async (YelpUri *uri)
+{
+    YelpUriPrivate *priv = GET_PRIV (uri);
+
+    if (g_str_has_prefix (priv->res_arg, "ghelp:")
+        || g_str_has_prefix (priv->res_arg, "gnome-help:")) {
+        resolve_ghelp_uri (uri);
+    }
+    else if (g_str_has_prefix (priv->res_arg, "file:")) {
+        resolve_file_uri (uri);
+    }
+    else if (g_str_has_prefix (priv->res_arg, "man:")) {
+        resolve_man_uri (uri);
+    }
+    else if (g_str_has_prefix (priv->res_arg, "info:")) {
+        resolve_info_uri (uri);
+    }
+    else {
+        resolve_file_path (uri);
+    }
+
+    g_idle_add ((GSourceFunc) resolve_final, uri);
+}
+
+static gboolean
+resolve_final (YelpUri *uri)
 {
-    return yelp_uri_resolve_relative (NULL, arg);
+    YelpUriPrivate *priv = GET_PRIV (uri);
+
+    priv->resolver = NULL;
+
+    if (priv->tmptype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
+        priv->doctype = priv->tmptype;
+    else
+        priv->doctype = YELP_URI_DOCUMENT_TYPE_ERROR;
+    
+    g_signal_emit (uri, uri_signals[RESOLVED], 0);
+    return FALSE;
 }
 
+/*
 YelpUri *
 yelp_uri_resolve_relative (YelpUri *base, const gchar *arg)
 {
@@ -148,18 +267,6 @@ yelp_uri_resolve_relative (YelpUri *base, const gchar *arg)
     priv = GET_PRIV (ret);
     priv->doctype = YELP_URI_DOCUMENT_TYPE_UNKNOWN;
 
-    if (g_str_has_prefix (arg, "ghelp:") || g_str_has_prefix (arg, "gnome-help:")) {
-        resolve_ghelp_uri (ret, arg);
-    }
-    else if (g_str_has_prefix (arg, "file:")) {
-        resolve_file_uri (ret, arg);
-    }
-    else if (g_str_has_prefix (arg, "man:")) {
-        resolve_man_uri (ret, arg);
-    }
-    else if (g_str_has_prefix (arg, "info:")) {
-        resolve_info_uri (ret, arg);
-    }
     else if (strchr (arg, ':')) {
         priv->doctype = YELP_URI_DOCUMENT_TYPE_EXTERNAL;
         priv->gfile = g_file_new_for_uri (arg);
@@ -171,6 +278,16 @@ yelp_uri_resolve_relative (YelpUri *base, const gchar *arg)
 
     return ret;
 }
+*/
+
+/******************************************************************************/
+
+gboolean
+yelp_uri_is_resolved (YelpUri *uri)
+{
+    YelpUriPrivate *priv = GET_PRIV (uri);
+    return priv->doctype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED;
+}
 
 YelpUriDocumentType
 yelp_uri_get_document_type (YelpUri *uri)
@@ -183,6 +300,8 @@ gchar *
 yelp_uri_get_base_uri (YelpUri *uri)
 {
     YelpUriPrivate *priv = GET_PRIV (uri);
+    if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
+        return NULL;
     return priv->gfile ? g_file_get_uri (priv->gfile) : NULL;
 }
 
@@ -190,6 +309,8 @@ gchar **
 yelp_uri_get_search_path (YelpUri *uri)
 {
     YelpUriPrivate *priv = GET_PRIV (uri);
+    if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
+        return NULL;
     return g_strdupv (priv->search_path);
 }
 
@@ -197,6 +318,8 @@ gchar *
 yelp_uri_get_page_id (YelpUri *uri)
 {
     YelpUriPrivate *priv = GET_PRIV (uri);
+    if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
+        return NULL;
     return g_strdup (priv->page_id);
 }
 
@@ -204,50 +327,55 @@ gchar *
 yelp_uri_get_frag_id (YelpUri *uri)
 {
     YelpUriPrivate *priv = GET_PRIV (uri);
+    if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
+        return NULL;
     return g_strdup (priv->frag_id);
 }
 
 /******************************************************************************/
 
 static void
-resolve_file_uri (YelpUri *ret, const gchar *arg)
+resolve_file_uri (YelpUri *uri)
 {
-    YelpUriPrivate *priv = GET_PRIV (ret);
-    gchar *uri;
-    const gchar *hash = strchr (arg, '#');
+    YelpUriPrivate *priv = GET_PRIV (uri);
+    gchar *uristr;
+    const gchar *hash = strchr (priv->res_arg, '#');
 
     if (hash)
-        uri = g_strndup (arg, hash - arg);
+        uristr = g_strndup (priv->res_arg, hash - priv->res_arg);
     else
-        uri = (gchar *) arg;
+        uristr = priv->res_arg;
 
-    priv->gfile = g_file_new_for_uri (uri);
+    priv->gfile = g_file_new_for_uri (uristr);
 
     if (hash) {
-        resolve_page_and_frag (ret, hash + 1);
-        g_free (uri);
+        resolve_page_and_frag (uri, hash + 1);
+        g_free (uristr);
     }
 
-    resolve_common (ret);
+    resolve_common (uri);
 }
 
 static void
-resolve_file_path (YelpUri *ret, YelpUri *base, const gchar *arg)
+resolve_file_path (YelpUri *uri)
 {
-    YelpUriPrivate *base_priv = GET_PRIV (base);
-    YelpUriPrivate *priv = GET_PRIV (ret);
+    YelpUriPrivate *base_priv;
+    YelpUriPrivate *priv = GET_PRIV (uri);
     gchar *path;
-    const gchar *hash = strchr (arg, '#');
+    const gchar *hash = strchr (priv->res_arg, '#');
+
+    if (priv->res_base)
+        base_priv = GET_PRIV (priv->res_base);
 
     if (hash)
-        path = g_strndup (arg, hash - arg);
+        path = g_strndup (priv->res_arg, hash - priv->res_arg);
     else
-        path = (gchar *) arg;
+        path = priv->res_arg;
 
-    if (arg[0] == '/') {
+    if (priv->res_arg[0] == '/') {
         priv->gfile = g_file_new_for_path (path);
     }
-    else if (base && base_priv->gfile) {
+    else if (base_priv->gfile) {
         priv->gfile = g_file_resolve_relative_path (base_priv->gfile, path);
     }
     else {
@@ -261,11 +389,11 @@ resolve_file_path (YelpUri *ret, YelpUri *base, const gchar *arg)
     }
 
     if (hash) {
-        resolve_page_and_frag (ret, hash + 1);
+        resolve_page_and_frag (uri, hash + 1);
         g_free (path);
     }
 
-    resolve_common (ret);
+    resolve_common (uri);
 }
 
 static void
@@ -281,7 +409,6 @@ resolve_data_dirs (YelpUri      *ret,
     gchar **searchpath = NULL;
     gint searchi, searchmax;
     gint datadir_i, subdir_i, lang_i;
-    YelpUriDocumentType type = YELP_URI_DOCUMENT_TYPE_UNKNOWN;
 
     searchi = 0;
     searchmax = 10;
@@ -304,13 +431,13 @@ resolve_data_dirs (YelpUri      *ret,
                 searchpath[searchi] = helpdir;
                 searchpath[++searchi] = NULL;
 
-                if (type != YELP_URI_DOCUMENT_TYPE_UNKNOWN)
+                if (priv->tmptype != YELP_URI_DOCUMENT_TYPE_UNRESOLVED)
                     /* We've already found it.  We're just adding to the search path now. */
                     continue;
 
                 filename = g_strdup_printf ("%s/index.page", helpdir);
                 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
-                    type = YELP_URI_DOCUMENT_TYPE_MALLARD;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
                     g_free (filename);
                     filename = g_strdup (helpdir);
                     continue;
@@ -319,14 +446,14 @@ resolve_data_dirs (YelpUri      *ret,
 
                 filename = g_strdup_printf ("%s/%s.xml", helpdir, pageid);
                 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
-                    type = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
                     continue;
                 }
                 g_free (filename);
 
                 filename = g_strdup_printf ("%s/%s.html", helpdir, pageid);
                 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
-                    type = YELP_URI_DOCUMENT_TYPE_HTML;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_HTML;
                     continue;
                 }
                 g_free (filename);
@@ -334,31 +461,30 @@ resolve_data_dirs (YelpUri      *ret,
         } /* end for subdirs */
     } /* end for datadirs */
 
-    if (type == YELP_URI_DOCUMENT_TYPE_UNKNOWN) {
+    if (priv->tmptype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED) {
         g_strfreev (searchpath);
-        priv->doctype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
+        priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
     }
     else {
-        priv->doctype = type;
         priv->gfile = g_file_new_for_path (filename);
         priv->search_path = searchpath;
     }
 }
 
 static void
-resolve_ghelp_uri (YelpUri *ret, const gchar *arg)
+resolve_ghelp_uri (YelpUri *uri)
 {
     /* ghelp:/path/to/file
      * ghelp:document
      */
-    YelpUriPrivate *priv = GET_PRIV (ret);
+    YelpUriPrivate *priv = GET_PRIV (uri);
     const gchar const *helpdirs[3] = {"help", "gnome/help", NULL};
     gchar *docid = NULL;
     gchar *colon, *hash, *slash, *pageid; /* do not free */
 
-    colon = strchr (arg, ':');
+    colon = strchr (priv->res_arg, ':');
     if (!colon) {
-        priv->doctype = YELP_URI_DOCUMENT_TYPE_ERROR;
+        priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
         return;
     }
 
@@ -366,8 +492,9 @@ resolve_ghelp_uri (YelpUri *ret, const gchar *arg)
     if (*colon == '/') {
         gchar *newuri;
         newuri = g_strdup_printf ("file:%s", colon);
-        resolve_file_uri (ret, newuri);
-        g_free (newuri);
+        g_free (priv->res_arg);
+        priv->res_arg = newuri;
+        resolve_file_uri (uri);
         return;
     }
 
@@ -375,7 +502,7 @@ resolve_ghelp_uri (YelpUri *ret, const gchar *arg)
     if (!hash)
         hash = strchr (colon, '#');
     if (hash) {
-        resolve_page_and_frag (ret, hash + 1);
+        resolve_page_and_frag (uri, hash + 1);
         docid = g_strndup (colon, hash - colon);
     }
     else {
@@ -391,14 +518,14 @@ resolve_ghelp_uri (YelpUri *ret, const gchar *arg)
         pageid = docid;
     }
 
-    resolve_data_dirs (ret, helpdirs, docid, pageid);
+    resolve_data_dirs (uri, helpdirs, docid, pageid);
 
     /* Specifying pages and anchors for Mallard documents with ghelp URIs is sort
-       hacked on.  This is a touch inconsistent, but it maintains compatibility
+       of hacked on.  This is a touch inconsistent, but it maintains compatibility
        with the way things worked in 2.28, and ghelp URIs should be entering
        compatibility-only mode.
      */
-    if (priv->doctype == YELP_URI_DOCUMENT_TYPE_MALLARD && pageid != docid) {
+    if (priv->tmptype == YELP_URI_DOCUMENT_TYPE_MALLARD && pageid != docid) {
         if (priv->page_id)
             g_free (priv->page_id);
         priv->page_id = g_strdup (pageid);
@@ -408,9 +535,9 @@ resolve_ghelp_uri (YelpUri *ret, const gchar *arg)
 }
 
 static void
-resolve_man_uri (YelpUri *ret, const gchar *arg)
+resolve_man_uri (YelpUri *uri)
 {
-    YelpUriPrivate *priv = GET_PRIV (ret);
+    YelpUriPrivate *priv = GET_PRIV (uri);
     /* man:/path/to/file
      * man:name(section)
      * man:name.section
@@ -429,12 +556,13 @@ resolve_man_uri (YelpUri *ret, const gchar *arg)
     gchar *rbrace = NULL;
     gint i, j, k;
 
-    if (g_str_has_prefix (arg, "man:/")) {
+    if (g_str_has_prefix (priv->res_arg, "man:/")) {
         gchar *newuri;
-        priv->doctype = YELP_URI_DOCUMENT_TYPE_MAN;
-        newuri = g_strdup_printf ("file:%s", arg + 4);
-        resolve_file_uri (ret, newuri);
-        g_free (newuri);
+        priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
+        newuri = g_strdup_printf ("file:%s", priv->res_arg + 4);
+        g_free (priv->res_arg);
+        priv->res_arg = newuri;
+        resolve_file_uri (uri);
         return;
     }
 
@@ -446,11 +574,11 @@ resolve_man_uri (YelpUri *ret, const gchar *arg)
         manpath = g_strsplit (env, ":", 0);
     }
 
-    colon = strchr (arg, ':');
+    colon = strchr (priv->res_arg, ':');
     if (colon)
         colon++;
     else
-        colon = (gchar *) arg;
+        colon = (gchar *) priv->res_arg;
 
     hash = strchr (colon, '#');
     if (hash)
@@ -541,30 +669,30 @@ resolve_man_uri (YelpUri *ret, const gchar *arg)
     }
 
     if (fullpath) {
-        priv->doctype = YELP_URI_DOCUMENT_TYPE_MAN;
+        priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
         priv->gfile = g_file_new_for_path (fullpath);
-        resolve_common (ret);
+        resolve_common (uri);
         g_free (fullpath);
     } else {
-        priv->doctype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
+        priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
     }
 
     if (hash)
-        resolve_page_and_frag (ret, hash + 1);
+        resolve_page_and_frag (uri, hash + 1);
 
     g_free (newarg);
 }
 
 static void
-resolve_info_uri (YelpUri *ret, const gchar *arg)
+resolve_info_uri (YelpUri *uri)
 {
     /* FIXME */
 }
 
 static void
-resolve_page_and_frag (YelpUri *ret, const gchar *arg)
+resolve_page_and_frag (YelpUri *uri, const gchar *arg)
 {
-    YelpUriPrivate *priv = GET_PRIV (ret);
+    YelpUriPrivate *priv = GET_PRIV (uri);
     gchar *hash;
 
     if (!arg || arg[0] == '\0')
@@ -582,9 +710,9 @@ resolve_page_and_frag (YelpUri *ret, const gchar *arg)
 }
 
 static void
-resolve_common (YelpUri *ret)
+resolve_common (YelpUri *uri)
 {
-    YelpUriPrivate *priv = GET_PRIV (ret);
+    YelpUriPrivate *priv = GET_PRIV (uri);
     GFileInfo *info;
     GError *error = NULL;
 
@@ -595,9 +723,9 @@ resolve_common (YelpUri *ret)
                               NULL, &error);
     if (error) {
         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
-            priv->doctype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
+            priv->tmptype = YELP_URI_DOCUMENT_TYPE_NOT_FOUND;
         else
-            priv->doctype = YELP_URI_DOCUMENT_TYPE_ERROR;
+            priv->tmptype = YELP_URI_DOCUMENT_TYPE_ERROR;
         g_error_free (error);
         return;
     }
@@ -615,10 +743,10 @@ resolve_common (YelpUri *ret)
         }
     }
 
-    if (priv->doctype == YELP_URI_DOCUMENT_TYPE_UNKNOWN) {
+    if (priv->tmptype == YELP_URI_DOCUMENT_TYPE_UNRESOLVED) {
         if (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) ==
             G_FILE_TYPE_DIRECTORY) {
-            priv->doctype = YELP_URI_DOCUMENT_TYPE_MALLARD;
+            priv->tmptype = YELP_URI_DOCUMENT_TYPE_MALLARD;
         }
         else {
             gchar *basename;
@@ -628,48 +756,48 @@ resolve_common (YelpUri *ret)
             if (g_str_equal (mime_type, "text/xml") ||
                 g_str_equal (mime_type, "application/docbook+xml") ||
                 g_str_equal (mime_type, "application/xml")) {
-                priv->doctype = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
+                priv->tmptype = YELP_URI_DOCUMENT_TYPE_DOCBOOK;
             }
             else if (g_str_equal (mime_type, "text/html")) {
-                priv->doctype = YELP_URI_DOCUMENT_TYPE_HTML;
+                priv->tmptype = YELP_URI_DOCUMENT_TYPE_HTML;
             }
             else if (g_str_equal (mime_type, "application/xhtml+xml")) {
-                priv->doctype = YELP_URI_DOCUMENT_TYPE_XHTML;
+                priv->tmptype = YELP_URI_DOCUMENT_TYPE_XHTML;
             }
             else if (g_str_equal (mime_type, "application/x-gzip")) {
                 if (g_str_has_suffix (basename, ".info.gz"))
-                    priv->doctype = YELP_URI_DOCUMENT_TYPE_INFO;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
                 else if (is_man_path (basename, "gz"))
-                    priv->doctype = YELP_URI_DOCUMENT_TYPE_MAN;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
             }
             else if (g_str_equal (mime_type, "application/x-bzip")) {
                 if (g_str_has_suffix (basename, ".info.bz2"))
-                    priv->doctype = YELP_URI_DOCUMENT_TYPE_INFO;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
                 else if (is_man_path (basename, "bz2"))
-                    priv->doctype = YELP_URI_DOCUMENT_TYPE_MAN;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
             }
             else if (g_str_equal (mime_type, "application/x-lzma")) {
                 if (g_str_has_suffix (basename, ".info.lzma"))
-                    priv->doctype = YELP_URI_DOCUMENT_TYPE_INFO;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
                 else if (is_man_path (basename, "lzma"))
-                    priv->doctype = YELP_URI_DOCUMENT_TYPE_MAN;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
             }
             else if (g_str_equal (mime_type, "application/octet-stream")) {
                 if (g_str_has_suffix (basename, ".info"))
-                    priv->doctype = YELP_URI_DOCUMENT_TYPE_INFO;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
                 else if (is_man_path (basename, NULL))
-                    priv->doctype = YELP_URI_DOCUMENT_TYPE_MAN;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
             }
             else if (g_str_equal (mime_type, "text/plain")) {
                 if (g_str_has_suffix (basename, ".info"))
-                    priv->doctype = YELP_URI_DOCUMENT_TYPE_INFO;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_INFO;
                 else if (is_man_path (basename, NULL))
-                    priv->doctype = YELP_URI_DOCUMENT_TYPE_MAN;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_MAN;
                 else
-                    priv->doctype = YELP_URI_DOCUMENT_TYPE_TEXT;
+                    priv->tmptype = YELP_URI_DOCUMENT_TYPE_TEXT;
             }
             else {
-                priv->doctype = YELP_URI_DOCUMENT_TYPE_EXTERNAL;
+                priv->tmptype = YELP_URI_DOCUMENT_TYPE_EXTERNAL;
             }
         }
     }
diff --git a/libyelp/yelp-uri.h b/libyelp/yelp-uri.h
index 2a6f144..b44d2c8 100644
--- a/libyelp/yelp-uri.h
+++ b/libyelp/yelp-uri.h
@@ -38,7 +38,7 @@ typedef struct _YelpUri      YelpUri;
 typedef struct _YelpUriClass YelpUriClass;
 
 typedef enum {
-    YELP_URI_DOCUMENT_TYPE_UNKNOWN = 0,
+    YELP_URI_DOCUMENT_TYPE_UNRESOLVED,
     YELP_URI_DOCUMENT_TYPE_DOCBOOK,
     YELP_URI_DOCUMENT_TYPE_MALLARD,
     YELP_URI_DOCUMENT_TYPE_MAN,
@@ -62,17 +62,20 @@ struct _YelpUriClass {
 };
 
 
-GType             yelp_uri_get_type     (void);
+GType                yelp_uri_get_type           (void);
 
-YelpUri *         yelp_uri_resolve           (const gchar *arg);
-YelpUri *         yelp_uri_resolve_relative  (YelpUri     *base,
-                                              const gchar *arg);
+YelpUri *            yelp_uri_new                (const gchar  *arg);
+YelpUri *            yelp_uri_new_relative       (YelpUri      *base,
+                                                  const gchar  *arg);
 
-YelpUriDocumentType  yelp_uri_get_document_type  (YelpUri   *uri);
-gchar *              yelp_uri_get_base_uri       (YelpUri   *uri);
-gchar **             yelp_uri_get_search_path    (YelpUri   *uri);
-gchar *              yelp_uri_get_page_id        (YelpUri   *uri);
-gchar *              yelp_uri_get_frag_id        (YelpUri   *uri);
+void                 yelp_uri_resolve            (YelpUri      *uri);
+
+gboolean             yelp_uri_is_resolved        (YelpUri      *uri);
+YelpUriDocumentType  yelp_uri_get_document_type  (YelpUri      *uri);
+gchar *              yelp_uri_get_base_uri       (YelpUri      *uri);
+gchar **             yelp_uri_get_search_path    (YelpUri      *uri);
+gchar *              yelp_uri_get_page_id        (YelpUri      *uri);
+gchar *              yelp_uri_get_frag_id        (YelpUri      *uri);
 
 G_END_DECLS
 
diff --git a/libyelp/yelp-view.c b/libyelp/yelp-view.c
index f77c74d..36f4ed5 100644
--- a/libyelp/yelp-view.c
+++ b/libyelp/yelp-view.c
@@ -46,9 +46,13 @@ static void        yelp_view_set_property         (GObject            *object,
                                                    const GValue       *value,
                                                    GParamSpec         *pspec);
 
+static void        view_clear_load                (YelpView           *view);
+static void        view_load_page                 (YelpView           *view);
 static void        view_show_error_page           (YelpView           *view,
                                                    GError             *error);
 
+static void        uri_resolved                   (YelpUri            *uri,
+                                                   YelpView           *view);
 static void        document_callback              (YelpDocument       *document,
                                                    YelpDocumentSignal  signal,
                                                    YelpView           *view,
@@ -71,6 +75,7 @@ G_DEFINE_TYPE (YelpView, yelp_view, WEBKIT_TYPE_WEB_VIEW);
 typedef struct _YelpViewPrivate YelpViewPrivate;
 struct _YelpViewPrivate {
     YelpUri       *uri;
+    gulong         uri_resolved;
     YelpDocument  *document;
     GCancellable  *cancellable;
 
@@ -203,7 +208,7 @@ void
 yelp_view_load (YelpView    *view,
                 const gchar *uri)
 {
-    YelpUri *yuri = yelp_uri_resolve (uri);
+    YelpUri *yuri = yelp_uri_new (uri);
     yelp_view_load_uri (view, yuri);
     g_object_unref (yuri);
 }
@@ -212,12 +217,21 @@ void
 yelp_view_load_uri (YelpView *view,
                     YelpUri  *uri)
 {
-    YelpDocument *document = yelp_document_get_for_uri (uri);
+    YelpViewPrivate *priv = GET_PRIV (view);
 
-    yelp_view_load_document (view, uri, document);
+    view_clear_load (view);
+    g_object_set (view, "state", YELP_VIEW_STATE_LOADING, NULL);
 
-    if (document)
-        g_object_unref (document);
+    priv->uri = g_object_ref (uri);
+    if (!yelp_uri_is_resolved (uri)) {
+        priv->uri_resolved = g_signal_connect (uri, "resolved",
+                                               G_CALLBACK (uri_resolved),
+                                               view);
+        yelp_uri_resolve (uri);
+    }
+    else {
+        uri_resolved (uri, view);
+    }
 }
 
 void
@@ -226,28 +240,74 @@ yelp_view_load_document (YelpView     *view,
                          YelpDocument *document)
 {
     YelpViewPrivate *priv = GET_PRIV (view);
-    gchar *page_id;
 
+    g_return_if_fail (yelp_uri_is_resolved (uri));
+
+    view_clear_load (view);
     g_object_set (view, "state", YELP_VIEW_STATE_LOADING, NULL);
 
-    if (!document) {
+    priv->uri = g_object_ref (uri);
+    g_object_ref (document);
+    if (priv->document)
+        g_object_unref (document);
+    priv->document = document;
+
+    view_load_page (view);
+}
+
+/******************************************************************************/
+
+static void
+view_clear_load (YelpView *view)
+{
+    YelpViewPrivate *priv = GET_PRIV (view);
+
+    if (priv->uri) {
+        if (priv->uri_resolved != 0) {
+            g_signal_handler_disconnect (priv->uri, priv->uri_resolved);
+            priv->uri_resolved = 0;
+        }
+        g_object_unref (priv->uri);
+        priv->uri = NULL;
+    }
+
+    if (priv->cancellable) {
+        g_cancellable_cancel (priv->cancellable);
+        priv->cancellable = NULL;
+    }
+}
+
+static void
+view_load_page (YelpView *view)
+{
+    YelpViewPrivate *priv = GET_PRIV (view);
+    gchar *page_id;
+
+    g_return_if_fail (priv->cancellable == NULL);
+
+    if (priv->document == NULL) {
         GError *error;
         gchar *base_uri;
-        base_uri = yelp_uri_get_base_uri (uri);
+        /* FIXME: and if priv->uri is NULL? */
+        base_uri = yelp_uri_get_base_uri (priv->uri);
         /* FIXME: CANT_READ isn't right */
-        error = g_error_new (YELP_ERROR, YELP_ERROR_CANT_READ,
-                             _("Could not load a document for â??%sâ??"),
-                             base_uri);
-        g_free (base_uri);
+        if (base_uri) {
+            error = g_error_new (YELP_ERROR, YELP_ERROR_CANT_READ,
+                                 _("Could not load a document for â??%sâ??"),
+                                 base_uri);
+            g_free (base_uri);
+        }
+        else {
+            error = g_error_new (YELP_ERROR, YELP_ERROR_CANT_READ,
+                                 _("Could not load a document"));
+        }
         view_show_error_page (view, error);
         return;
     }
 
-    page_id = yelp_uri_get_page_id (uri);
-    priv->uri = g_object_ref (uri);
+    page_id = yelp_uri_get_page_id (priv->uri);
     priv->cancellable = g_cancellable_new ();
-    priv->document = g_object_ref (document);
-    yelp_document_request_page (document,
+    yelp_document_request_page (priv->document,
                                 page_id,
                                 priv->cancellable,
                                 (YelpDocumentCallback) document_callback,
@@ -306,6 +366,23 @@ view_show_error_page (YelpView *view,
     g_free (page);
 }
 
+/******************************************************************************/
+
+static void
+uri_resolved (YelpUri  *uri,
+              YelpView *view)
+{
+    YelpViewPrivate *priv = GET_PRIV (view);
+    YelpDocument *document = yelp_document_get_for_uri (uri);
+
+    if (priv->document)
+        g_object_unref (priv->document);
+
+    priv->document = document;
+
+    view_load_page (view);
+}
+
 static void
 document_callback (YelpDocument       *document,
                    YelpDocumentSignal  signal,
diff --git a/tests/test-uri.c b/tests/test-uri.c
index 73f44df..96f9d5e 100644
--- a/tests/test-uri.c
+++ b/tests/test-uri.c
@@ -25,9 +25,12 @@
 #include <string.h>
 
 #include <gio/gio.h>
+#include <gio/gunixoutputstream.h>
 
 #include "yelp-uri.h"
 
+GMainLoop *loop;
+
 static void 
 print_uri (YelpUri *uri, GOutputStream *stream)
 {
@@ -70,8 +73,8 @@ print_uri (YelpUri *uri, GOutputStream *stream)
     case YELP_URI_DOCUMENT_TYPE_ERROR:
         type = "ERROR";
         break;
-    case YELP_URI_DOCUMENT_TYPE_UNKNOWN:
-        type = "UNKNOWN";
+    case YELP_URI_DOCUMENT_TYPE_UNRESOLVED:
+        type = "UNRESOLVED";
         break;
     }
 
@@ -135,7 +138,13 @@ static void run_test (gconstpointer data)
                                        NULL, NULL));
     newline = strchr (contents, '\n');
     curi = g_strndup (contents, newline - contents);
-    uri = yelp_uri_resolve (curi);
+    uri = yelp_uri_new (curi);
+    yelp_uri_resolve (uri);
+
+    while (!yelp_uri_is_resolved (uri))
+        while (g_main_context_pending (NULL))
+             g_main_context_iteration (NULL, FALSE);
+
     outstream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
     print_uri (uri, outstream);
     out = (gchar *) g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (outstream));
@@ -174,6 +183,15 @@ run_all_tests (int argc, char **argv)
     return g_test_run ();
 }
 
+static void
+uri_resolved (YelpUri *uri)
+{
+    GOutputStream *stream = g_unix_output_stream_new (1, FALSE);
+    print_uri (uri, stream);
+    g_object_unref (uri);
+    g_main_loop_quit (loop);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -181,28 +199,31 @@ main (int argc, char **argv)
     YelpUri *uri = NULL;
 
     g_type_init ();
+    g_thread_init (NULL);
     g_log_set_always_fatal (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
         
-    g_test_init (&argc, &argv);
     if (argc < 2) {
+        g_test_init (&argc, &argv, NULL);
         return run_all_tests (argc, argv);
     }
     else {
         if (argc > 2) {
-            parent = yelp_uri_resolve (argv[1]);
-            uri = yelp_uri_resolve_relative (parent, argv[2]);
+            parent = yelp_uri_new (argv[1]);
+            uri = yelp_uri_new_relative (parent, argv[2]);
         } else {
-            uri = yelp_uri_resolve (argv[1]);
+            uri = yelp_uri_new (argv[1]);
         }
         if (uri) {
-            GOutputStream *stream = g_unix_output_stream_new (1, FALSE);
-            print_uri (uri, stream);
-            g_object_unref (uri);
+            g_signal_connect (uri, "resolved", G_CALLBACK (uri_resolved), NULL);
+            yelp_uri_resolve (uri);
         }
         if (parent) {
             g_object_unref (parent);
         }
     }
 
+    loop = g_main_loop_new (NULL, FALSE);
+    g_main_loop_run (loop);
+
     return 0;
 }



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