/** * Callback to determine if a node is a linking element whose target has been * visited. * * \param pw HTML document * \param node DOM node * \param match Pointer to location to receive result * \return CSS_OK. * * \post \a match will contain true if the node matches and false otherwise. */ css_error node_is_visited(void *pw, void *node, bool *match) { nscss_select_ctx *ctx = pw; nsurl *url; nserror error; const struct url_data *data; dom_exception exc; dom_node *n = node; dom_string *s = NULL; *match = false; exc = dom_node_get_node_name(n, &s); if ((exc != DOM_NO_ERR) || (s == NULL)) { return CSS_NOMEM; } if (!dom_string_caseless_lwc_isequal(s, corestring_lwc_a)) { /* Can't be visited; not ancher element */ dom_string_unref(s); return CSS_OK; } /* Finished with node name string */ dom_string_unref(s); s = NULL; exc = dom_element_get_attribute(n, corestring_dom_href, &s); if ((exc != DOM_NO_ERR) || (s == NULL)) { /* Can't be visited; not got a URL */ return CSS_OK; } /* Make href absolute */ /* TODO: this duplicates what we do for box->href * should we put the absolute URL on the dom node? */ error = nsurl_join(ctx->base_url, dom_string_data(s), &url); /* Finished with href string */ dom_string_unref(s); if (error != NSERROR_OK) { /* Couldn't make nsurl object */ return CSS_NOMEM; } data = urldb_get_url_data(url); /* Visited if in the db and has * non-zero visit count */ if (data != NULL && data->visits > 0) *match = true; nsurl_unref(url); return CSS_OK; }
END_TEST /** * check join asserts on NULL parameter */ START_TEST(nsurl_api_assert_join1_test) { const char *rel = "moo"; nsurl *res; nserror err; err = nsurl_join(NULL, rel, &res); ck_assert(err != NSERROR_OK); }
static bool save_complete_rewrite_url_value(save_complete_ctx *ctx, const char *value, size_t value_len) { nsurl *url; hlcache_handle *content; char *escaped; nserror error; utf8_convert_ret ret; error = nsurl_join(ctx->base, value, &url); if (error == NSERROR_NOMEM) return false; if (url != NULL) { content = save_complete_ctx_find_content(ctx, url); if (content != NULL) { /* found a match */ nsurl_unref(url); fprintf(ctx->fp, "\"%p\"", content); } else { /* no match found */ ret = utf8_to_html(nsurl_access(url), "UTF-8", nsurl_length(url), &escaped); nsurl_unref(url); if (ret != UTF8_CONVERT_OK) return false; fprintf(ctx->fp, "\"%s\"", escaped); free(escaped); } } else { ret = utf8_to_html(value, "UTF-8", value_len, &escaped); if (ret != UTF8_CONVERT_OK) return false; fprintf(ctx->fp, "\"%s\"", escaped); free(escaped); } return true; }
END_TEST /** * check join asserts on NULL parameter */ START_TEST(nsurl_api_assert_join2_test) { nsurl *url; nsurl *res; nserror err; err = nsurl_create(base_str, &url); ck_assert(err == NSERROR_OK); err = nsurl_join(url, NULL, &res); ck_assert(err != NSERROR_OK); nsurl_unref(url); }
/** * URL resolution callback for libcss * * \param pw Resolution context * \param base Base URI * \param rel Relative URL * \param abs Pointer to location to receive resolved URL * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if resolution failed. */ css_error nscss_resolve_url(void *pw, const char *base, lwc_string *rel, lwc_string **abs) { lwc_error lerror; nserror error; nsurl *nsbase; nsurl *nsabs; /* Create nsurl from base */ /* TODO: avoid this */ error = nsurl_create(base, &nsbase); if (error != NSERROR_OK) { return error == NSERROR_NOMEM ? CSS_NOMEM : CSS_INVALID; } /* Resolve URI */ error = nsurl_join(nsbase, lwc_string_data(rel), &nsabs); if (error != NSERROR_OK) { nsurl_unref(nsbase); return error == NSERROR_NOMEM ? CSS_NOMEM : CSS_INVALID; } nsurl_unref(nsbase); /* Intern it */ lerror = lwc_intern_string(nsurl_access(nsabs), nsurl_length(nsabs), abs); if (lerror != lwc_error_ok) { *abs = NULL; nsurl_unref(nsabs); return lerror == lwc_error_oom ? CSS_NOMEM : CSS_INVALID; } nsurl_unref(nsabs); return CSS_OK; }
/** * Test nsurl */ int main(void) { nsurl *base; nsurl *joined; char *string; size_t len; const char *url; const struct test_pairs *test; int passed = 0; int count = 0; /* Create base URL */ if (nsurl_create("http://a/b/c/d;p?q", &base) != NSERROR_OK) { assert(0 && "Failed to create base URL."); } if (nsurl_get(base, NSURL_WITH_FRAGMENT, &string, &len) != NSERROR_OK) { LOG(("Failed to get string")); } else { LOG(("Testing nsurl_join with base %s", string)); free(string); } for (test = join_tests; test->test != NULL; test++) { if (nsurl_join(base, test->test, &joined) != NSERROR_OK) { LOG(("Failed to join test URL.")); } else { if (nsurl_get(joined, NSURL_WITH_FRAGMENT, &string, &len) != NSERROR_OK) { LOG(("Failed to get string")); } else { if (strcmp(test->res, string) == 0) { LOG(("\tPASS: \"%s\"\t--> %s", test->test, string)); passed++; } else { LOG(("\tFAIL: \"%s\"\t--> %s", test->test, string)); LOG(("\t\tExpecting: %s", test->res)); } free(string); } nsurl_unref(joined); } count++; } nsurl_unref(base); /* Create tests */ LOG(("Testing nsurl_create")); for (test = create_tests; test->test != NULL; test++) { if (nsurl_create(test->test, &base) != NSERROR_OK) { LOG(("Failed to create URL:\n\t\t%s.", test->test)); } else { if (strcmp(nsurl_access(base), test->res) == 0) { LOG(("\tPASS: \"%s\"\t--> %s", test->test, nsurl_access(base))); passed++; } else { LOG(("\tFAIL: \"%s\"\t--> %s", test->test, nsurl_access(base))); LOG(("\t\tExpecting %s", test->res)); } nsurl_unref(base); } count++; } if (passed == count) { LOG(("Testing complete: SUCCESS")); } else { LOG(("Testing complete: FAILURE")); LOG(("Failed %d out of %d", count - passed, count)); } return 0; }
bool html_css_process_link(html_content *htmlc, dom_node *node) { dom_string *rel, *type_attr, *media, *href; struct html_stylesheet *stylesheets; nsurl *joined; dom_exception exc; nserror ns_error; hlcache_child_context child; /* rel=<space separated list, including 'stylesheet'> */ exc = dom_element_get_attribute(node, corestring_dom_rel, &rel); if (exc != DOM_NO_ERR || rel == NULL) return true; if (strcasestr(dom_string_data(rel), "stylesheet") == 0) { dom_string_unref(rel); return true; } else if (strcasestr(dom_string_data(rel), "alternate") != 0) { /* Ignore alternate stylesheets */ dom_string_unref(rel); return true; } dom_string_unref(rel); /* type='text/css' or not present */ exc = dom_element_get_attribute(node, corestring_dom_type, &type_attr); if (exc == DOM_NO_ERR && type_attr != NULL) { if (!dom_string_caseless_lwc_isequal(type_attr, corestring_lwc_text_css)) { dom_string_unref(type_attr); return true; } dom_string_unref(type_attr); } /* media contains 'screen' or 'all' or not present */ exc = dom_element_get_attribute(node, corestring_dom_media, &media); if (exc == DOM_NO_ERR && media != NULL) { if (strcasestr(dom_string_data(media), "screen") == NULL && strcasestr(dom_string_data(media), "all") == NULL) { dom_string_unref(media); return true; } dom_string_unref(media); } /* href='...' */ exc = dom_element_get_attribute(node, corestring_dom_href, &href); if (exc != DOM_NO_ERR || href == NULL) return true; /* TODO: only the first preferred stylesheets (ie. * those with a title attribute) should be loaded * (see HTML4 14.3) */ ns_error = nsurl_join(htmlc->base_url, dom_string_data(href), &joined); if (ns_error != NSERROR_OK) { dom_string_unref(href); goto no_memory; } dom_string_unref(href); LOG("linked stylesheet %i '%s'", htmlc->stylesheet_count, nsurl_access(joined)); /* extend stylesheets array to allow for new sheet */ stylesheets = realloc(htmlc->stylesheets, sizeof(struct html_stylesheet) * (htmlc->stylesheet_count + 1)); if (stylesheets == NULL) { nsurl_unref(joined); ns_error = NSERROR_NOMEM; goto no_memory; } htmlc->stylesheets = stylesheets; htmlc->stylesheets[htmlc->stylesheet_count].node = NULL; htmlc->stylesheets[htmlc->stylesheet_count].modified = false; /* start fetch */ child.charset = htmlc->encoding; child.quirks = htmlc->base.quirks; ns_error = hlcache_handle_retrieve(joined, 0, content_get_url(&htmlc->base), NULL, html_convert_css_callback, htmlc, &child, CONTENT_CSS, &htmlc->stylesheets[htmlc->stylesheet_count].sheet); nsurl_unref(joined); if (ns_error != NSERROR_OK) goto no_memory; htmlc->stylesheet_count++; htmlc->base.active++; LOG("%d fetches active", htmlc->base.active); return true; no_memory: content_broadcast_errorcode(&htmlc->base, ns_error); return false; }
/** * process a script with a src tag */ static dom_hubbub_error exec_src_script(html_content *c, dom_node *node, dom_string *mimetype, dom_string *src) { nserror ns_error; nsurl *joined; hlcache_child_context child; struct html_script *nscript; union content_msg_data msg_data; bool async; bool defer; enum html_script_type script_type; hlcache_handle_callback script_cb; dom_hubbub_error ret = DOM_HUBBUB_OK; dom_exception exc; /* returned by libdom functions */ /* src url */ ns_error = nsurl_join(c->base_url, dom_string_data(src), &joined); if (ns_error != NSERROR_OK) { msg_data.error = messages_get("NoMemory"); content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); return DOM_HUBBUB_NOMEM; } LOG(("script %i '%s'", c->scripts_count, nsurl_access(joined))); /* there are three ways to process the script tag at this point: * * Syncronously pause the parent parse and continue after * the script has downloaded and executed. (default) * Async Start the script downloading and execute it when it * becomes available. * Defered Start the script downloading and execute it when * the page has completed parsing, may be set along * with async where it is ignored. */ /* we interpret the presence of the async and defer attribute * as true and ignore its value, technically only the empty * value or the attribute name itself are valid. However * various browsers interpret this in various ways the most * compatible approach is to be liberal and accept any * value. Note setting the values to "false" still makes them true! */ exc = dom_element_has_attribute(node, corestring_dom_async, &async); if (exc != DOM_NO_ERR) { return DOM_HUBBUB_OK; /* dom error */ } if (async) { /* asyncronous script */ script_type = HTML_SCRIPT_ASYNC; script_cb = convert_script_async_cb; } else { exc = dom_element_has_attribute(node, corestring_dom_defer, &defer); if (exc != DOM_NO_ERR) { return DOM_HUBBUB_OK; /* dom error */ } if (defer) { /* defered script */ script_type = HTML_SCRIPT_DEFER; script_cb = convert_script_defer_cb; } else { /* syncronous script */ script_type = HTML_SCRIPT_SYNC; script_cb = convert_script_sync_cb; } } nscript = html_process_new_script(c, mimetype, script_type); if (nscript == NULL) { nsurl_unref(joined); msg_data.error = messages_get("NoMemory"); content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); return DOM_HUBBUB_NOMEM; } /* set up child fetch encoding and quirks */ child.charset = c->encoding; child.quirks = c->base.quirks; ns_error = hlcache_handle_retrieve(joined, 0, content_get_url(&c->base), NULL, script_cb, c, &child, CONTENT_SCRIPT, &nscript->data.handle); nsurl_unref(joined); if (ns_error != NSERROR_OK) { /* @todo Deal with fetch error better. currently assume * fetch never became active */ /* mark duff script fetch as already started */ nscript->already_started = true; LOG(("Fetch failed with error %d",ns_error)); } else { /* update base content active fetch count */ c->base.active++; LOG(("%d fetches active", c->base.active)); switch (script_type) { case HTML_SCRIPT_SYNC: ret = DOM_HUBBUB_HUBBUB_ERR | HUBBUB_PAUSED; case HTML_SCRIPT_ASYNC: break; case HTML_SCRIPT_DEFER: break; default: assert(0); } } return ret; }
static char *save_complete_rewrite_stylesheet_urls(save_complete_ctx *ctx, const char *source, unsigned long size, const nsurl *base, unsigned long *osize) { char *rewritten; unsigned long offset = 0; unsigned int imports = 0; nserror error; /* count number occurrences of @import to (over)estimate result size */ /* can't use strstr because source is not 0-terminated string */ for (offset = 0; SLEN("@import") < size && offset <= size - SLEN("@import"); offset++) { if (source[offset] == '@' && tolower(source[offset + 1]) == 'i' && tolower(source[offset + 2]) == 'm' && tolower(source[offset + 3]) == 'p' && tolower(source[offset + 4]) == 'o' && tolower(source[offset + 5]) == 'r' && tolower(source[offset + 6]) == 't') imports++; } rewritten = malloc(size + imports * 20); if (rewritten == NULL) return NULL; *osize = 0; offset = 0; while (offset < size) { const char *import_url = NULL; char *import_url_copy; int import_url_len = 0; nsurl *url = NULL; regmatch_t match[11]; int m = regexec(&save_complete_import_re, source + offset, 11, match, 0); if (m) break; if (match[2].rm_so != -1) { import_url = source + offset + match[2].rm_so; import_url_len = match[2].rm_eo - match[2].rm_so; } else if (match[4].rm_so != -1) { import_url = source + offset + match[4].rm_so; import_url_len = match[4].rm_eo - match[4].rm_so; } else if (match[6].rm_so != -1) { import_url = source + offset + match[6].rm_so; import_url_len = match[6].rm_eo - match[6].rm_so; } else if (match[8].rm_so != -1) { import_url = source + offset + match[8].rm_so; import_url_len = match[8].rm_eo - match[8].rm_so; } else if (match[10].rm_so != -1) { import_url = source + offset + match[10].rm_so; import_url_len = match[10].rm_eo - match[10].rm_so; } assert(import_url != NULL); import_url_copy = strndup(import_url, import_url_len); if (import_url_copy == NULL) { free(rewritten); return NULL; } error = nsurl_join(base, import_url_copy, &url); free(import_url_copy); if (error == NSERROR_NOMEM) { free(rewritten); return NULL; } /* copy data before match */ memcpy(rewritten + *osize, source + offset, match[0].rm_so); *osize += match[0].rm_so; if (url != NULL) { hlcache_handle *content; content = save_complete_ctx_find_content(ctx, url); if (content != NULL) { /* replace import */ char buf[64]; snprintf(buf, sizeof buf, "@import '%p'", content); memcpy(rewritten + *osize, buf, strlen(buf)); *osize += strlen(buf); } else { /* copy import */ memcpy(rewritten + *osize, source + offset + match[0].rm_so, match[0].rm_eo - match[0].rm_so); *osize += match[0].rm_eo - match[0].rm_so; } nsurl_unref(url); } else { /* copy import */ memcpy(rewritten + *osize, source + offset + match[0].rm_so, match[0].rm_eo - match[0].rm_so); *osize += match[0].rm_eo - match[0].rm_so; } assert(0 < match[0].rm_eo); offset += match[0].rm_eo; } /* copy rest of source */ if (offset < size) { memcpy(rewritten + *osize, source + offset, size - offset); *osize += size - offset; } return rewritten; }