/* * Copyright 2009 John-Mark Bell * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * NetSurf is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "content/content_protected.h" #include "content/fetch.h" #include "content/hlcache.h" #include "css/css.h" #include "css/internal.h" #include "desktop/gui.h" #include "render/html.h" #include "utils/utils.h" #include "utils/http.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/talloc.h" /* Define to trace import fetches */ #undef NSCSS_IMPORT_TRACE /** * CSS content data */ typedef struct nscss_content { struct content base; /**< Underlying content object */ struct content_css_data data; /**< CSS data */ } nscss_content; /** * Context for import fetches */ typedef struct { struct content_css_data *css; /**< Object containing import */ uint32_t index; /**< Index into parent sheet's * imports array */ } nscss_import_ctx; static nserror nscss_create(const content_handler *handler, lwc_string *imime_type, const http_parameter *params, llcache_handle *llcache, const char *fallback_charset, bool quirks, struct content **c); static bool nscss_process_data(struct content *c, const char *data, unsigned int size); static bool nscss_convert(struct content *c); static void nscss_destroy(struct content *c); static nserror nscss_clone(const struct content *old, struct content **newc); static bool nscss_matches_quirks(const struct content *c, bool quirks); static content_type nscss_content_type(void); static void nscss_content_done(struct content_css_data *css, void *pw); static css_error nscss_handle_import(void *pw, css_stylesheet *parent, lwc_string *url, uint64_t media); static nserror nscss_import(hlcache_handle *handle, const hlcache_event *event, void *pw); static css_error nscss_import_complete(nscss_import_ctx *ctx); static css_error nscss_register_imports(struct content_css_data *c); static css_error nscss_register_import(struct content_css_data *c, const hlcache_handle *import); static lwc_string *css_charset; static css_stylesheet *blank_import; dom_string *nscss_dom_string_a; dom_string *nscss_dom_string_abscenter; dom_string *nscss_dom_string_absmiddle; dom_string *nscss_dom_string_align; dom_string *nscss_dom_string_applet; dom_string *nscss_dom_string_background; dom_string *nscss_dom_string_bgcolor; dom_string *nscss_dom_string_body; dom_string *nscss_dom_string_bordercolor; dom_string *nscss_dom_string_bottom; dom_string *nscss_dom_string_caption; dom_string *nscss_dom_string_cellpadding; dom_string *nscss_dom_string_cellspacing; dom_string *nscss_dom_string_center; dom_string *nscss_dom_string_color; dom_string *nscss_dom_string_cols; dom_string *nscss_dom_string_div; dom_string *nscss_dom_string_embed; dom_string *nscss_dom_string_font; dom_string *nscss_dom_string_h1; dom_string *nscss_dom_string_h2; dom_string *nscss_dom_string_h3; dom_string *nscss_dom_string_h4; dom_string *nscss_dom_string_h5; dom_string *nscss_dom_string_h6; dom_string *nscss_dom_string_height; dom_string *nscss_dom_string_hr; dom_string *nscss_dom_string_href; dom_string *nscss_dom_string_hspace; dom_string *nscss_dom_string_iframe; dom_string *nscss_dom_string_img; dom_string *nscss_dom_string_input; dom_string *nscss_dom_string_justify; dom_string *nscss_dom_string_left; dom_string *nscss_dom_string_link; dom_string *nscss_dom_string_middle; dom_string *nscss_dom_string_object; dom_string *nscss_dom_string_p; dom_string *nscss_dom_string_password; dom_string *nscss_dom_string_right; dom_string *nscss_dom_string_rows; dom_string *nscss_dom_string_size; dom_string *nscss_dom_string_table; dom_string *nscss_dom_string_tbody; dom_string *nscss_dom_string_td; dom_string *nscss_dom_string_text; dom_string *nscss_dom_string_textarea; dom_string *nscss_dom_string_tfoot; dom_string *nscss_dom_string_th; dom_string *nscss_dom_string_thead; dom_string *nscss_dom_string_tr; dom_string *nscss_dom_string_type; dom_string *nscss_dom_string_valign; dom_string *nscss_dom_string_vlink; dom_string *nscss_dom_string_vspace; dom_string *nscss_dom_string_width; /** * Initialise a CSS content * * \param c Content to initialise * \param params Content-Type parameters * \return true on success, false on failure */ nserror nscss_create(const content_handler *handler, lwc_string *imime_type, const http_parameter *params, llcache_handle *llcache, const char *fallback_charset, bool quirks, struct content **c) { nscss_content *result; const char *charset = NULL; lwc_string *charset_value = NULL; union content_msg_data msg_data; nserror error; result = talloc_zero(0, nscss_content); if (result == NULL) return NSERROR_NOMEM; error = content__init(&result->base, handler, imime_type, params, llcache, fallback_charset, quirks); if (error != NSERROR_OK) { talloc_free(result); return error; } /* Find charset specified on HTTP layer, if any */ error = http_parameter_list_find_item(params, css_charset, &charset_value); if (error != NSERROR_OK || lwc_string_length(charset_value) == 0) { /* No charset specified, use fallback, if any */ /** \todo libcss will take this as gospel, which is wrong */ charset = fallback_charset; } else { charset = lwc_string_data(charset_value); } error = nscss_create_css_data(&result->data, nsurl_access(content_get_url(&result->base)), charset, result->base.quirks, nscss_content_done, result); if (error != NSERROR_OK) { msg_data.error = messages_get("NoMemory"); content_broadcast(&result->base, CONTENT_MSG_ERROR, msg_data); if (charset_value != NULL) lwc_string_unref(charset_value); talloc_free(result); return error; } if (charset_value != NULL) lwc_string_unref(charset_value); *c = (struct content *) result; return NSERROR_OK; } /** * Create a struct content_css_data, creating a stylesheet object * * \param c Struct to populate * \param url URL of stylesheet * \param charset Stylesheet charset * \param quirks Stylesheet quirks mode * \param done Callback to call when content has completed * \param pw Client data for \a done * \return NSERROR_OK on success, NSERROR_NOMEM on memory exhaustion */ nserror nscss_create_css_data(struct content_css_data *c, const char *url, const char *charset, bool quirks, nscss_done_callback done, void *pw) { css_error error; css_stylesheet_params params; c->pw = pw; c->done = done; c->next_to_register = (uint32_t) -1; c->import_count = 0; c->imports = NULL; if (charset != NULL) c->charset = strdup(charset); else c->charset = NULL; params.params_version = CSS_STYLESHEET_PARAMS_VERSION_1; params.level = CSS_LEVEL_DEFAULT; params.charset = charset; params.url = url; params.title = NULL; params.allow_quirks = quirks; params.inline_style = false; params.resolve = nscss_resolve_url; params.resolve_pw = NULL; params.import = nscss_handle_import; params.import_pw = c; params.color = gui_system_colour; params.color_pw = NULL; params.font = NULL; params.font_pw = NULL; error = css_stylesheet_create(¶ms, ns_realloc, NULL, &c->sheet); if (error != CSS_OK) { return NSERROR_NOMEM; } return NSERROR_OK; } /** * Process CSS source data * * \param c Content structure * \param data Data to process * \param size Number of bytes to process * \return true on success, false on failure */ bool nscss_process_data(struct content *c, const char *data, unsigned int size) { nscss_content *css = (nscss_content *) c; union content_msg_data msg_data; css_error error; error = nscss_process_css_data(&css->data, data, size); if (error != CSS_OK && error != CSS_NEEDDATA) { msg_data.error = "?"; content_broadcast(c, CONTENT_MSG_ERROR, msg_data); } return (error == CSS_OK || error == CSS_NEEDDATA); } /** * Process CSS data * * \param c CSS content object * \param data Data to process * \param size Number of bytes to process * \return CSS_OK on success, appropriate error otherwise */ css_error nscss_process_css_data(struct content_css_data *c, const char *data, unsigned int size) { return css_stylesheet_append_data(c->sheet, (const uint8_t *) data, size); } /** * Convert a CSS content ready for use * * \param c Content to convert * \return true on success, false on failure */ bool nscss_convert(struct content *c) { nscss_content *css = (nscss_content *) c; union content_msg_data msg_data; css_error error; error = nscss_convert_css_data(&css->data); if (error != CSS_OK) { msg_data.error = "?"; content_broadcast(c, CONTENT_MSG_ERROR, msg_data); return false; } return true; } /** * Convert CSS data ready for use * * \param c CSS data to convert * \return CSS error */ css_error nscss_convert_css_data(struct content_css_data *c) { css_error error; error = css_stylesheet_data_done(c->sheet); /* Process pending imports */ if (error == CSS_IMPORTS_PENDING) { /* We must not have registered any imports yet */ assert(c->next_to_register == (uint32_t) -1); /* Start registering, until we find one that * hasn't finished fetching */ c->next_to_register = 0; error = nscss_register_imports(c); } else if (error == CSS_OK) { /* No imports, and no errors, so complete conversion */ c->done(c, c->pw); } else { const char *url; if (css_stylesheet_get_url(c->sheet, &url) == CSS_OK) { LOG(("Failed converting %p %s (%d)", c, url, error)); } else { LOG(("Failed converting %p (%d)", c, error)); } } return error; } /** * Clean up a CSS content * * \param c Content to clean up */ void nscss_destroy(struct content *c) { nscss_content *css = (nscss_content *) c; nscss_destroy_css_data(&css->data); } /** * Clean up CSS data * * \param c CSS data to clean up */ void nscss_destroy_css_data(struct content_css_data *c) { uint32_t i; for (i = 0; i < c->import_count; i++) { if (c->imports[i].c != NULL) { hlcache_handle_release(c->imports[i].c); } c->imports[i].c = NULL; } free(c->imports); if (c->sheet != NULL) { css_stylesheet_destroy(c->sheet); c->sheet = NULL; } free(c->charset); } nserror nscss_clone(const struct content *old, struct content **newc) { const nscss_content *old_css = (const nscss_content *) old; nscss_content *new_css; const char *data; unsigned long size; nserror error; new_css = talloc_zero(0, nscss_content); if (new_css == NULL) return NSERROR_NOMEM; /* Clone content */ error = content__clone(old, &new_css->base); if (error != NSERROR_OK) { content_destroy(&new_css->base); return error; } /* Simply replay create/process/convert */ error = nscss_create_css_data(&new_css->data, nsurl_access(content_get_url(&new_css->base)), old_css->data.charset, new_css->base.quirks, nscss_content_done, new_css); if (error != NSERROR_OK) { content_destroy(&new_css->base); return error; } data = content__get_source_data(&new_css->base, &size); if (size > 0) { if (nscss_process_data(&new_css->base, data, size) == false) { content_destroy(&new_css->base); return NSERROR_CLONE_FAILED; } } if (old->status == CONTENT_STATUS_READY || old->status == CONTENT_STATUS_DONE) { if (nscss_convert(&new_css->base) == false) { content_destroy(&new_css->base); return NSERROR_CLONE_FAILED; } } *newc = (struct content *) new_css; return NSERROR_OK; } bool nscss_matches_quirks(const struct content *c, bool quirks) { return c->quirks == quirks; } /** * Retrieve the stylesheet object associated with a CSS content * * \param h Stylesheet content * \return Pointer to stylesheet object */ css_stylesheet *nscss_get_stylesheet(struct hlcache_handle *h) { nscss_content *c = (nscss_content *) hlcache_handle_get_content(h); assert(c != NULL); return c->data.sheet; } /** * Retrieve imported stylesheets * * \param h Stylesheet containing imports * \param n Pointer to location to receive number of imports * \return Pointer to array of imported stylesheets */ struct nscss_import *nscss_get_imports(hlcache_handle *h, uint32_t *n) { nscss_content *c = (nscss_content *) hlcache_handle_get_content(h); assert(c != NULL); assert(n != NULL); *n = c->data.import_count; return c->data.imports; } /** * Compute the type of a content * * \return CONTENT_CSS */ content_type nscss_content_type(void) { return CONTENT_CSS; } /***************************************************************************** * Object completion * *****************************************************************************/ /** * Handle notification that a CSS object is done * * \param css CSS object * \param pw Private data */ void nscss_content_done(struct content_css_data *css, void *pw) { union content_msg_data msg_data; struct content *c = pw; uint32_t i; size_t size; css_error error; /* Retrieve the size of this sheet */ error = css_stylesheet_size(css->sheet, &size); if (error != CSS_OK) { msg_data.error = "?"; content_broadcast(c, CONTENT_MSG_ERROR, msg_data); content_set_error(c); return; } c->size += size; /* Add on the size of the imported sheets */ for (i = 0; i < css->import_count; i++) { if (css->imports[i].c != NULL) { struct content *import = hlcache_handle_get_content( css->imports[i].c); if (import != NULL) { c->size += import->size; } } } /* Finally, catch the content's users up with reality */ content_set_ready(c); content_set_done(c); } /***************************************************************************** * Import handling * *****************************************************************************/ /** * Handle notification of the need for an imported stylesheet * * \param pw CSS object requesting the import * \param parent Stylesheet requesting the import * \param url URL of the imported sheet * \param media Applicable media for the imported sheet * \return CSS_OK on success, appropriate error otherwise */ css_error nscss_handle_import(void *pw, css_stylesheet *parent, lwc_string *url, uint64_t media) { content_type accept = CONTENT_CSS; struct content_css_data *c = pw; nscss_import_ctx *ctx; hlcache_child_context child; struct nscss_import *imports; const char *referer; css_error error; nserror nerror; nsurl *ns_url; nsurl *ns_ref; assert(parent == c->sheet); error = css_stylesheet_get_url(c->sheet, &referer); if (error != CSS_OK) { return error; } ctx = malloc(sizeof(*ctx)); if (ctx == NULL) return CSS_NOMEM; ctx->css = c; ctx->index = c->import_count; /* Increase space in table */ imports = realloc(c->imports, (c->import_count + 1) * sizeof(struct nscss_import)); if (imports == NULL) { free(ctx); return CSS_NOMEM; } c->imports = imports; /** \todo fallback charset */ child.charset = NULL; error = css_stylesheet_quirks_allowed(c->sheet, &child.quirks); if (error != CSS_OK) { free(ctx); return error; } /* Create content */ c->imports[c->import_count].media = media; /* TODO: Why aren't we getting a relative url part, to join? */ nerror = nsurl_create(lwc_string_data(url), &ns_url); if (nerror != NSERROR_OK) { free(ctx); return CSS_NOMEM; } /* TODO: Constructing nsurl for referer here is silly, avoid */ nerror = nsurl_create(referer, &ns_ref); if (nerror != NSERROR_OK) { nsurl_unref(ns_url); free(ctx); return CSS_NOMEM; } /* Avoid importing ourself */ if (nsurl_compare(ns_url, ns_ref, NSURL_COMPLETE)) { c->imports[c->import_count].c = NULL; /* No longer require context as we're not fetching anything */ free(ctx); ctx = NULL; } else { nerror = hlcache_handle_retrieve(ns_url, 0, ns_ref, NULL, nscss_import, ctx, &child, accept, &c->imports[c->import_count].c); if (nerror != NSERROR_OK) { free(ctx); return CSS_NOMEM; } } nsurl_unref(ns_url); nsurl_unref(ns_ref); #ifdef NSCSS_IMPORT_TRACE LOG(("Import %d '%s' -> (handle: %p ctx: %p)", c->import_count, lwc_string_data(url), c->imports[c->import_count].c, ctx)); #endif c->import_count++; return CSS_OK; } /** * Handler for imported stylesheet events * * \param handle Handle for stylesheet * \param event Event object * \param pw Callback context * \return NSERROR_OK on success, appropriate error otherwise */ nserror nscss_import(hlcache_handle *handle, const hlcache_event *event, void *pw) { nscss_import_ctx *ctx = pw; css_error error = CSS_OK; #ifdef NSCSS_IMPORT_TRACE LOG(("Event %d for %p (%p)", event->type, handle, ctx)); #endif assert(ctx->css->imports[ctx->index].c == handle); switch (event->type) { case CONTENT_MSG_LOADING: break; case CONTENT_MSG_READY: break; case CONTENT_MSG_DONE: error = nscss_import_complete(ctx); break; case CONTENT_MSG_ERROR: hlcache_handle_release(handle); ctx->css->imports[ctx->index].c = NULL; error = nscss_import_complete(ctx); /* Already released handle */ break; case CONTENT_MSG_STATUS: break; default: assert(0); } /* Preserve out-of-memory. Anything else is OK */ return error == CSS_NOMEM ? NSERROR_NOMEM : NSERROR_OK; } /** * Handle an imported stylesheet completing * * \param ctx Import context * \return CSS_OK on success, appropriate error otherwise */ css_error nscss_import_complete(nscss_import_ctx *ctx) { css_error error = CSS_OK; /* If this import is the next to be registered, do so */ if (ctx->css->next_to_register == ctx->index) error = nscss_register_imports(ctx->css); #ifdef NSCSS_IMPORT_TRACE LOG(("Destroying import context %p for %d", ctx, ctx->index)); #endif /* No longer need import context */ free(ctx); return error; } /***************************************************************************** * Import registration * *****************************************************************************/ /** * Register imports with a stylesheet * * \param c CSS object containing the imports * \return CSS_OK on success, appropriate error otherwise */ css_error nscss_register_imports(struct content_css_data *c) { uint32_t index; css_error error; assert(c->next_to_register != (uint32_t) -1); assert(c->next_to_register < c->import_count); /* Register imported sheets */ for (index = c->next_to_register; index < c->import_count; index++) { /* Stop registering if we encounter one whose fetch hasn't * completed yet. We'll resume at this point when it has * completed. */ if (c->imports[index].c != NULL && content_get_status(c->imports[index].c) != CONTENT_STATUS_DONE) { break; } error = nscss_register_import(c, c->imports[index].c); if (error != CSS_OK) return error; } /* Record identity of the next import to register */ c->next_to_register = (uint32_t) index; if (c->next_to_register == c->import_count) { /* No more imports: notify parent that we're DONE */ c->done(c, c->pw); } return CSS_OK; } /** * Register an import with a stylesheet * * \param c CSS object that requested the import * \param import Cache handle of import, or NULL for blank * \return CSS_OK on success, appropriate error otherwise */ css_error nscss_register_import(struct content_css_data *c, const hlcache_handle *import) { css_stylesheet *sheet; css_error error; if (import != NULL) { nscss_content *s = (nscss_content *) hlcache_handle_get_content(import); sheet = s->data.sheet; } else { /* Create a blank sheet if needed. */ if (blank_import == NULL) { css_stylesheet_params params; params.params_version = CSS_STYLESHEET_PARAMS_VERSION_1; params.level = CSS_LEVEL_DEFAULT; params.charset = NULL; params.url = ""; params.title = NULL; params.allow_quirks = false; params.inline_style = false; params.resolve = nscss_resolve_url; params.resolve_pw = NULL; params.import = NULL; params.import_pw = NULL; params.color = gui_system_colour; params.color_pw = NULL; params.font = NULL; params.font_pw = NULL; error = css_stylesheet_create(¶ms, ns_realloc, NULL, &blank_import); if (error != CSS_OK) { return error; } error = css_stylesheet_data_done(blank_import); if (error != CSS_OK) { css_stylesheet_destroy(blank_import); return error; } } sheet = blank_import; } error = css_stylesheet_register_import(c->sheet, sheet); if (error != CSS_OK) { return error; } return error; } /** * Clean up after the CSS content handler */ static void nscss_fini(void) { #define CSS_DOM_STRING_UNREF(NAME) \ do { \ if (nscss_dom_string_##NAME != NULL) { \ dom_string_unref(nscss_dom_string_##NAME); \ nscss_dom_string_##NAME = NULL; \ } \ } while (0) \ CSS_DOM_STRING_UNREF(a); CSS_DOM_STRING_UNREF(abscenter); CSS_DOM_STRING_UNREF(absmiddle); CSS_DOM_STRING_UNREF(align); CSS_DOM_STRING_UNREF(applet); CSS_DOM_STRING_UNREF(background); CSS_DOM_STRING_UNREF(bgcolor); CSS_DOM_STRING_UNREF(body); CSS_DOM_STRING_UNREF(bordercolor); CSS_DOM_STRING_UNREF(bottom); CSS_DOM_STRING_UNREF(caption); CSS_DOM_STRING_UNREF(cellpadding); CSS_DOM_STRING_UNREF(cellspacing); CSS_DOM_STRING_UNREF(center); CSS_DOM_STRING_UNREF(color); CSS_DOM_STRING_UNREF(cols); CSS_DOM_STRING_UNREF(div); CSS_DOM_STRING_UNREF(embed); CSS_DOM_STRING_UNREF(font); CSS_DOM_STRING_UNREF(h1); CSS_DOM_STRING_UNREF(h2); CSS_DOM_STRING_UNREF(h3); CSS_DOM_STRING_UNREF(h4); CSS_DOM_STRING_UNREF(h5); CSS_DOM_STRING_UNREF(h6); CSS_DOM_STRING_UNREF(height); CSS_DOM_STRING_UNREF(hr); CSS_DOM_STRING_UNREF(href); CSS_DOM_STRING_UNREF(hspace); CSS_DOM_STRING_UNREF(iframe); CSS_DOM_STRING_UNREF(img); CSS_DOM_STRING_UNREF(input); CSS_DOM_STRING_UNREF(justify); CSS_DOM_STRING_UNREF(left); CSS_DOM_STRING_UNREF(link); CSS_DOM_STRING_UNREF(middle); CSS_DOM_STRING_UNREF(object); CSS_DOM_STRING_UNREF(p); CSS_DOM_STRING_UNREF(password); CSS_DOM_STRING_UNREF(right); CSS_DOM_STRING_UNREF(rows); CSS_DOM_STRING_UNREF(size); CSS_DOM_STRING_UNREF(table); CSS_DOM_STRING_UNREF(tbody); CSS_DOM_STRING_UNREF(td); CSS_DOM_STRING_UNREF(text); CSS_DOM_STRING_UNREF(textarea); CSS_DOM_STRING_UNREF(tfoot); CSS_DOM_STRING_UNREF(th); CSS_DOM_STRING_UNREF(thead); CSS_DOM_STRING_UNREF(tr); CSS_DOM_STRING_UNREF(type); CSS_DOM_STRING_UNREF(valign); CSS_DOM_STRING_UNREF(vlink); CSS_DOM_STRING_UNREF(vspace); CSS_DOM_STRING_UNREF(width); #undef CSS_DOM_STRING_UNREF if (css_charset != NULL) { lwc_string_unref(css_charset); css_charset = NULL; } if (blank_import != NULL) { css_stylesheet_destroy(blank_import); blank_import = NULL; } } static const content_handler css_content_handler = { .fini = nscss_fini, .create = nscss_create, .process_data = nscss_process_data, .data_complete = nscss_convert, .destroy = nscss_destroy, .clone = nscss_clone, .matches_quirks = nscss_matches_quirks, .type = nscss_content_type, .no_share = false, }; /** * Initialise the CSS content handler */ nserror nscss_init(void) { lwc_error lerror; nserror error; dom_exception exc; lerror = lwc_intern_string("charset", SLEN("charset"), &css_charset); if (lerror != lwc_error_ok) { error = NSERROR_NOMEM; goto error; } #define CSS_DOM_STRING_INTERN(NAME) \ exc = dom_string_create_interned((const uint8_t *)#NAME, \ sizeof(#NAME) - 1, \ &nscss_dom_string_##NAME ); \ if ((exc != DOM_NO_ERR) || (nscss_dom_string_##NAME == NULL)) \ goto error CSS_DOM_STRING_INTERN(a); CSS_DOM_STRING_INTERN(abscenter); CSS_DOM_STRING_INTERN(absmiddle); CSS_DOM_STRING_INTERN(align); CSS_DOM_STRING_INTERN(applet); CSS_DOM_STRING_INTERN(background); CSS_DOM_STRING_INTERN(bgcolor); CSS_DOM_STRING_INTERN(body); CSS_DOM_STRING_INTERN(bordercolor); CSS_DOM_STRING_INTERN(bottom); CSS_DOM_STRING_INTERN(caption); CSS_DOM_STRING_INTERN(cellpadding); CSS_DOM_STRING_INTERN(cellspacing); CSS_DOM_STRING_INTERN(center); CSS_DOM_STRING_INTERN(color); CSS_DOM_STRING_INTERN(cols); CSS_DOM_STRING_INTERN(div); CSS_DOM_STRING_INTERN(embed); CSS_DOM_STRING_INTERN(font); CSS_DOM_STRING_INTERN(h1); CSS_DOM_STRING_INTERN(h2); CSS_DOM_STRING_INTERN(h3); CSS_DOM_STRING_INTERN(h4); CSS_DOM_STRING_INTERN(h5); CSS_DOM_STRING_INTERN(h6); CSS_DOM_STRING_INTERN(height); CSS_DOM_STRING_INTERN(hr); CSS_DOM_STRING_INTERN(href); CSS_DOM_STRING_INTERN(hspace); CSS_DOM_STRING_INTERN(iframe); CSS_DOM_STRING_INTERN(img); CSS_DOM_STRING_INTERN(input); CSS_DOM_STRING_INTERN(justify); CSS_DOM_STRING_INTERN(left); CSS_DOM_STRING_INTERN(link); CSS_DOM_STRING_INTERN(middle); CSS_DOM_STRING_INTERN(object); CSS_DOM_STRING_INTERN(p); CSS_DOM_STRING_INTERN(password); CSS_DOM_STRING_INTERN(right); CSS_DOM_STRING_INTERN(rows); CSS_DOM_STRING_INTERN(size); CSS_DOM_STRING_INTERN(table); CSS_DOM_STRING_INTERN(tbody); CSS_DOM_STRING_INTERN(td); CSS_DOM_STRING_INTERN(text); CSS_DOM_STRING_INTERN(textarea); CSS_DOM_STRING_INTERN(tfoot); CSS_DOM_STRING_INTERN(th); CSS_DOM_STRING_INTERN(thead); CSS_DOM_STRING_INTERN(tr); CSS_DOM_STRING_INTERN(type); CSS_DOM_STRING_INTERN(valign); CSS_DOM_STRING_INTERN(vlink); CSS_DOM_STRING_INTERN(vspace); CSS_DOM_STRING_INTERN(width); #undef CSS_DOM_STRING_INTERN error = content_factory_register_handler("text/css", &css_content_handler); if (error != NSERROR_OK) goto error; return NSERROR_OK; error: nscss_fini(); return error; }