summaryrefslogtreecommitdiff
path: root/src/select/select.c
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2011-01-05 00:44:04 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2011-01-05 00:44:04 +0000
commit4860bd14179e94a7d073714c1c5b21fd9c200dd7 (patch)
treed6343a55826ee6d6ae3801c0453dcce12a0e987e /src/select/select.c
parente094fcbc9fcf39fb2d71cd521e8076f4703fa100 (diff)
downloadlibcss-4860bd14179e94a7d073714c1c5b21fd9c200dd7.tar.gz
libcss-4860bd14179e94a7d073714c1c5b21fd9c200dd7.tar.bz2
Simultaneously select styles for base + pseudo elements.
svn path=/trunk/libcss/; revision=11211
Diffstat (limited to 'src/select/select.c')
-rw-r--r--src/select/select.c313
1 files changed, 195 insertions, 118 deletions
diff --git a/src/select/select.c b/src/select/select.c
index 48062d7..5c68699 100644
--- a/src/select/select.c
+++ b/src/select/select.c
@@ -45,8 +45,10 @@ struct css_select_ctx {
void *pw; /**< Client-specific private data */
};
-static css_error set_hint(css_select_state *state, uint32_t i);
-static css_error set_initial(css_select_state *state, uint32_t i, void *parent);
+static css_error set_hint(css_select_state *state, uint32_t prop);
+static css_error set_initial(css_select_state *state,
+ uint32_t prop, css_pseudo_element pseudo,
+ void *parent);
static css_error select_from_sheet(css_select_ctx *ctx,
const css_stylesheet *sheet, css_origin origin,
@@ -65,10 +67,10 @@ static css_error match_universal_combinator(css_select_ctx *ctx,
css_select_state *state, void *node, void **next_node);
static css_error match_details(css_select_ctx *ctx, void *node,
const css_selector_detail *detail, css_select_state *state,
- bool *match);
+ bool *match, css_pseudo_element *pseudo_element);
static css_error match_detail(css_select_ctx *ctx, void *node,
const css_selector_detail *detail, css_select_state *state,
- bool *match, bool *match_pseudo_element);
+ bool *match, css_pseudo_element *pseudo_element);
static css_error cascade_style(const css_style *style, css_select_state *state);
#ifdef DEBUG_CHAIN_MATCHING
@@ -272,12 +274,11 @@ css_error css_select_ctx_get_sheet(css_select_ctx *ctx, uint32_t index,
*
* \param ctx Selection context to use
* \param node Node to select style for
- * \param pseudo_element Pseudo element to select for, instead
* \param media Currently active media types
* \param inline_style Corresponding inline style for node, or NULL
- * \param result Pointer to style to populate (assumed clean)
* \param handler Dispatch table of handler functions
* \param pw Client-specific private data for handler functions
+ * \param result Pointer to location to receive result set
* \return CSS_OK on success, appropriate error otherwise.
*
* In computing the style, no reference is made to the parent node's
@@ -290,12 +291,11 @@ css_error css_select_ctx_get_sheet(css_select_ctx *ctx, uint32_t index,
* update the fully computed style for a node when layout changes.
*/
css_error css_select_style(css_select_ctx *ctx, void *node,
- uint32_t pseudo_element, uint64_t media,
- const css_stylesheet *inline_style,
- css_computed_style *result,
- css_select_handler *handler, void *pw)
+ uint64_t media, const css_stylesheet *inline_style,
+ css_select_handler *handler, void *pw,
+ css_select_results **result)
{
- uint32_t i;
+ uint32_t i, j;
css_error error;
css_select_state state;
void *parent = NULL;
@@ -303,22 +303,37 @@ css_error css_select_style(css_select_ctx *ctx, void *node,
if (ctx == NULL || node == NULL || result == NULL || handler == NULL)
return CSS_BADPARM;
- /* Inline style has no meaning if selecting for a pseudo element */
- if (pseudo_element != CSS_PSEUDO_ELEMENT_NONE && inline_style != NULL)
- return CSS_BADPARM;
-
/* Set up the selection state */
memset(&state, 0, sizeof(css_select_state));
state.node = node;
- state.pseudo_element = pseudo_element;
state.media = media;
- state.result = result;
state.handler = handler;
state.pw = pw;
- error = handler->parent_node(pw, node, &parent);
- if (error)
+ /* Allocate the result set */
+ state.results = ctx->alloc(NULL, sizeof(css_select_results) +
+ CSS_PSEUDO_ELEMENT_COUNT *
+ sizeof(css_computed_style *), ctx->pw);
+ if (state.results == NULL)
+ return CSS_NOMEM;
+
+ state.results->alloc = ctx->alloc;
+ state.results->pw = ctx->pw;
+
+ for (i = 0; i < CSS_PSEUDO_ELEMENT_COUNT; i++)
+ state.results->styles[i] = NULL;
+
+ /* Base element style is guaranteed to exist */
+ error = css_computed_style_create(ctx->alloc, ctx->pw,
+ &state.results->styles[CSS_PSEUDO_ELEMENT_NONE]);
+ if (error != CSS_OK) {
+ ctx->alloc(state.results, 0, ctx->pw);
return error;
+ }
+
+ error = handler->parent_node(pw, node, &parent);
+ if (error != CSS_OK)
+ goto cleanup;
/* Iterate through the top-level stylesheets, selecting styles
* from those which apply to our current media requirements and
@@ -348,6 +363,11 @@ css_error css_select_style(css_select_ctx *ctx, void *node,
/* No bytecode if input was empty or wholly invalid */
if (sel->style != NULL) {
+ /* Inline style applies to base element only */
+ state.current_pseudo = CSS_PSEUDO_ELEMENT_NONE;
+ state.computed = state.results->styles[
+ CSS_PSEUDO_ELEMENT_NONE];
+
error = cascade_style(sel->style, &state);
if (error != CSS_OK)
goto cleanup;
@@ -357,26 +377,44 @@ css_error css_select_style(css_select_ctx *ctx, void *node,
/* Take account of presentational hints and fix up any remaining
* unset properties. */
for (i = 0; i < CSS_N_PROPERTIES; i++) {
- /* Apply presentational hints if we're not selecting for
- * a pseudo element, and the property is unset or the
- * existing property value did not come from an author
- * stylesheet or a user sheet using !important. */
- if (pseudo_element == CSS_PSEUDO_ELEMENT_NONE &&
- (state.props[i].set == false ||
- (state.props[i].origin != CSS_ORIGIN_AUTHOR &&
- state.props[i].important == false))) {
- error = set_hint(&state, i);
- if (error != CSS_OK)
- goto cleanup;
- }
+ for (j = 0; j < CSS_PSEUDO_ELEMENT_COUNT; j++) {
+ prop_state *prop = &state.props[i][j];
+
+ /* Skip non-existent pseudo elements */
+ if (state.results->styles[j] == NULL)
+ continue;
+
+ /* Apply presentational hints if we're not selecting for
+ * a pseudo element, and the property is unset or the
+ * existing property value did not come from an author
+ * stylesheet or a user sheet using !important. */
+ if (j == CSS_PSEUDO_ELEMENT_NONE &&
+ (prop->set == false ||
+ (prop->origin != CSS_ORIGIN_AUTHOR &&
+ prop->important == false))) {
+ state.current_pseudo = j;
+ state.computed = state.results->styles[j];
+
+ error = set_hint(&state, i);
+ if (error != CSS_OK)
+ goto cleanup;
+ }
- /* If the property is still unset or it's set to inherit and
- * we're the root element, then set it to its initial value. */
- if (state.props[i].set == false || (parent == NULL &&
- state.props[i].inherit == true)) {
- error = set_initial(&state, i, parent);
- if (error != CSS_OK)
- goto cleanup;
+ /* If the property is still unset or we're not selecting
+ * for a pseudo element and it's set to inherit and
+ * we're the root element, then set it to its initial
+ * value. */
+ if (prop->set == false ||
+ (j == CSS_PSEUDO_ELEMENT_NONE &&
+ parent == NULL &&
+ prop->inherit == true)) {
+ state.current_pseudo = j;
+ state.computed = state.results->styles[j];
+
+ error = set_initial(&state, i, j, parent);
+ if (error != CSS_OK)
+ goto cleanup;
+ }
}
}
@@ -385,14 +423,25 @@ css_error css_select_style(css_select_ctx *ctx, void *node,
* computed, and the default border-{top,right,bottom,left}-color
* is set to the computed value of color. */
if (parent == NULL) {
- error = compute_absolute_values(NULL, result,
+ /* Only compute absolute values for the base element */
+ error = compute_absolute_values(NULL,
+ state.results->styles[CSS_PSEUDO_ELEMENT_NONE],
handler->compute_font_size, pw);
if (error != CSS_OK)
goto cleanup;
}
+ *result = state.results;
error = CSS_OK;
+
cleanup:
+ /* Only clean up the results if there's an error.
+ * If there is no error, we're going to pass ownership of
+ * the results to the client */
+ if (error != CSS_OK && state.results != NULL) {
+ css_select_results_destroy(state.results);
+ }
+
if (ctx->n_sheets > 0 && ctx->sheets[0].sheet != NULL) {
if (state.universal != NULL)
lwc_string_unref(state.universal);
@@ -417,14 +466,40 @@ cleanup:
if (state.after != NULL)
lwc_string_unref(state.after);
}
+
return error;
}
+/**
+ * Destroy a selection result set
+ *
+ * \param results Result set to destroy
+ * \return CSS_OK on success, appropriate error otherwise
+ */
+css_error css_select_results_destroy(css_select_results *results)
+{
+ uint32_t i;
+
+ if (results == NULL)
+ return CSS_BADPARM;
+
+ if (results->styles != NULL) {
+ for (i = 0; i < CSS_PSEUDO_ELEMENT_COUNT; i++) {
+ if (results->styles[i] != NULL)
+ css_computed_style_destroy(results->styles[i]);
+ }
+ }
+
+ results->alloc(results, 0, results->pw);
+
+ return CSS_OK;
+}
+
/******************************************************************************
* Selection engine internals below here *
******************************************************************************/
-css_error set_hint(css_select_state *state, uint32_t i)
+css_error set_hint(css_select_state *state, uint32_t prop)
{
css_hint hint;
css_error error;
@@ -434,35 +509,40 @@ css_error set_hint(css_select_state *state, uint32_t i)
/* Retrieve this property's hint from the client */
error = state->handler->node_presentational_hint(state->pw,
- state->node, i, &hint);
+ state->node, prop, &hint);
if (error != CSS_OK)
return (error == CSS_PROPERTY_NOT_SET) ? CSS_OK : error;
/* Hint defined -- set it in the result */
- error = prop_dispatch[i].set_from_hint(&hint, state->result);
+ error = prop_dispatch[prop].set_from_hint(&hint, state->computed);
if (error != CSS_OK)
return error;
/* Keep selection state in sync with reality */
- state->props[i].set = 1;
- state->props[i].specificity = 0;
- state->props[i].origin = CSS_ORIGIN_AUTHOR;
- state->props[i].important = 0;
- state->props[i].inherit = (hint.status == 0);
+ state->props[prop][CSS_PSEUDO_ELEMENT_NONE].set = 1;
+ state->props[prop][CSS_PSEUDO_ELEMENT_NONE].specificity = 0;
+ state->props[prop][CSS_PSEUDO_ELEMENT_NONE].origin = CSS_ORIGIN_AUTHOR;
+ state->props[prop][CSS_PSEUDO_ELEMENT_NONE].important = 0;
+ state->props[prop][CSS_PSEUDO_ELEMENT_NONE].inherit =
+ (hint.status == 0);
return CSS_OK;
}
-css_error set_initial(css_select_state *state, uint32_t i, void *parent)
+css_error set_initial(css_select_state *state,
+ uint32_t prop, css_pseudo_element pseudo,
+ void *parent)
{
css_error error;
/* Do nothing if this property is inherited (the default state
* of a clean computed style is for everything to be set to inherit)
*
- * If the node is tree root, everything should be defaulted.
+ * If the node is tree root and we're dealing with the base element,
+ * everything should be defaulted.
*/
- if (prop_dispatch[i].inherited == false || parent == NULL) {
+ if (prop_dispatch[prop].inherited == false ||
+ (pseudo == CSS_PSEUDO_ELEMENT_NONE && parent == NULL)) {
/* Remaining properties are neither inherited nor
* already set. Thus, we set them to their initial
* values here. Except, however, if the property in
@@ -472,23 +552,23 @@ css_error set_initial(css_select_state *state, uint32_t i, void *parent)
* accessors to return the initial values for the
* property.
*/
- if (prop_dispatch[i].group == GROUP_NORMAL) {
- error = prop_dispatch[i].initial(state);
+ if (prop_dispatch[prop].group == GROUP_NORMAL) {
+ error = prop_dispatch[prop].initial(state);
if (error != CSS_OK)
return error;
- } else if (prop_dispatch[i].group == GROUP_UNCOMMON &&
- state->result->uncommon != NULL) {
- error = prop_dispatch[i].initial(state);
+ } else if (prop_dispatch[prop].group == GROUP_UNCOMMON &&
+ state->computed->uncommon != NULL) {
+ error = prop_dispatch[prop].initial(state);
if (error != CSS_OK)
return error;
- } else if (prop_dispatch[i].group == GROUP_PAGE &&
- state->result->page != NULL) {
- error = prop_dispatch[i].initial(state);
+ } else if (prop_dispatch[prop].group == GROUP_PAGE &&
+ state->computed->page != NULL) {
+ error = prop_dispatch[prop].initial(state);
if (error != CSS_OK)
return error;
- } else if (prop_dispatch[i].group == GROUP_AURAL &&
- state->result->aural != NULL) {
- error = prop_dispatch[i].initial(state);
+ } else if (prop_dispatch[prop].group == GROUP_AURAL &&
+ state->computed->aural != NULL) {
+ error = prop_dispatch[prop].initial(state);
if (error != CSS_OK)
return error;
}
@@ -541,6 +621,8 @@ css_error select_from_sheet(css_select_ctx *ctx, const css_stylesheet *sheet,
state->sheet = s;
state->current_origin = origin;
+ /** \todo This can be hoisted into css_select_style:
+ * the lwc context is global now */
error = intern_strings_for_sheet(ctx, s, state);
if (error != CSS_OK)
return error;
@@ -882,6 +964,9 @@ css_error match_selector_chain(css_select_ctx *ctx,
{
const css_selector *s = selector;
void *node = state->node;
+ const css_selector_detail *detail = &s->data;
+ bool match = false;
+ css_pseudo_element pseudo;
css_error error;
#ifdef DEBUG_CHAIN_MATCHING
@@ -890,31 +975,24 @@ css_error match_selector_chain(css_select_ctx *ctx,
fprintf(stderr, "\n");
#endif
- do {
- void *next_node = NULL;
- const css_selector_detail *detail = &s->data;
- bool match = false;
-
- /* If this is the first selector in the chain, we must match
- * its details. The details of subsequent selectors will be
- * matched when processing the combinator.
- *
- * Note that pseudo elements will only appear as details of
- * the first selector in the chain, as the parser will reject
- * any selector chains containing pseudo elements anywhere
- * else.
- */
- if (s == selector) {
- /* Match details on this selector */
- error = match_details(ctx, node, detail, state, &match);
- if (error != CSS_OK)
- return error;
+ /* Match the details of the first selector in the chain.
+ *
+ * Note that pseudo elements will only appear as details of
+ * the first selector in the chain, as the parser will reject
+ * any selector chains containing pseudo elements anywhere
+ * else.
+ */
+ error = match_details(ctx, node, detail, state, &match, &pseudo);
+ if (error != CSS_OK)
+ return error;
- /* Details don't match, so reject selector chain */
- if (match == false)
- return CSS_OK;
+ /* Details don't match, so reject selector chain */
+ if (match == false)
+ return CSS_OK;
- }
+ /* Iterate up the selector chain, matching combinators */
+ do {
+ void *next_node = NULL;
/* Consider any combinator on this selector */
if (s->data.comb != CSS_COMBINATOR_NONE &&
@@ -953,6 +1031,17 @@ css_error match_selector_chain(css_select_ctx *ctx,
if (((css_rule_selector *) selector->rule)->style == NULL)
return CSS_OK;
+ /* Ensure that the appropriate computed style exists */
+ if (state->results->styles[pseudo] == NULL) {
+ error = css_computed_style_create(ctx->alloc, ctx->pw,
+ &state->results->styles[pseudo]);
+ if (error != CSS_OK)
+ return error;
+ }
+
+ state->current_pseudo = pseudo;
+ state->computed = state->results->styles[pseudo];
+
return cascade_style(((css_rule_selector *) selector->rule)->style,
state);
}
@@ -994,7 +1083,8 @@ css_error match_named_combinator(css_select_ctx *ctx, css_combinator type,
if (n != NULL) {
/* Match its details */
- error = match_details(ctx, n, detail, state, &match);
+ error = match_details(ctx, n, detail, state,
+ &match, NULL);
if (error != CSS_OK)
return error;
@@ -1046,7 +1136,8 @@ css_error match_universal_combinator(css_select_ctx *ctx, css_combinator type,
if (n != NULL) {
/* Match its details */
- error = match_details(ctx, n, detail, state, &match);
+ error = match_details(ctx, n, detail, state,
+ &match, NULL);
if (error != CSS_OK)
return error;
@@ -1070,10 +1161,10 @@ css_error match_universal_combinator(css_select_ctx *ctx, css_combinator type,
css_error match_details(css_select_ctx *ctx, void *node,
const css_selector_detail *detail, css_select_state *state,
- bool *match)
+ bool *match, css_pseudo_element *pseudo_element)
{
css_error error;
- bool match_pseudo_element = false;
+ css_pseudo_element pseudo = CSS_PSEUDO_ELEMENT_NONE;
/* We match by default (if there are no details than the element
* selector, then we must match) */
@@ -1086,8 +1177,7 @@ css_error match_details(css_select_ctx *ctx, void *node,
* can be avoided unless absolutely necessary)? */
do {
- error = match_detail(ctx, node, detail, state, match,
- &match_pseudo_element);
+ error = match_detail(ctx, node, detail, state, match, &pseudo);
if (error != CSS_OK)
return error;
@@ -1101,19 +1191,16 @@ css_error match_details(css_select_ctx *ctx, void *node,
detail = NULL;
} while (detail != NULL);
- /* If we're selecting for a pseudo element and the selector chain did
- * not match the pseudo element, it's not a match; reject the selector
- * chain */
- if (state->pseudo_element != CSS_PSEUDO_ELEMENT_NONE &&
- match_pseudo_element == false)
- *match = false;
+ /* Return the applicable pseudo element, if required */
+ if (pseudo_element != NULL)
+ *pseudo_element = pseudo;
return CSS_OK;
}
css_error match_detail(css_select_ctx *ctx, void *node,
const css_selector_detail *detail, css_select_state *state,
- bool *match, bool *match_pseudo_element)
+ bool *match, css_pseudo_element *pseudo_element)
{
css_error error = CSS_OK;
@@ -1160,26 +1247,16 @@ css_error match_detail(css_select_ctx *ctx, void *node,
*match = false;
break;
case CSS_SELECTOR_PSEUDO_ELEMENT:
- if (detail->name == state->first_line &&
- state->pseudo_element ==
- CSS_PSEUDO_ELEMENT_FIRST_LINE) {
- *match = true;
- *match_pseudo_element = true;
- } else if (detail->name == state->first_letter &&
- state->pseudo_element ==
- CSS_PSEUDO_ELEMENT_FIRST_LETTER) {
- *match = true;
- *match_pseudo_element = true;
- } else if (detail->name == state->before &&
- state->pseudo_element ==
- CSS_PSEUDO_ELEMENT_BEFORE) {
- *match = true;
- *match_pseudo_element = true;
- } else if (detail->name == state->after &&
- state->pseudo_element ==
- CSS_PSEUDO_ELEMENT_AFTER) {
- *match = true;
- *match_pseudo_element = true;
+ *match = true;
+
+ if (detail->name == state->first_line) {
+ *pseudo_element = CSS_PSEUDO_ELEMENT_FIRST_LINE;
+ } else if (detail->name == state->first_letter) {
+ *pseudo_element = CSS_PSEUDO_ELEMENT_FIRST_LETTER;
+ } else if (detail->name == state->before) {
+ *pseudo_element = CSS_PSEUDO_ELEMENT_BEFORE;
+ } else if (detail->name == state->after) {
+ *pseudo_element = CSS_PSEUDO_ELEMENT_AFTER;
} else
*match = false;
break;
@@ -1228,7 +1305,7 @@ css_error cascade_style(const css_style *style, css_select_state *state)
bool outranks_existing(uint16_t op, bool important, css_select_state *state,
bool inherit)
{
- prop_state *existing = &state->props[op];
+ prop_state *existing = &state->props[op][state->current_pseudo];
bool outranks = false;
/* Sorting on origin & importance gives the following: