static nserror mimesniff__compute_text_or_binary(const uint8_t *data, size_t len, lwc_string **effective_type) { if (data == NULL) return NSERROR_NEED_DATA; len = min(len, 512); if (len >= 3 && ((data[0] == 0xfe && data[1] == 0xff) || (data[0] == 0xff && data[1] == 0xfe) || (data[0] == 0xef && data[1] == 0xbb && data[2] == 0xbf))) { /* Found a BOM => text/plain */ *effective_type = lwc_string_ref(text_plain); return NSERROR_OK; } if (mimesniff__has_binary_octets(data, len) == false) { /* No binary octets => text/plain */ *effective_type = lwc_string_ref(text_plain); return NSERROR_OK; } if (mimesniff__match_unknown(data, len, false, effective_type) == NSERROR_OK) return NSERROR_OK; *effective_type = lwc_string_ref(application_octet_stream); return NSERROR_OK; }
static nserror mimesniff__compute_image(lwc_string *official_type, const uint8_t *data, size_t len, lwc_string **effective_type) { #define SIG(t, s) { (const uint8_t *) s, SLEN(s), t } static const struct it_s { const uint8_t *sig; size_t len; lwc_string **type; } image_types[] = { SIG(&image_gif, "GIF87a"), SIG(&image_gif, "GIF89a"), SIG(&image_png, "\x89PNG\r\n\x1a\n"), SIG(&image_jpeg, "\xff\xd8\xff"), SIG(&image_bmp, "BM"), SIG(&image_vnd_microsoft_icon, "\x00\x00\x01\x00"), { NULL, 0, NULL } }; #undef SIG const struct it_s *it; if (data == NULL) { lwc_string_unref(official_type); return NSERROR_NEED_DATA; } for (it = image_types; it->sig != NULL; it++) { if (it->len <= len && memcmp(data, it->sig, it->len) == 0) { lwc_string_unref(official_type); *effective_type = lwc_string_ref(*it->type); return NSERROR_OK; } } /* WebP has a signature that doesn't fit into the above table */ if (SLEN("RIFF????WEBPVP") <= len && memcmp(data, "RIFF", SLEN("RIFF")) == 0 && memcmp(data + SLEN("RIFF????"), "WEBPVP", SLEN("WEBPVP")) == 0 ) { lwc_string_unref(official_type); *effective_type = lwc_string_ref(image_webp); return NSERROR_OK; } *effective_type = official_type; return NSERROR_OK; }
/** * Make the dom_string be interned * * \param str The dom_string to be interned * \param lwcstr The result lwc_string * \return DOM_NO_ERR on success, appropriate dom_exception on failure. */ dom_exception dom_string_intern(dom_string *str, struct lwc_string_s **lwcstr) { dom_string_internal *istr = (dom_string_internal *) str; /* If this string is already interned, do nothing */ if (istr->type != DOM_STRING_INTERNED) { lwc_string *ret; lwc_error lerr; lerr = lwc_intern_string((const char *) istr->data.cdata.ptr, istr->data.cdata.len, &ret); if (lerr != lwc_error_ok) { return _dom_exception_from_lwc_error(lerr); } free(istr->data.cdata.ptr); istr->data.intern = ret; istr->type = DOM_STRING_INTERNED; } *lwcstr = lwc_string_ref(istr->data.intern); return DOM_NO_ERR; }
static nserror mimesniff__match_unknown_exact(const uint8_t *data, size_t len, bool allow_unsafe, lwc_string **effective_type) { #define SIG(t, s, x) { (const uint8_t *) s, SLEN(s), x, t } static const struct map_s exact_match_types[] = { SIG(&image_gif, "GIF87a", true), SIG(&image_gif, "GIF89a", true), SIG(&image_png, "\x89PNG\r\n\x1a\n", true), SIG(&image_jpeg, "\xff\xd8\xff", true), SIG(&image_bmp, "BM", true), SIG(&image_vnd_microsoft_icon, "\x00\x00\x01\x00", true), SIG(&application_ogg, "OggS\x00", true), SIG(&video_webm, "\x1a\x45\xdf\xa3", true), SIG(&application_x_rar_compressed, "Rar \x1a\x07\x00", true), SIG(&application_zip, "PK\x03\x04", true), SIG(&application_x_gzip, "\x1f\x8b\x08", true), SIG(&application_postscript, "%!PS-Adobe-", true), SIG(&application_pdf, "%PDF-", false), { NULL, 0, false, NULL } }; #undef SIG const struct map_s *it; for (it = exact_match_types; it->sig != NULL; it++) { if (it->len <= len && memcmp(data, it->sig, it->len) == 0 && (allow_unsafe || it->safe)) { *effective_type = lwc_string_ref(*it->type); return NSERROR_OK; } } return NSERROR_NOT_FOUND; }
END_TEST START_TEST (test_lwc_string_ref_unref_ok) { lwc_string_ref(intern_one); lwc_string_unref(intern_one); }
END_TEST START_TEST (test_lwc_string_ref_ok) { fail_unless(lwc_string_ref(intern_one) == intern_one, "Oddly, reffing a string didn't return it"); }
css_error node_classes(void *pw, void *n, lwc_string ***classes, uint32_t *n_classes) { node *node = n; uint32_t i; line_ctx *lc = pw; for (i = 0; i < node->n_attrs; i++) { bool amatch = false; assert(lwc_string_caseless_isequal( node->attrs[i].name, lc->attr_class, &amatch) == lwc_error_ok); if (amatch == true) break; } if (i != node->n_attrs) { *classes = realloc(NULL, sizeof(lwc_string **)); if (*classes == NULL) return CSS_NOMEM; *(classes[0]) = lwc_string_ref(node->attrs[i].value); *n_classes = 1; } else { *classes = NULL; *n_classes = 0; } return CSS_OK; }
/* exported interface documented in content/content_protected.h */ lwc_string *content__get_mime_type(struct content *c) { if (c == NULL) return NULL; return lwc_string_ref(c->mime_type); }
bool content__add_rfc5988_link(struct content *c, const struct content_rfc5988_link *link) { struct content_rfc5988_link *newlink; union content_msg_data msg_data; /* a link relation must be present for it to be a link */ if (link->rel == NULL) { return false; } /* a link href must be present for it to be a link */ if (link->href == NULL) { return false; } newlink = calloc(1, sizeof(struct content_rfc5988_link)); if (newlink == NULL) { return false; } /* copy values */ newlink->rel = lwc_string_ref(link->rel); newlink->href = nsurl_ref(link->href); if (link->hreflang != NULL) { newlink->hreflang = lwc_string_ref(link->hreflang); } if (link->type != NULL) { newlink->type = lwc_string_ref(link->type); } if (link->media != NULL) { newlink->media = lwc_string_ref(link->media); } if (link->sizes != NULL) { newlink->sizes = lwc_string_ref(link->sizes); } /* add to metadata link to list */ newlink->next = c->links; c->links = newlink; /* broadcast the data */ msg_data.rfc5988_link = newlink; content_broadcast(c, CONTENT_MSG_LINK, msg_data); return true; }
nserror content__init(struct content *c, const content_handler *handler, lwc_string *imime_type, const http_parameter *params, llcache_handle *llcache, const char *fallback_charset, bool quirks) { struct content_user *user_sentinel; nserror error; LOG(("url "URL_FMT_SPC" -> %p", nsurl_access(llcache_handle_get_url(llcache)), c)); user_sentinel = calloc(1, sizeof(struct content_user)); if (user_sentinel == NULL) { return NSERROR_NOMEM; } if (fallback_charset != NULL) { c->fallback_charset = strdup(fallback_charset); if (c->fallback_charset == NULL) { free(user_sentinel); return NSERROR_NOMEM; } } c->llcache = llcache; c->mime_type = lwc_string_ref(imime_type); c->handler = handler; c->status = CONTENT_STATUS_LOADING; c->width = 0; c->height = 0; c->available_width = 0; c->quirks = quirks; c->refresh = 0; c->time = wallclock(); c->size = 0; c->title = NULL; c->active = 0; user_sentinel->callback = NULL; user_sentinel->pw = NULL; user_sentinel->next = NULL; c->user_list = user_sentinel; c->sub_status[0] = 0; c->locked = false; c->total_size = 0; c->http_code = 0; c->error_count = 0; content_set_status(c, messages_get("Loading")); /* Finally, claim low-level cache events */ error = llcache_handle_change_callback(llcache, content_llcache_callback, c); if (error != NSERROR_OK) { lwc_string_unref(c->mime_type); return error; } return NSERROR_OK; }
/** * Cache a CURL handle for the provided host (if wanted) */ static void fetch_curl_cache_handle(CURL *handle, lwc_string *host) { #if LIBCURL_VERSION_NUM >= 0x071e00 /* 7.30.0 or later has its own connection caching; suppress ours */ curl_easy_cleanup(handle); return; #else struct cache_handle *h = 0; int c; RING_FINDBYLWCHOST(curl_handle_ring, h, host); if (h) { /* Already have a handle cached for this hostname */ curl_easy_cleanup(handle); return; } /* We do not have a handle cached, first up determine if the cache is full */ RING_GETSIZE(struct cache_handle, curl_handle_ring, c); if (c >= nsoption_int(max_cached_fetch_handles)) { /* Cache is full, so, we rotate the ring by one and * replace the oldest handle with this one. We do this * without freeing/allocating memory (except the * hostname) and without removing the entry from the * ring and then re-inserting it, in order to be as * efficient as we can. */ if (curl_handle_ring != NULL) { h = curl_handle_ring; curl_handle_ring = h->r_next; curl_easy_cleanup(h->handle); h->handle = handle; lwc_string_unref(h->host); h->host = lwc_string_ref(host); } else { /* Actually, we don't want to cache any handles */ curl_easy_cleanup(handle); } return; } /* The table isn't full yet, so make a shiny new handle to add to the ring */ h = (struct cache_handle*)malloc(sizeof(struct cache_handle)); h->handle = handle; h->host = lwc_string_ref(host); RING_INSERT(curl_handle_ring, h); #endif }
css_error node_name(void *pw, void *n, css_qname *qname) { node *node = n; UNUSED(pw); qname->name = lwc_string_ref(node->name); return CSS_OK; }
static css_error resolve_url(void *pw, const char *base, lwc_string *rel, lwc_string **abs) { UNUSED(pw); UNUSED(base); /* About as useless as possible */ *abs = lwc_string_ref(rel); return CSS_OK; }
static nserror mimesniff__compute_unknown(const uint8_t *data, size_t len, lwc_string **effective_type) { if (data == NULL) return NSERROR_NEED_DATA; len = min(len, 512); if (mimesniff__match_unknown(data, len, true, effective_type) == NSERROR_OK) return NSERROR_OK; if (mimesniff__has_binary_octets(data, len) == false) { /* No binary octets => text/plain */ *effective_type = lwc_string_ref(text_plain); return NSERROR_OK; } *effective_type = lwc_string_ref(application_octet_stream); return NSERROR_OK; }
/** * Set a font-face's font-family name * * \param font_face The font-face * \param font_family Font-family name * \param result Pointer to location to receive result * \return CSS_OK on success, * CSS_BADPARM on bad parameters. */ css_error css__font_face_set_font_family(css_font_face *font_face, lwc_string *font_family) { if (font_face == NULL || font_family == NULL) return CSS_BADPARM; if (font_face->font_family != NULL) lwc_string_unref(font_face->font_family); font_face->font_family = lwc_string_ref(font_family); return CSS_OK; }
void fetch_data_register(void) { lwc_string *scheme = lwc_string_ref(corestring_lwc_data); fetch_add_fetcher(scheme, fetch_data_initialise, fetch_data_can_fetch, fetch_data_setup, fetch_data_start, fetch_data_abort, fetch_data_free, fetch_data_poll, fetch_data_finalise); }
static css_error node_classes(void *pw, void *n, lwc_string ***classes, uint32_t *n_classes) { unsigned int i; node *node = n; UNUSED(pw); *classes = node->classes; *n_classes = node->n_classes; for (i = 0; i < *n_classes; i++) (*classes)[i] = lwc_string_ref(node->classes[i]); return CSS_OK; }
/** * Register javascript scheme fetcher with fetcher factory. * * \return NSERROR_OK on success or appropriate error code on faliure. */ nserror fetch_javascript_register(void) { lwc_string *scheme = lwc_string_ref(corestring_lwc_javascript); const struct fetcher_operation_table fetcher_ops = { .initialise = fetch_javascript_initialise, .acceptable = fetch_javascript_can_fetch, .setup = fetch_javascript_setup, .start = fetch_javascript_start, .abort = fetch_javascript_abort, .free = fetch_javascript_free, .poll = fetch_javascript_poll, .finalise = fetch_javascript_finalise }; return fetcher_add(scheme, &fetcher_ops); }
static nserror mimesniff__match_unknown_bom(const uint8_t *data, size_t len, lwc_string **effective_type) { #define SIG(t, s, x) { (const uint8_t *) s, SLEN(s), x, t } static const struct map_s bom_match_types[] = { SIG(&text_plain, "\xfe\xff", false), SIG(&text_plain, "\xff\xfe", false), SIG(&text_plain, "\xef\xbb\xbf", false), { NULL, 0, false, NULL } }; #undef SIG const struct map_s *it; for (it = bom_match_types; it->sig != NULL; it++) { if (it->len <= len && memcmp(data, it->sig, it->len) == 0) { *effective_type = lwc_string_ref(*it->type); return NSERROR_OK; } } return NSERROR_NOT_FOUND; }
/** * Create a string from a list of IDENT/S tokens if the next token is IDENT * or references the next token's string if it is a STRING * * \param c Parsing context * \param vector Vector containing tokens * \param ctx Vector iteration context * \param reserved Callback to determine if an identifier is reserved * \param result Pointer to location to receive resulting string * \return CSS_OK on success, appropriate error otherwise. * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. * * The resulting string's reference is passed to the caller */ css_error css__ident_list_or_string_to_string(css_language *c, const parserutils_vector *vector, int *ctx, bool (*reserved)(css_language *c, const css_token *ident), lwc_string **result) { const css_token *token; token = parserutils_vector_peek(vector, *ctx); if (token == NULL) return CSS_INVALID; if (token->type == CSS_TOKEN_STRING) { token = parserutils_vector_iterate(vector, ctx); *result = lwc_string_ref(token->idata); return CSS_OK; } else if(token->type == CSS_TOKEN_IDENT) { return css__ident_list_to_string(c, vector, ctx, reserved, result); } return CSS_INVALID; }
css_error node_id(void *pw, void *n, lwc_string **id) { node *node = n; uint32_t i; line_ctx *lc = pw; for (i = 0; i < node->n_attrs; i++) { bool amatch = false; assert(lwc_string_caseless_isequal( node->attrs[i].name, lc->attr_id, &amatch) == lwc_error_ok); if (amatch == true) break; } if (i != node->n_attrs) *id = lwc_string_ref(node->attrs[i].value); else *id = NULL; return CSS_OK; }
static nserror mimesniff__match_unknown_riff(const uint8_t *data, size_t len, lwc_string **effective_type) { #define SIG(t, s, x) { (const uint8_t *) s, SLEN(s), x, t } static const struct map_s riff_match_types[] = { SIG(&image_webp, "WEBPVP", true), SIG(&audio_wave, "WAVE", true), { NULL, 0, false, NULL } }; #undef SIG const struct map_s *it; for (it = riff_match_types; it->sig != NULL; it++) { if (it->len + SLEN("RIFF????") <= len && memcmp(data, "RIFF", SLEN("RIFF")) == 0 && memcmp(data + SLEN("RIFF????"), it->sig, it->len) == 0) { *effective_type = lwc_string_ref(*it->type); return NSERROR_OK; } } return NSERROR_NOT_FOUND; }
/** * Parse content * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result Pointer to location to receive resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__parse_content(css_language *c, const parserutils_vector *vector, int *ctx, css_style *result) { int orig_ctx = *ctx; css_error error; const css_token *token; bool match; /* IDENT(normal, none, inherit) | [ ... ]+ */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[INHERIT], &match) == lwc_error_ok && match)) { error = css_stylesheet_style_inherit(result, CSS_PROP_CONTENT); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[NORMAL], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_CONTENT, 0, CONTENT_NORMAL); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[NONE], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_CONTENT, 0, CONTENT_NONE); } else { /* Macro to output the value marker, awkward because we need to check * first to determine how the value is constructed. */ #define CSS_APPEND(CSSVAL) css__stylesheet_style_append(result, first?buildOPV(CSS_PROP_CONTENT, 0, CSSVAL):CSSVAL) bool first = true; int prev_ctx = orig_ctx; /* [ * IDENT(open-quote, close-quote, no-open-quote, * no-close-quote) | * STRING | * URI | * FUNCTION(attr) IDENT ')' | * FUNCTION(counter) IDENT IDENT? ')' | * FUNCTION(counters) IDENT STRING IDENT? ')' * ]+ */ while (token != NULL) { if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal( token->idata, c->strings[OPEN_QUOTE], &match) == lwc_error_ok && match)) { error = CSS_APPEND(CONTENT_OPEN_QUOTE); } else if (token->type == CSS_TOKEN_IDENT && (lwc_string_caseless_isequal( token->idata, c->strings[CLOSE_QUOTE], &match) == lwc_error_ok && match)) { error = CSS_APPEND(CONTENT_CLOSE_QUOTE); } else if (token->type == CSS_TOKEN_IDENT && (lwc_string_caseless_isequal( token->idata, c->strings[NO_OPEN_QUOTE], &match) == lwc_error_ok && match)) { error = CSS_APPEND(CONTENT_NO_OPEN_QUOTE); } else if (token->type == CSS_TOKEN_IDENT && (lwc_string_caseless_isequal( token->idata, c->strings[NO_CLOSE_QUOTE], &match) == lwc_error_ok && match)) { error = CSS_APPEND(CONTENT_NO_CLOSE_QUOTE); } else if (token->type == CSS_TOKEN_STRING) { uint32_t snumber; error = css__stylesheet_string_add(c->sheet, lwc_string_ref(token->idata), &snumber); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = CSS_APPEND(CONTENT_STRING); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = css__stylesheet_style_append(result, snumber); } else if (token->type == CSS_TOKEN_URI) { lwc_string *uri; uint32_t uri_snumber; error = c->sheet->resolve(c->sheet->resolve_pw, c->sheet->url, token->idata, &uri); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = css__stylesheet_string_add(c->sheet, uri, &uri_snumber); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = CSS_APPEND(CONTENT_URI); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = css__stylesheet_style_append(result, uri_snumber); } else if (token->type == CSS_TOKEN_FUNCTION && (lwc_string_caseless_isequal( token->idata, c->strings[ATTR], &match) == lwc_error_ok && match)) { uint32_t snumber; consumeWhitespace(vector, ctx); /* Expect IDENT */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || token->type != CSS_TOKEN_IDENT) { *ctx = orig_ctx; return CSS_INVALID; } error = css__stylesheet_string_add(c->sheet, lwc_string_ref(token->idata), &snumber); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = CSS_APPEND(CONTENT_ATTR); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = css__stylesheet_style_append(result, snumber); consumeWhitespace(vector, ctx); /* Expect ')' */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || tokenIsChar(token, ')') == false) { *ctx = orig_ctx; return CSS_INVALID; } } else if (token->type == CSS_TOKEN_FUNCTION && (lwc_string_caseless_isequal( token->idata, c->strings[COUNTER], &match) == lwc_error_ok && match)) { lwc_string *name; uint32_t snumber; uint32_t opv; opv = CONTENT_COUNTER; consumeWhitespace(vector, ctx); /* Expect IDENT */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || token->type != CSS_TOKEN_IDENT) { *ctx = orig_ctx; return CSS_INVALID; } name = token->idata; consumeWhitespace(vector, ctx); /* Possible ',' */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL || (tokenIsChar(token, ',') == false && tokenIsChar(token, ')') == false)) { *ctx = orig_ctx; return CSS_INVALID; } if (tokenIsChar(token, ',')) { uint16_t v; parserutils_vector_iterate(vector, ctx); consumeWhitespace(vector, ctx); /* Expect IDENT */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL || token->type != CSS_TOKEN_IDENT) { *ctx = orig_ctx; return CSS_INVALID; } error = css__parse_list_style_type_value(c, token, &v); if (error != CSS_OK) { *ctx = orig_ctx; return error; } opv |= v << CONTENT_COUNTER_STYLE_SHIFT; parserutils_vector_iterate(vector, ctx); consumeWhitespace(vector, ctx); } else { opv |= LIST_STYLE_TYPE_DECIMAL << CONTENT_COUNTER_STYLE_SHIFT; } /* Expect ')' */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || tokenIsChar(token, ')') == false) { *ctx = orig_ctx; return CSS_INVALID; } error = css__stylesheet_string_add(c->sheet, lwc_string_ref(name), &snumber); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = CSS_APPEND(opv); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = css__stylesheet_style_append(result, snumber); } else if (token->type == CSS_TOKEN_FUNCTION && (lwc_string_caseless_isequal( token->idata, c->strings[COUNTERS], &match) == lwc_error_ok && match)) { lwc_string *name; lwc_string *sep; uint32_t name_snumber; uint32_t sep_snumber; uint32_t opv; opv = CONTENT_COUNTERS; consumeWhitespace(vector, ctx); /* Expect IDENT */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || token->type != CSS_TOKEN_IDENT) { *ctx = orig_ctx; return CSS_INVALID; } name = token->idata; consumeWhitespace(vector, ctx); /* Expect ',' */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || tokenIsChar(token, ',') == false) { *ctx = orig_ctx; return CSS_INVALID; } consumeWhitespace(vector, ctx); /* Expect STRING */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || token->type != CSS_TOKEN_STRING) { *ctx = orig_ctx; return CSS_INVALID; } sep = token->idata; consumeWhitespace(vector, ctx); /* Possible ',' */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL || (tokenIsChar(token, ',') == false && tokenIsChar(token, ')') == false)) { *ctx = orig_ctx; return CSS_INVALID; } if (tokenIsChar(token, ',')) { uint16_t v; parserutils_vector_iterate(vector, ctx); consumeWhitespace(vector, ctx); /* Expect IDENT */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL || token->type != CSS_TOKEN_IDENT) { *ctx = orig_ctx; return CSS_INVALID; } error = css__parse_list_style_type_value(c, token, &v); if (error != CSS_OK) { *ctx = orig_ctx; return error; } opv |= v << CONTENT_COUNTERS_STYLE_SHIFT; parserutils_vector_iterate(vector, ctx); consumeWhitespace(vector, ctx); } else { opv |= LIST_STYLE_TYPE_DECIMAL << CONTENT_COUNTERS_STYLE_SHIFT; } /* Expect ')' */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || tokenIsChar(token, ')') == false) { *ctx = orig_ctx; return CSS_INVALID; } error = css__stylesheet_string_add(c->sheet, lwc_string_ref(name), &name_snumber); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = css__stylesheet_string_add(c->sheet, lwc_string_ref(sep), &sep_snumber); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = CSS_APPEND(opv); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = css__stylesheet_style_append(result, name_snumber); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = css__stylesheet_style_append(result, sep_snumber); } else if (first) { /* Invalid if this is the first iteration */ error = CSS_INVALID; } else { /* Give up, ensuring current token is reprocessed */ *ctx = prev_ctx; error = CSS_OK; break; } /* if there was an error bail */ if (error != CSS_OK) { *ctx = orig_ctx; return error; } first = false; consumeWhitespace(vector, ctx); prev_ctx = *ctx; token = parserutils_vector_iterate(vector, ctx); } /* while */ /* Write list terminator */ css__stylesheet_style_append(result, CONTENT_NORMAL); } if (error != CSS_OK) *ctx = orig_ctx; return error; }
void css__parse_tree_data(line_ctx *ctx, const char *data, size_t len) { const char *p = data; const char *end = data + len; const char *name = NULL; const char *value = NULL; size_t namelen = 0; size_t valuelen = 0; uint32_t depth = 0; bool target = false; /* ' '{depth+1} [ <element> '*'? | <attr> ] * * <element> ::= [^=*[:space:]]+ * <attr> ::= [^=*[:space:]]+ '=' [^[:space:]]* */ while (p < end && isspace(*p)) { depth++; p++; } depth--; /* Get element/attribute name */ name = p; while (p < end && *p != '=' && *p != '*' && isspace(*p) == false) { namelen++; p++; } /* Skip whitespace */ while (p < end && isspace(*p)) p++; if (p < end && *p == '=') { /* Attribute value */ p++; value = p; while (p < end && isspace(*p) == false) { valuelen++; p++; } } else if (p < end && *p == '*') { /* Element is target node */ target = true; } if (value == NULL) { /* We have an element, so create it */ node *n = malloc(sizeof(node)); assert(n != NULL); memset(n, 0, sizeof(node)); lwc_intern_string(name, namelen, &n->name); /* Insert it into tree */ if (ctx->tree == NULL) { ctx->tree = n; } else { assert(depth > 0); assert(depth <= ctx->depth + 1); /* Find node to insert into */ while (depth <= ctx->depth) { ctx->depth--; ctx->current = ctx->current->parent; } /* Insert into current node */ if (ctx->current->children == NULL) { ctx->current->children = n; ctx->current->last_child = n; } else { ctx->current->last_child->next = n; n->prev = ctx->current->last_child; ctx->current->last_child = n; } n->parent = ctx->current; } ctx->current = n; ctx->depth = depth; /* Mark the target, if it's us */ if (target) ctx->target = n; } else { /* New attribute */ bool amatch = false; attribute *attr; node *n = ctx->current; attribute *temp = realloc(n->attrs, (n->n_attrs + 1) * sizeof(attribute)); assert(temp != NULL); n->attrs = temp; attr = &n->attrs[n->n_attrs]; lwc_intern_string(name, namelen, &attr->name); lwc_intern_string(value, valuelen, &attr->value); assert(lwc_string_caseless_isequal( n->attrs[n->n_attrs].name, ctx->attr_class, &amatch) == lwc_error_ok); if (amatch == true) { n->classes = realloc(NULL, sizeof(lwc_string **)); assert(n->classes != NULL); n->classes[0] = lwc_string_ref( n->attrs[n->n_attrs]. value); n->n_classes = 1; } n->n_attrs++; } }
extern void rust_lwc_string_ref(lwc_string *str) { lwc_string_ref(str); }
/** * Clone a history entry * * \param history opaque history structure, as returned by history_create() * \param entry entry to clone * \return A cloned history entry or NULL on error */ static struct history_entry * browser_window_history__clone_entry(struct history *history, struct history_entry *entry) { struct history_entry *child; struct history_entry *new_child; struct history_entry *prev = NULL; struct history_entry *new_entry; assert(entry->page.url); assert(entry->page.title); /* clone the entry */ new_entry = malloc(sizeof *entry); if (!new_entry) return NULL; memcpy(new_entry, entry, sizeof *entry); new_entry->page.url = nsurl_ref(entry->page.url); if (entry->page.frag_id) new_entry->page.frag_id = lwc_string_ref(entry->page.frag_id); new_entry->page.title = strdup(entry->page.title); if (!new_entry->page.url || !new_entry->page.title || ((entry->page.frag_id) && (!new_entry->page.frag_id))) { nsurl_unref(new_entry->page.url); if (new_entry->page.frag_id) lwc_string_unref(new_entry->page.frag_id); free(new_entry->page.title); free(new_entry); return NULL; } /* update references */ if (history->current == entry) history->current = new_entry; /* recurse for all children */ for (child = new_entry->forward; child; child = child->next) { new_child = browser_window_history__clone_entry(history, child); if (new_child) { new_child->back = new_entry; } else { nsurl_unref(new_entry->page.url); if (new_entry->page.frag_id) lwc_string_unref(new_entry->page.frag_id); free(new_entry->page.title); free(new_entry); return NULL; } if (prev) prev->next = new_child; if (new_entry->forward == child) new_entry->forward = new_child; if (new_entry->forward_pref == child) new_entry->forward_pref = new_child; if (new_entry->forward_last == child) new_entry->forward_last = new_child; prev = new_child; } return new_entry; }
/** * Recursively redraw a history_entry. * * \param history history containing the entry * \param entry entry to render * \param x0 area top left x coordinate * \param y0 area top left y coordinate * \param x1 area bottom right x coordinate * \param y1 area bottom right y coordinate * \param x window x offset * \param y window y offset * \param clip clip redraw * \param ctx current redraw context */ static bool browser_window_history__redraw_entry(struct history *history, struct history_entry *entry, int x0, int y0, int x1, int y1, int x, int y, bool clip, const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; size_t char_offset; int actual_x; struct history_entry *child; colour c = entry == history->current ? HISTORY_COLOUR_SELECTED : HISTORY_COLOUR_FOREGROUND; int tailsize = 5; int xoffset = x - x0; int yoffset = y - y0; plot_style_t pstyle_history_rect = { .stroke_type = PLOT_OP_TYPE_SOLID, .stroke_colour = c, .stroke_width = entry == history->current ? 3 : 1, }; plot_font_style_t fstyle = *plot_style_font; if (clip) { struct rect rect; rect.x0 = x0 + xoffset; rect.y0 = y0 + yoffset; rect.x1 = x1 + xoffset; rect.y1 = y1 + yoffset; if (!plot->clip(&rect)) { return false; } } /* Only attempt to plot bitmap if it is present */ if (entry->bitmap != NULL) { plot->bitmap(entry->x + xoffset, entry->y + yoffset, WIDTH, HEIGHT, entry->bitmap, 0xffffff, 0); } if (!plot->rectangle(entry->x - 1 + xoffset, entry->y - 1 + yoffset, entry->x + xoffset + WIDTH, entry->y + yoffset + HEIGHT, &pstyle_history_rect)) return false; if (!nsfont.font_position_in_string(plot_style_font, entry->page.title, strlen(entry->page.title), WIDTH, &char_offset, &actual_x)) return false; fstyle.background = HISTORY_COLOUR_BACKGROUND; fstyle.foreground = c; fstyle.weight = entry == history->current ? 900 : 400; if (!plot->text(entry->x + xoffset, entry->y + HEIGHT + 12 + yoffset, entry->page.title, char_offset, &fstyle)) return false; for (child = entry->forward; child; child = child->next) { if (!plot->line(entry->x + WIDTH + xoffset, entry->y + HEIGHT / 2 + yoffset, entry->x + WIDTH + tailsize + xoffset, entry->y + HEIGHT / 2 + yoffset, plot_style_stroke_history)) return false; if (!plot->line(entry->x + WIDTH + tailsize + xoffset, entry->y + HEIGHT / 2 + yoffset, child->x - tailsize +xoffset, child->y + HEIGHT / 2 + yoffset, plot_style_stroke_history)) return false; if (!plot->line(child->x - tailsize + xoffset, child->y + HEIGHT / 2 + yoffset, child->x + xoffset, child->y + HEIGHT / 2 + yoffset, plot_style_stroke_history)) return false; if (!browser_window_history__redraw_entry(history, child, x0, y0, x1, y1, x, y, clip, ctx)) return false; } return true; } /** * Find the history entry at a position. * * \param entry entry to search from * \param x coordinate * \param y coordinate * \return an entry if found, 0 if none */ static struct history_entry *browser_window_history__find_position( struct history_entry *entry, int x, int y) { struct history_entry *child; struct history_entry *found; if (!entry) return 0; if (entry->x <= x && x <= entry->x + WIDTH && entry->y <= y && y <= entry->y + HEIGHT) return entry; for (child = entry->forward; child; child = child->next) { found = browser_window_history__find_position(child, x, y); if (found) return found; } return 0; } /** * Enumerate subentries in history * See also history_enumerate() * * \param bw The browser window to enumerate history of * \param entry entry to start enumeration at * \param cb callback function * \param ud context pointer passed to cb * \return true to continue enumeration, false to cancel */ static bool browser_window_history__enumerate_entry( const struct browser_window *bw, const struct history_entry *entry, browser_window_history_enumerate_cb cb, void *ud) { const struct history_entry *child; if (!cb(bw, entry->x, entry->y, entry->x + WIDTH, entry->y + HEIGHT, entry, ud)) return false; for (child = entry->forward; child; child = child->next) { if (!browser_window_history__enumerate_entry(bw, child, cb, ud)) return false; } return true; } /* -------------------------------------------------------------------------- */ /* exported interface documented in desktop/browser_history.h */ nserror browser_window_history_create(struct browser_window *bw) { struct history *history; bw->history = NULL; history = calloc(1, sizeof *history); if (history == NULL) { return NSERROR_NOMEM; } history->width = RIGHT_MARGIN / 2; history->height = BOTTOM_MARGIN / 2; bw->history = history; return NSERROR_OK; } /* exported interface documented in desktop/browser_history.h */ nserror browser_window_history_clone(const struct browser_window *existing, struct browser_window *clone) { struct history *new_history; clone->history = NULL; if (existing == NULL || existing->history == NULL || existing->history->start == NULL) /* Nothing to clone, create new history for clone window */ return browser_window_history_create(clone); /* Make cloned history */ new_history = malloc(sizeof *new_history); if (!new_history) return NSERROR_NOMEM; clone->history = new_history; memcpy(new_history, existing->history, sizeof *new_history); new_history->start = browser_window_history__clone_entry(new_history, new_history->start); if (!new_history->start) { LOG("Insufficient memory to clone history"); browser_window_history_destroy(clone); clone->history = NULL; return NSERROR_NOMEM; } return NSERROR_OK; } /* exported interface documented in desktop/browser_history.h */ nserror browser_window_history_add(struct browser_window *bw, struct hlcache_handle *content, lwc_string *frag_id) { struct history *history; struct history_entry *entry; nsurl *nsurl = hlcache_handle_get_url(content); char *title; struct bitmap *bitmap; nserror ret; assert(bw); assert(bw->history); assert(content); history = bw->history; /* allocate space */ entry = malloc(sizeof *entry); if (entry == NULL) { return NSERROR_NOMEM; } title = strdup(content_get_title(content)); if (title == NULL) { free(entry); return NSERROR_NOMEM; } entry->page.url = nsurl_ref(nsurl); entry->page.frag_id = frag_id ? lwc_string_ref(frag_id) : 0; entry->page.title = title; entry->back = history->current; entry->next = 0; entry->forward = entry->forward_pref = entry->forward_last = 0; entry->children = 0; entry->bitmap = 0; if (history->current) { if (history->current->forward_last) history->current->forward_last->next = entry; else history->current->forward = entry; history->current->forward_pref = entry; history->current->forward_last = entry; history->current->children++; } else { history->start = entry; } history->current = entry; /* if we have a thumbnail, don't update until the page has finished * loading */ bitmap = urldb_get_thumbnail(nsurl); if (bitmap == NULL) { LOG("Creating thumbnail for %s", nsurl_access(nsurl)); bitmap = guit->bitmap->create(WIDTH, HEIGHT, BITMAP_NEW | BITMAP_CLEAR_MEMORY | BITMAP_OPAQUE); if (bitmap != NULL) { ret = guit->bitmap->render(bitmap, content); if (ret == NSERROR_OK) { /* Successful thumbnail so register it * with the url. */ urldb_set_thumbnail(nsurl, bitmap); } else { /* Thumbnailing failed. Ignore it * silently but clean up bitmap. */ LOG("Thumbnail renderfailed"); guit->bitmap->destroy(bitmap); bitmap = NULL; } } } entry->bitmap = bitmap; browser_window_history__layout(history); return NSERROR_OK; } /* exported interface documented in desktop/browser_history.h */ nserror browser_window_history_update(struct browser_window *bw, struct hlcache_handle *content) { struct history *history; char *title; assert(bw != NULL); history = bw->history; if (!history || !history->current || !history->current->bitmap) { return NSERROR_INVALID; } assert(history->current->page.url); assert(history->current->page.title); title = strdup(content_get_title(content)); if (title == NULL) { return NSERROR_NOMEM; } free(history->current->page.title); history->current->page.title = title; guit->bitmap->render(history->current->bitmap, content); return NSERROR_OK; } /* exported interface documented in desktop/browser_history.h */ void browser_window_history_destroy(struct browser_window *bw) { assert(bw != NULL); if (bw->history == NULL) return; browser_window_history__free_entry(bw->history->start); free(bw->history); bw->history = NULL; }
/** * Parse a comma separated list, converting to bytecode * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param reserved Callback to determine if an identifier is reserved * \param get_value Callback to retrieve bytecode value for a token * \param style Pointer to output style * \return CSS_OK on success, * CSS_INVALID if the input is invalid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__comma_list_to_style(css_language *c, const parserutils_vector *vector, int *ctx, bool (*reserved)(css_language *c, const css_token *ident), css_code_t (*get_value)(css_language *c, const css_token *token, bool first), css_style *result) { int orig_ctx = *ctx; int prev_ctx = orig_ctx; const css_token *token; bool first = true; css_error error = CSS_OK; token = parserutils_vector_iterate(vector, ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } while (token != NULL) { if (token->type == CSS_TOKEN_IDENT) { css_code_t value = get_value(c, token, first); if (reserved(c, token) == false) { lwc_string *str = NULL; uint32_t snumber; *ctx = prev_ctx; error = css__ident_list_to_string(c, vector, ctx, reserved, &str); if (error != CSS_OK) goto cleanup; error = css__stylesheet_string_add(c->sheet, str, &snumber); if (error != CSS_OK) goto cleanup; error = css__stylesheet_style_append(result, value); if (error != CSS_OK) goto cleanup; error = css__stylesheet_style_append(result, snumber); if (error != CSS_OK) goto cleanup; } else { error = css__stylesheet_style_append(result, value); if (error != CSS_OK) goto cleanup; } } else if (token->type == CSS_TOKEN_STRING) { css_code_t value = get_value(c, token, first); uint32_t snumber; error = css__stylesheet_string_add(c->sheet, lwc_string_ref(token->idata), &snumber); if (error != CSS_OK) goto cleanup; error = css__stylesheet_style_append(result, value); if (error != CSS_OK) goto cleanup; error = css__stylesheet_style_append(result, snumber); if (error != CSS_OK) goto cleanup; } else { error = CSS_INVALID; goto cleanup; } consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token != NULL && tokenIsChar(token, ',')) { parserutils_vector_iterate(vector, ctx); consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token == NULL || (token->type != CSS_TOKEN_IDENT && token->type != CSS_TOKEN_STRING)) { error = CSS_INVALID; goto cleanup; } } else { break; } first = false; prev_ctx = *ctx; token = parserutils_vector_iterate(vector, ctx); } cleanup: if (error != CSS_OK) *ctx = orig_ctx; return error; }
/* See mimesniff.h for documentation */ nserror mimesniff_compute_effective_type(llcache_handle *handle, const uint8_t *data, size_t len, bool sniff_allowed, bool image_only, lwc_string **effective_type) { #define S(s) { s, SLEN(s) } static const struct tt_s { const char *data; size_t len; } text_types[] = { S("text/plain"), S("text/plain; charset=ISO-8859-1"), S("text/plain; charset=iso-8859-1"), S("text/plain; charset=UTF-8"), { NULL, 0 } }; #undef S const char *content_type_header; size_t content_type_header_len; http_content_type *ct; const struct tt_s *tt; bool match; nserror error; content_type_header = llcache_handle_get_header(handle, "Content-Type"); if (content_type_header == NULL) { if (sniff_allowed == false) return NSERROR_NOT_FOUND; /* No official type => unknown */ return mimesniff__compute_unknown(data, len, effective_type); } error = http_parse_content_type(content_type_header, &ct); if (error != NSERROR_OK) { if (sniff_allowed == false) return NSERROR_NOT_FOUND; /* Unparseable => unknown */ return mimesniff__compute_unknown(data, len, effective_type); } if (sniff_allowed == false) { *effective_type = lwc_string_ref(ct->media_type); http_content_type_destroy(ct); return NSERROR_OK; } if (image_only) { lwc_string *official_type; if (lwc_string_caseless_isequal(ct->media_type, image_svg, &match) == lwc_error_ok && match) { *effective_type = lwc_string_ref(image_svg); http_content_type_destroy(ct); return NSERROR_OK; } official_type = lwc_string_ref(ct->media_type); http_content_type_destroy(ct); return mimesniff__compute_image(official_type, data, len, effective_type); } content_type_header_len = strlen(content_type_header); /* Look for text types */ for (tt = text_types; tt->data != NULL; tt++) { if (tt->len == content_type_header_len && memcmp(tt->data, content_type_header, content_type_header_len) == 0) { http_content_type_destroy(ct); return mimesniff__compute_text_or_binary(data, len, effective_type); } } /* unknown/unknown, application/unknown, * / * */ if ((lwc_string_caseless_isequal(ct->media_type, unknown_unknown, &match) == lwc_error_ok && match) || (lwc_string_caseless_isequal(ct->media_type, application_unknown, &match) == lwc_error_ok && match) || (lwc_string_caseless_isequal(ct->media_type, any, &match) == lwc_error_ok && match)) { http_content_type_destroy(ct); return mimesniff__compute_unknown(data, len, effective_type); } /* +xml */ if (lwc_string_length(ct->media_type) > SLEN("+xml") && strncasecmp(lwc_string_data(ct->media_type) + lwc_string_length(ct->media_type) - SLEN("+xml"), "+xml", SLEN("+xml")) == 0) { /* Use official type */ *effective_type = lwc_string_ref(ct->media_type); http_content_type_destroy(ct); return NSERROR_OK; } /* text/xml, application/xml */ if ((lwc_string_caseless_isequal(ct->media_type, text_xml, &match) == lwc_error_ok && match) || (lwc_string_caseless_isequal(ct->media_type, application_xml, &match) == lwc_error_ok && match)) { /* Use official type */ *effective_type = lwc_string_ref(ct->media_type); http_content_type_destroy(ct); return NSERROR_OK; } /* Image types */ if (content_factory_type_from_mime_type(ct->media_type) == CONTENT_IMAGE) { lwc_string *official_type = lwc_string_ref(ct->media_type); http_content_type_destroy(ct); return mimesniff__compute_image(official_type, data, len, effective_type); } /* text/html */ if ((lwc_string_caseless_isequal(ct->media_type, text_html, &match) == lwc_error_ok && match)) { http_content_type_destroy(ct); return mimesniff__compute_feed_or_html(data, len, effective_type); } /* Use official type */ *effective_type = lwc_string_ref(ct->media_type); http_content_type_destroy(ct); return NSERROR_OK; }
/** * Clone a content's data members * * \param c Content to clone * \param nc Content to populate * \return NSERROR_OK on success, appropriate error otherwise */ nserror content__clone(const struct content *c, struct content *nc) { nserror error; error = llcache_handle_clone(c->llcache, &(nc->llcache)); if (error != NSERROR_OK) { return error; } llcache_handle_change_callback(nc->llcache, content_llcache_callback, nc); nc->mime_type = lwc_string_ref(c->mime_type); nc->handler = c->handler; nc->status = c->status; nc->width = c->width; nc->height = c->height; nc->available_width = c->available_width; nc->quirks = c->quirks; if (c->fallback_charset != NULL) { nc->fallback_charset = strdup(c->fallback_charset); if (nc->fallback_charset == NULL) { return NSERROR_NOMEM; } } if (c->refresh != NULL) { nc->refresh = nsurl_ref(c->refresh); if (nc->refresh == NULL) { return NSERROR_NOMEM; } } nc->time = c->time; nc->reformat_time = c->reformat_time; nc->size = c->size; if (c->title != NULL) { nc->title = strdup(c->title); if (nc->title == NULL) { return NSERROR_NOMEM; } } nc->active = c->active; nc->user_list = calloc(1, sizeof(struct content_user)); if (nc->user_list == NULL) { return NSERROR_NOMEM; } memcpy(&(nc->status_message), &(c->status_message), 120); memcpy(&(nc->sub_status), &(c->sub_status), 80); nc->locked = c->locked; nc->total_size = c->total_size; nc->http_code = c->http_code; return NSERROR_OK; }