mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-11-26 16:29:36 +03:00
refactor stylesheet handling to separate object from within html rendering
This commit is contained in:
parent
bba74b7a0b
commit
cc7f45898b
@ -13,9 +13,9 @@ S_CSS := css.c dump.c internal.c select.c utils.c
|
||||
|
||||
S_RENDER := box.c box_construct.c box_normalise.c box_textarea.c \
|
||||
font.c form.c \
|
||||
html.c html_script.c html_interaction.c html_redraw.c \
|
||||
html_forms.c imagemap.c layout.c list.c search.c table.c \
|
||||
textplain.c
|
||||
html.c html_css.c html_script.c html_interaction.c \
|
||||
html_redraw.c html_forms.c imagemap.c layout.c list.c \
|
||||
search.c table.c textplain.c
|
||||
|
||||
S_UTILS := base64.c corestrings.c filename.c filepath.c hashtable.c \
|
||||
libdom.c locale.c log.c messages.c nsurl.c talloc.c url.c \
|
||||
|
682
render/html.c
682
render/html.c
@ -72,48 +72,6 @@ static const char *html_types[] = {
|
||||
/* forward declared functions */
|
||||
static void html_object_refresh(void *p);
|
||||
|
||||
/* pre-interned character set */
|
||||
static lwc_string *html_charset;
|
||||
|
||||
static nsurl *html_default_stylesheet_url;
|
||||
static nsurl *html_adblock_stylesheet_url;
|
||||
static nsurl *html_quirks_stylesheet_url;
|
||||
static nsurl *html_user_stylesheet_url;
|
||||
|
||||
static nserror css_error_to_nserror(css_error error)
|
||||
{
|
||||
switch (error) {
|
||||
case CSS_OK:
|
||||
return NSERROR_OK;
|
||||
|
||||
case CSS_NOMEM:
|
||||
return NSERROR_NOMEM;
|
||||
|
||||
case CSS_BADPARM:
|
||||
return NSERROR_BAD_PARAMETER;
|
||||
|
||||
case CSS_INVALID:
|
||||
return NSERROR_INVALID;
|
||||
|
||||
case CSS_FILENOTFOUND:
|
||||
return NSERROR_NOT_FOUND;
|
||||
|
||||
case CSS_NEEDDATA:
|
||||
return NSERROR_NEED_DATA;
|
||||
|
||||
case CSS_BADCHARSET:
|
||||
return NSERROR_BAD_ENCODING;
|
||||
|
||||
case CSS_EOF:
|
||||
case CSS_IMPORTS_PENDING:
|
||||
case CSS_PROPERTY_NOT_SET:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NSERROR_CSS;
|
||||
}
|
||||
|
||||
|
||||
static void html_destroy_objects(html_content *html)
|
||||
{
|
||||
while (html->object_list != NULL) {
|
||||
@ -218,8 +176,6 @@ void html_finish_conversion(html_content *c)
|
||||
union content_msg_data msg_data;
|
||||
dom_exception exc; /* returned by libdom functions */
|
||||
dom_node *html;
|
||||
uint32_t i;
|
||||
css_error css_ret;
|
||||
nserror error;
|
||||
|
||||
/* Bail out if we've been aborted */
|
||||
@ -229,55 +185,14 @@ void html_finish_conversion(html_content *c)
|
||||
return;
|
||||
}
|
||||
|
||||
/* check that the base stylesheet loaded; layout fails without it */
|
||||
if (c->stylesheets[STYLESHEET_BASE].data.external == NULL) {
|
||||
content_broadcast_errorcode(&c->base, NSERROR_CSS_BASE);
|
||||
/* create new css selection context */
|
||||
error = html_css_new_selection_context(c, &c->select_ctx);
|
||||
if (error != NSERROR_OK) {
|
||||
content_broadcast_errorcode(&c->base, error);
|
||||
content_set_error(&c->base);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create selection context */
|
||||
css_ret = css_select_ctx_create(ns_realloc, c, &c->select_ctx);
|
||||
if (css_ret != CSS_OK) {
|
||||
content_broadcast_errorcode(&c->base,
|
||||
css_error_to_nserror(css_ret));
|
||||
content_set_error(&c->base);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Add sheets to it */
|
||||
for (i = STYLESHEET_BASE; i != c->stylesheet_count; i++) {
|
||||
const struct html_stylesheet *hsheet = &c->stylesheets[i];
|
||||
css_stylesheet *sheet;
|
||||
css_origin origin = CSS_ORIGIN_AUTHOR;
|
||||
|
||||
if (i < STYLESHEET_USER)
|
||||
origin = CSS_ORIGIN_UA;
|
||||
else if (i < STYLESHEET_START)
|
||||
origin = CSS_ORIGIN_USER;
|
||||
|
||||
if (hsheet->type == HTML_STYLESHEET_EXTERNAL &&
|
||||
hsheet->data.external != NULL) {
|
||||
sheet = nscss_get_stylesheet(hsheet->data.external);
|
||||
} else if (hsheet->type == HTML_STYLESHEET_INTERNAL) {
|
||||
sheet = hsheet->data.internal->sheet;
|
||||
} else {
|
||||
sheet = NULL;
|
||||
}
|
||||
|
||||
if (sheet != NULL) {
|
||||
css_ret = css_select_ctx_append_sheet(c->select_ctx,
|
||||
sheet,
|
||||
origin,
|
||||
CSS_MEDIA_SCREEN);
|
||||
if (css_ret != CSS_OK) {
|
||||
content_broadcast_errorcode(&c->base,
|
||||
css_error_to_nserror(css_ret));
|
||||
content_set_error(&c->base);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* fire a simple event named load at the Document's Window
|
||||
* object, but with its target set to the Document object (and
|
||||
@ -311,492 +226,6 @@ void html_finish_conversion(html_content *c)
|
||||
dom_node_unref(html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for fetchcache() for linked stylesheets.
|
||||
*/
|
||||
|
||||
static nserror
|
||||
html_convert_css_callback(hlcache_handle *css,
|
||||
const hlcache_event *event,
|
||||
void *pw)
|
||||
{
|
||||
html_content *parent = pw;
|
||||
unsigned int i;
|
||||
struct html_stylesheet *s;
|
||||
|
||||
/* Find sheet */
|
||||
for (i = 0, s = parent->stylesheets;
|
||||
i != parent->stylesheet_count; i++, s++) {
|
||||
if (s->type == HTML_STYLESHEET_EXTERNAL &&
|
||||
s->data.external == css)
|
||||
break;
|
||||
}
|
||||
|
||||
assert(i != parent->stylesheet_count);
|
||||
|
||||
switch (event->type) {
|
||||
case CONTENT_MSG_LOADING:
|
||||
break;
|
||||
|
||||
case CONTENT_MSG_READY:
|
||||
break;
|
||||
|
||||
case CONTENT_MSG_DONE:
|
||||
LOG(("done stylesheet slot %d '%s'", i,
|
||||
nsurl_access(hlcache_handle_get_url(css))));
|
||||
parent->base.active--;
|
||||
LOG(("%d fetches active", parent->base.active));
|
||||
break;
|
||||
|
||||
case CONTENT_MSG_ERROR:
|
||||
LOG(("stylesheet %s failed: %s",
|
||||
nsurl_access(hlcache_handle_get_url(css)),
|
||||
event->data.error));
|
||||
hlcache_handle_release(css);
|
||||
s->data.external = NULL;
|
||||
parent->base.active--;
|
||||
LOG(("%d fetches active", parent->base.active));
|
||||
content_add_error(&parent->base, "?", 0);
|
||||
break;
|
||||
|
||||
case CONTENT_MSG_STATUS:
|
||||
if (event->data.explicit_status_text == NULL) {
|
||||
/* Object content's status text updated */
|
||||
html_set_status(parent,
|
||||
content_get_status_message(css));
|
||||
content_broadcast(&parent->base, CONTENT_MSG_STATUS,
|
||||
event->data);
|
||||
} else {
|
||||
/* Object content wants to set explicit message */
|
||||
content_broadcast(&parent->base, CONTENT_MSG_STATUS,
|
||||
event->data);
|
||||
}
|
||||
break;
|
||||
|
||||
case CONTENT_MSG_POINTER:
|
||||
/* Really don't want this to continue after the switch */
|
||||
return NSERROR_OK;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (parent->base.active == 0) {
|
||||
html_begin_conversion(parent);
|
||||
}
|
||||
|
||||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise core stylesheets
|
||||
*
|
||||
* \param c content structure
|
||||
* \return true on success, false if an error occurred
|
||||
*/
|
||||
|
||||
static bool html_init_stylesheets(html_content *c)
|
||||
{
|
||||
nserror ns_error;
|
||||
hlcache_child_context child;
|
||||
|
||||
if (c->stylesheets != NULL) {
|
||||
return true; /* already initialised */
|
||||
}
|
||||
|
||||
/* stylesheet 0 is the base style sheet,
|
||||
* stylesheet 1 is the quirks mode style sheet,
|
||||
* stylesheet 2 is the adblocking stylesheet,
|
||||
* stylesheet 3 is the user stylesheet */
|
||||
c->stylesheets = calloc(STYLESHEET_START, sizeof(struct html_stylesheet));
|
||||
if (c->stylesheets == NULL) {
|
||||
ns_error = NSERROR_NOMEM;
|
||||
goto html_find_stylesheets_no_memory;
|
||||
}
|
||||
|
||||
c->stylesheets[STYLESHEET_BASE].type = HTML_STYLESHEET_EXTERNAL;
|
||||
c->stylesheets[STYLESHEET_BASE].data.external = NULL;
|
||||
c->stylesheets[STYLESHEET_QUIRKS].type = HTML_STYLESHEET_EXTERNAL;
|
||||
c->stylesheets[STYLESHEET_QUIRKS].data.external = NULL;
|
||||
c->stylesheets[STYLESHEET_ADBLOCK].type = HTML_STYLESHEET_EXTERNAL;
|
||||
c->stylesheets[STYLESHEET_ADBLOCK].data.external = NULL;
|
||||
c->stylesheets[STYLESHEET_USER].type = HTML_STYLESHEET_EXTERNAL;
|
||||
c->stylesheets[STYLESHEET_USER].data.external = NULL;
|
||||
c->stylesheet_count = STYLESHEET_START;
|
||||
|
||||
child.charset = c->encoding;
|
||||
child.quirks = c->base.quirks;
|
||||
|
||||
ns_error = hlcache_handle_retrieve(html_default_stylesheet_url, 0,
|
||||
content_get_url(&c->base), NULL,
|
||||
html_convert_css_callback, c, &child, CONTENT_CSS,
|
||||
&c->stylesheets[STYLESHEET_BASE].data.external);
|
||||
if (ns_error != NSERROR_OK)
|
||||
goto html_find_stylesheets_no_memory;
|
||||
|
||||
c->base.active++;
|
||||
LOG(("%d fetches active", c->base.active));
|
||||
|
||||
if (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL) {
|
||||
ns_error = hlcache_handle_retrieve(html_quirks_stylesheet_url,
|
||||
0, content_get_url(&c->base), NULL,
|
||||
html_convert_css_callback, c, &child,
|
||||
CONTENT_CSS,
|
||||
&c->stylesheets[STYLESHEET_QUIRKS].data.external);
|
||||
if (ns_error != NSERROR_OK)
|
||||
goto html_find_stylesheets_no_memory;
|
||||
|
||||
c->base.active++;
|
||||
LOG(("%d fetches active", c->base.active));
|
||||
|
||||
}
|
||||
|
||||
if (nsoption_bool(block_ads)) {
|
||||
ns_error = hlcache_handle_retrieve(html_adblock_stylesheet_url,
|
||||
0, content_get_url(&c->base), NULL,
|
||||
html_convert_css_callback, c, &child, CONTENT_CSS,
|
||||
&c->stylesheets[STYLESHEET_ADBLOCK].
|
||||
data.external);
|
||||
if (ns_error != NSERROR_OK)
|
||||
goto html_find_stylesheets_no_memory;
|
||||
|
||||
c->base.active++;
|
||||
LOG(("%d fetches active", c->base.active));
|
||||
|
||||
}
|
||||
|
||||
ns_error = hlcache_handle_retrieve(html_user_stylesheet_url, 0,
|
||||
content_get_url(&c->base), NULL,
|
||||
html_convert_css_callback, c, &child, CONTENT_CSS,
|
||||
&c->stylesheets[STYLESHEET_USER].data.external);
|
||||
if (ns_error != NSERROR_OK)
|
||||
goto html_find_stylesheets_no_memory;
|
||||
|
||||
c->base.active++;
|
||||
LOG(("%d fetches active", c->base.active));
|
||||
|
||||
return true;
|
||||
|
||||
html_find_stylesheets_no_memory:
|
||||
content_broadcast_errorcode(&c->base, ns_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle notification of inline style completion
|
||||
*
|
||||
* \param css Inline style object
|
||||
* \param pw Private data
|
||||
*/
|
||||
static void html_inline_style_done(struct content_css_data *css, void *pw)
|
||||
{
|
||||
html_content *html = pw;
|
||||
|
||||
html->base.active--;
|
||||
LOG(("%d fetches active", html->base.active));
|
||||
}
|
||||
|
||||
static nserror
|
||||
html_stylesheet_from_domnode(html_content *c,
|
||||
dom_node *node,
|
||||
struct content_css_data **ret_sheet)
|
||||
{
|
||||
dom_node *child, *next;
|
||||
dom_exception exc;
|
||||
struct content_css_data *sheet;
|
||||
nserror error;
|
||||
css_error csserror;
|
||||
|
||||
/* create stylesheet */
|
||||
sheet = calloc(1, sizeof(struct content_css_data));
|
||||
if (sheet == NULL) {
|
||||
return NSERROR_NOMEM;
|
||||
}
|
||||
|
||||
error = nscss_create_css_data(sheet,
|
||||
nsurl_access(c->base_url), NULL, c->quirks,
|
||||
html_inline_style_done, c);
|
||||
if (error != NSERROR_OK) {
|
||||
free(sheet);
|
||||
return error;
|
||||
}
|
||||
|
||||
exc = dom_node_get_first_child(node, &child);
|
||||
if (exc != DOM_NO_ERR) {
|
||||
nscss_destroy_css_data(sheet);
|
||||
free(sheet);
|
||||
return NSERROR_DOM;
|
||||
}
|
||||
|
||||
while (child != NULL) {
|
||||
dom_string *data;
|
||||
|
||||
exc = dom_node_get_text_content(child, &data);
|
||||
if (exc != DOM_NO_ERR) {
|
||||
dom_node_unref(child);
|
||||
nscss_destroy_css_data(sheet);
|
||||
free(sheet);
|
||||
return NSERROR_DOM;
|
||||
}
|
||||
|
||||
if (nscss_process_css_data(sheet,
|
||||
dom_string_data(data),
|
||||
dom_string_byte_length(data)) == false) {
|
||||
dom_string_unref(data);
|
||||
dom_node_unref(child);
|
||||
nscss_destroy_css_data(sheet);
|
||||
free(sheet);
|
||||
return NSERROR_CSS;
|
||||
}
|
||||
|
||||
dom_string_unref(data);
|
||||
|
||||
exc = dom_node_get_next_sibling(child, &next);
|
||||
if (exc != DOM_NO_ERR) {
|
||||
dom_node_unref(child);
|
||||
nscss_destroy_css_data(sheet);
|
||||
free(sheet);
|
||||
return NSERROR_DOM;
|
||||
}
|
||||
|
||||
dom_node_unref(child);
|
||||
child = next;
|
||||
}
|
||||
|
||||
c->base.active++;
|
||||
LOG(("%d fetches active", c->base.active));
|
||||
|
||||
/* Convert the content -- manually, as we want the result */
|
||||
csserror = nscss_convert_css_data(sheet);
|
||||
if (csserror != CSS_OK) {
|
||||
/* conversion failed */
|
||||
c->base.active--;
|
||||
LOG(("%d fetches active", c->base.active));
|
||||
nscss_destroy_css_data(sheet);
|
||||
free(sheet);
|
||||
return css_error_to_nserror(csserror);
|
||||
}
|
||||
|
||||
*ret_sheet = sheet;
|
||||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an inline stylesheet in the document.
|
||||
*
|
||||
* \param c content structure
|
||||
* \param style xml node of style element
|
||||
* \return true on success, false if an error occurred
|
||||
*/
|
||||
|
||||
static struct html_stylesheet *
|
||||
html_create_style_element(html_content *c, dom_node *style)
|
||||
{
|
||||
dom_string *val;
|
||||
dom_exception exc;
|
||||
struct html_stylesheet *stylesheets;
|
||||
|
||||
/* ensure sheets are initialised */
|
||||
if (html_init_stylesheets(c) == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* type='text/css', or not present (invalid but common) */
|
||||
exc = dom_element_get_attribute(style, corestring_dom_type, &val);
|
||||
if (exc == DOM_NO_ERR && val != NULL) {
|
||||
if (!dom_string_caseless_lwc_isequal(val,
|
||||
corestring_lwc_text_css)) {
|
||||
dom_string_unref(val);
|
||||
return NULL;
|
||||
}
|
||||
dom_string_unref(val);
|
||||
}
|
||||
|
||||
/* media contains 'screen' or 'all' or not present */
|
||||
exc = dom_element_get_attribute(style, corestring_dom_media, &val);
|
||||
if (exc == DOM_NO_ERR && val != NULL) {
|
||||
if (strcasestr(dom_string_data(val), "screen") == NULL &&
|
||||
strcasestr(dom_string_data(val),
|
||||
"all") == NULL) {
|
||||
dom_string_unref(val);
|
||||
return NULL;
|
||||
}
|
||||
dom_string_unref(val);
|
||||
}
|
||||
|
||||
/* Extend array */
|
||||
stylesheets = realloc(c->stylesheets,
|
||||
sizeof(struct html_stylesheet) * (c->stylesheet_count + 1));
|
||||
if (stylesheets == NULL) {
|
||||
|
||||
content_broadcast_errorcode(&c->base, NSERROR_NOMEM);
|
||||
return false;
|
||||
|
||||
}
|
||||
c->stylesheets = stylesheets;
|
||||
|
||||
c->stylesheets[c->stylesheet_count].type = HTML_STYLESHEET_INTERNAL;
|
||||
c->stylesheets[c->stylesheet_count].node = style;
|
||||
c->stylesheets[c->stylesheet_count].data.internal = NULL;
|
||||
c->stylesheet_count++;
|
||||
|
||||
return c->stylesheets + (c->stylesheet_count - 1);
|
||||
}
|
||||
|
||||
static bool html_process_style_element_update(html_content *c, dom_node *style)
|
||||
{
|
||||
struct content_css_data *sheet = NULL;
|
||||
nserror error;
|
||||
unsigned int i;
|
||||
struct html_stylesheet *s;
|
||||
|
||||
/* Find sheet */
|
||||
for (i = 0, s = c->stylesheets; i != c->stylesheet_count; i++, s++) {
|
||||
if ((s->type == HTML_STYLESHEET_INTERNAL) &&
|
||||
(s->node == style))
|
||||
break;
|
||||
}
|
||||
if (i == c->stylesheet_count) {
|
||||
s = html_create_style_element(c, style);
|
||||
}
|
||||
if (s == NULL) {
|
||||
LOG(("Could not find or create inline stylesheet for %p",
|
||||
style));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(("Found sheet %p slot %d for node %p", s,i, style));
|
||||
|
||||
error = html_stylesheet_from_domnode(c, style, &sheet);
|
||||
if (error != NSERROR_OK) {
|
||||
LOG(("Failed to update sheet"));
|
||||
content_broadcast_errorcode(&c->base, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(("Updating sheet %p with %p", s->data.internal, sheet));
|
||||
|
||||
/* Update index */
|
||||
if (s->data.internal != NULL) {
|
||||
nscss_destroy_css_data(s->data.internal);
|
||||
free(s->data.internal);
|
||||
}
|
||||
s->data.internal = sheet;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool html_process_stylesheet_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;
|
||||
|
||||
/* ensure sheets are initialised */
|
||||
if (html_init_stylesheets(htmlc) == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 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].type = HTML_STYLESHEET_EXTERNAL;
|
||||
|
||||
/* 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].data.external);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* callback for DOMNodeInserted end type */
|
||||
static void
|
||||
dom_default_action_DOMNodeInserted_cb(struct dom_event *evt, void *pw)
|
||||
@ -816,7 +245,7 @@ dom_default_action_DOMNodeInserted_cb(struct dom_event *evt, void *pw)
|
||||
if ((exc == DOM_NO_ERR) && (name != NULL)) {
|
||||
/* LOG(("element htmlc:%p node %p name:%s", htmlc, node, dom_string_data(name))); */
|
||||
if (dom_string_caseless_isequal(name, corestring_dom_link)) {
|
||||
html_process_stylesheet_link(htmlc, (dom_node *)node);
|
||||
html_css_process_link(htmlc, (dom_node *)node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -842,7 +271,7 @@ dom_default_action_DOMSubtreeModified_cb(struct dom_event *evt, void *pw)
|
||||
if ((exc == DOM_NO_ERR) && (name != NULL)) {
|
||||
/* LOG(("element htmlc:%p node:%p name:%s", htmlc, node, dom_string_data(name))); */
|
||||
if (dom_string_caseless_isequal(name, corestring_dom_style)) {
|
||||
html_process_style_element_update(htmlc, (dom_node *)node);
|
||||
html_css_update_style(htmlc, (dom_node *)node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -922,7 +351,7 @@ html_create_html_data(html_content *c, const http_parameter *params)
|
||||
|
||||
selection_prepare(&c->sel, (struct content *)c, true);
|
||||
|
||||
nerror = http_parameter_list_find_item(params, html_charset, &charset);
|
||||
nerror = http_parameter_list_find_item(params, corestring_lwc_charset, &charset);
|
||||
if (nerror == NSERROR_OK) {
|
||||
c->encoding = strdup(lwc_string_data(charset));
|
||||
|
||||
@ -961,7 +390,6 @@ html_create_html_data(html_content *c, const http_parameter *params)
|
||||
&c->parser,
|
||||
&c->document);
|
||||
}
|
||||
|
||||
if (error != DOM_HUBBUB_OK) {
|
||||
nsurl_unref(c->base_url);
|
||||
c->base_url = NULL;
|
||||
@ -1013,6 +441,13 @@ html_create(const content_handler *handler,
|
||||
return error;
|
||||
}
|
||||
|
||||
error = html_css_new_stylesheets(html);
|
||||
if (error != NSERROR_OK) {
|
||||
content_broadcast_errorcode(&html->base, error);
|
||||
free(html);
|
||||
return error;
|
||||
}
|
||||
|
||||
*c = (struct content *) html;
|
||||
|
||||
return NSERROR_OK;
|
||||
@ -2243,11 +1678,6 @@ html_begin_conversion(html_content *htmlc)
|
||||
dom_node_unref(head);
|
||||
dom_node_unref(html);
|
||||
|
||||
/* ensure stylesheets are initialised */
|
||||
if (html_init_stylesheets(htmlc) == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (htmlc->base.active == 0) {
|
||||
html_finish_conversion(htmlc);
|
||||
}
|
||||
@ -2522,7 +1952,6 @@ static void html_free_layout(html_content *htmlc)
|
||||
static void html_destroy(struct content *c)
|
||||
{
|
||||
html_content *html = (html_content *) c;
|
||||
unsigned int i;
|
||||
struct form *f, *g;
|
||||
|
||||
LOG(("content %p", c));
|
||||
@ -2582,19 +2011,7 @@ static void html_destroy(struct content *c)
|
||||
}
|
||||
|
||||
/* Free stylesheets */
|
||||
for (i = 0; i != html->stylesheet_count; i++) {
|
||||
if (html->stylesheets[i].type == HTML_STYLESHEET_EXTERNAL &&
|
||||
html->stylesheets[i].data.external != NULL) {
|
||||
hlcache_handle_release(
|
||||
html->stylesheets[i].data.external);
|
||||
} else if (html->stylesheets[i].type ==
|
||||
HTML_STYLESHEET_INTERNAL &&
|
||||
html->stylesheets[i].data.internal != NULL) {
|
||||
nscss_destroy_css_data(
|
||||
html->stylesheets[i].data.internal);
|
||||
}
|
||||
}
|
||||
free(html->stylesheets);
|
||||
html_css_free_stylesheets(html);
|
||||
|
||||
/* Free scripts */
|
||||
html_free_scripts(html);
|
||||
@ -3290,25 +2707,6 @@ const char *html_get_base_target(hlcache_handle *h)
|
||||
return c->base_target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve stylesheets used by HTML document
|
||||
*
|
||||
* \param h Content to retrieve stylesheets from
|
||||
* \param n Pointer to location to receive number of sheets
|
||||
* \return Pointer to array of stylesheets
|
||||
*/
|
||||
struct html_stylesheet *html_get_stylesheets(hlcache_handle *h, unsigned int *n)
|
||||
{
|
||||
html_content *c = (html_content *) hlcache_handle_get_content(h);
|
||||
|
||||
assert(c != NULL);
|
||||
assert(n != NULL);
|
||||
|
||||
*n = c->stylesheet_count;
|
||||
|
||||
return c->stylesheets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve objects used by HTML document
|
||||
*
|
||||
@ -3369,30 +2767,7 @@ static void html_fini(void)
|
||||
{
|
||||
box_construct_fini();
|
||||
|
||||
if (html_user_stylesheet_url != NULL) {
|
||||
nsurl_unref(html_user_stylesheet_url);
|
||||
html_user_stylesheet_url = NULL;
|
||||
}
|
||||
|
||||
if (html_quirks_stylesheet_url != NULL) {
|
||||
nsurl_unref(html_quirks_stylesheet_url);
|
||||
html_quirks_stylesheet_url = NULL;
|
||||
}
|
||||
|
||||
if (html_adblock_stylesheet_url != NULL) {
|
||||
nsurl_unref(html_adblock_stylesheet_url);
|
||||
html_adblock_stylesheet_url = NULL;
|
||||
}
|
||||
|
||||
if (html_default_stylesheet_url != NULL) {
|
||||
nsurl_unref(html_default_stylesheet_url);
|
||||
html_default_stylesheet_url = NULL;
|
||||
}
|
||||
|
||||
if (html_charset != NULL) {
|
||||
lwc_string_unref(html_charset);
|
||||
html_charset = NULL;
|
||||
}
|
||||
html_css_fini();
|
||||
}
|
||||
|
||||
static const content_handler html_content_handler = {
|
||||
@ -3423,32 +2798,9 @@ static const content_handler html_content_handler = {
|
||||
nserror html_init(void)
|
||||
{
|
||||
uint32_t i;
|
||||
lwc_error lerror;
|
||||
nserror error;
|
||||
|
||||
lerror = lwc_intern_string("charset", SLEN("charset"), &html_charset);
|
||||
if (lerror != lwc_error_ok) {
|
||||
error = NSERROR_NOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
error = nsurl_create("resource:default.css",
|
||||
&html_default_stylesheet_url);
|
||||
if (error != NSERROR_OK)
|
||||
goto error;
|
||||
|
||||
error = nsurl_create("resource:adblock.css",
|
||||
&html_adblock_stylesheet_url);
|
||||
if (error != NSERROR_OK)
|
||||
goto error;
|
||||
|
||||
error = nsurl_create("resource:quirks.css",
|
||||
&html_quirks_stylesheet_url);
|
||||
if (error != NSERROR_OK)
|
||||
goto error;
|
||||
|
||||
error = nsurl_create("resource:user.css",
|
||||
&html_user_stylesheet_url);
|
||||
error = html_css_init();
|
||||
if (error != NSERROR_OK)
|
||||
goto error;
|
||||
|
||||
|
@ -182,8 +182,17 @@ struct content_html_frames *html_get_frameset(struct hlcache_handle *h);
|
||||
struct content_html_iframe *html_get_iframe(struct hlcache_handle *h);
|
||||
nsurl *html_get_base_url(struct hlcache_handle *h);
|
||||
const char *html_get_base_target(struct hlcache_handle *h);
|
||||
|
||||
/**
|
||||
* Retrieve stylesheets used by HTML document
|
||||
*
|
||||
* \param h Content to retrieve stylesheets from
|
||||
* \param n Pointer to location to receive number of sheets
|
||||
* \return Pointer to array of stylesheets
|
||||
*/
|
||||
struct html_stylesheet *html_get_stylesheets(struct hlcache_handle *h,
|
||||
unsigned int *n);
|
||||
|
||||
struct content_html_object *html_get_objects(struct hlcache_handle *h,
|
||||
unsigned int *n);
|
||||
bool html_get_id_offset(struct hlcache_handle *h, lwc_string *frag_id,
|
||||
|
678
render/html_css.c
Normal file
678
render/html_css.c
Normal file
@ -0,0 +1,678 @@
|
||||
/*
|
||||
* Copyright 2013 Vincent Sanders <vince@netsurf-browser.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* Processing for html content css operations.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "content/hlcache.h"
|
||||
#include "desktop/options.h"
|
||||
#include "render/html_internal.h"
|
||||
#include "utils/corestrings.h"
|
||||
#include "utils/config.h"
|
||||
#include "utils/log.h"
|
||||
|
||||
static nsurl *html_default_stylesheet_url;
|
||||
static nsurl *html_adblock_stylesheet_url;
|
||||
static nsurl *html_quirks_stylesheet_url;
|
||||
static nsurl *html_user_stylesheet_url;
|
||||
|
||||
static nserror css_error_to_nserror(css_error error)
|
||||
{
|
||||
switch (error) {
|
||||
case CSS_OK:
|
||||
return NSERROR_OK;
|
||||
|
||||
case CSS_NOMEM:
|
||||
return NSERROR_NOMEM;
|
||||
|
||||
case CSS_BADPARM:
|
||||
return NSERROR_BAD_PARAMETER;
|
||||
|
||||
case CSS_INVALID:
|
||||
return NSERROR_INVALID;
|
||||
|
||||
case CSS_FILENOTFOUND:
|
||||
return NSERROR_NOT_FOUND;
|
||||
|
||||
case CSS_NEEDDATA:
|
||||
return NSERROR_NEED_DATA;
|
||||
|
||||
case CSS_BADCHARSET:
|
||||
return NSERROR_BAD_ENCODING;
|
||||
|
||||
case CSS_EOF:
|
||||
case CSS_IMPORTS_PENDING:
|
||||
case CSS_PROPERTY_NOT_SET:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NSERROR_CSS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for fetchcache() for linked stylesheets.
|
||||
*/
|
||||
|
||||
static nserror
|
||||
html_convert_css_callback(hlcache_handle *css,
|
||||
const hlcache_event *event,
|
||||
void *pw)
|
||||
{
|
||||
html_content *parent = pw;
|
||||
unsigned int i;
|
||||
struct html_stylesheet *s;
|
||||
|
||||
/* Find sheet */
|
||||
for (i = 0, s = parent->stylesheets;
|
||||
i != parent->stylesheet_count;
|
||||
i++, s++) {
|
||||
if (s->type == HTML_STYLESHEET_EXTERNAL &&
|
||||
s->data.external == css)
|
||||
break;
|
||||
}
|
||||
|
||||
assert(i != parent->stylesheet_count);
|
||||
|
||||
switch (event->type) {
|
||||
case CONTENT_MSG_LOADING:
|
||||
break;
|
||||
|
||||
case CONTENT_MSG_READY:
|
||||
break;
|
||||
|
||||
case CONTENT_MSG_DONE:
|
||||
LOG(("done stylesheet slot %d '%s'", i,
|
||||
nsurl_access(hlcache_handle_get_url(css))));
|
||||
parent->base.active--;
|
||||
LOG(("%d fetches active", parent->base.active));
|
||||
break;
|
||||
|
||||
case CONTENT_MSG_ERROR:
|
||||
LOG(("stylesheet %s failed: %s",
|
||||
nsurl_access(hlcache_handle_get_url(css)),
|
||||
event->data.error));
|
||||
hlcache_handle_release(css);
|
||||
s->data.external = NULL;
|
||||
parent->base.active--;
|
||||
LOG(("%d fetches active", parent->base.active));
|
||||
content_add_error(&parent->base, "?", 0);
|
||||
break;
|
||||
|
||||
case CONTENT_MSG_STATUS:
|
||||
if (event->data.explicit_status_text == NULL) {
|
||||
/* Object content's status text updated */
|
||||
html_set_status(parent,
|
||||
content_get_status_message(css));
|
||||
content_broadcast(&parent->base, CONTENT_MSG_STATUS,
|
||||
event->data);
|
||||
} else {
|
||||
/* Object content wants to set explicit message */
|
||||
content_broadcast(&parent->base, CONTENT_MSG_STATUS,
|
||||
event->data);
|
||||
}
|
||||
break;
|
||||
|
||||
case CONTENT_MSG_POINTER:
|
||||
/* Really don't want this to continue after the switch */
|
||||
return NSERROR_OK;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (parent->base.active == 0) {
|
||||
html_begin_conversion(parent);
|
||||
}
|
||||
|
||||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
/* exported interface documented in render/html.h */
|
||||
struct html_stylesheet *html_get_stylesheets(hlcache_handle *h, unsigned int *n)
|
||||
{
|
||||
html_content *c = (html_content *) hlcache_handle_get_content(h);
|
||||
|
||||
assert(c != NULL);
|
||||
assert(n != NULL);
|
||||
|
||||
*n = c->stylesheet_count;
|
||||
|
||||
return c->stylesheets;
|
||||
}
|
||||
|
||||
|
||||
/* exported interface documented in render/html_internal.h */
|
||||
nserror html_css_free_stylesheets(html_content *html)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i != html->stylesheet_count; i++) {
|
||||
if ((html->stylesheets[i].type == HTML_STYLESHEET_EXTERNAL) &&
|
||||
(html->stylesheets[i].data.external != NULL)) {
|
||||
hlcache_handle_release(html->stylesheets[i].data.external);
|
||||
} else if ((html->stylesheets[i].type == HTML_STYLESHEET_INTERNAL) &&
|
||||
(html->stylesheets[i].data.internal != NULL)) {
|
||||
nscss_destroy_css_data(html->stylesheets[i].data.internal);
|
||||
}
|
||||
}
|
||||
free(html->stylesheets);
|
||||
|
||||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
/* exported interface documented in render/html_internal.h */
|
||||
nserror html_css_new_stylesheets(html_content *c)
|
||||
{
|
||||
nserror ns_error;
|
||||
hlcache_child_context child;
|
||||
|
||||
if (c->stylesheets != NULL) {
|
||||
return NSERROR_OK; /* already initialised */
|
||||
}
|
||||
|
||||
/* stylesheet 0 is the base style sheet,
|
||||
* stylesheet 1 is the quirks mode style sheet,
|
||||
* stylesheet 2 is the adblocking stylesheet,
|
||||
* stylesheet 3 is the user stylesheet */
|
||||
c->stylesheets = calloc(STYLESHEET_START, sizeof(struct html_stylesheet));
|
||||
if (c->stylesheets == NULL) {
|
||||
return NSERROR_NOMEM;
|
||||
}
|
||||
|
||||
c->stylesheets[STYLESHEET_BASE].type = HTML_STYLESHEET_EXTERNAL;
|
||||
c->stylesheets[STYLESHEET_BASE].data.external = NULL;
|
||||
c->stylesheets[STYLESHEET_QUIRKS].type = HTML_STYLESHEET_EXTERNAL;
|
||||
c->stylesheets[STYLESHEET_QUIRKS].data.external = NULL;
|
||||
c->stylesheets[STYLESHEET_ADBLOCK].type = HTML_STYLESHEET_EXTERNAL;
|
||||
c->stylesheets[STYLESHEET_ADBLOCK].data.external = NULL;
|
||||
c->stylesheets[STYLESHEET_USER].type = HTML_STYLESHEET_EXTERNAL;
|
||||
c->stylesheets[STYLESHEET_USER].data.external = NULL;
|
||||
c->stylesheet_count = STYLESHEET_START;
|
||||
|
||||
child.charset = c->encoding;
|
||||
child.quirks = c->base.quirks;
|
||||
|
||||
ns_error = hlcache_handle_retrieve(html_default_stylesheet_url, 0,
|
||||
content_get_url(&c->base), NULL,
|
||||
html_convert_css_callback, c, &child, CONTENT_CSS,
|
||||
&c->stylesheets[STYLESHEET_BASE].data.external);
|
||||
if (ns_error != NSERROR_OK) {
|
||||
return ns_error;
|
||||
}
|
||||
|
||||
c->base.active++;
|
||||
LOG(("%d fetches active", c->base.active));
|
||||
|
||||
if (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL) {
|
||||
ns_error = hlcache_handle_retrieve(html_quirks_stylesheet_url,
|
||||
0, content_get_url(&c->base), NULL,
|
||||
html_convert_css_callback, c, &child,
|
||||
CONTENT_CSS,
|
||||
&c->stylesheets[STYLESHEET_QUIRKS].data.external);
|
||||
if (ns_error != NSERROR_OK) {
|
||||
return ns_error;
|
||||
}
|
||||
|
||||
c->base.active++;
|
||||
LOG(("%d fetches active", c->base.active));
|
||||
|
||||
}
|
||||
|
||||
if (nsoption_bool(block_ads)) {
|
||||
ns_error = hlcache_handle_retrieve(html_adblock_stylesheet_url,
|
||||
0, content_get_url(&c->base), NULL,
|
||||
html_convert_css_callback, c, &child, CONTENT_CSS,
|
||||
&c->stylesheets[STYLESHEET_ADBLOCK].
|
||||
data.external);
|
||||
if (ns_error != NSERROR_OK) {
|
||||
return ns_error;
|
||||
}
|
||||
|
||||
c->base.active++;
|
||||
LOG(("%d fetches active", c->base.active));
|
||||
|
||||
}
|
||||
|
||||
ns_error = hlcache_handle_retrieve(html_user_stylesheet_url, 0,
|
||||
content_get_url(&c->base), NULL,
|
||||
html_convert_css_callback, c, &child, CONTENT_CSS,
|
||||
&c->stylesheets[STYLESHEET_USER].data.external);
|
||||
if (ns_error != NSERROR_OK) {
|
||||
return ns_error;
|
||||
}
|
||||
|
||||
c->base.active++;
|
||||
LOG(("%d fetches active", c->base.active));
|
||||
|
||||
return ns_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle notification of inline style completion
|
||||
*
|
||||
* \param css Inline style object
|
||||
* \param pw Private data
|
||||
*/
|
||||
static void html_inline_style_done(struct content_css_data *css, void *pw)
|
||||
{
|
||||
html_content *html = pw;
|
||||
|
||||
html->base.active--;
|
||||
LOG(("%d fetches active", html->base.active));
|
||||
}
|
||||
|
||||
static nserror
|
||||
html_stylesheet_from_domnode(html_content *c,
|
||||
dom_node *node,
|
||||
struct content_css_data **ret_sheet)
|
||||
{
|
||||
dom_node *child, *next;
|
||||
dom_exception exc;
|
||||
struct content_css_data *sheet;
|
||||
nserror error;
|
||||
css_error csserror;
|
||||
|
||||
/* create stylesheet */
|
||||
sheet = calloc(1, sizeof(struct content_css_data));
|
||||
if (sheet == NULL) {
|
||||
return NSERROR_NOMEM;
|
||||
}
|
||||
|
||||
error = nscss_create_css_data(sheet,
|
||||
nsurl_access(c->base_url), NULL, c->quirks,
|
||||
html_inline_style_done, c);
|
||||
if (error != NSERROR_OK) {
|
||||
free(sheet);
|
||||
return error;
|
||||
}
|
||||
|
||||
exc = dom_node_get_first_child(node, &child);
|
||||
if (exc != DOM_NO_ERR) {
|
||||
nscss_destroy_css_data(sheet);
|
||||
free(sheet);
|
||||
return NSERROR_DOM;
|
||||
}
|
||||
|
||||
while (child != NULL) {
|
||||
dom_string *data;
|
||||
|
||||
exc = dom_node_get_text_content(child, &data);
|
||||
if (exc != DOM_NO_ERR) {
|
||||
dom_node_unref(child);
|
||||
nscss_destroy_css_data(sheet);
|
||||
free(sheet);
|
||||
return NSERROR_DOM;
|
||||
}
|
||||
|
||||
if (nscss_process_css_data(sheet,
|
||||
dom_string_data(data),
|
||||
dom_string_byte_length(data)) == false) {
|
||||
dom_string_unref(data);
|
||||
dom_node_unref(child);
|
||||
nscss_destroy_css_data(sheet);
|
||||
free(sheet);
|
||||
return NSERROR_CSS;
|
||||
}
|
||||
|
||||
dom_string_unref(data);
|
||||
|
||||
exc = dom_node_get_next_sibling(child, &next);
|
||||
if (exc != DOM_NO_ERR) {
|
||||
dom_node_unref(child);
|
||||
nscss_destroy_css_data(sheet);
|
||||
free(sheet);
|
||||
return NSERROR_DOM;
|
||||
}
|
||||
|
||||
dom_node_unref(child);
|
||||
child = next;
|
||||
}
|
||||
|
||||
c->base.active++;
|
||||
LOG(("%d fetches active", c->base.active));
|
||||
|
||||
/* Convert the content -- manually, as we want the result */
|
||||
csserror = nscss_convert_css_data(sheet);
|
||||
if (csserror != CSS_OK) {
|
||||
/* conversion failed */
|
||||
c->base.active--;
|
||||
LOG(("%d fetches active", c->base.active));
|
||||
nscss_destroy_css_data(sheet);
|
||||
free(sheet);
|
||||
return css_error_to_nserror(csserror);
|
||||
}
|
||||
|
||||
*ret_sheet = sheet;
|
||||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an inline stylesheet in the document.
|
||||
*
|
||||
* \param c content structure
|
||||
* \param style xml node of style element
|
||||
* \return true on success, false if an error occurred
|
||||
*/
|
||||
|
||||
static struct html_stylesheet *
|
||||
html_create_style_element(html_content *c, dom_node *style)
|
||||
{
|
||||
dom_string *val;
|
||||
dom_exception exc;
|
||||
struct html_stylesheet *stylesheets;
|
||||
|
||||
/* type='text/css', or not present (invalid but common) */
|
||||
exc = dom_element_get_attribute(style, corestring_dom_type, &val);
|
||||
if (exc == DOM_NO_ERR && val != NULL) {
|
||||
if (!dom_string_caseless_lwc_isequal(val,
|
||||
corestring_lwc_text_css)) {
|
||||
dom_string_unref(val);
|
||||
return NULL;
|
||||
}
|
||||
dom_string_unref(val);
|
||||
}
|
||||
|
||||
/* media contains 'screen' or 'all' or not present */
|
||||
exc = dom_element_get_attribute(style, corestring_dom_media, &val);
|
||||
if (exc == DOM_NO_ERR && val != NULL) {
|
||||
if (strcasestr(dom_string_data(val), "screen") == NULL &&
|
||||
strcasestr(dom_string_data(val),
|
||||
"all") == NULL) {
|
||||
dom_string_unref(val);
|
||||
return NULL;
|
||||
}
|
||||
dom_string_unref(val);
|
||||
}
|
||||
|
||||
/* Extend array */
|
||||
stylesheets = realloc(c->stylesheets,
|
||||
sizeof(struct html_stylesheet) * (c->stylesheet_count + 1));
|
||||
if (stylesheets == NULL) {
|
||||
|
||||
content_broadcast_errorcode(&c->base, NSERROR_NOMEM);
|
||||
return false;
|
||||
|
||||
}
|
||||
c->stylesheets = stylesheets;
|
||||
|
||||
c->stylesheets[c->stylesheet_count].type = HTML_STYLESHEET_INTERNAL;
|
||||
c->stylesheets[c->stylesheet_count].node = style;
|
||||
c->stylesheets[c->stylesheet_count].data.internal = NULL;
|
||||
c->stylesheet_count++;
|
||||
|
||||
return c->stylesheets + (c->stylesheet_count - 1);
|
||||
}
|
||||
|
||||
bool html_css_update_style(html_content *c, dom_node *style)
|
||||
{
|
||||
struct content_css_data *sheet = NULL;
|
||||
nserror error;
|
||||
unsigned int i;
|
||||
struct html_stylesheet *s;
|
||||
|
||||
/* Find sheet */
|
||||
for (i = 0, s = c->stylesheets; i != c->stylesheet_count; i++, s++) {
|
||||
if ((s->type == HTML_STYLESHEET_INTERNAL) &&
|
||||
(s->node == style))
|
||||
break;
|
||||
}
|
||||
if (i == c->stylesheet_count) {
|
||||
s = html_create_style_element(c, style);
|
||||
}
|
||||
if (s == NULL) {
|
||||
LOG(("Could not find or create inline stylesheet for %p",
|
||||
style));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(("Found sheet %p slot %d for node %p", s,i, style));
|
||||
|
||||
error = html_stylesheet_from_domnode(c, style, &sheet);
|
||||
if (error != NSERROR_OK) {
|
||||
LOG(("Failed to update sheet"));
|
||||
content_broadcast_errorcode(&c->base, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(("Updating sheet %p with %p", s->data.internal, sheet));
|
||||
|
||||
/* Update index */
|
||||
if (s->data.internal != NULL) {
|
||||
nscss_destroy_css_data(s->data.internal);
|
||||
free(s->data.internal);
|
||||
}
|
||||
s->data.internal = sheet;
|
||||
return true;
|
||||
}
|
||||
|
||||
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].type = HTML_STYLESHEET_EXTERNAL;
|
||||
|
||||
/* 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].data.external);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
nserror
|
||||
html_css_new_selection_context(html_content *c, css_select_ctx **ret_select_ctx)
|
||||
{
|
||||
uint32_t i;
|
||||
css_error css_ret;
|
||||
css_select_ctx *select_ctx;
|
||||
|
||||
/* check that the base stylesheet loaded; layout fails without it */
|
||||
if (c->stylesheets[STYLESHEET_BASE].data.external == NULL) {
|
||||
return NSERROR_CSS_BASE;
|
||||
}
|
||||
|
||||
/* Create selection context */
|
||||
css_ret = css_select_ctx_create(ns_realloc, c, &select_ctx);
|
||||
if (css_ret != CSS_OK) {
|
||||
return css_error_to_nserror(css_ret);
|
||||
}
|
||||
|
||||
/* Add sheets to it */
|
||||
for (i = STYLESHEET_BASE; i != c->stylesheet_count; i++) {
|
||||
const struct html_stylesheet *hsheet = &c->stylesheets[i];
|
||||
css_stylesheet *sheet = NULL;
|
||||
css_origin origin = CSS_ORIGIN_AUTHOR;
|
||||
|
||||
if (i < STYLESHEET_USER) {
|
||||
origin = CSS_ORIGIN_UA;
|
||||
} else if (i < STYLESHEET_START) {
|
||||
origin = CSS_ORIGIN_USER;
|
||||
}
|
||||
|
||||
if ((hsheet->type == HTML_STYLESHEET_EXTERNAL) &&
|
||||
(hsheet->data.external != NULL)) {
|
||||
sheet = nscss_get_stylesheet(hsheet->data.external);
|
||||
} else if (hsheet->type == HTML_STYLESHEET_INTERNAL) {
|
||||
sheet = hsheet->data.internal->sheet;
|
||||
}
|
||||
|
||||
if (sheet != NULL) {
|
||||
css_ret = css_select_ctx_append_sheet(select_ctx,
|
||||
sheet,
|
||||
origin,
|
||||
CSS_MEDIA_SCREEN);
|
||||
if (css_ret != CSS_OK) {
|
||||
css_select_ctx_destroy(select_ctx);
|
||||
return css_error_to_nserror(css_ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return new selection context to caller */
|
||||
*ret_select_ctx = select_ctx;
|
||||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
nserror html_css_init(void)
|
||||
{
|
||||
nserror error;
|
||||
|
||||
error = nsurl_create("resource:default.css",
|
||||
&html_default_stylesheet_url);
|
||||
if (error != NSERROR_OK)
|
||||
return error;
|
||||
|
||||
error = nsurl_create("resource:adblock.css",
|
||||
&html_adblock_stylesheet_url);
|
||||
if (error != NSERROR_OK)
|
||||
return error;
|
||||
|
||||
error = nsurl_create("resource:quirks.css",
|
||||
&html_quirks_stylesheet_url);
|
||||
if (error != NSERROR_OK)
|
||||
return error;
|
||||
|
||||
error = nsurl_create("resource:user.css",
|
||||
&html_user_stylesheet_url);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void html_css_fini(void)
|
||||
{
|
||||
if (html_user_stylesheet_url != NULL) {
|
||||
nsurl_unref(html_user_stylesheet_url);
|
||||
html_user_stylesheet_url = NULL;
|
||||
}
|
||||
|
||||
if (html_quirks_stylesheet_url != NULL) {
|
||||
nsurl_unref(html_quirks_stylesheet_url);
|
||||
html_quirks_stylesheet_url = NULL;
|
||||
}
|
||||
|
||||
if (html_adblock_stylesheet_url != NULL) {
|
||||
nsurl_unref(html_adblock_stylesheet_url);
|
||||
html_adblock_stylesheet_url = NULL;
|
||||
}
|
||||
|
||||
if (html_default_stylesheet_url != NULL) {
|
||||
nsurl_unref(html_default_stylesheet_url);
|
||||
html_default_stylesheet_url = NULL;
|
||||
}
|
||||
}
|
@ -255,6 +255,24 @@ struct form *html_forms_get_forms(const char *docenc, dom_html_document *doc);
|
||||
struct form_control *html_forms_get_control_for_node(struct form *forms,
|
||||
dom_node *node);
|
||||
|
||||
/* in render/html_css.c */
|
||||
nserror html_css_init(void);
|
||||
void html_css_fini(void);
|
||||
|
||||
/**
|
||||
* Initialise core stylesheets for a content
|
||||
*
|
||||
* \param c content structure to update
|
||||
* \return nserror
|
||||
*/
|
||||
nserror html_css_new_stylesheets(html_content *c);
|
||||
nserror html_css_free_stylesheets(html_content *html);
|
||||
|
||||
bool html_css_process_link(html_content *htmlc, dom_node *node);
|
||||
bool html_css_update_style(html_content *c, dom_node *style);
|
||||
|
||||
nserror html_css_new_selection_context(html_content *c, css_select_ctx **ret_select_ctx);
|
||||
|
||||
/* Useful dom_string pointers */
|
||||
struct dom_string;
|
||||
|
||||
|
@ -38,6 +38,7 @@ lwc_string *corestring_lwc_bottom;
|
||||
lwc_string *corestring_lwc_button;
|
||||
lwc_string *corestring_lwc_caption;
|
||||
lwc_string *corestring_lwc_center;
|
||||
lwc_string *corestring_lwc_charset;
|
||||
lwc_string *corestring_lwc_checkbox;
|
||||
lwc_string *corestring_lwc_circle;
|
||||
lwc_string *corestring_lwc_col;
|
||||
@ -257,6 +258,7 @@ void corestrings_fini(void)
|
||||
CSS_LWC_STRING_UNREF(bottom);
|
||||
CSS_LWC_STRING_UNREF(button);
|
||||
CSS_LWC_STRING_UNREF(caption);
|
||||
CSS_LWC_STRING_UNREF(charset);
|
||||
CSS_LWC_STRING_UNREF(center);
|
||||
CSS_LWC_STRING_UNREF(checkbox);
|
||||
CSS_LWC_STRING_UNREF(circle);
|
||||
@ -497,6 +499,7 @@ nserror corestrings_init(void)
|
||||
CSS_LWC_STRING_INTERN(bottom);
|
||||
CSS_LWC_STRING_INTERN(button);
|
||||
CSS_LWC_STRING_INTERN(caption);
|
||||
CSS_LWC_STRING_INTERN(charset);
|
||||
CSS_LWC_STRING_INTERN(center);
|
||||
CSS_LWC_STRING_INTERN(checkbox);
|
||||
CSS_LWC_STRING_INTERN(circle);
|
||||
|
@ -42,6 +42,7 @@ extern lwc_string *corestring_lwc_bottom;
|
||||
extern lwc_string *corestring_lwc_button;
|
||||
extern lwc_string *corestring_lwc_caption;
|
||||
extern lwc_string *corestring_lwc_center;
|
||||
extern lwc_string *corestring_lwc_charset;
|
||||
extern lwc_string *corestring_lwc_checkbox;
|
||||
extern lwc_string *corestring_lwc_circle;
|
||||
extern lwc_string *corestring_lwc_col;
|
||||
|
Loading…
Reference in New Issue
Block a user