From 7ecab92e9a8508c44a758a8ca58defe9a5965bb4 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Sat, 14 Feb 2009 00:06:01 +0000 Subject: A motley selection of changes. Vague summary: + Add config makefile (not that there's anything to configure at present :) + Fix dumping of UNIT_MS to actually use sprintf and not printf + Extend computed style dumping (still loads of properties missing) + Make result buffer larger in select-auto.c -- avoids buffer overflows when there's way more output than expected + Expand expected test output to contain defaulted properties (more of this will be needed once the computed style dumping is complete) + Store interned string pointers in css_select_state. + Intern pseudo class/element names at start of selecting styles for a sheet + Group properties so we know which ones appear in the extension blocks + Fixup unset properties once the cascade has completed + Implement matching of pseudo class/element selectors + Fix setting of background-image and list-style-image when there's no URL. svn path=/trunk/libcss/; revision=6470 --- build/Makefile.common | 3 + build/Makefile.config | 4 + src/select/propset.h | 18 +- src/select/select.c | 455 ++++++++++++++++++++++++++++++++------------ test/data/select/tests1.dat | 27 +++ test/dump.h | 2 +- test/dump_computed.h | 301 +++++++++++++++++++++++++++++ test/select-auto.c | 10 +- 8 files changed, 688 insertions(+), 132 deletions(-) create mode 100644 build/Makefile.config diff --git a/build/Makefile.common b/build/Makefile.common index f6bc147..73a4880 100644 --- a/build/Makefile.common +++ b/build/Makefile.common @@ -24,6 +24,9 @@ TARGET_TESTS := # Source files SOURCES := +# Include configuration Makefile fragment +include build/Makefile.config + # Include Makefile fragments in subdirectories define do_include diff --git a/build/Makefile.config b/build/Makefile.config new file mode 100644 index 0000000..1d27350 --- /dev/null +++ b/build/Makefile.config @@ -0,0 +1,4 @@ +# Configuration Makefile fragment + +# Cater for local configuration changes +-include build/Makefile.config.override diff --git a/src/select/propset.h b/src/select/propset.h index ff9658c..bdc85bb 100644 --- a/src/select/propset.h +++ b/src/select/propset.h @@ -457,8 +457,13 @@ static inline css_error set_background_image( *bits = (*bits & ~BACKGROUND_IMAGE_MASK) | ((type & 0x1) << BACKGROUND_IMAGE_SHIFT); - style->background_image.data = (uint8_t *) url->data; - style->background_image.len = url->len; + if (url != NULL) { + style->background_image.data = (uint8_t *) url->data; + style->background_image.len = url->len; + } else { + style->background_image.data = NULL; + style->background_image.len = 0; + } return CSS_OK; } @@ -500,8 +505,13 @@ static inline css_error set_list_style_image( *bits = (*bits & ~LIST_STYLE_IMAGE_MASK) | ((type & 0x1) << LIST_STYLE_IMAGE_SHIFT); - style->list_style_image.data = (uint8_t *) url->data; - style->list_style_image.len = url->len; + if (url != NULL) { + style->list_style_image.data = (uint8_t *) url->data; + style->list_style_image.len = url->len; + } else { + style->list_style_image.data = NULL; + style->list_style_image.len = 0; + } return CSS_OK; } diff --git a/src/select/select.c b/src/select/select.c index 2397bf8..5fafcd1 100644 --- a/src/select/select.c +++ b/src/select/select.c @@ -58,16 +58,33 @@ typedef struct css_select_state { css_origin current_origin; /* Origin of current sheet */ uint32_t current_specificity; /* Specificity of current rule */ + /* Useful interned strings */ + const parserutils_hash_entry *universal; + const parserutils_hash_entry *first_child; + const parserutils_hash_entry *link; + const parserutils_hash_entry *visited; + const parserutils_hash_entry *hover; + const parserutils_hash_entry *active; + const parserutils_hash_entry *focus; + const parserutils_hash_entry *left; + const parserutils_hash_entry *right; + const parserutils_hash_entry *first; + const parserutils_hash_entry *first_line; + const parserutils_hash_entry *first_letter; + const parserutils_hash_entry *before; + const parserutils_hash_entry *after; + prop_state props[N_OPCODES]; } css_select_state; static css_error select_from_sheet(css_select_ctx *ctx, const css_stylesheet *sheet, css_select_state *state); +static css_error intern_strings_for_sheet(css_select_ctx *ctx, + const css_stylesheet *sheet, css_select_state *state); static css_error match_selectors_in_sheet(css_select_ctx *ctx, const css_stylesheet *sheet, css_select_state *state); static css_error match_selector_chain(css_select_ctx *ctx, - const css_selector *selector, css_select_state *state, - const parserutils_hash_entry *universal); + const css_selector *selector, css_select_state *state); static css_error match_named_combinator(css_select_ctx *ctx, css_combinator type, const parserutils_hash_entry *name, css_select_state *state, void *node, void **next_node); @@ -88,6 +105,16 @@ static bool outranks_existing(uint16_t op, bool important, /* Property handlers */ #include "select/properties.c" +/** + * Enumeration of property groups + */ +enum prop_group { + GROUP_NORMAL = 0x0, + GROUP_UNCOMMON = 0x1, + GROUP_PAGE = 0x2, + GROUP_AURAL = 0x3 +}; + /** * Dispatch table for properties, indexed by opcode */ @@ -96,107 +123,108 @@ static struct prop_table { css_select_state *state); css_error (*initial)(css_computed_style *style); - uint32_t inherited : 1; + uint32_t inherited : 1, + group : 2; } properties[N_OPCODES] = { - { cascade_azimuth, initial_azimuth, 1 }, - { cascade_background_attachment, initial_background_attachment, 0 }, - { cascade_background_color, initial_background_color, 0 }, - { cascade_background_image, initial_background_image, 0 }, - { cascade_background_position, initial_background_position, 0 }, - { cascade_background_repeat, initial_background_repeat, 0 }, - { cascade_border_collapse, initial_border_collapse, 1 }, - { cascade_border_spacing, initial_border_spacing, 1 }, - { cascade_border_top_color, initial_border_top_color, 0 }, - { cascade_border_right_color, initial_border_right_color, 0 }, - { cascade_border_bottom_color, initial_border_bottom_color, 0 }, - { cascade_border_left_color, initial_border_left_color, 0 }, - { cascade_border_top_style, initial_border_top_style, 0 }, - { cascade_border_right_style, initial_border_right_style, 0 }, - { cascade_border_bottom_style, initial_border_bottom_style, 0 }, - { cascade_border_left_style, initial_border_left_style, 0 }, - { cascade_border_top_width, initial_border_top_width, 0 }, - { cascade_border_right_width, initial_border_right_width, 0 }, - { cascade_border_bottom_width, initial_border_bottom_width, 0 }, - { cascade_border_left_width, initial_border_left_width, 0 }, - { cascade_bottom, initial_bottom, 0 }, - { cascade_caption_side, initial_caption_side, 1 }, - { cascade_clear, initial_clear, 0 }, - { cascade_clip, initial_clip, 0 }, - { cascade_color, initial_color, 1 }, - { cascade_content, initial_content, 0 }, - { cascade_counter_increment, initial_counter_increment, 0 }, - { cascade_counter_reset, initial_counter_reset, 0 }, - { cascade_cue_after, initial_cue_after, 0 }, - { cascade_cue_before, initial_cue_before, 0 }, - { cascade_cursor, initial_cursor, 1 }, - { cascade_direction, initial_direction, 1 }, - { cascade_display, initial_display, 0 }, - { cascade_elevation, initial_elevation, 1 }, - { cascade_empty_cells, initial_empty_cells, 1 }, - { cascade_float, initial_float, 0 }, - { cascade_font_family, initial_font_family, 1 }, - { cascade_font_size, initial_font_size, 1 }, - { cascade_font_style, initial_font_style, 1 }, - { cascade_font_variant, initial_font_variant, 1 }, - { cascade_font_weight, initial_font_weight, 1 }, - { cascade_height, initial_height, 0 }, - { cascade_left, initial_left, 0 }, - { cascade_letter_spacing, initial_letter_spacing, 1 }, - { cascade_line_height, initial_line_height, 1 }, - { cascade_list_style_image, initial_list_style_image, 1 }, - { cascade_list_style_position, initial_list_style_position, 1 }, - { cascade_list_style_type, initial_list_style_type, 1 }, - { cascade_margin_top, initial_margin_top, 0 }, - { cascade_margin_right, initial_margin_right, 0 }, - { cascade_margin_bottom, initial_margin_bottom, 0 }, - { cascade_margin_left, initial_margin_left, 0 }, - { cascade_max_height, initial_max_height, 0 }, - { cascade_max_width, initial_max_width, 0 }, - { cascade_min_height, initial_min_height, 0 }, - { cascade_min_width, initial_min_width, 0 }, - { cascade_orphans, initial_orphans, 1 }, - { cascade_outline_color, initial_outline_color, 0 }, - { cascade_outline_style, initial_outline_style, 0 }, - { cascade_outline_width, initial_outline_width, 0 }, - { cascade_overflow, initial_overflow, 0 }, - { cascade_padding_top, initial_padding_top, 0 }, - { cascade_padding_right, initial_padding_right, 0 }, - { cascade_padding_bottom, initial_padding_bottom, 0 }, - { cascade_padding_left, initial_padding_left, 0 }, - { cascade_page_break_after, initial_page_break_after, 0 }, - { cascade_page_break_before, initial_page_break_before, 0 }, - { cascade_page_break_inside, initial_page_break_inside, 1 }, - { cascade_pause_after, initial_pause_after, 0 }, - { cascade_pause_before, initial_pause_before, 0 }, - { cascade_pitch_range, initial_pitch_range, 1 }, - { cascade_pitch, initial_pitch, 1 }, - { cascade_play_during, initial_play_during, 0 }, - { cascade_position, initial_position, 0 }, - { cascade_quotes, initial_quotes, 1 }, - { cascade_richness, initial_richness, 1 }, - { cascade_right, initial_right, 0 }, - { cascade_speak_header, initial_speak_header, 1 }, - { cascade_speak_numeral, initial_speak_numeral, 1 }, - { cascade_speak_punctuation, initial_speak_punctuation, 1 }, - { cascade_speak, initial_speak, 1 }, - { cascade_speech_rate, initial_speech_rate, 1 }, - { cascade_stress, initial_stress, 1 }, - { cascade_table_layout, initial_table_layout, 0 }, - { cascade_text_align, initial_text_align, 1 }, - { cascade_text_decoration, initial_text_decoration, 0 }, - { cascade_text_indent, initial_text_indent, 1 }, - { cascade_text_transform, initial_text_transform, 1 }, - { cascade_top, initial_top, 0 }, - { cascade_unicode_bidi, initial_unicode_bidi, 0 }, - { cascade_vertical_align, initial_vertical_align, 0 }, - { cascade_visibility, initial_visibility, 1 }, - { cascade_voice_family, initial_voice_family, 1 }, - { cascade_volume, initial_volume, 1 }, - { cascade_white_space, initial_white_space, 1 }, - { cascade_widows, initial_widows, 1 }, - { cascade_width, initial_width, 0 }, - { cascade_word_spacing, initial_word_spacing, 1 }, - { cascade_z_index, initial_z_index, 0 } + { cascade_azimuth, initial_azimuth, 1, GROUP_AURAL}, + { cascade_background_attachment, initial_background_attachment, 0, GROUP_NORMAL }, + { cascade_background_color, initial_background_color, 0, GROUP_NORMAL }, + { cascade_background_image, initial_background_image, 0, GROUP_NORMAL }, + { cascade_background_position, initial_background_position, 0, GROUP_NORMAL }, + { cascade_background_repeat, initial_background_repeat, 0, GROUP_NORMAL }, + { cascade_border_collapse, initial_border_collapse, 1, GROUP_NORMAL }, + { cascade_border_spacing, initial_border_spacing, 1, GROUP_UNCOMMON }, + { cascade_border_top_color, initial_border_top_color, 0, GROUP_NORMAL }, + { cascade_border_right_color, initial_border_right_color, 0, GROUP_NORMAL }, + { cascade_border_bottom_color, initial_border_bottom_color, 0, GROUP_NORMAL }, + { cascade_border_left_color, initial_border_left_color, 0, GROUP_NORMAL }, + { cascade_border_top_style, initial_border_top_style, 0, GROUP_NORMAL }, + { cascade_border_right_style, initial_border_right_style, 0, GROUP_NORMAL }, + { cascade_border_bottom_style, initial_border_bottom_style, 0, GROUP_NORMAL }, + { cascade_border_left_style, initial_border_left_style, 0, GROUP_NORMAL }, + { cascade_border_top_width, initial_border_top_width, 0, GROUP_NORMAL }, + { cascade_border_right_width, initial_border_right_width, 0, GROUP_NORMAL }, + { cascade_border_bottom_width, initial_border_bottom_width, 0, GROUP_NORMAL }, + { cascade_border_left_width, initial_border_left_width, 0, GROUP_NORMAL }, + { cascade_bottom, initial_bottom, 0, GROUP_NORMAL }, + { cascade_caption_side, initial_caption_side, 1, GROUP_NORMAL }, + { cascade_clear, initial_clear, 0, GROUP_NORMAL }, + { cascade_clip, initial_clip, 0, GROUP_UNCOMMON }, + { cascade_color, initial_color, 1, GROUP_NORMAL }, + { cascade_content, initial_content, 0, GROUP_UNCOMMON }, + { cascade_counter_increment, initial_counter_increment, 0, GROUP_UNCOMMON }, + { cascade_counter_reset, initial_counter_reset, 0, GROUP_UNCOMMON }, + { cascade_cue_after, initial_cue_after, 0, GROUP_AURAL }, + { cascade_cue_before, initial_cue_before, 0, GROUP_AURAL }, + { cascade_cursor, initial_cursor, 1, GROUP_UNCOMMON }, + { cascade_direction, initial_direction, 1, GROUP_NORMAL }, + { cascade_display, initial_display, 0, GROUP_NORMAL }, + { cascade_elevation, initial_elevation, 1, GROUP_AURAL }, + { cascade_empty_cells, initial_empty_cells, 1, GROUP_NORMAL }, + { cascade_float, initial_float, 0, GROUP_NORMAL }, + { cascade_font_family, initial_font_family, 1, GROUP_NORMAL }, + { cascade_font_size, initial_font_size, 1, GROUP_NORMAL }, + { cascade_font_style, initial_font_style, 1, GROUP_NORMAL }, + { cascade_font_variant, initial_font_variant, 1, GROUP_NORMAL }, + { cascade_font_weight, initial_font_weight, 1, GROUP_NORMAL }, + { cascade_height, initial_height, 0, GROUP_NORMAL }, + { cascade_left, initial_left, 0, GROUP_NORMAL }, + { cascade_letter_spacing, initial_letter_spacing, 1, GROUP_UNCOMMON }, + { cascade_line_height, initial_line_height, 1, GROUP_NORMAL }, + { cascade_list_style_image, initial_list_style_image, 1, GROUP_NORMAL }, + { cascade_list_style_position, initial_list_style_position, 1, GROUP_NORMAL }, + { cascade_list_style_type, initial_list_style_type, 1, GROUP_NORMAL }, + { cascade_margin_top, initial_margin_top, 0, GROUP_NORMAL }, + { cascade_margin_right, initial_margin_right, 0, GROUP_NORMAL }, + { cascade_margin_bottom, initial_margin_bottom, 0, GROUP_NORMAL }, + { cascade_margin_left, initial_margin_left, 0, GROUP_NORMAL }, + { cascade_max_height, initial_max_height, 0, GROUP_NORMAL }, + { cascade_max_width, initial_max_width, 0, GROUP_NORMAL }, + { cascade_min_height, initial_min_height, 0, GROUP_NORMAL }, + { cascade_min_width, initial_min_width, 0, GROUP_NORMAL }, + { cascade_orphans, initial_orphans, 1, GROUP_PAGE }, + { cascade_outline_color, initial_outline_color, 0, GROUP_UNCOMMON }, + { cascade_outline_style, initial_outline_style, 0, GROUP_NORMAL }, + { cascade_outline_width, initial_outline_width, 0, GROUP_UNCOMMON }, + { cascade_overflow, initial_overflow, 0, GROUP_NORMAL }, + { cascade_padding_top, initial_padding_top, 0, GROUP_NORMAL }, + { cascade_padding_right, initial_padding_right, 0, GROUP_NORMAL }, + { cascade_padding_bottom, initial_padding_bottom, 0, GROUP_NORMAL }, + { cascade_padding_left, initial_padding_left, 0, GROUP_NORMAL }, + { cascade_page_break_after, initial_page_break_after, 0, GROUP_PAGE }, + { cascade_page_break_before, initial_page_break_before, 0, GROUP_PAGE }, + { cascade_page_break_inside, initial_page_break_inside, 1, GROUP_PAGE }, + { cascade_pause_after, initial_pause_after, 0, GROUP_AURAL }, + { cascade_pause_before, initial_pause_before, 0, GROUP_AURAL }, + { cascade_pitch_range, initial_pitch_range, 1, GROUP_AURAL }, + { cascade_pitch, initial_pitch, 1, GROUP_AURAL }, + { cascade_play_during, initial_play_during, 0, GROUP_AURAL }, + { cascade_position, initial_position, 0, GROUP_NORMAL }, + { cascade_quotes, initial_quotes, 1, GROUP_UNCOMMON }, + { cascade_richness, initial_richness, 1, GROUP_AURAL }, + { cascade_right, initial_right, 0, GROUP_NORMAL }, + { cascade_speak_header, initial_speak_header, 1, GROUP_AURAL }, + { cascade_speak_numeral, initial_speak_numeral, 1, GROUP_AURAL }, + { cascade_speak_punctuation, initial_speak_punctuation, 1, GROUP_AURAL }, + { cascade_speak, initial_speak, 1, GROUP_AURAL }, + { cascade_speech_rate, initial_speech_rate, 1, GROUP_AURAL }, + { cascade_stress, initial_stress, 1, GROUP_AURAL }, + { cascade_table_layout, initial_table_layout, 0, GROUP_NORMAL }, + { cascade_text_align, initial_text_align, 1, GROUP_NORMAL }, + { cascade_text_decoration, initial_text_decoration, 0, GROUP_NORMAL }, + { cascade_text_indent, initial_text_indent, 1, GROUP_NORMAL }, + { cascade_text_transform, initial_text_transform, 1, GROUP_NORMAL }, + { cascade_top, initial_top, 0, GROUP_NORMAL }, + { cascade_unicode_bidi, initial_unicode_bidi, 0, GROUP_NORMAL }, + { cascade_vertical_align, initial_vertical_align, 0, GROUP_NORMAL }, + { cascade_visibility, initial_visibility, 1, GROUP_NORMAL }, + { cascade_voice_family, initial_voice_family, 1, GROUP_AURAL }, + { cascade_volume, initial_volume, 1, GROUP_AURAL }, + { cascade_white_space, initial_white_space, 1, GROUP_NORMAL }, + { cascade_widows, initial_widows, 1, GROUP_PAGE }, + { cascade_width, initial_width, 0, GROUP_NORMAL }, + { cascade_word_spacing, initial_word_spacing, 1, GROUP_UNCOMMON }, + { cascade_z_index, initial_z_index, 0, GROUP_NORMAL } }; /** @@ -385,7 +413,7 @@ css_error css_select_ctx_get_sheet(css_select_ctx *ctx, uint32_t index, * \param pseudo_element Pseudo element to select for, instead * \param pseudo_classes Currently active pseudo classes * \param media Currently active media types - * \param result Pointer to style to populate + * \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 * \return CSS_OK on success, appropriate error otherwise. @@ -435,7 +463,45 @@ css_error css_select_style(css_select_ctx *ctx, void *node, * Those properties which are inherited need to be set as inherit. * Those which are not inherited need to be set to their default value. */ - /** \todo fixup unset properties */ + for (i = 0; i < N_OPCODES; i++) { + /* Do nothing if this property is set */ + if (state.props[i].set) + continue; + + /* Do nothing if this property is inherited (the default state + * of a clean computed style is for everything to be set to + * inherit) */ + if (properties[i].inherited) + continue; + + /* Remaining properties are neither inherited nor already set. + * Thus, we set them to their initial values here. Except, + * however, if the property in question resides in one of the + * extension blocks and the extension block has yet to be + * allocated. In that case, we do nothing and leave it to the + * property accessors to return the initial values for the + * property. */ + if (properties[i].group == GROUP_NORMAL) { + error = properties[i].initial(result); + if (error != CSS_OK) + return error; + } else if (properties[i].group == GROUP_UNCOMMON && + result->uncommon != NULL) { + error = properties[i].initial(result); + if (error != CSS_OK) + return error; + } else if (properties[i].group == GROUP_PAGE && + result->page != NULL) { + error = properties[i].initial(result); + if (error != CSS_OK) + return error; + } else if (properties[i].group == GROUP_AURAL && + result->aural != NULL) { + error = properties[i].initial(result); + if (error != CSS_OK) + return error; + } + } return CSS_OK; } @@ -482,6 +548,10 @@ css_error select_from_sheet(css_select_ctx *ctx, const css_stylesheet *sheet, state->sheet = s; state->current_origin = s->origin; + error = intern_strings_for_sheet(ctx, s, state); + if (error != CSS_OK) + return error; + error = match_selectors_in_sheet(ctx, s, state); if (error != CSS_OK) return error; @@ -499,10 +569,105 @@ css_error select_from_sheet(css_select_ctx *ctx, const css_stylesheet *sheet, return CSS_OK; } +css_error intern_strings_for_sheet(css_select_ctx *ctx, + const css_stylesheet *sheet, css_select_state *state) +{ + parserutils_error perror; + + UNUSED(ctx); + + /* Universal selector */ + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "*", SLEN("*"), &state->universal); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + /* Pseudo classes */ + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "first-child", SLEN("first-child"), + &state->first_child); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "link", SLEN("link"), + &state->link); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "visited", SLEN("visited"), + &state->visited); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "hover", SLEN("hover"), + &state->hover); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "active", SLEN("active"), + &state->active); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "focus", SLEN("focus"), + &state->focus); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "left", SLEN("left"), + &state->left); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "right", SLEN("right"), + &state->right); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "first", SLEN("first"), + &state->first); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + /* Pseudo elements */ + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "first-line", SLEN("first-line"), + &state->first_line); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "first_letter", SLEN("first-letter"), + &state->first_letter); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "before", SLEN("before"), + &state->before); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + perror = parserutils_hash_insert(sheet->dictionary, + (const uint8_t *) "after", SLEN("after"), + &state->after); + if (perror != PARSERUTILS_OK) + return css_error_from_parserutils_error(perror); + + return CSS_OK; +} + css_error match_selectors_in_sheet(css_select_ctx *ctx, const css_stylesheet *sheet, css_select_state *state) { - const parserutils_hash_entry *universal; const parserutils_hash_entry *element; const css_selector **selectors; const uint8_t *name; @@ -521,12 +686,6 @@ css_error match_selectors_in_sheet(css_select_ctx *ctx, if (perror != PARSERUTILS_OK) return css_error_from_parserutils_error(perror); - /* Intern universal selector string */ - perror = parserutils_hash_insert(sheet->dictionary, - (const uint8_t *) "*", 1, &universal); - if (perror != PARSERUTILS_OK) - return css_error_from_parserutils_error(perror); - /* Find hash chain that applies to current node */ error = css_selector_hash_find(sheet->selectors, element, &selectors); if (error != CSS_OK) @@ -534,7 +693,7 @@ css_error match_selectors_in_sheet(css_select_ctx *ctx, /* Process any matching selectors */ while (*selectors != NULL) { - error = match_selector_chain(ctx, *selectors, state, universal); + error = match_selector_chain(ctx, *selectors, state); if (error != CSS_OK) return error; @@ -545,13 +704,14 @@ css_error match_selectors_in_sheet(css_select_ctx *ctx, } /* Find hash chain for universal selector */ - error = css_selector_hash_find(sheet->selectors, universal, &selectors); + error = css_selector_hash_find(sheet->selectors, state->universal, + &selectors); if (error != CSS_OK) return error; /* Process any matching selectors */ while (*selectors != NULL) { - error = match_selector_chain(ctx, *selectors, state, universal); + error = match_selector_chain(ctx, *selectors, state); if (error != CSS_OK) return error; @@ -565,8 +725,7 @@ css_error match_selectors_in_sheet(css_select_ctx *ctx, } css_error match_selector_chain(css_select_ctx *ctx, - const css_selector *selector, css_select_state *state, - const parserutils_hash_entry *universal) + const css_selector *selector, css_select_state *state) { const css_selector *s = selector; void *node = state->node; @@ -579,7 +738,7 @@ css_error match_selector_chain(css_select_ctx *ctx, /* First, consider any named combinator on this selector */ if (s->data.comb != CSS_COMBINATOR_NONE && - s->combinator->data.name != universal) { + s->combinator->data.name != state->universal) { error = match_named_combinator(ctx, s->data.comb, s->combinator->data.name, state, node, &next_node); @@ -602,7 +761,7 @@ css_error match_selector_chain(css_select_ctx *ctx, /* If we had a universal combinator, then consider that */ if (s->data.comb != CSS_COMBINATOR_NONE && - s->combinator->data.name == universal) { + s->combinator->data.name == state->universal) { error = match_universal_combinator(ctx, s->data.comb, s->combinator, state, node, &next_node); if (error != CSS_OK) @@ -762,12 +921,64 @@ css_error match_detail(css_select_ctx *ctx, void *node, match); break; case CSS_SELECTOR_PSEUDO_CLASS: - /** \todo pseudo classes */ - *match = false; + if (detail->name == state->first_child && + (state->pseudo_classes & + CSS_PSEUDO_CLASS_FIRST_CHILD)) + *match = true; + else if (detail->name == state->link && + (state->pseudo_classes & + CSS_PSEUDO_CLASS_LINK)) + *match = true; + else if (detail->name == state->visited && + (state->pseudo_classes & + CSS_PSEUDO_CLASS_VISITED)) + *match = true; + else if (detail->name == state->hover && + (state->pseudo_classes & + CSS_PSEUDO_CLASS_HOVER)) + *match = true; + else if (detail->name == state->active && + (state->pseudo_classes & + CSS_PSEUDO_CLASS_ACTIVE)) + *match = true; + else if (detail->name == state->focus && + (state->pseudo_classes & + CSS_PSEUDO_CLASS_FOCUS)) + *match = true; + else if (detail->name == state->left && + (state->pseudo_classes & + CSS_PSEUDO_CLASS_LEFT)) + *match = true; + else if (detail->name == state->right && + (state->pseudo_classes & + CSS_PSEUDO_CLASS_RIGHT)) + *match = true; + else if (detail->name == state->first && + (state->pseudo_classes & + CSS_PSEUDO_CLASS_FIRST)) + *match = true; + else + *match = false; break; case CSS_SELECTOR_PSEUDO_ELEMENT: - /** \todo pseudo elements */ - *match = false; + if (detail->name == state->first_line && + (state->pseudo_element & + CSS_PSEUDO_ELEMENT_FIRST_LINE)) + *match = true; + else if (detail->name == state->first_letter && + (state->pseudo_element & + CSS_PSEUDO_ELEMENT_FIRST_LETTER)) + *match = true; + else if (detail->name == state->before && + (state->pseudo_element & + CSS_PSEUDO_ELEMENT_BEFORE)) + *match = true; + else if (detail->name == state->after && + (state->pseudo_element & + CSS_PSEUDO_ELEMENT_AFTER)) + *match = true; + else + *match = false; break; case CSS_SELECTOR_ATTRIBUTE: error = state->handler->node_has_attribute(state->pw, node, diff --git a/test/data/select/tests1.dat b/test/data/select/tests1.dat index d8a55b0..5b415f3 100644 --- a/test/data/select/tests1.dat +++ b/test/data/select/tests1.dat @@ -12,6 +12,15 @@ div#foo { background-color: #bbc; } .green { color: #0f0; } #errors #expected +background-attachment: scroll +background-color: transparent +background-image: none +background-position: 0% 0% +background-repeat: repeat +border-top-color: #00000000 +border-right-color: #00000000 +border-bottom-color: #00000000 +border-left-color: #00000000 color: #ff000000 display: block #reset @@ -24,6 +33,15 @@ div { display: block; } div { display: inline; } #errors #expected +background-attachment: scroll +background-color: transparent +background-image: none +background-position: 0% 0% +background-repeat: repeat +border-top-color: #00000000 +border-right-color: #00000000 +border-bottom-color: #00000000 +border-left-color: #00000000 display: block #reset @@ -34,6 +52,15 @@ div { background-color: #000; } div:active { background-color: #bbc; } #errors #expected +background-attachment: scroll background-color: #bbbbcc00 +background-image: none +background-position: 0% 0% +background-repeat: repeat +border-top-color: #00000000 +border-right-color: #00000000 +border-bottom-color: #00000000 +border-left-color: #00000000 +display: inline #reset diff --git a/test/dump.h b/test/dump.h index c217565..98ec1bb 100644 --- a/test/dump.h +++ b/test/dump.h @@ -436,7 +436,7 @@ static void dump_unit(fixed val, uint32_t unit, char **ptr) *ptr += sprintf(*ptr, "rad"); break; case UNIT_MS: - *ptr += printf(*ptr, "ms"); + *ptr += sprintf(*ptr, "ms"); break; case UNIT_S: *ptr += sprintf(*ptr, "s"); diff --git a/test/dump_computed.h b/test/dump_computed.h index dda07c1..f573e9d 100644 --- a/test/dump_computed.h +++ b/test/dump_computed.h @@ -1,6 +1,141 @@ #include #include +#include "utils/fpmath.h" + +static size_t dump_css_fixed(css_fixed f, char *ptr, size_t len) +{ +#define ABS(x) (uint32_t)((x) < 0 ? -(x) : (x)) + uint32_t uintpart = FIXTOINT(ABS(f)); + /* + 500 to ensure round to nearest (division will truncate) */ + uint32_t fracpart = ((ABS(f) & 0x3ff) * 1000 + 500) / (1 << 10); +#undef ABS + size_t flen = 0; + char tmp[20]; + size_t tlen = 0; + char *buf = ptr; + + if (len == 0) + return 0; + + if (f < 0) { + buf[0] = '-'; + buf++; + len--; + } + + do { + tmp[tlen] = "0123456789"[uintpart % 10]; + tlen++; + + uintpart /= 10; + } while (tlen < 20 && uintpart != 0); + + while (tlen > 0 && len > 0) { + buf[0] = tmp[--tlen]; + buf++; + len--; + } + + if (len > 0) { + buf[0] = '.'; + buf++; + len--; + } + + do { + tmp[tlen] = "0123456789"[fracpart % 10]; + tlen++; + + fracpart /= 10; + } while (tlen < 20 && fracpart != 0); + + while (tlen > 0 && len > 0) { + buf[0] = tmp[--tlen]; + buf++; + flen++; + len--; + } + + while (flen < 3 && len > 0) { + buf[0] = '0'; + buf++; + flen++; + len--; + } + + if (len > 0) + buf[0] = '\0'; + + return buf - ptr; +} +static size_t dump_css_number(css_fixed val, char *ptr, size_t len) +{ + if (INTTOFIX(FIXTOINT(val)) == val) + return snprintf(ptr, len, "%d", FIXTOINT(val)); + else + return dump_css_fixed(val, ptr, len); +} + +static size_t dump_css_unit(css_fixed val, css_unit unit, char *ptr, size_t len) +{ + size_t ret = dump_css_number(val, ptr, len); + + switch (unit) { + case CSS_UNIT_PX: + ret += snprintf(ptr + ret, len - ret, "px"); + break; + case CSS_UNIT_EX: + ret += snprintf(ptr + ret, len - ret, "ex"); + break; + case CSS_UNIT_EM: + ret += snprintf(ptr + ret, len - ret, "em"); + break; + case CSS_UNIT_IN: + ret += snprintf(ptr + ret, len - ret, "in"); + break; + case CSS_UNIT_CM: + ret += snprintf(ptr + ret, len - ret, "cm"); + break; + case CSS_UNIT_MM: + ret += snprintf(ptr + ret, len - ret, "mm"); + break; + case CSS_UNIT_PT: + ret += snprintf(ptr + ret, len - ret, "pt"); + break; + case CSS_UNIT_PC: + ret += snprintf(ptr + ret, len - ret, "pc"); + break; + case CSS_UNIT_PCT: + ret += snprintf(ptr + ret, len - ret, "%%"); + break; + case CSS_UNIT_DEG: + ret += snprintf(ptr + ret, len - ret, "deg"); + break; + case CSS_UNIT_GRAD: + ret += snprintf(ptr + ret, len - ret, "grad"); + break; + case CSS_UNIT_RAD: + ret += snprintf(ptr + ret, len - ret, "rad"); + break; + case CSS_UNIT_MS: + ret += snprintf(ptr + ret, len - ret, "ms"); + break; + case CSS_UNIT_S: + ret += snprintf(ptr + ret, len - ret, "s"); + break; + case CSS_UNIT_HZ: + ret += snprintf(ptr + ret, len - ret, "Hz"); + break; + case CSS_UNIT_KHZ: + ret += snprintf(ptr + ret, len - ret, "kHz"); + break; + } + + return ret; +} + + static void dump_computed_style(const css_computed_style *style, char *buf, size_t *len) { @@ -8,6 +143,9 @@ static void dump_computed_style(const css_computed_style *style, char *buf, size_t wrote = 0; uint8_t val; css_color color; + const css_string *url; + css_fixed len1, len2; + css_unit unit1, unit2; val = css_computed_background_attachment(style); switch (val) { @@ -39,6 +177,169 @@ static void dump_computed_style(const css_computed_style *style, char *buf, ptr += wrote; *len -= wrote; + val = css_computed_background_image(style, &url); + if (val == CSS_BACKGROUND_IMAGE_IMAGE && url->data != NULL) { + wrote = snprintf(ptr, *len, "background-image: url('%.*s')\n", + (int) url->len, url->data); + } else if (val == CSS_BACKGROUND_IMAGE_NONE) { + wrote = snprintf(ptr, *len, "background-image: none\n"); + } else { + wrote = 0; + } + ptr += wrote; + *len -= wrote; + + val = css_computed_background_position(style, &len1, &unit1, + &len2, &unit2); + if (val == CSS_BACKGROUND_POSITION_SET) { + wrote = snprintf(ptr, *len, "background-position: "); + ptr += wrote; + *len -= wrote; + + wrote = dump_css_unit(len1, unit1, ptr, *len); + ptr += wrote; + *len -= wrote; + + wrote = snprintf(ptr, *len, " "); + ptr += wrote; + *len -= wrote; + + wrote = dump_css_unit(len2, unit2, ptr, *len); + ptr += wrote; + *len -= wrote; + + wrote = snprintf(ptr, *len, "\n"); + ptr += wrote; + *len -= wrote; + } + + val = css_computed_background_repeat(style); + switch (val) { + case CSS_BACKGROUND_REPEAT_REPEAT_X: + wrote = snprintf(ptr, *len, "background-repeat: repeat-x\n"); + break; + case CSS_BACKGROUND_REPEAT_REPEAT_Y: + wrote = snprintf(ptr, *len, "background-repeat: repeat-y\n"); + break; + case CSS_BACKGROUND_REPEAT_REPEAT: + wrote = snprintf(ptr, *len, "background-repeat: repeat\n"); + break; + case CSS_BACKGROUND_REPEAT_NO_REPEAT: + wrote = snprintf(ptr, *len, "background-repeat: no-repeat\n"); + break; + default: + wrote = 0; + break; + } + ptr += wrote; + *len -= wrote; + + val = css_computed_border_collapse(style); + switch (val) { + case CSS_BORDER_COLLAPSE_SEPARATE: + wrote = snprintf(ptr, *len, "border-collapse: separate\n"); + break; + case CSS_BORDER_COLLAPSE_COLLAPSE: + wrote = snprintf(ptr, *len, "border-collapse: collapse\n"); + break; + default: + wrote = 0; + break; + } + ptr += wrote; + *len -= wrote; + + if (style->uncommon != NULL) { + val = css_computed_border_spacing(style, &len1, &unit1, + &len2, &unit2); + if (val == CSS_BORDER_SPACING_SET) { + wrote = snprintf(ptr, *len, "border-spacing: "); + ptr += wrote; + *len -= wrote; + + wrote = dump_css_unit(len1, unit1, ptr, *len); + ptr += wrote; + *len -= wrote; + + wrote = snprintf(ptr, *len, " "); + ptr += wrote; + *len -= wrote; + + wrote = dump_css_unit(len2, unit2, ptr, *len); + ptr += wrote; + *len -= wrote; + + wrote = snprintf(ptr, *len, "\n"); + ptr += wrote; + *len -= wrote; + } + } + + val = css_computed_border_top_color(style, &color); + switch (val) { + case CSS_BORDER_COLOR_TRANSPARENT: + wrote = snprintf(ptr, *len, "border-top-color: transparent\n"); + break; + case CSS_BORDER_COLOR_COLOR: + wrote = snprintf(ptr, *len, "border-top-color: #%08x\n", color); + break; + default: + wrote = 0; + break; + } + ptr += wrote; + *len -= wrote; + + val = css_computed_border_right_color(style, &color); + switch (val) { + case CSS_BORDER_COLOR_TRANSPARENT: + wrote = snprintf(ptr, *len, + "border-right-color: transparent\n"); + break; + case CSS_BORDER_COLOR_COLOR: + wrote = snprintf(ptr, *len, + "border-right-color: #%08x\n", color); + break; + default: + wrote = 0; + break; + } + ptr += wrote; + *len -= wrote; + + val = css_computed_border_bottom_color(style, &color); + switch (val) { + case CSS_BORDER_COLOR_TRANSPARENT: + wrote = snprintf(ptr, *len, + "border-bottom-color: transparent\n"); + break; + case CSS_BORDER_COLOR_COLOR: + wrote = snprintf(ptr, *len, + "border-bottom-color: #%08x\n", color); + break; + default: + wrote = 0; + break; + } + ptr += wrote; + *len -= wrote; + + val = css_computed_border_left_color(style, &color); + switch (val) { + case CSS_BORDER_COLOR_TRANSPARENT: + wrote = snprintf(ptr, *len, "border-left-color: transparent\n"); + break; + case CSS_BORDER_COLOR_COLOR: + wrote = snprintf(ptr, *len, + "border-left-color: #%08x\n", color); + break; + default: + wrote = 0; + break; + } + ptr += wrote; + *len -= wrote; + val = css_computed_color(style, &color); if (val == CSS_COLOR_COLOR) { wrote = snprintf(ptr, *len, "color: #%08x\n", color); diff --git a/test/select-auto.c b/test/select-auto.c index 75558c4..19af180 100644 --- a/test/select-auto.c +++ b/test/select-auto.c @@ -624,11 +624,11 @@ void run_test(line_ctx *ctx, const char *exp, size_t explen) UNUSED(exp); - buf = malloc(2 * explen); + buf = malloc(8192); if (buf == NULL) { assert(0 && "No memory for result data"); } - buflen = 2 * explen; + buflen = 8192; assert(css_select_ctx_create(myrealloc, NULL, &select) == CSS_OK); @@ -647,10 +647,10 @@ void run_test(line_ctx *ctx, const char *exp, size_t explen) dump_computed_style(computed, buf, &buflen); - if (2 * explen - buflen != explen || memcmp(buf, exp, explen) != 0) { + if (8192 - buflen != explen || memcmp(buf, exp, explen) != 0) { printf("Expected (%zu):\n%.*s\n", explen, (int) explen, exp); - printf("Result (%zu):\n%.*s\n", 2 * explen - buflen, - (int) (2 * explen - buflen), buf); + printf("Result (%zu):\n%.*s\n", 8192 - buflen, + (int) (8192 - buflen), buf); assert(0 && "Result doesn't match expected"); } -- cgit v1.2.3