/** * Callback to determine if a node is a linking element. * * \param pw HTML document * \param n 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_link(void *pw, void *n, bool *match) { dom_node *node = n; dom_exception exc; dom_string *node_name = NULL; exc = dom_node_get_node_name(node, &node_name); if ((exc != DOM_NO_ERR) || (node_name == NULL)) { return CSS_NOMEM; } if (dom_string_caseless_lwc_isequal(node_name, corestring_lwc_a)) { bool has_href; exc = dom_element_has_attribute(node, corestring_dom_href, &has_href); if ((exc == DOM_NO_ERR) && (has_href)) { *match = true; } else { *match = false; } } else { *match = false; } dom_string_unref(node_name); return CSS_OK; }
/** * Callback to determine if a node has an attribute with the given name. * * \param pw HTML document * \param node DOM node * \param qname Name to match * \param match Pointer to location to receive result * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion. * * \post \a match will contain true if the node matches and false otherwise. */ css_error node_has_attribute(void *pw, void *node, const css_qname *qname, bool *match) { dom_node *n = node; dom_string *name; dom_exception err; err = dom_string_create_interned( (const uint8_t *) lwc_string_data(qname->name), lwc_string_length(qname->name), &name); if (err != DOM_NO_ERR) return CSS_NOMEM; err = dom_element_has_attribute(n, name, match); if (err != DOM_NO_ERR) { dom_string_unref(name); return CSS_OK; } dom_string_unref(name); return CSS_OK; }
/** * 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 bool save_complete_handle_element(save_complete_ctx *ctx, dom_node *node, save_complete_event_type event_type) { dom_string *name; dom_namednodemap *attrs; const char *name_data; size_t name_len; bool process = true; dom_exception error; ctx->iter_state = STATE_NORMAL; error = dom_node_get_node_name(node, &name); if (error != DOM_NO_ERR) return false; if (name == NULL) return true; name_data = dom_string_data(name); name_len = dom_string_byte_length(name); if (name_len == SLEN("base") && strncasecmp(name_data, "base", name_len) == 0) { /* Elide BASE elements from the output */ process = false; } else if (name_len == SLEN("meta") && strncasecmp(name_data, "meta", name_len) == 0) { /* Don't emit close tags for META elements */ if (event_type == EVENT_LEAVE) { process = false; } else { /* Elide meta charsets */ dom_string *value; error = dom_element_get_attribute(node, corestring_dom_http_equiv, &value); if (error != DOM_NO_ERR) { dom_string_unref(name); return false; } if (value != NULL) { if (dom_string_length(value) == SLEN("Content-Type") && strncasecmp(dom_string_data(value), "Content-Type", SLEN("Content-Type")) == 0) process = false; dom_string_unref(value); } else { bool yes; error = dom_element_has_attribute(node, corestring_dom_charset, &yes); if (error != DOM_NO_ERR) { dom_string_unref(name); return false; } if (yes) process = false; } } } else if (event_type == EVENT_LEAVE && ((name_len == SLEN("link") && strncasecmp(name_data, "link", name_len) == 0))) { /* Don't emit close tags for void elements */ process = false; } if (process == false) { dom_string_unref(name); return true; } fputc('<', ctx->fp); if (event_type == EVENT_LEAVE) fputc('/', ctx->fp); fwrite(name_data, sizeof(*name_data), name_len, ctx->fp); if (event_type == EVENT_ENTER) { error = dom_node_get_attributes(node, &attrs); if (error != DOM_NO_ERR) { dom_string_unref(name); return false; } if (save_complete_handle_attrs(ctx, name, attrs) == false) { dom_namednodemap_unref(attrs); dom_string_unref(name); return false; } dom_namednodemap_unref(attrs); } fputc('>', ctx->fp); /* Rewrite contents of style elements */ if (event_type == EVENT_ENTER && name_len == SLEN("style") && strncasecmp(name_data, "style", name_len) == 0) { dom_string *content; error = dom_node_get_text_content(node, &content); if (error != DOM_NO_ERR) { dom_string_unref(name); return false; } if (content != NULL) { char *rewritten; unsigned long len; /* Rewrite @import rules */ rewritten = save_complete_rewrite_stylesheet_urls( ctx, dom_string_data(content), dom_string_byte_length(content), ctx->base, &len); if (rewritten == NULL) { dom_string_unref(content); dom_string_unref(name); return false; } dom_string_unref(content); fwrite(rewritten, sizeof(*rewritten), len, ctx->fp); free(rewritten); } ctx->iter_state = STATE_IN_STYLE; } else if (event_type == EVENT_ENTER && name_len == SLEN("head") && strncasecmp(name_data, "head", name_len) == 0) { /* If this is a HEAD element, insert a meta charset */ fputs("<META http-equiv=\"Content-Type\" " "content=\"text/html; charset=utf-8\">", ctx->fp); } dom_string_unref(name); return true; }
/** * Adds an imagemap entry to the list * * \param c The html content that the imagemap belongs to * \param n The xmlNode representing the entry to add * \param base_url Base URL for resolving relative URLs * \param entry Pointer to list of entries * \param tagtype The type of tag * \return false on memory exhaustion, true otherwise */ static bool imagemap_addtolist(const struct html_content *c, dom_node *n, nsurl *base_url, struct mapentry **entry, dom_string *tagtype) { dom_exception exc; dom_string *href = NULL, *target = NULL, *shape = NULL; dom_string *coords = NULL; struct mapentry *new_map, *temp; bool ret = true; if (dom_string_caseless_isequal(tagtype, corestring_dom_area)) { bool nohref = false; exc = dom_element_has_attribute(n, corestring_dom_nohref, &nohref); if ((exc != DOM_NO_ERR) || nohref) /* Skip <area nohref="anything" /> */ goto ok_out; } exc = dom_element_get_attribute(n, corestring_dom_href, &href); if (exc != DOM_NO_ERR || href == NULL) { /* No href="" attribute, skip this element */ goto ok_out; } exc = dom_element_get_attribute(n, corestring_dom_target, &target); if (exc != DOM_NO_ERR) { goto ok_out; } exc = dom_element_get_attribute(n, corestring_dom_shape, &shape); if (exc != DOM_NO_ERR) { goto ok_out; } /* If there's no shape, we default to rectangles */ if (shape == NULL) shape = dom_string_ref(corestring_dom_rect); if (!dom_string_caseless_lwc_isequal(shape, corestring_lwc_default)) { /* If not 'default' and there's no 'coords' give up */ exc = dom_element_get_attribute(n, corestring_dom_coords, &coords); if (exc != DOM_NO_ERR || coords == NULL) { goto ok_out; } } new_map = calloc(1, sizeof(*new_map)); if (new_map == NULL) { goto bad_out; } if (dom_string_caseless_lwc_isequal(shape, corestring_lwc_rect) || dom_string_caseless_lwc_isequal(shape, corestring_lwc_rectangle)) new_map->type = IMAGEMAP_RECT; else if (dom_string_caseless_lwc_isequal(shape, corestring_lwc_circle)) new_map->type = IMAGEMAP_CIRCLE; else if (dom_string_caseless_lwc_isequal(shape, corestring_lwc_poly) || dom_string_caseless_lwc_isequal(shape, corestring_lwc_polygon)) new_map->type = IMAGEMAP_POLY; else if (dom_string_caseless_lwc_isequal(shape, corestring_lwc_default)) new_map->type = IMAGEMAP_DEFAULT; else goto bad_out; if (box_extract_link(c, href, base_url, &new_map->url) == false) goto bad_out; if (new_map->url == NULL) { /* non-fatal error -> ignore this */ goto ok_free_map_out; } if (target != NULL) { /* Copy target into the map */ new_map->target = malloc(dom_string_byte_length(target) + 1); if (new_map->target == NULL) goto bad_out; /* Safe, but relies on dom_strings being NULL terminated */ /* \todo Do this better */ strcpy(new_map->target, dom_string_data(target)); } if (new_map->type != IMAGEMAP_DEFAULT) { int x, y; float *xcoords, *ycoords; /* coordinates are a comma-separated list of values */ char *val = strtok((char *)dom_string_data(coords), ","); int num = 1; switch (new_map->type) { case IMAGEMAP_RECT: /* (left, top, right, bottom) */ while (val != NULL && num <= 4) { switch (num) { case 1: new_map->bounds.rect.x0 = atoi(val); break; case 2: new_map->bounds.rect.y0 = atoi(val); break; case 3: new_map->bounds.rect.x1 = atoi(val); break; case 4: new_map->bounds.rect.y1 = atoi(val); break; } num++; val = strtok(NULL, ","); } break; case IMAGEMAP_CIRCLE: /* (x, y, radius ) */ while (val != NULL && num <= 3) { switch (num) { case 1: new_map->bounds.circle.x = atoi(val); break; case 2: new_map->bounds.circle.y = atoi(val); break; case 3: new_map->bounds.circle.r = atoi(val); break; } num++; val = strtok(NULL, ","); } break; case IMAGEMAP_POLY: new_map->bounds.poly.xcoords = NULL; new_map->bounds.poly.ycoords = NULL; while (val != NULL) { x = atoi(val); val = strtok(NULL, ","); if (val == NULL) break; y = atoi(val); xcoords = realloc(new_map->bounds.poly.xcoords, num * sizeof(float)); if (xcoords == NULL) { goto bad_out; } new_map->bounds.poly.xcoords = xcoords; ycoords = realloc(new_map->bounds.poly.ycoords, num * sizeof(float)); if (ycoords == NULL) { goto bad_out; } new_map->bounds.poly.ycoords = ycoords; new_map->bounds.poly.xcoords[num - 1] = x; new_map->bounds.poly.ycoords[num - 1] = y; num++; val = strtok(NULL, ","); } new_map->bounds.poly.num = num - 1; break; default: break; } } new_map->next = NULL; if (*entry) { /* add to END of list */ for (temp = (*entry); temp->next != NULL; temp = temp->next) ; temp->next = new_map; } else { (*entry) = new_map; } /* All good, linked in, let's clean up */ goto ok_out; bad_out: ret = false; ok_free_map_out: if (new_map != NULL) { if (new_map->url != NULL) nsurl_unref(new_map->url); if (new_map->type == IMAGEMAP_POLY && new_map->bounds.poly.ycoords != NULL) free(new_map->bounds.poly.ycoords); if (new_map->type == IMAGEMAP_POLY && new_map->bounds.poly.xcoords != NULL) free(new_map->bounds.poly.xcoords); if (new_map->target != NULL) free(new_map->target); free(new_map); } ok_out: if (href != NULL) dom_string_unref(href); if (target != NULL) dom_string_unref(target); if (shape != NULL) dom_string_unref(shape); if (coords != NULL) dom_string_unref(coords); return ret; }