diff options
author | Shaun McCance <shaunm@gnome.org> | 2009-10-06 23:10:53 -0500 |
---|---|---|
committer | Shaun McCance <shaunm@gnome.org> | 2009-10-06 23:10:53 -0500 |
commit | 0708e72ace823babfe0bc46c19221981baac70ab (patch) | |
tree | 741b2792b2044b284e2382cd099b71fcda18995d | |
parent | 88ad4966010f46623476f24a134d70dada4359d1 (diff) | |
download | yelp-0708e72ace823babfe0bc46c19221981baac70ab.tar.gz |
[yelp-uri] Resolve URIs asynchronously, no blocking IO
-rw-r--r-- | libyelp/yelp-document.c | 8 | ||||
-rw-r--r-- | libyelp/yelp-settings.h | 6 | ||||
-rw-r--r-- | libyelp/yelp-uri.c | 334 | ||||
-rw-r--r-- | libyelp/yelp-uri.h | 23 | ||||
-rw-r--r-- | libyelp/yelp-view.c | 109 | ||||
-rw-r--r-- | 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 1efcb906..d464d44d 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 91a8e329..000f5f88 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 5b6de6e3..b8d157a5 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 2a6f144f..b44d2c83 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 f77c74d4..36f4ed53 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 73f44dfd..96f9d5e2 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; } |