From ddeadd1c02880367ad786b113d352a519f45ec73 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Thu, 23 Jul 2009 23:05:34 +0000 Subject: Merge LibCSS port to trunk. svn path=/trunk/netsurf/; revision=8752 --- css/Makefile | 44 - css/css.c | 3464 ++++---------------------------------------------------- css/css.h | 705 +----------- css/css_enums | 29 - css/dump.c | 1791 +++++++++++++++++++++++++++++ css/dump.h | 26 + css/internal.c | 82 ++ css/internal.h | 27 + css/makeenum | 46 - css/parser.y | 441 -------- css/ruleset.c | 3152 --------------------------------------------------- css/scanner.l | 112 -- css/select.c | 1981 ++++++++++++++++++++++++++++++++ css/select.h | 51 + css/testcss.c | 181 --- css/utils.c | 124 ++ css/utils.h | 44 + 17 files changed, 4388 insertions(+), 7912 deletions(-) delete mode 100644 css/Makefile delete mode 100644 css/css_enums create mode 100644 css/dump.c create mode 100644 css/dump.h create mode 100644 css/internal.c create mode 100644 css/internal.h delete mode 100755 css/makeenum delete mode 100644 css/parser.y delete mode 100644 css/ruleset.c delete mode 100644 css/scanner.l create mode 100644 css/select.c create mode 100644 css/select.h delete mode 100644 css/testcss.c create mode 100644 css/utils.c create mode 100644 css/utils.h (limited to 'css') diff --git a/css/Makefile b/css/Makefile deleted file mode 100644 index 6be322a6f..000000000 --- a/css/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -# Makefile for CSS test code - -AR := ar -CC := gcc -LD := gcc - -CFLAGS := -O2 -I.. -I/usr/include/libxml2 -std=c99 -LDFLAGS := -lxml2 -lz -lcurl - -LIBOBJS := css.o css_enum.o parser.o ruleset.o scanner.o \ - messages.o hashtable.o talloc.o url.o utils.o - -.PHONY: default lib clean - -default: - $(error "You probably wanted 'make test'") - -test: lib testcss.o - $(LD) -o $@ testcss.o $(LDFLAGS) -L. -lnscss - -lib: $(LIBOBJS) - $(AR) -cru libnscss.a $^ - -clean: - $(RM) $(LIBOBJS) testcss.o test libnscss.a - -messages.o: ../utils/messages.c - $(CC) $(CFLAGS) -c -o $@ $< - -hashtable.o: ../utils/hashtable.c - $(CC) $(CFLAGS) -c -o $@ $< - -talloc.o: ../utils/talloc.c - $(CC) $(CFLAGS) -c -o $@ $< - -url.o: ../utils/url.c - $(CC) $(CFLAGS) -c -o $@ $< - -utils.o: ../utils/utils.c - $(CC) $(CFLAGS) -c -o $@ $< - -%.o : %.c - $(CC) $(CFLAGS) -c -o $@ $< - diff --git a/css/css.c b/css/css.c index b035323d4..dbb4f53be 100644 --- a/css/css.c +++ b/css/css.c @@ -1,7 +1,5 @@ /* - * Copyright 2004 James Bursa - * Copyright 2004 John M Bell - * Copyright 2008 Michael Drake + * Copyright 2009 John-Mark Bell * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -18,3313 +16,337 @@ * along with this program. If not, see . */ -/** \file - * CSS handling (implementation). - * - * See CSS 2.1 chapter 5 for the terms used here. - * - * CSS style sheets are stored as a hash table mapping selectors to styles. - * Selectors are hashed by the type selector of the last simple - * selector in the selector. The universal selector is hashed to - * chain 0. - * - * A simple selector is a struct css_selector with type - * CSS_SELECTOR_ELEMENT. The data field is the type selector, or 0 for - * the universal selector. Any attribute selectors, ID - * selectors, or pseudo-classes form a linked list of - * css_selector hanging from detail. - * - * A selector is a linked list by the combiner field of these simple - * selectors, in reverse order that they appear in the concrete syntax. The last - * simple selector is first, then the previous one is linked at combiner and has - * relationship comb to the last, etc. - * - * Selectors are then linked in each hash chain by next, in order of increasing - * specificity. - * - * As an example, the stylesheet - * \code - * th { [1] } - * div#id1 > h4.class1 { [2] } - * center * { [3] } \endcode - * - * would result in a struct css_stylesheet (content::data.css.css) like this - * \dot - * digraph example { - * node [shape=record, fontname=Helvetica, fontsize=9]; - * edge [fontname=Helvetica, fontsize=9]; - * css -> n0 [label="rule[0]"]; - * css -> n2 [label="rule[29]"]; - * - * n0 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata 0\lcomb CSS_COMB_ANCESTOR\lspecificity 2\l"]; - * n0 -> n1 [label="combiner"]; - * n0 -> n0style [label="style"]; n0style [label="[3]"]; - * - * n1 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata \"center\"\lcomb CSS_COMB_NONE\lspecificity 1\l"]; - * - * n2 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata \"th\"\lcomb CSS_COMB_NONE\lspecificity 1\l"]; - * n2 -> n3 [label="next"]; - * n2 -> n2style [label="style"]; n2style [label="[1]"]; - * - * n3 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata \"h4\"\lcomb CSS_COMB_PARENT\lspecificity 0x10102\l"]; - * n3 -> n4 [label="detail"]; - * n3 -> n5 [label="combiner"]; - * n3 -> n3style [label="style"]; n3style [label="[2]"]; - * - * n4 [label="css_selector\ntype CSS_SELECTOR_CLASS\ldata \"class1\"\lcomb CSS_COMB_NONE\lspecificity 0x100\l"]; - * - * n5 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata \"div\"\lcomb CSS_COMB_NONE\lspecificity 0x10001\l"]; - * n5 -> n6 [label="detail"]; - * - * n6 [label="css_selector\ntype CSS_SELECTOR_ID\ldata \"#id1\"\lcomb CSS_COMB_NONE\lspecificity 0x10000\l"]; - * } - * \enddot - * - * (any fields not shown are 0). In this example the first two rules happen to - * have hashed to the same value. - */ - -#define _GNU_SOURCE /* for strndup */ #include -#include -#include -#include -#include -#include -#include -#define CSS_INTERNALS -#include "utils/config.h" + +#include + #include "content/content.h" #include "content/fetch.h" #include "content/fetchcache.h" #include "css/css.h" -#include "css/parser.h" +#include "css/internal.h" #include "desktop/gui.h" -#include "desktop/options.h" -#include "utils/log.h" -#include "utils/messages.h" -#include "utils/talloc.h" -#include "utils/url.h" -#include "utils/utils.h" - -/* Define this to debug the working stylesheet */ -#undef DEBUG_WORKING_STYLESHEET -struct css_working_stylesheet { - struct css_selector **rule[HASH_SIZE]; -}; +#include "render/html.h" - -static void css_deep_free_style(struct css_style *style); -static void css_atimport_callback(content_msg msg, struct content *css, +static void nscss_import(content_msg msg, struct content *c, intptr_t p1, intptr_t p2, union content_msg_data data); -static bool css_working_list_imports(struct content **import, - unsigned int import_count, - struct content ***css, unsigned int *css_count); -static bool css_working_merge_chains( - struct css_working_stylesheet *working_stylesheet, - struct content **css, unsigned int css_count, - unsigned int chain, - struct css_selector **rule); -static bool css_match_rule(struct css_selector *rule, xmlNode *element); -static bool css_match_detail(const struct css_selector *detail, - xmlNode *element); -static bool css_match_first_child(const struct css_selector *detail, - xmlNode *element); -static void css_dump_length(FILE *stream, - const struct css_length * const length); -static void css_dump_selector(const struct css_selector *r); -#ifdef DEBUG_WORKING_STYLESHEET -static void css_dump_working_stylesheet( - const struct css_working_stylesheet *ws); -#endif -static void css_importance_reset(struct css_importance *i); - -/** Default style for a document. These are the 'Initial values' from the - * spec. */ -const struct css_style css_base_style = { - CSS_BACKGROUND_ATTACHMENT_SCROLL, - 0xffffff, - { CSS_BACKGROUND_IMAGE_NONE, 0 }, - { { CSS_BACKGROUND_POSITION_PERCENT, { 0.0 } }, - { CSS_BACKGROUND_POSITION_PERCENT, { 0.0 } } }, - CSS_BACKGROUND_REPEAT_REPEAT, - { { 0x000000, { CSS_BORDER_WIDTH_LENGTH, - { 2, CSS_UNIT_PX } }, CSS_BORDER_STYLE_NONE }, - { 0x000000, { CSS_BORDER_WIDTH_LENGTH, - { 2, CSS_UNIT_PX } }, CSS_BORDER_STYLE_NONE }, - { 0x000000, { CSS_BORDER_WIDTH_LENGTH, - { 2, CSS_UNIT_PX } }, CSS_BORDER_STYLE_NONE }, - { 0x000000, { CSS_BORDER_WIDTH_LENGTH, - { 2, CSS_UNIT_PX } }, CSS_BORDER_STYLE_NONE } }, - CSS_BORDER_COLLAPSE_SEPARATE, - { CSS_BORDER_SPACING_LENGTH, - { 0, CSS_UNIT_PX }, { 0, CSS_UNIT_PX } }, - CSS_CAPTION_SIDE_TOP, - CSS_CLEAR_NONE, - { CSS_CLIP_AUTO, { { CSS_CLIP_RECT_AUTO, { 0, CSS_UNIT_PX } }, - { CSS_CLIP_RECT_AUTO, { 0, CSS_UNIT_PX } }, - { CSS_CLIP_RECT_AUTO, { 0, CSS_UNIT_PX } }, - { CSS_CLIP_RECT_AUTO, { 0, CSS_UNIT_PX } } } }, - 0x000000, - { CSS_CONTENT_NORMAL, 0 }, - { CSS_COUNTER_RESET_NONE, 0 }, - { CSS_COUNTER_INCREMENT_NONE, 0 }, - CSS_CURSOR_AUTO, - CSS_DIRECTION_LTR, - CSS_DISPLAY_BLOCK, - CSS_EMPTY_CELLS_SHOW, - CSS_FLOAT_NONE, - CSS_FONT_FAMILY_SANS_SERIF, - { CSS_FONT_SIZE_LENGTH, { { 10, CSS_UNIT_PT } } }, - CSS_FONT_STYLE_NORMAL, - CSS_FONT_VARIANT_NORMAL, - CSS_FONT_WEIGHT_NORMAL, - { CSS_HEIGHT_AUTO, { { 1, CSS_UNIT_EM } } }, - { CSS_LETTER_SPACING_NORMAL, { 0, CSS_UNIT_PX } }, - { CSS_LINE_HEIGHT_ABSOLUTE, { 1.33 } }, - { CSS_LIST_STYLE_IMAGE_NONE, 0 }, - CSS_LIST_STYLE_POSITION_OUTSIDE, - CSS_LIST_STYLE_TYPE_DISC, - { { CSS_MARGIN_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_MARGIN_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_MARGIN_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_MARGIN_LENGTH, { { 0, CSS_UNIT_PX } } } }, - { CSS_MAX_HEIGHT_NONE, { { 0, CSS_UNIT_PX } } }, - { CSS_MAX_WIDTH_NONE, { { 0, CSS_UNIT_PX } } }, - { CSS_MIN_HEIGHT_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_MIN_WIDTH_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_ORPHANS_INTEGER, 2 }, - { { CSS_OUTLINE_COLOR_INVERT, 0x000000 }, - { CSS_BORDER_WIDTH_LENGTH, { 2, CSS_UNIT_PX } }, - CSS_BORDER_STYLE_NONE }, - CSS_OVERFLOW_VISIBLE, - { { CSS_PADDING_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_PADDING_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_PADDING_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_PADDING_LENGTH, { { 0, CSS_UNIT_PX } } } }, - CSS_PAGE_BREAK_AFTER_AUTO, - CSS_PAGE_BREAK_BEFORE_AUTO, - CSS_PAGE_BREAK_INSIDE_AUTO, - { { CSS_POS_AUTO, { { 0, CSS_UNIT_PX } } }, - { CSS_POS_AUTO, { { 0, CSS_UNIT_PX } } }, - { CSS_POS_AUTO, { { 0, CSS_UNIT_PX } } }, - { CSS_POS_AUTO, { { 0, CSS_UNIT_PX } } } }, - CSS_POSITION_STATIC, - CSS_TABLE_LAYOUT_AUTO, - CSS_TEXT_ALIGN_LEFT, - CSS_TEXT_DECORATION_NONE, - { CSS_TEXT_INDENT_LENGTH, { { 0, CSS_UNIT_EM } } }, - CSS_TEXT_TRANSFORM_NONE, - CSS_UNICODE_BIDI_NORMAL, - { CSS_VERTICAL_ALIGN_BASELINE, { { 0, CSS_UNIT_PX } } }, - CSS_VISIBILITY_VISIBLE, - CSS_WHITE_SPACE_NORMAL, - { CSS_WIDOWS_INTEGER, 2 }, - { CSS_WIDTH_AUTO, { { 1, CSS_UNIT_EM } } }, - { CSS_WORD_SPACING_NORMAL, { 0, CSS_UNIT_PX } }, - { CSS_Z_INDEX_AUTO, 0 } -}; - -/** Style with no values set. */ -const struct css_style css_empty_style = { - CSS_BACKGROUND_ATTACHMENT_NOT_SET, - CSS_COLOR_NOT_SET, - { CSS_BACKGROUND_IMAGE_NOT_SET, 0 }, - { { CSS_BACKGROUND_POSITION_NOT_SET, { 0.0 } }, - { CSS_BACKGROUND_POSITION_NOT_SET, { 0.0 } } }, - CSS_BACKGROUND_REPEAT_NOT_SET, - { { CSS_COLOR_NOT_SET, { CSS_BORDER_WIDTH_NOT_SET, - { 0, CSS_UNIT_PX } }, CSS_BORDER_STYLE_NOT_SET }, - { CSS_COLOR_NOT_SET, { CSS_BORDER_WIDTH_NOT_SET, - { 0, CSS_UNIT_PX } }, CSS_BORDER_STYLE_NOT_SET }, - { CSS_COLOR_NOT_SET, { CSS_BORDER_WIDTH_NOT_SET, - { 0, CSS_UNIT_PX } }, CSS_BORDER_STYLE_NOT_SET }, - { CSS_COLOR_NOT_SET, { CSS_BORDER_WIDTH_NOT_SET, - { 0, CSS_UNIT_PX } }, CSS_BORDER_STYLE_NOT_SET } }, - CSS_BORDER_COLLAPSE_NOT_SET, - { CSS_BORDER_SPACING_NOT_SET, - { 0, CSS_UNIT_PX }, { 0, CSS_UNIT_PX } }, - CSS_CAPTION_SIDE_NOT_SET, - CSS_CLEAR_NOT_SET, - { CSS_CLIP_NOT_SET, { { CSS_CLIP_RECT_AUTO, { 0, CSS_UNIT_PX } }, - { CSS_CLIP_RECT_AUTO, { 0, CSS_UNIT_PX } }, - { CSS_CLIP_RECT_AUTO, { 0, CSS_UNIT_PX } }, - { CSS_CLIP_RECT_AUTO, { 0, CSS_UNIT_PX } } } }, - CSS_COLOR_NOT_SET, - { CSS_CONTENT_NOT_SET, 0 }, - { CSS_COUNTER_RESET_NOT_SET, 0 }, - { CSS_COUNTER_INCREMENT_NOT_SET, 0 }, - CSS_CURSOR_NOT_SET, - CSS_DIRECTION_NOT_SET, - CSS_DISPLAY_NOT_SET, - CSS_EMPTY_CELLS_NOT_SET, - CSS_FLOAT_NOT_SET, - CSS_FONT_FAMILY_NOT_SET, - { CSS_FONT_SIZE_NOT_SET, { { 1, CSS_UNIT_PT } } }, - CSS_FONT_STYLE_NOT_SET, - CSS_FONT_VARIANT_NOT_SET, - CSS_FONT_WEIGHT_NOT_SET, - { CSS_HEIGHT_NOT_SET, { { 1, CSS_UNIT_EM } } }, - { CSS_LETTER_SPACING_NOT_SET, { 0, CSS_UNIT_PX } }, - { CSS_LINE_HEIGHT_NOT_SET, { 1.33 } }, - { CSS_LIST_STYLE_IMAGE_NOT_SET, 0 }, - CSS_LIST_STYLE_POSITION_NOT_SET, - CSS_LIST_STYLE_TYPE_NOT_SET, - { { CSS_MARGIN_NOT_SET, { { 0, CSS_UNIT_PX } } }, - { CSS_MARGIN_NOT_SET, { { 0, CSS_UNIT_PX } } }, - { CSS_MARGIN_NOT_SET, { { 0, CSS_UNIT_PX } } }, - { CSS_MARGIN_NOT_SET, { { 0, CSS_UNIT_PX } } } }, - { CSS_MAX_HEIGHT_NOT_SET, { { 0, CSS_UNIT_PX } } }, - { CSS_MAX_WIDTH_NOT_SET, { { 0, CSS_UNIT_PX } } }, - { CSS_MIN_HEIGHT_NOT_SET, { { 0, CSS_UNIT_PX } } }, - { CSS_MIN_WIDTH_NOT_SET, { { 0, CSS_UNIT_PX } } }, - { CSS_ORPHANS_NOT_SET, 0 }, - { { CSS_OUTLINE_COLOR_NOT_SET, CSS_COLOR_NOT_SET }, - { CSS_BORDER_WIDTH_NOT_SET, { 0, CSS_UNIT_PX } }, - CSS_BORDER_STYLE_NOT_SET }, - CSS_OVERFLOW_NOT_SET, - { { CSS_PADDING_NOT_SET, { { 0, CSS_UNIT_PX } } }, - { CSS_PADDING_NOT_SET, { { 0, CSS_UNIT_PX } } }, - { CSS_PADDING_NOT_SET, { { 0, CSS_UNIT_PX } } }, - { CSS_PADDING_NOT_SET, { { 0, CSS_UNIT_PX } } } }, - CSS_PAGE_BREAK_AFTER_NOT_SET, - CSS_PAGE_BREAK_BEFORE_NOT_SET, - CSS_PAGE_BREAK_INSIDE_NOT_SET, - { { CSS_POS_NOT_SET, { { 0, CSS_UNIT_PX } } }, - { CSS_POS_NOT_SET, { { 0, CSS_UNIT_PX } } }, - { CSS_POS_NOT_SET, { { 0, CSS_UNIT_PX } } }, - { CSS_POS_NOT_SET, { { 0, CSS_UNIT_PX } } } }, - CSS_POSITION_NOT_SET, - CSS_TABLE_LAYOUT_NOT_SET, - CSS_TEXT_ALIGN_NOT_SET, - CSS_TEXT_DECORATION_NOT_SET, - { CSS_TEXT_INDENT_NOT_SET, { { 0, CSS_UNIT_EM } } }, - CSS_TEXT_TRANSFORM_NOT_SET, - CSS_UNICODE_BIDI_NOT_SET, - { CSS_VERTICAL_ALIGN_NOT_SET, { { 0, CSS_UNIT_PX } } }, - CSS_VISIBILITY_NOT_SET, - CSS_WHITE_SPACE_NOT_SET, - { CSS_WIDOWS_NOT_SET, 0 }, - { CSS_WIDTH_NOT_SET, { { 1, CSS_UNIT_EM } } }, - { CSS_WORD_SPACING_NOT_SET, { 0, CSS_UNIT_PX } }, - { CSS_Z_INDEX_NOT_SET, 0 } -}; - -/** Default style for an element. These should be INHERIT if 'Inherited' is yes, - * and the 'Initial value' otherwise. */ -const struct css_style css_blank_style = { - CSS_BACKGROUND_ATTACHMENT_SCROLL, - NS_TRANSPARENT, - { CSS_BACKGROUND_IMAGE_NONE, 0 }, - { { CSS_BACKGROUND_POSITION_PERCENT, { 0.0 } }, - { CSS_BACKGROUND_POSITION_PERCENT, { 0.0 } } }, - CSS_BACKGROUND_REPEAT_REPEAT, - { { 0x000000, { CSS_BORDER_WIDTH_LENGTH, - { 2, CSS_UNIT_PX } }, CSS_BORDER_STYLE_NONE }, - { 0x000000, { CSS_BORDER_WIDTH_LENGTH, - { 2, CSS_UNIT_PX } }, CSS_BORDER_STYLE_NONE }, - { 0x000000, { CSS_BORDER_WIDTH_LENGTH, - { 2, CSS_UNIT_PX } }, CSS_BORDER_STYLE_NONE }, - { 0x000000, { CSS_BORDER_WIDTH_LENGTH, - { 2, CSS_UNIT_PX } }, CSS_BORDER_STYLE_NONE } }, - CSS_BORDER_COLLAPSE_INHERIT, - { CSS_BORDER_SPACING_INHERIT, - { 0, CSS_UNIT_PX }, { 0, CSS_UNIT_PX } }, - CSS_CAPTION_SIDE_INHERIT, - CSS_CLEAR_NONE, - { CSS_CLIP_AUTO, { { CSS_CLIP_RECT_AUTO, { 0, CSS_UNIT_PX } }, - { CSS_CLIP_RECT_AUTO, { 0, CSS_UNIT_PX } }, - { CSS_CLIP_RECT_AUTO, { 0, CSS_UNIT_PX } }, - { CSS_CLIP_RECT_AUTO, { 0, CSS_UNIT_PX } } } }, - CSS_COLOR_INHERIT, - { CSS_CONTENT_NORMAL, 0 }, - { CSS_COUNTER_RESET_NONE, 0 }, - { CSS_COUNTER_INCREMENT_NONE, 0 }, - CSS_CURSOR_INHERIT, - CSS_DIRECTION_INHERIT, - CSS_DISPLAY_INLINE, - CSS_EMPTY_CELLS_INHERIT, - CSS_FLOAT_NONE, - CSS_FONT_FAMILY_INHERIT, - { CSS_FONT_SIZE_INHERIT, { { 1, CSS_UNIT_EM } } }, - CSS_FONT_STYLE_INHERIT, - CSS_FONT_VARIANT_INHERIT, - CSS_FONT_WEIGHT_INHERIT, - { CSS_HEIGHT_AUTO, { { 1, CSS_UNIT_EM } } }, - { CSS_LETTER_SPACING_INHERIT, { 0, CSS_UNIT_PX } }, - { CSS_LINE_HEIGHT_INHERIT, { 1.33 } }, - { CSS_LIST_STYLE_IMAGE_INHERIT, 0 }, - CSS_LIST_STYLE_POSITION_INHERIT, - CSS_LIST_STYLE_TYPE_INHERIT, - { { CSS_MARGIN_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_MARGIN_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_MARGIN_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_MARGIN_LENGTH, { { 0, CSS_UNIT_PX } } } }, - { CSS_MAX_HEIGHT_NONE, { { 0, CSS_UNIT_PX } } }, - { CSS_MAX_WIDTH_NONE, { { 0, CSS_UNIT_PX } } }, - { CSS_MIN_HEIGHT_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_MIN_WIDTH_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_ORPHANS_INHERIT, 0 }, - { { CSS_OUTLINE_COLOR_INVERT, 0x000000 }, - { CSS_BORDER_WIDTH_LENGTH, { 2, CSS_UNIT_PX } }, - CSS_BORDER_STYLE_NONE }, - CSS_OVERFLOW_VISIBLE, - { { CSS_PADDING_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_PADDING_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_PADDING_LENGTH, { { 0, CSS_UNIT_PX } } }, - { CSS_PADDING_LENGTH, { { 0, CSS_UNIT_PX } } } }, - CSS_PAGE_BREAK_AFTER_AUTO, - CSS_PAGE_BREAK_BEFORE_AUTO, - CSS_PAGE_BREAK_INSIDE_INHERIT, - { { CSS_POS_AUTO, { { 0, CSS_UNIT_PX } } }, - { CSS_POS_AUTO, { { 0, CSS_UNIT_PX } } }, - { CSS_POS_AUTO, { { 0, CSS_UNIT_PX } } }, - { CSS_POS_AUTO, { { 0, CSS_UNIT_PX } } } }, - CSS_POSITION_STATIC, - CSS_TABLE_LAYOUT_AUTO, - CSS_TEXT_ALIGN_INHERIT, - CSS_TEXT_DECORATION_NONE, - { CSS_TEXT_INDENT_INHERIT, { { 0, CSS_UNIT_EM } } }, - CSS_TEXT_TRANSFORM_INHERIT, - CSS_UNICODE_BIDI_NORMAL, - { CSS_VERTICAL_ALIGN_BASELINE, { { 0, CSS_UNIT_PX } } }, - CSS_VISIBILITY_INHERIT, - CSS_WHITE_SPACE_INHERIT, - { CSS_WIDOWS_INHERIT, 0 }, - { CSS_WIDTH_AUTO, { { 1, CSS_UNIT_EM } } }, - { CSS_WORD_SPACING_INHERIT, { 0, CSS_UNIT_PX } }, - { CSS_Z_INDEX_AUTO, 0 } -}; - -/** Dots per inch for the display device. - * - * This is the number of pixels per inch of the display device. - * This variable should be treated as constant during the runtime of - * the program unless the core can be persuaded to re-layout fully - * on change. - * - * We default to 90.0 because RISC OS defaults to 90.0 dpi. - */ -float css_screen_dpi = 90.0; - -/** - * Convert a CONTENT_CSS for use. - */ - -bool css_convert(struct content *c, int width, int height) -{ - unsigned char *source_data; - unsigned char *current, *end, *token_text; - unsigned int i; - int token; - void *parser; - struct css_parser_params param = {false, c, 0, false, false, false}; - struct css_parser_token token_data; - union content_msg_data msg_data; - - /* css_parser_Trace(stderr, "CSS: "); */ - - c->data.css.css = malloc(sizeof *c->data.css.css); - parser = css_parser_Alloc(malloc); - source_data = (unsigned char *) talloc_realloc(c, c->source_data, char, - c->source_size + 10); - - if (!c->data.css.css || !parser || !source_data) { - free(c->data.css.css); - css_parser_Free(parser, free); - - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; - } - - for (i = 0; i != HASH_SIZE; i++) - c->data.css.css->rule[i] = 0; - c->data.css.import_count = 0; - c->data.css.import_content = 0; - c->data.css.origin = CSS_ORIGIN_UA; - c->active = 0; - c->source_data = (char *) source_data; - - for (i = 0; i != 10; i++) - source_data[c->source_size + i] = 0; - - current = source_data; - end = source_data + c->source_size; - while (current < end - && (token = css_tokenise(¤t, end + 10, - &token_text))) { - token_data.text = (char *) token_text; - token_data.length = current - token_text; - css_parser_(parser, token, token_data, ¶m); - if (param.syntax_error) { - LOG(("syntax error near offset %li (%s)", - (unsigned long) (token_text - source_data), - c->url)); - param.syntax_error = false; - } else if (param.memory_error) { - LOG(("out of memory")); - break; - } - } - - css_parser_(parser, 0, token_data, ¶m); - css_parser_Free(parser, free); - - if (param.memory_error) { - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; - } - - /*css_dump_stylesheet(c->data.css.css);*/ - - /* complete fetch of any imported stylesheets */ - while (c->active != 0) { - fetch_poll(); - gui_multitask(); - } - - c->status = CONTENT_STATUS_DONE; - return true; -} - - -/** - * Destroy a CONTENT_CSS and free all resources it owns. - */ - -void css_destroy(struct content *c) -{ - unsigned int i; - struct css_selector *r; - - if (c->data.css.css) { - for (i = 0; i != HASH_SIZE; i++) { - for (r = c->data.css.css->rule[i]; r != 0; - r = r->next) { - css_deep_free_style(r->style); - } - css_free_selector(c->data.css.css->rule[i]); - } - free(c->data.css.css); - } - - /* imported stylesheets */ - for (i = 0; i != c->data.css.import_count; i++) - if (c->data.css.import_content[i] != 0) { - content_remove_user(c->data.css.import_content[i], - css_atimport_callback, (intptr_t) c, i); - } - free(c->data.css.import_content); -} - - -/** - * Set the origin of a stylesheet. - * - * \param c content of type CONTENT_CSS - * \param origin new origin - * - * This may only be called once per stylesheet. - */ - -void css_set_origin(struct content *c, css_origin origin) -{ - unsigned int chain, i; - unsigned long specificity = 0; - struct css_selector *selector; - - assert(c->type == CONTENT_CSS); - - if (origin == c->data.css.origin) - return; - - switch (origin) - { - case CSS_ORIGIN_AUTHOR: specificity = CSS_SPECIFICITY_AUTHOR; break; - case CSS_ORIGIN_USER: specificity = CSS_SPECIFICITY_USER; break; - case CSS_ORIGIN_UA: specificity = CSS_SPECIFICITY_UA; break; - } - - for (chain = 0; chain != HASH_SIZE; chain++) - for (selector = c->data.css.css->rule[chain]; - selector; - selector = selector->next) - selector->specificity += specificity; - c->data.css.origin = origin; - - for (i = 0; i != c->data.css.import_count; i++) - if (c->data.css.import_content[i]) - css_set_origin(c->data.css.import_content[i], origin); -} - /** - * Duplicate a CSS style struct. + * Allocation callback for libcss * - * \param style The style to duplicate - * \return The duplicate style, or NULL if out of memory. + * \param ptr Pointer to reallocate, or NULL for new allocation + * \param size Number of bytes requires + * \param pw Allocation context + * \return Pointer to allocated block, or NULL on failure */ - -struct css_style *css_duplicate_style(const struct css_style * const style) +static void *myrealloc(void *ptr, size_t size, void *pw) { - struct css_style *dup; - - assert(style); - - /* create duplicated style */ - dup = malloc(sizeof (struct css_style)); - if (!dup) - return NULL; - - /* copy all style information into duplicate style */ - memcpy(dup, style, sizeof(struct css_style)); - - return dup; + return realloc(ptr, size); } - /** - * Free a CSS style. + * Initialise a CSS content * - * \param style The style to free + * \param c Content to initialise + * \param parent Parent content, or NULL if top-level + * \param params Content-Type parameters + * \return true on success, false on failure */ - -void css_free_style(struct css_style *style) +bool nscss_create(struct content *c, struct content *parent, + const char *params[]) { - assert(style); - - free(style); -} - + css_origin origin = CSS_ORIGIN_AUTHOR; + css_media_type media = CSS_MEDIA_ALL; + lwc_context *dict = NULL; + bool quirks = true; + css_error error; -/** - * Free a CSS style, deleting all alloced elements. - * - * \param style The style to free - */ - -void css_deep_free_style(struct css_style *style) -{ - assert(style); + /** \todo extract charset from params */ + /** \todo what happens about the allocator? */ - if (style->background_image.type == CSS_BACKGROUND_IMAGE_URI) - free(style->background_image.uri); + if (parent != NULL) { + assert(parent->type == CONTENT_HTML || + parent->type == CONTENT_CSS); - if (style->list_style_image.type == CSS_LIST_STYLE_IMAGE_URI) - free(style->list_style_image.uri); + if (parent->type == CONTENT_HTML) { + assert(parent->data.html.dict != NULL); - if (style->content.type == CSS_CONTENT_INTERPRET) - css_deep_free_content(style->content.content); + if (c == parent->data.html. + stylesheet_content[STYLESHEET_BASE] || + c == parent->data.html. + stylesheet_content[STYLESHEET_QUIRKS] || + c == parent->data.html. + stylesheet_content[STYLESHEET_ADBLOCK]) + origin = CSS_ORIGIN_UA; - if (style->counter_reset.type == CSS_COUNTER_RESET_INTERPRET) - css_deep_free_counter_control(style->counter_reset.data); + /** \todo media types */ - if (style->counter_increment.type == CSS_COUNTER_INCREMENT_INTERPRET) - css_deep_free_counter_control(style->counter_increment.data); + quirks = (parent->data.html.quirks != + BINDING_QUIRKS_MODE_NONE); - free(style); -} + dict = parent->data.html.dict; + } else { + assert(parent->data.css.sheet != NULL); + assert(parent->data.css.dict != NULL); + error = css_stylesheet_get_origin( + parent->data.css.sheet, &origin); + if (error != CSS_OK) + return false; -/** - * Free all auto-generated content data. - * - * \param content the auto-generated content data to free - */ + error = css_stylesheet_quirks_allowed( + parent->data.css.sheet, &quirks); + if (error != CSS_OK) + return false; -void css_deep_free_content(struct css_content *content) -{ - struct css_content *next; + /** \todo media types */ - while (content) { - next = content->next; - switch (content->type) { - case CSS_CONTENT_STRING: - free(content->data.string); - break; - case CSS_CONTENT_URI: - free(content->data.uri); - break; - case CSS_CONTENT_COUNTER: - free(content->data.counter.name); - free(content->data.counter.separator); - break; - case CSS_CONTENT_ATTR: - free(content->data.attr); - break; - case CSS_CONTENT_OPEN_QUOTE: - case CSS_CONTENT_CLOSE_QUOTE: - case CSS_CONTENT_NO_OPEN_QUOTE: - case CSS_CONTENT_NO_CLOSE_QUOTE: - break; + dict = parent->data.css.dict; } - free(content); - content = next; } -} - - -/** - * Free all counter control data. - * - * \param counter the counter control data to free - */ -void css_deep_free_counter_control(struct css_counter_control *control) -{ - struct css_counter_control *next; + if (dict == NULL) { + lwc_error lerror = lwc_create_context(myrealloc, NULL, &dict); - while (control) { - next = control->next; - free(control->name); - free(control); - control = next; + if (lerror != lwc_error_ok) + return false; } -} - - -/** - * Create a new struct css_node. - * - * \param stylesheet content of type CONTENT_CSS - * \param type type of node - * \param data string for data, not copied - * \param data_length length of data - * \return allocated node, or 0 if memory exhausted - * - * Used by the parser. - */ -struct css_node * css_new_node(struct content *stylesheet, - css_node_type type, - const char *data, unsigned int data_length) -{ - struct css_node *node = malloc(sizeof *node); - if (!node) - return 0; - node->type = type; - node->data = data; - node->data_length = data_length; - node->value = 0; - node->next = 0; - node->comb = CSS_COMB_NONE; - node->style = 0; - node->specificity = 0; - node->stylesheet = stylesheet; + c->data.css.dict = lwc_context_ref(dict); + c->data.css.import_count = 0; + c->data.css.imports = NULL; + + error = css_stylesheet_create(CSS_LEVEL_21, NULL, + c->url, NULL, origin, media, quirks, false, + c->data.css.dict, + myrealloc, NULL, + nscss_resolve_url, NULL, + &c->data.css.sheet); + if (error != CSS_OK) { + lwc_context_unref(c->data.css.dict); + c->data.css.dict = NULL; + return false; + } - return node; + return true; } - /** - * Free a struct css_node recursively. - * - * \param node css_node to free + * Process CSS source data * - * Used by the parser. + * \param c Content structure + * \param data Data to process + * \param size Number of bytes to process + * \return true on success, false on failure */ - -void css_free_node(struct css_node *node) +bool nscss_process_data(struct content *c, char *data, unsigned int size) { - if (!node) - return; - if (node->value) - css_free_node(node->value); - if (node->next) - css_free_node(node->next); - free(node); -} + css_error error; + error = css_stylesheet_append_data(c->data.css.sheet, + (const uint8_t *) data, size); -/** - * Create a new struct css_selector. - * - * \param type type of selector - * \param data string for data, not copied - * \param data_length length of data - * \return allocated selector, or 0 if memory exhausted - */ - -struct css_selector * css_new_selector(css_selector_type type, - const char *data, unsigned int data_length) -{ - struct css_selector *node = malloc(sizeof *node); - if (!node) - return 0; - node->type = type; - node->data = data; - node->data_length = data_length; - node->data2 = 0; - node->detail = 0; - node->combiner = 0; - node->next = 0; - node->comb = CSS_COMB_NONE; - node->style = 0; - node->specificity = 0; - return node; + return (error == CSS_OK || error == CSS_NEEDDATA); } - /** - * Free a struct css_selector recursively. + * Convert a CSS content ready for use * - * \param node css_selector to free - */ - -void css_free_selector(struct css_selector *node) -{ - if (!node) - return; - if (node->detail) - css_free_selector(node->detail); - if (node->combiner) - css_free_selector(node->combiner); - if (node->next) - css_free_selector(node->next); - free(node); -} - - -/** - * Process an \@import rule. + * \param c Content to convert + * \param w Width of area content will be displayed in + * \param h Height of area content will be displayed in + * \return true on success, false on failure */ - -void css_atimport(struct content *c, struct css_node *node) +bool nscss_convert(struct content *c, int w, int h) { - const char *s; - char *t, *url, *url1; - bool string = false, screen = true; - unsigned int i; - struct content **import_content; - url_func_result res; + css_error error; - LOG(("@import rule")); + error = css_stylesheet_data_done(c->data.css.sheet); - import_content = realloc(c->data.css.import_content, - (c->data.css.import_count + 1) * - sizeof(*c->data.css.import_content)); - if (!import_content) { - /** \todo report to user */ - return; - } - c->data.css.import_content = import_content; + /* Process pending imports */ + while (error == CSS_IMPORTS_PENDING) { + struct content **imports; + uint32_t i; + lwc_string *uri; + uint64_t media; + css_stylesheet *sheet; + char *temp_url; + + error = css_stylesheet_next_pending_import(c->data.css.sheet, + &uri, &media); + if (error != CSS_OK && error != CSS_INVALID) { + c->status = CONTENT_STATUS_ERROR; + return false; + } - /* uri(...) or "..." */ - switch (node->type) { - case CSS_NODE_URI: - LOG(("URI '%.*s'", node->data_length, node->data)); - for (s = node->data + 4; - *s == ' ' || *s == '\t' || *s == '\r' || - *s == '\n' || *s == '\f'; - s++) - ; - if (*s == '\'' || *s == '"') { - string = true; - s++; - } - url = strndup(s, node->data_length - (s - node->data)); - if (!url) { - /** \todo report to user */ - return; - } - for (t = url + strlen(url) - 2; - *t == ' ' || *t == '\t' || *t == '\r' || - *t == '\n' || *t == '\f'; - t--) - ; - if (string) - *t = 0; - else - *(t + 1) = 0; - break; - case CSS_NODE_STRING: - LOG(("STRING '%.*s'", node->data_length, node->data)); - url = strndup(node->data, node->data_length); - if (!url) { - /** \todo report to user */ - return; - } + /* Give up if there are no more imports */ + if (error == CSS_INVALID) { + error = CSS_OK; break; - default: - return; - } + } - /* media not specified, 'screen', or 'all' */ - for (node = node->next; node != 0; node = node->next) { - screen = false; - if (node->type != CSS_NODE_IDENT) { - free(url); - return; + /* Copy URI and ensure it's NUL terminated */ + temp_url = malloc(lwc_string_length(uri) + 1); + if (temp_url == NULL) { + c->status = CONTENT_STATUS_ERROR; + return false; } - LOG(("medium '%s'", node->data)); - if ((node->data_length == 6 && - strncmp(node->data, "screen", 6) == 0) || - (node->data_length == 3 && - strncmp(node->data, "all", 3) == 0)) { - screen = true; - break; + memcpy(temp_url, lwc_string_data(uri), lwc_string_length(uri)); + temp_url[lwc_string_length(uri)] = '\0'; + + /* Increase space in table */ + imports = realloc(c->data.css.imports, + (c->data.css.import_count + 1) * + sizeof(struct content *)); + if (imports == NULL) { + c->status = CONTENT_STATUS_ERROR; + return false; } - node = node->next; - if (node == 0 || node->type != CSS_NODE_COMMA) { - free(url); - return; + c->data.css.imports = imports; + + /* Create content */ + i = c->data.css.import_count; + c->data.css.imports[c->data.css.import_count++] = + fetchcache(temp_url, + nscss_import, (intptr_t) c, i, + c->width, c->height, true, NULL, NULL, + false, false); + if (c->data.css.imports[i] == NULL) { + free(temp_url); + c->status = CONTENT_STATUS_ERROR; + return false; } - } - if (!screen) { - free(url); - return; - } - - /* Make URL absolute */ - res = url_join(url, c->url, &url1); - if (res != URL_FUNC_OK) { - free(url); - return; - } - - /* Destroy raw url data */ - free(url); - - /* URL must be normalized */ - res = url_normalize(url1, &url); - if (res != URL_FUNC_OK) { - free(url1); - return; - } - - /* Destroy non-normalized data */ - free(url1); - /* start the fetch */ - c->data.css.import_count++; - i = c->data.css.import_count - 1; - c->data.css.import_content[i] = fetchcache(url, - css_atimport_callback, (intptr_t) c, i, - c->width, c->height, true, 0, 0, false, false); - if (c->data.css.import_content[i]) { + /* Fetch content */ c->active++; - fetchcache_go(c->data.css.import_content[i], c->url, - css_atimport_callback, (intptr_t) c, i, - c->width, c->height, - 0, 0, false, c); - } - - free(url); -} - - -/** - * Fetchcache callback for imported stylesheets. - */ - -void css_atimport_callback(content_msg msg, struct content *css, - intptr_t p1, intptr_t p2, union content_msg_data data) -{ - struct content *c = (struct content *) p1; - unsigned int i = p2; - - switch (msg) { - case CONTENT_MSG_LOADING: - if (css->type != CONTENT_CSS) { - content_remove_user(css, css_atimport_callback, - (intptr_t) c, i); - if (!css->user_list->next) { - /* We were only user and we don't - * want this content, so stop it - * fetching and mark it as having - * an error so it gets removed from - * the cache next time - * content_clean() gets called */ - fetch_abort(css->fetch); - css->fetch = 0; - css->status = CONTENT_STATUS_ERROR; - } - c->data.css.import_content[i] = 0; - c->active--; - content_add_error(c, "NotCSS", 0); + fetchcache_go(c->data.css.imports[i], c->url, + nscss_import, (intptr_t) c, i, + c->width, c->height, NULL, NULL, false, c); + + free(temp_url); + + /* Wait for import to fetch + convert */ + while (c->active > 0) { + fetch_poll(); + gui_multitask(); + } + + if (c->data.css.imports[i] != NULL) { + sheet = c->data.css.imports[i]->data.css.sheet; + c->data.css.imports[i]->data.css.sheet = NULL; + } else { + error = css_stylesheet_create(CSS_LEVEL_DEFAULT, + NULL, "", NULL, CSS_ORIGIN_AUTHOR, + media, false, false, c->data.css.dict, + myrealloc, NULL, + nscss_resolve_url, NULL, + &sheet); + if (error != CSS_OK) { + c->status = CONTENT_STATUS_ERROR; + return false; } - break; - - case CONTENT_MSG_READY: - break; + } - case CONTENT_MSG_DONE: - LOG(("got imported stylesheet '%s'", css->url)); - /*css_dump_stylesheet(css->data.css);*/ - c->active--; - break; + error = css_stylesheet_register_import( + c->data.css.sheet, sheet); + if (error != CSS_OK) { + c->status = CONTENT_STATUS_ERROR; + return false; + } - case CONTENT_MSG_AUTH: - case CONTENT_MSG_SSL: - /* todo: handle AUTH and SSL */ - case CONTENT_MSG_LAUNCH: - /* Fall through */ - case CONTENT_MSG_ERROR: - /* The stylesheet we were fetching may have been - * redirected, in that case, the object pointers - * will differ, so ensure that the object that's - * in error is still in use by us before invalidating - * the pointer */ - if (c->data.css.import_content[i] == css) { - c->data.css.import_content[i] = 0; - c->active--; - content_add_error(c, "?", 0); - } - break; + error = CSS_IMPORTS_PENDING; + } - case CONTENT_MSG_STATUS: - break; + c->status = CONTENT_STATUS_DONE; - case CONTENT_MSG_NEWPTR: - c->data.css.import_content[i] = css; - break; + /* Filthy hack to stop this content being reused + * when whatever is using it has finished with it. */ + c->fresh = false; - default: - assert(0); - } + return error == CSS_OK; } - /** - * Prepare a working stylesheet with pre-sorted lists of selectors from an - * array of stylesheets. + * Clean up a CSS content * - * \param stylesheet_content array of contents of type CONTENT_CSS (each may - * be 0) - * \param stylesheet_count number of entries in css - * \return working stylesheet, or 0 on memory exhaustion - * - * See CSS 2.1 6.4. + * \param c Content to clean up */ - -struct css_working_stylesheet *css_make_working_stylesheet( - struct content **stylesheet_content, - unsigned int stylesheet_count) +void nscss_destroy(struct content *c) { - struct content **css = 0; - unsigned int css_count = 0; - struct css_working_stylesheet *working_stylesheet; - unsigned int chain; - struct css_selector **rule_scratch; - - working_stylesheet = talloc(0, struct css_working_stylesheet); - if (!working_stylesheet) - return 0; - - /* make a complete list of stylesheets involved by walking @imports */ - css_working_list_imports(stylesheet_content, stylesheet_count, - &css, &css_count); + uint32_t i; - rule_scratch = talloc_array(working_stylesheet, struct css_selector *, - css_count); - if (!rule_scratch) { - free(css); - talloc_free(working_stylesheet); - return 0; - } - - /* merge the corresponding sorted hash chains from each stylesheet */ - for (chain = 0; chain != HASH_SIZE; chain++) { - if (!css_working_merge_chains(working_stylesheet, css, - css_count, chain, rule_scratch)) { - free(css); - talloc_free(working_stylesheet); - return 0; + for (i = 0; i < c->data.css.import_count; i++) { + if (c->data.css.imports[i] != NULL) { + content_remove_user(c->data.css.imports[i], + nscss_import, (uintptr_t) c, i); } + c->data.css.imports[i] = NULL; } - free(css); - talloc_free(rule_scratch); - -#ifdef DEBUG_WORKING_STYLESHEET - css_dump_working_stylesheet(working_stylesheet); -#endif - - return working_stylesheet; -} - - -/** - * Recursively make a list of stylesheets and their imports. - * - * \param import array of contents of type CONTENT_CSS - * \param import_count number of elements in import - * \param css pointer to array of contents for result - * \param css_count number of elements used so far in *css - */ + free(c->data.css.imports); -bool css_working_list_imports(struct content **import, - unsigned int import_count, - struct content ***css, unsigned int *css_count) -{ - unsigned int i, j; - struct content **css2; - for (i = 0; i != import_count; i++) { - if (!import[i]) - continue; - /* search for import[i] in css[0..css_count) */ - for (j = 0; j != *css_count && (*css)[j] != import[i]; j++) - ; - if (j != *css_count) - /* we've seen this stylesheet already */ - continue; - /* recurse into imports of import[i] */ - if (!css_working_list_imports(import[i]->data.css. - import_content, - import[i]->data.css.import_count, - css, css_count)) - return false; - css2 = realloc(*css, sizeof *css * (*css_count + 1)); - if (!css2) - return false; - *css = css2; - (*css)[*css_count] = import[i]; - (*css_count)++; + if (c->data.css.sheet != NULL) { + css_stylesheet_destroy(c->data.css.sheet); + c->data.css.sheet = NULL; } - return true; -} - - -/** - * Merge hash chains of rules into an array of pointers ordered by specificity. - * - * \param working_stylesheet working stylesheet to add array to - * \param css array of contents of type CONTENT_CSS - * \param css_count number of elements in css - * \param chain hash chain index to merge - * \param rule scratch array of css_selector with css_count entries - * \return true on success, false if memory exhausted - */ - -bool css_working_merge_chains(struct css_working_stylesheet *working_stylesheet, - struct content **css, unsigned int css_count, - unsigned int chain, - struct css_selector **rule) -{ - unsigned int sheet, rules, rules_done = 0; - struct css_selector *selector; - unsigned int best = 0; - unsigned long min; - - /* count total rules */ - rules = 0; - for (sheet = 0; sheet != css_count; sheet++) - for (selector = css[sheet]->data.css.css->rule[chain]; - selector; - selector = selector->next) - rules++; - working_stylesheet->rule[chain] = talloc_array(working_stylesheet, - struct css_selector *, rules + 1); - if (!working_stylesheet->rule[chain]) - return false; - /* mergesort by specificity (increasing) */ - for (sheet = 0; sheet != css_count; sheet++) { - rule[sheet] = 0; - rule[sheet] = css[sheet]->data.css.css->rule[chain]; + if (c->data.css.dict != NULL) { + lwc_context_unref(c->data.css.dict); + c->data.css.dict = NULL; } - for (; rules_done != rules; rules_done++) { - /* find rule with lowest specificity */ - min = ULONG_MAX; - for (sheet = 0; sheet != css_count; sheet++) { - if (rule[sheet] && rule[sheet]->specificity < min) { - min = rule[sheet]->specificity; - best = sheet; - } - } - assert(min != ULONG_MAX); - working_stylesheet->rule[chain][rules_done] = rule[best]; - rule[best] = rule[best]->next; - } - assert(rules_done == rules); - working_stylesheet->rule[chain][rules] = 0; - - return true; } - /** - * Find the style which applies to an element. - * - * \param working_stylesheet working stylesheet - * \param element element in xml tree to match - * \param style style to update - * \pram author updated to indicate properties with author level css - * importance + * Fetchcache handler for imported stylesheets * - * The style is updated with any rules that match the element. + * \param msg Message type + * \param c Content being fetched + * \param p1 Parent content + * \param p2 Index into parent's imported stylesheet array + * \param data Message data */ - -void css_get_style(struct css_working_stylesheet *working_stylesheet, - xmlNode *element, struct css_style *style, - struct css_importance *author) +void nscss_import(content_msg msg, struct content *c, + intptr_t p1, intptr_t p2, union content_msg_data data) { - unsigned int hash, rule_0 = 0, rule_h = 0; - struct css_selector *rule; - css_importance_reset(author); /* initialise to sub-author level */ - - hash = css_hash((const char *) element->name, - strlen((const char *) element->name)); - - /* merge sort rules from special hash chain 0 (universal selector) and - * rules from hash chain for element name */ - while (working_stylesheet->rule[0] && - working_stylesheet->rule[0][rule_0] && - working_stylesheet->rule[hash] && - working_stylesheet->rule[hash][rule_h]) { - if (working_stylesheet->rule[0][rule_0]->specificity < - working_stylesheet->rule[hash][rule_h]-> - specificity) { - rule = working_stylesheet->rule[0][rule_0]; - rule_0++; - } else { - rule = working_stylesheet->rule[hash][rule_h]; - rule_h++; - } - if (css_match_rule(rule, element)) - css_merge(style, rule->style, rule->specificity, - author); - } + struct content *parent = (struct content *) p1; + uint32_t i = (uint32_t) p2; - /* remaining rules from hash chain 0 */ - while (working_stylesheet->rule[0] && - working_stylesheet->rule[0][rule_0]) { - rule = working_stylesheet->rule[0][rule_0]; - rule_0++; - if (css_match_rule(rule, element)) - css_merge(style, rule->style, rule->specificity, - author); - } + switch (msg) { + case CONTENT_MSG_LOADING: + if (c->type != CONTENT_CSS) { + content_remove_user(c, nscss_import, p1, p2); + if (c->user_list->next == NULL) { + fetch_abort(c->fetch); + c->fetch = NULL; + c->status = CONTENT_STATUS_ERROR; + } - /* remaining rules from hash chain for element name */ - while (working_stylesheet->rule[hash] && - working_stylesheet->rule[hash][rule_h]) { - rule = working_stylesheet->rule[hash][rule_h]; - rule_h++; - if (css_match_rule(rule, element)) - css_merge(style, rule->style, rule->specificity, - author); + parent->data.css.imports[i] = NULL; + parent->active--; + content_add_error(parent, "NotCSS", 0); + } + break; + case CONTENT_MSG_READY: + break; + case CONTENT_MSG_DONE: + parent->active--; + break; + case CONTENT_MSG_AUTH: + case CONTENT_MSG_SSL: + case CONTENT_MSG_LAUNCH: + case CONTENT_MSG_ERROR: + if (parent->data.css.imports[i] == c) { + parent->data.css.imports[i] = NULL; + parent->active--; + } + break; + case CONTENT_MSG_STATUS: + break; + case CONTENT_MSG_NEWPTR: + parent->data.css.imports[i] = c; + break; + default: + assert(0); } } - -/** - * Determine if a rule applies to an element. - */ - -bool css_match_rule(struct css_selector *rule, xmlNode *element) -{ - struct css_selector *detail; - xmlNode *anc, *prev; - - assert(element->type == XML_ELEMENT_NODE); - - if (rule->data && (rule->data_length != - strlen((const char *) element->name) || - strncasecmp(rule->data, (const char *) element->name, - rule->data_length) != 0)) - return false; - - for (detail = rule->detail; detail; detail = detail->next) { - if (!css_match_detail(detail, element)) - return false; - } - - if (!rule->combiner) - return true; - - switch (rule->comb) { - case CSS_COMB_ANCESTOR: - for (anc = element->parent; anc; anc = anc->parent) - if (anc->type == XML_ELEMENT_NODE && - css_match_rule(rule->combiner, anc)) - return true; - break; - - case CSS_COMB_PRECEDED: - for (prev = element->prev; - prev && prev->type != XML_ELEMENT_NODE; - prev = prev->prev) - ; - if (!prev) - return false; - return css_match_rule(rule->combiner, prev); - break; - - case CSS_COMB_PARENT: - for (anc = element->parent; - anc && anc->type != XML_ELEMENT_NODE; - anc = anc->parent) - ; - if (!anc) - return false; - return css_match_rule(rule->combiner, anc); - break; - - default: - assert(0); - } - - return false; -} - - -/** - * Determine if a selector detail matches an element. - * - * \param detail a css_selector of type other than CSS_SELECTOR_ELEMENT - * \param element element in xml tree to match - * \return true if the selector matches the element - */ - -bool css_match_detail(const struct css_selector *detail, - xmlNode *element) -{ - bool match = false; - char *s = 0; - char *space, *word; - size_t length; - - switch (detail->type) { - case CSS_SELECTOR_ID: - s = (char *) xmlGetProp(element, - (const xmlChar *) "id"); - /* case sensitive, according to HTML4.01 */ - if (s && strlen(s) == detail->data_length && - strncmp(detail->data, s, - detail->data_length) == 0) - match = true; - break; - - case CSS_SELECTOR_CLASS: - s = (char *) xmlGetProp(element, - (const xmlChar *) "class"); - if (!s) - break; - word = s; - do { - space = strchr(word, ' '); - if (space) - length = space - word; - else - length = strlen(word); - /* case sensitive, according to HTML4.01 */ - if (length == detail->data_length && - strncmp(word, detail->data, - length) == 0) { - match = true; - break; - } - word = space + 1; - } while (space); - break; - - case CSS_SELECTOR_ATTRIB: - /* matches if an attribute is present */ - word = strndup(detail->data, detail->data_length); - if (!word) { - /** \todo report to user */ - return false; - } - s = (char *) xmlGetProp(element, - (const xmlChar *) word); - free(word); - if (s) - match = true; - break; - - case CSS_SELECTOR_ATTRIB_EQ: - /* matches if an attribute has a certain value*/ - word = strndup(detail->data, detail->data_length); - if (!word) { - /** \todo report to user */ - return false; - } - s = (char *) xmlGetProp(element, - (const xmlChar *) word); - free(word); - if (s && strlen(s) == detail->data2_length && - strncasecmp(detail->data2, s, - detail->data2_length) == 0) - match = true; - break; - - case CSS_SELECTOR_ATTRIB_INC: - /* matches if one of the space separated words - * in the attribute is equal */ - word = strndup(detail->data, - detail->data_length); - if (!word) { - /** \todo report to user */ - return false; - } - s = (char *) xmlGetProp(element, - (const xmlChar *) word); - free(word); - if (!s) - break; - word = s; - do { - space = strchr(word, ' '); - if (space) - length = space - word; - else - length = strlen(word); - if (length == detail->data2_length && - strncasecmp(word, detail->data2, - length) == 0) { - match = true; - break; - } - word = space + 1; - } while (space); - break; - - case CSS_SELECTOR_ATTRIB_DM: - /* matches if a prefix up to a hyphen matches */ - word = strndup(detail->data, - detail->data_length); - if (!word) { - /** \todo report to user */ - return false; - } - s = (char *) xmlGetProp(element, - (const xmlChar *) word); - free(word); - if (!s) - break; - length = detail->data2_length; - if (strncasecmp(detail->data2, s, length) == 0 && - (s[length] == '-' || s[length] == 0)) - match = true; - break; - - case CSS_SELECTOR_ATTRIB_PRE: - /* matches if the attribute begins with a certain - * value (CSS3) */ - word = strndup(detail->data, detail->data_length); - if (!word) { - /** \todo report to user */ - return false; - } - s = (char *)xmlGetProp(element, - (const xmlChar *) word); - free(word); - if (s && strncasecmp(detail->data2, s, - detail->data2_length) == 0) - match = true; - break; - - case CSS_SELECTOR_ATTRIB_SUF: - /* matches if the attribute ends with a certain - * value (CSS3) */ - word = strndup(detail->data, detail->data_length); - if (!word) { - /** \todo report to user */ - return false; - } - s = (char *)xmlGetProp(element, - (const xmlChar *) word); - free(word); - if (!s) - break; - length = strlen(s); - if (detail->data2_length <= length) { - word = s + (length - detail->data2_length); - if (s && strncasecmp(detail->data2, word, - detail->data2_length) == 0) - match = true; - } - break; - - case CSS_SELECTOR_ATTRIB_SUB: - /* matches if the attribute contains a certain - * value (CSS3) */ - word = strndup(detail->data, detail->data_length); - if (!word) { - /** \todo report to user */ - return false; - } - s = (char *)xmlGetProp(element, - (const xmlChar *) word); - free(word); - if (!s) - break; - /* case insensitive strstr follows */ - /* space -> last possible start position */ - /* word -> start of string to search */ - space = s + (strlen(s) - detail->data2_length); - word = s; - while (word <= space) { - if (strncasecmp(detail->data2, word, - detail->data2_length) == 0) { - match = true; - break; - } - word++; - } - break; - - case CSS_SELECTOR_PSEUDO: - if (detail->data_length == 11 && - strncasecmp(detail->data, - "first-child", 11) == 0) { - match = css_match_first_child(detail, - element); - } - break; - - default: - assert(0); - } - - if (s) - xmlFree(s); - - return match; -} - - -/** - * Handle :first-child pseudo-class. - * - * \param detail a css_selector of type other than CSS_SELECTOR_ELEMENT - * \param element element in xml tree to match - * \return true if the selector matches the element - */ - -bool css_match_first_child(const struct css_selector *detail, - xmlNode *element) -{ - xmlNode *prev; - - for (prev = element->prev; prev && prev->type != XML_ELEMENT_NODE; - prev = prev->prev) - ; - - if (!prev) - return true; - - return false; -} - - -/** - * Parse a stand-alone CSS property list. - * - * \param c parent content - * \param style css_style to update - * \param str property list, as found in HTML style attributes - */ - -void css_parse_property_list(struct content *c, struct css_style * style, - char * str) -{ - unsigned char *source_data; - unsigned char *current, *end, *token_text; - size_t length; - unsigned int i; - int token; - void *parser; - struct css_parser_params param = {true, c, 0, false, false, false}; - struct css_parser_token token_data; - const struct css_parser_token token_start = { "{", 1 }; - const struct css_parser_token token_end = { "}", 1 }; - - length = strlen(str); - - parser = css_parser_Alloc(malloc); - source_data = malloc(length + 10); - - if (!parser || !source_data) { - free(parser); - css_parser_Free(parser, free); - return; - } - - strcpy((char *) source_data, str); - for (i = 0; i != 10; i++) - source_data[length + i] = 0; - - css_parser_(parser, LBRACE, token_start, ¶m); - - current = source_data; - end = source_data + strlen(str); - while (current < end - && (token = css_tokenise(¤t, end + 10, - &token_text))) { - token_data.text = (char *) token_text; - token_data.length = current - token_text; - css_parser_(parser, token, token_data, ¶m); - if (param.syntax_error) { - LOG(("syntax error near offset %li", - (unsigned long) (token_text - source_data))); - param.syntax_error = false; - } else if (param.memory_error) { - LOG(("out of memory")); - break; - } - } - css_parser_(parser, RBRACE, token_end, ¶m); - css_parser_(parser, 0, token_data, ¶m); - - css_parser_Free(parser, free); - - if (param.memory_error) { - css_free_node(param.declaration); - return; - } - - css_add_declarations(style, param.declaration); - - css_free_node(param.declaration); - - free(source_data); -} - - -/** - * Dump a css_style to stderr in CSS-like syntax. - */ - -void css_dump_style(FILE *stream, const struct css_style * const style) -{ - unsigned int i; - fprintf(stream, "{ "); - -#define DUMP_COLOR(z, s) \ - if (style->z != CSS_COLOR_NOT_SET) { \ - if (style->z == NS_TRANSPARENT) \ - fprintf(stream, s ": transparent; "); \ - else if (style->z == CSS_COLOR_NONE) \ - fprintf(stream, s ": none; "); \ - else \ - fprintf(stream, s ": #%.6x; ", style->z); \ - } - -#define DUMP_KEYWORD(z, s, n) \ - if (style->z != css_empty_style.z) \ - fprintf(stream, s ": %s; ", n[style->z]); - - DUMP_COLOR(background_color, "background-color"); - if (style->background_attachment != - css_empty_style.background_attachment || - style->background_image.type != - css_empty_style.background_image.type || - style->background_position.horz.pos != - css_empty_style.background_position.horz.pos || - style->background_position.vert.pos != - css_empty_style.background_position.vert.pos || - style->background_repeat != - css_empty_style.background_repeat) { - fprintf(stream, "background:"); - switch (style->background_image.type) { - case CSS_BACKGROUND_IMAGE_NONE: - fprintf(stream, " none"); - break; - case CSS_BACKGROUND_IMAGE_INHERIT: - fprintf(stream, " inherit"); - break; - case CSS_BACKGROUND_IMAGE_URI: - fprintf(stream, " (%p) \"%s\"", - style->background_image.uri, - style->background_image.uri); - break; - case CSS_BACKGROUND_IMAGE_NOT_SET: - ; - break; - default: - fprintf(stream, " UNKNOWN"); - break; - } - - if (style->background_repeat == - CSS_BACKGROUND_REPEAT_UNKNOWN) - fprintf(stream, " UNKNOWN"); - else if (style->background_repeat == - CSS_BACKGROUND_REPEAT_NOT_SET) - ; - else - fprintf(stream, " %s", - css_background_repeat_name[ - style->background_repeat]); - - if (style->background_attachment == - CSS_BACKGROUND_ATTACHMENT_UNKNOWN) - fprintf(stream, " UNKNOWN"); - else if (style->background_attachment == CSS_BACKGROUND_ATTACHMENT_NOT_SET) - ; - else - fprintf(stream, " %s", - css_background_attachment_name[ - style->background_attachment]); - - switch (style->background_position.horz.pos) { - case CSS_BACKGROUND_POSITION_LENGTH: - fprintf(stream, " "); - css_dump_length(stream, &style->background_position. - horz.value.length); - break; - case CSS_BACKGROUND_POSITION_PERCENT: - fprintf(stream, " %g%%", - style->background_position. - horz.value.percent); - break; - case CSS_BACKGROUND_POSITION_INHERIT: - fprintf(stream, " inherit"); - break; - case CSS_BACKGROUND_POSITION_NOT_SET: - break; - default: - fprintf(stream, " UNKNOWN"); - break; - } - switch (style->background_position.vert.pos) { - case CSS_BACKGROUND_POSITION_LENGTH: - fprintf(stream, " "); - css_dump_length(stream, &style->background_position. - vert.value.length); - break; - case CSS_BACKGROUND_POSITION_PERCENT: - fprintf(stream, " %g%%", - style->background_position. - vert.value.percent); - break; - case CSS_BACKGROUND_POSITION_INHERIT: - fprintf(stream, " inherit"); - break; - case CSS_BACKGROUND_POSITION_NOT_SET: - break; - default: - fprintf(stream, " UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - for (i = 0; i != 4; i++) { - if (style->border[i].color != CSS_COLOR_NOT_SET || - style->border[i].width.width != - CSS_BORDER_WIDTH_NOT_SET || - style->border[i].style != - CSS_BORDER_STYLE_NOT_SET) { - fprintf(stream, "border-"); - switch (i) { - case TOP: - fprintf(stream, "top:"); - break; - case RIGHT: - fprintf(stream, "right:"); - break; - case BOTTOM: - fprintf(stream, "bottom:"); - break; - case LEFT: - fprintf(stream, "left:"); - break; - } - switch (style->border[i].width.width) { - case CSS_BORDER_WIDTH_INHERIT: - fprintf(stream, " inherit"); - break; - case CSS_BORDER_WIDTH_LENGTH: - fprintf(stream, " "); - css_dump_length(stream, - &style->border[i].width.value); - break; - case CSS_BORDER_WIDTH_NOT_SET: - break; - default: - fprintf(stream, " UNKNOWN"); - break; - } - - if (style->border[i].style == - CSS_BORDER_STYLE_UNKNOWN) - fprintf(stream, " UNKNOWN"); - else if (style->border[i].style == - CSS_BORDER_STYLE_NOT_SET) - ; - else - fprintf(stream, " %s", - css_border_style_name[ - style->border[i].style]); - - if (style->border[i].color == NS_TRANSPARENT) - fprintf(stream, " transparent"); - else if (style->border[i].color == CSS_COLOR_NONE) - fprintf(stream, " none"); - else if (style->border[i].color == CSS_COLOR_INHERIT) - fprintf(stream, " inherit"); - else if (style->border[i].color == CSS_COLOR_NOT_SET) - ; - else - fprintf(stream, " #%.6x", - style->border[i].color); - fprintf(stream, "; "); - } - } - DUMP_KEYWORD(border_collapse, "border-collapse", - css_border_collapse_name); - if (style->border_spacing.border_spacing != - CSS_BORDER_SPACING_NOT_SET) { - fprintf(stream, "border-spacing: "); - css_dump_length(stream, &style->border_spacing.horz); - fprintf(stream, " "); - css_dump_length(stream, &style->border_spacing.vert); - fprintf(stream, "; "); - } - - DUMP_KEYWORD(caption_side, "caption-side", css_caption_side_name); - DUMP_KEYWORD(clear, "clear", css_clear_name); - - if (style->clip.clip != CSS_CLIP_NOT_SET) { - fprintf(stream, "clip: "); - switch (style->clip.clip) { - case CSS_CLIP_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_CLIP_AUTO: - fprintf(stream, "auto"); - break; - case CSS_CLIP_RECT: - fprintf(stream, "rect("); - for (i = 0; i != 4; i++) { - switch (style->clip.rect[i].rect) { - case CSS_CLIP_RECT_AUTO: - fprintf(stream, "auto"); - break; - case CSS_CLIP_RECT_LENGTH: - css_dump_length(stream, - &style->clip.rect[i].value); - break; - } - if (i != 3) - fprintf(stream, ", "); - } - fprintf(stream, ")"); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - DUMP_COLOR(color, "color"); - DUMP_KEYWORD(cursor, "cursor", css_cursor_name); - DUMP_KEYWORD(direction, "direction", css_direction_name); - DUMP_KEYWORD(display, "display", css_display_name); - DUMP_KEYWORD(empty_cells, "empty-cells", css_empty_cells_name); - DUMP_KEYWORD(float_, "float", css_float_name); - - if (style->font_style != CSS_FONT_STYLE_NOT_SET || - style->font_weight != CSS_FONT_WEIGHT_NOT_SET || - style->font_size.size != CSS_FONT_SIZE_NOT_SET || - style->line_height.size != CSS_LINE_HEIGHT_NOT_SET || - style->font_family != CSS_FONT_FAMILY_NOT_SET || - style->font_variant != CSS_FONT_VARIANT_NOT_SET) { - fprintf(stream, "font:"); - - if (style->font_style == CSS_FONT_STYLE_UNKNOWN) - fprintf(stream, " UNKNOWN"); - else if (style->font_style == CSS_FONT_STYLE_NOT_SET) - ; - else - fprintf(stream, " %s", - css_font_style_name[style->font_style]); - - if (style->font_weight == CSS_FONT_WEIGHT_UNKNOWN) - fprintf(stream, " UNKNOWN"); - else if (style->font_weight == CSS_FONT_WEIGHT_NOT_SET) - ; - else - fprintf(stream, " %s", - css_font_weight_name[style->font_weight]); - - switch (style->font_size.size) { - case CSS_FONT_SIZE_ABSOLUTE: - fprintf(stream, " [%g]", - style->font_size.value.absolute); - break; - case CSS_FONT_SIZE_LENGTH: - fprintf(stream, " "); - css_dump_length(stream, &style->font_size.value.length); - break; - case CSS_FONT_SIZE_PERCENT: - fprintf(stream, " %g%%", - style->font_size.value.percent); - break; - case CSS_FONT_SIZE_INHERIT: - fprintf(stream, " inherit"); - break; - case CSS_FONT_SIZE_NOT_SET: - break; - default: - fprintf(stream, " UNKNOWN"); - break; - } - switch (style->line_height.size) { - case CSS_LINE_HEIGHT_ABSOLUTE: - fprintf(stream, "/[%g]", - style->line_height.value.absolute); - break; - case CSS_LINE_HEIGHT_LENGTH: - fprintf(stream, "/"); - css_dump_length(stream, - &style->line_height.value.length); - break; - case CSS_LINE_HEIGHT_PERCENT: - fprintf(stream, "/%g%%", - style->line_height.value.percent); - break; - case CSS_LINE_HEIGHT_INHERIT: - fprintf(stream, "/inherit"); - break; - case CSS_LINE_HEIGHT_NOT_SET: - break; - default: - fprintf(stream, "/UNKNOWN"); - break; - } - if (style->font_family == CSS_FONT_FAMILY_UNKNOWN) - fprintf(stream, " UNKNOWN"); - else if (style->font_family == CSS_FONT_FAMILY_NOT_SET) - ; - else - fprintf(stream, " %s", - css_font_family_name[style->font_family]); - - if (style->font_variant == CSS_FONT_VARIANT_UNKNOWN) - fprintf(stream, " UNKNOWN"); - else if (style->font_variant == CSS_FONT_VARIANT_NOT_SET) - ; - else - fprintf(stream, " %s", - css_font_variant_name[style->font_variant]); - fprintf(stream, "; "); - } - - if (style->height.height != CSS_HEIGHT_NOT_SET) { - fprintf(stream, "height: "); - switch (style->height.height) { - case CSS_HEIGHT_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_HEIGHT_AUTO: - fprintf(stream, "auto"); - break; - case CSS_HEIGHT_LENGTH: - css_dump_length(stream, &style->height.value.length); - break; - case CSS_HEIGHT_PERCENT: - fprintf(stream, "%g%%", - style->height.value.percent); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - if (style->letter_spacing.letter_spacing != - CSS_LETTER_SPACING_NOT_SET) { - fprintf(stream, "letter-spacing: "); - switch (style->letter_spacing.letter_spacing) { - case CSS_LETTER_SPACING_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_LETTER_SPACING_NORMAL: - fprintf(stream, "normal"); - break; - case CSS_LETTER_SPACING_LENGTH: - css_dump_length(stream, &style->letter_spacing.length); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - if (style->list_style_type != CSS_LIST_STYLE_TYPE_NOT_SET || - style->list_style_position != - CSS_LIST_STYLE_POSITION_NOT_SET || - style->list_style_image.type != - CSS_LIST_STYLE_IMAGE_NOT_SET) { - fprintf(stream, "list-style:"); - - if (style->list_style_type == CSS_LIST_STYLE_TYPE_UNKNOWN) - fprintf(stream, " UNKNOWN"); - else if (style->list_style_type == CSS_LIST_STYLE_TYPE_NOT_SET) - ; - else - fprintf(stream, " %s", - css_list_style_type_name[ - style->list_style_type]); - - if (style->list_style_position == - CSS_LIST_STYLE_POSITION_UNKNOWN) - fprintf(stream, " UNKNOWN"); - else if (style->list_style_position == - CSS_LIST_STYLE_POSITION_NOT_SET) - ; - else - fprintf(stream, " %s", - css_list_style_position_name[ - style->list_style_position]); - - switch (style->list_style_image.type) { - case CSS_LIST_STYLE_IMAGE_INHERIT: - fprintf(stream, " inherit"); - break; - case CSS_LIST_STYLE_IMAGE_NONE: - fprintf(stream, " none"); - break; - case CSS_LIST_STYLE_IMAGE_URI: - fprintf(stream, " url('%s')", - style->list_style_image.uri); - break; - case CSS_LIST_STYLE_IMAGE_NOT_SET: - break; - default: - fprintf(stream, " UNKNOWN"); - } - fprintf(stream, "; "); - } - - if (style->margin[0].margin != CSS_MARGIN_NOT_SET || - style->margin[1].margin != CSS_MARGIN_NOT_SET || - style->margin[2].margin != CSS_MARGIN_NOT_SET || - style->margin[3].margin != CSS_MARGIN_NOT_SET) { - fprintf(stream, "margin:"); - for (i = 0; i != 4; i++) { - switch (style->margin[i].margin) { - case CSS_MARGIN_INHERIT: - fprintf(stream, " inherit"); - break; - case CSS_MARGIN_LENGTH: - fprintf(stream, " "); - css_dump_length(stream, - &style->margin[i].value.length); - break; - case CSS_MARGIN_PERCENT: - fprintf(stream, " %g%%", - style->margin[i].value.percent); - break; - case CSS_MARGIN_AUTO: - fprintf(stream, " auto"); - break; - case CSS_MARGIN_NOT_SET: - fprintf(stream, " ."); - break; - default: - fprintf(stream, " UNKNOWN"); - break; - } - } - fprintf(stream, "; "); - } - - if (style->max_height.max_height != CSS_MAX_HEIGHT_NOT_SET) { - fprintf(stream, "max-height: "); - switch (style->max_height.max_height) { - case CSS_MAX_HEIGHT_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_MAX_HEIGHT_NONE: - fprintf(stream, "none"); - break; - case CSS_MAX_HEIGHT_LENGTH: - css_dump_length(stream, - &style->max_height.value.length); - break; - case CSS_MAX_HEIGHT_PERCENT: - fprintf(stream, "%g%%", - style->max_height.value.percent); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - if (style->max_width.max_width != CSS_MAX_WIDTH_NOT_SET) { - fprintf(stream, "max-width: "); - switch (style->max_width.max_width) { - case CSS_MAX_WIDTH_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_MAX_WIDTH_NONE: - fprintf(stream, "none"); - break; - case CSS_MAX_WIDTH_LENGTH: - css_dump_length(stream, &style->max_width.value.length); - break; - case CSS_MAX_WIDTH_PERCENT: - fprintf(stream, "%g%%", - style->max_width.value.percent); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - if (style->min_height.min_height != CSS_MIN_HEIGHT_NOT_SET) { - fprintf(stream, "min-height: "); - switch (style->min_height.min_height) { - case CSS_MIN_HEIGHT_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_MIN_HEIGHT_LENGTH: - css_dump_length(stream, - &style->min_height.value.length); - break; - case CSS_MIN_HEIGHT_PERCENT: - fprintf(stream, "%g%%", - style->min_height.value.percent); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - if (style->min_width.min_width != CSS_MIN_WIDTH_NOT_SET) { - fprintf(stream, "min-width: "); - switch (style->min_width.min_width) { - case CSS_MIN_WIDTH_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_MIN_WIDTH_LENGTH: - css_dump_length(stream, &style->min_width.value.length); - break; - case CSS_MIN_WIDTH_PERCENT: - fprintf(stream, "%g%%", - style->min_width.value.percent); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - if (style->orphans.orphans != CSS_ORPHANS_NOT_SET) { - fprintf(stream, "orphans: "); - switch (style->orphans.orphans) { - case CSS_ORPHANS_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_ORPHANS_INTEGER: - fprintf(stream, "%d", - style->orphans.value); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - if (style->outline.color.color != CSS_OUTLINE_COLOR_NOT_SET || - style->outline.width.width != CSS_BORDER_WIDTH_NOT_SET || - style->outline.style != CSS_BORDER_STYLE_NOT_SET) { - fprintf(stream, "outline:"); - switch (style->outline.color.color) { - case CSS_OUTLINE_COLOR_INHERIT: - fprintf(stream, " inherit"); - break; - case CSS_OUTLINE_COLOR_INVERT: - fprintf(stream, " invert"); - break; - case CSS_OUTLINE_COLOR_COLOR: - if (style->outline.color.value == NS_TRANSPARENT) - fprintf(stream, " transparent"); - else if (style->outline.color.value == CSS_COLOR_NONE) - fprintf(stream, " none"); - else if (style->outline.color.value == CSS_COLOR_INHERIT) - fprintf(stream, " inherit"); - else if (style->outline.color.value == CSS_COLOR_NOT_SET) - fprintf(stream, " ."); - else - fprintf(stream, " #%.6x", style->outline.color.value); - break; - case CSS_OUTLINE_COLOR_NOT_SET: - break; - default: - fprintf(stream, " UNKNOWN"); - break; - } - - if (style->outline.style == CSS_BORDER_STYLE_UNKNOWN) - fprintf(stream, " UNKNOWN"); - else if (style->outline.style == CSS_BORDER_STYLE_NOT_SET) - ; - else - fprintf(stream, " %s", - css_border_style_name[style->outline.style]); - - switch (style->outline.width.width) { - case CSS_BORDER_WIDTH_INHERIT: - fprintf(stream, " inherit"); - break; - case CSS_BORDER_WIDTH_LENGTH: - fprintf(stream, " "); - css_dump_length(stream, &style->outline.width.value); - break; - case CSS_BORDER_WIDTH_NOT_SET: - break; - default: - fprintf(stream, " UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - DUMP_KEYWORD(overflow, "overflow", css_overflow_name); - - if (style->padding[0].padding != CSS_PADDING_NOT_SET || - style->padding[1].padding != CSS_PADDING_NOT_SET || - style->padding[2].padding != CSS_PADDING_NOT_SET || - style->padding[3].padding != CSS_PADDING_NOT_SET) { - fprintf(stream, "padding:"); - for (i = 0; i != 4; i++) { - switch (style->padding[i].padding) { - case CSS_PADDING_INHERIT: - fprintf(stream, " inherit"); - break; - case CSS_PADDING_LENGTH: - fprintf(stream, " "); - css_dump_length(stream, - &style->padding[i].value.length); - break; - case CSS_PADDING_PERCENT: - fprintf(stream, " %g%%", - style->padding[i].value.percent); - break; - case CSS_PADDING_NOT_SET: - fprintf(stream, " ."); - break; - default: - fprintf(stream, " UNKNOWN"); - break; - } - } - fprintf(stream, "; "); - } - - DUMP_KEYWORD(page_break_after, "page-break-after", - css_page_break_after_name); - DUMP_KEYWORD(page_break_before, "page-break-before", - css_page_break_before_name); - DUMP_KEYWORD(page_break_inside, "page-break-inside", - css_page_break_inside_name); - - for (i = 0; i != 4; i++) { - if (style->pos[i].pos != CSS_POS_NOT_SET) { - switch (i) { - case TOP: - fprintf(stream, "top: "); - break; - case RIGHT: - fprintf(stream, "right: "); - break; - case BOTTOM: - fprintf(stream, "bottom: "); - break; - case LEFT: - fprintf(stream, "left: "); - break; - } - switch (style->pos[i].pos) { - case CSS_POS_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_POS_AUTO: - fprintf(stream, "auto"); - break; - case CSS_POS_PERCENT: - fprintf(stream, "%g%%", - style->pos[i].value.percent); - break; - case CSS_POS_LENGTH: - css_dump_length(stream, - &style->pos[i].value.length); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - } - DUMP_KEYWORD(position, "position", css_position_name); - - DUMP_KEYWORD(table_layout, "table-layout", css_table_layout_name); - DUMP_KEYWORD(text_align, "text-align", css_text_align_name); - - if (style->text_decoration != CSS_TEXT_DECORATION_NOT_SET) { - fprintf(stream, "text-decoration:"); - if (style->text_decoration == CSS_TEXT_DECORATION_NONE) - fprintf(stream, " none"); - if (style->text_decoration == CSS_TEXT_DECORATION_INHERIT) - fprintf(stream, " inherit"); - if (style->text_decoration & CSS_TEXT_DECORATION_UNDERLINE) - fprintf(stream, " underline"); - if (style->text_decoration & CSS_TEXT_DECORATION_OVERLINE) - fprintf(stream, " overline"); - if (style->text_decoration & CSS_TEXT_DECORATION_LINE_THROUGH) - fprintf(stream, " line-through"); - if (style->text_decoration & CSS_TEXT_DECORATION_BLINK) - fprintf(stream, " blink"); - fprintf(stream, "; "); - } - - if (style->text_indent.size != CSS_TEXT_INDENT_NOT_SET) { - fprintf(stream, "text-indent: "); - switch (style->text_indent.size) { - case CSS_TEXT_INDENT_LENGTH: - css_dump_length(stream, - &style->text_indent.value.length); - break; - case CSS_TEXT_INDENT_PERCENT: - fprintf(stream, "%g%%", - style->text_indent.value.percent); - break; - case CSS_TEXT_INDENT_INHERIT: - fprintf(stream, "inherit"); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - DUMP_KEYWORD(text_transform, "text-transform", css_text_transform_name); - - DUMP_KEYWORD(unicode_bidi, "unicode-bidi", css_unicode_bidi_name); - - if (style->vertical_align.type != CSS_VERTICAL_ALIGN_NOT_SET) { - fprintf(stream, "vertical-align: "); - switch (style->vertical_align.type) { - case CSS_VERTICAL_ALIGN_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_VERTICAL_ALIGN_BASELINE: - fprintf(stream, "baseline"); - break; - case CSS_VERTICAL_ALIGN_SUB: - fprintf(stream, "sub"); - break; - case CSS_VERTICAL_ALIGN_SUPER: - fprintf(stream, "super"); - break; - case CSS_VERTICAL_ALIGN_TOP: - fprintf(stream, "top"); - break; - case CSS_VERTICAL_ALIGN_TEXT_TOP: - fprintf(stream, "text-top"); - break; - case CSS_VERTICAL_ALIGN_MIDDLE: - fprintf(stream, "middle"); - break; - case CSS_VERTICAL_ALIGN_BOTTOM: - fprintf(stream, "bottom"); - break; - case CSS_VERTICAL_ALIGN_TEXT_BOTTOM: - fprintf(stream, "text-bottom"); - break; - case CSS_VERTICAL_ALIGN_LENGTH: - css_dump_length(stream, - &style->vertical_align.value.length); - break; - case CSS_VERTICAL_ALIGN_PERCENT: - fprintf(stream, "%g%%", - style->vertical_align.value.percent); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - DUMP_KEYWORD(visibility, "visibility", css_visibility_name); - DUMP_KEYWORD(white_space, "white-space", css_white_space_name); - - if (style->widows.widows != CSS_WIDOWS_NOT_SET) { - fprintf(stream, "widows: "); - switch (style->widows.widows) { - case CSS_WIDOWS_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_WIDOWS_INTEGER: - fprintf(stream, "%d", - style->widows.value); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - if (style->width.width != CSS_WIDTH_NOT_SET) { - fprintf(stream, "width: "); - switch (style->width.width) { - case CSS_WIDTH_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_WIDTH_AUTO: - fprintf(stream, "auto"); - break; - case CSS_WIDTH_LENGTH: - css_dump_length(stream, &style->width.value.length); - break; - case CSS_WIDTH_PERCENT: - fprintf(stream, "%g%%", style->width.value.percent); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - if (style->word_spacing.word_spacing != CSS_WORD_SPACING_NOT_SET) { - fprintf(stream, "word-spacing: "); - switch (style->word_spacing.word_spacing) { - case CSS_WORD_SPACING_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_WORD_SPACING_NORMAL: - fprintf(stream, "normal"); - break; - case CSS_WORD_SPACING_LENGTH: - css_dump_length(stream, &style->word_spacing.length); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - if (style->z_index.z_index != CSS_Z_INDEX_NOT_SET) { - fprintf(stream, "z-index: "); - switch (style->z_index.z_index) { - case CSS_Z_INDEX_INHERIT: - fprintf(stream, "inherit"); - break; - case CSS_Z_INDEX_AUTO: - fprintf(stream, "auto"); - break; - case CSS_Z_INDEX_INTEGER: - fprintf(stream, "%d", - style->z_index.value); - break; - default: - fprintf(stream, "UNKNOWN"); - break; - } - fprintf(stream, "; "); - } - - fprintf(stream, "}"); -} - - -/** - * Dump a css_length to the given stream - */ - -void css_dump_length(FILE *stream, const struct css_length * const length) -{ - if (fabs(length->value) < 0.0001) - fprintf(stream, "0"); - else - fprintf(stream, "%g%s", length->value, - css_unit_name[length->unit]); -} - -#ifdef DEBUG_WORKING_STYLESHEET -/** - * Dump a complete css_working_stylesheet to stderr in CSS syntax. - */ - -void css_dump_working_stylesheet(const struct css_working_stylesheet *ws) -{ - unsigned int i, j; - - for (i = 0; i != HASH_SIZE; i++) { - /*fprintf(stderr, "hash %i:\n", i);*/ - for (j = 0; ws->rule[i][j]; j++) { - css_dump_selector(ws->rule[i][j]); - fprintf(stderr, " <%lx> ", ws->rule[i][j]->specificity); - css_dump_style(ws->rule[i][j]->style); - fprintf(stderr, "\n"); - } - } -} -#endif - -/** - * Set all members to false - */ -void css_importance_reset(struct css_importance *i) { - int j; - i->background_color = false; - i->background_image = false; - i->border_spacing = false; - i->color = false; - i->height = false; - i->width = false; - - /**< top, right, bottom, left */ - for (j = 0; j < 4; j++) { - i->border_color[j] = false; - i->border_style[j] = false; - i->border_width[j] = false; - i->margin[j] = false; - i->padding[j] = false; - } -} - -/** - * Dump a complete css_stylesheet to stderr in CSS syntax. - */ - -void css_dump_stylesheet(const struct css_stylesheet * stylesheet) -{ - unsigned int i; - struct css_selector *r; - for (i = 0; i != HASH_SIZE; i++) { - /*fprintf(stderr, "hash %i:\n", i);*/ - for (r = stylesheet->rule[i]; r != 0; r = r->next) { - css_dump_selector(r); - fprintf(stderr, " <%lx> ", r->specificity); - css_dump_style(stderr, r->style); - fprintf(stderr, "\n"); - } - } -} - - -/** - * Dump a css_selector to stderr in CSS syntax. - */ - -void css_dump_selector(const struct css_selector *r) -{ - struct css_selector *m; - - if (r->combiner) - css_dump_selector(r->combiner); - - switch (r->comb) { - case CSS_COMB_NONE: break; - case CSS_COMB_ANCESTOR: fprintf(stderr, " "); break; - case CSS_COMB_PARENT: fprintf(stderr, " > "); break; - case CSS_COMB_PRECEDED: fprintf(stderr, " + "); break; - } - - if (r->data) - fprintf(stderr, "%.*s", r->data_length, r->data); - else - fprintf(stderr, "*"); - - for (m = r->detail; m; m = m->next) { - switch (m->type) { - case CSS_SELECTOR_ID: - fprintf(stderr, "#%.*s", - m->data_length, m->data); - break; - case CSS_SELECTOR_CLASS: - fprintf(stderr, ".%.*s", - m->data_length, m->data); - break; - case CSS_SELECTOR_ATTRIB: - fprintf(stderr, "[%.*s]", - m->data_length, m->data); - break; - case CSS_SELECTOR_ATTRIB_EQ: - fprintf(stderr, "[%.*s=%.*s]", - m->data_length, m->data, - m->data2_length, m->data2); - break; - case CSS_SELECTOR_ATTRIB_INC: - fprintf(stderr, "[%.*s~=%.*s]", - m->data_length, m->data, - m->data2_length, m->data2); - break; - case CSS_SELECTOR_ATTRIB_DM: - fprintf(stderr, "[%.*s|=%.*s]", - m->data_length, m->data, - m->data2_length, m->data2); - break; - case CSS_SELECTOR_ATTRIB_PRE: - fprintf(stderr, "[%.*s^=%.*s]", - m->data_length, m->data, - m->data2_length, m->data2); - break; - case CSS_SELECTOR_ATTRIB_SUF: - fprintf(stderr, "[%.*s$=%.*s]", - m->data_length, m->data, - m->data2_length, m->data2); - break; - case CSS_SELECTOR_ATTRIB_SUB: - fprintf(stderr, "[%.*s*=%.*s]", - m->data_length, m->data, - m->data2_length, m->data2); - break; - case CSS_SELECTOR_PSEUDO: - fprintf(stderr, ":%.*s", - m->data_length, m->data); - break; - default: - fprintf(stderr, "(unexpected detail)"); - } - } -} - - -/** - * Cascade styles. - * - * \param style css_style to modify - * \param apply css_style to cascade onto style - * \param author updated to indicate which properties have greater - * than author level CSS importance. (NULL if - * importance isn't required.) - * - * Attributes which have the value 'inherit' or 'unset' in apply are - * unchanged in style. - * Other attributes are copied to style, calculating percentages relative to - * style where applicable. - */ - -void css_cascade(struct css_style * const style, - const struct css_style * const apply, - struct css_importance * const author) -{ - unsigned int i; - float f; - - if (apply->background_attachment != - CSS_BACKGROUND_ATTACHMENT_INHERIT && - apply->background_attachment != - CSS_BACKGROUND_ATTACHMENT_NOT_SET) - style->background_attachment = apply->background_attachment; - if (apply->background_color != CSS_COLOR_INHERIT && - apply->background_color != CSS_COLOR_NOT_SET) - style->background_color = apply->background_color; - if (apply->background_image.type != CSS_BACKGROUND_IMAGE_INHERIT && - apply->background_image.type != - CSS_BACKGROUND_IMAGE_NOT_SET) - style->background_image = apply->background_image; - if (apply->background_repeat != CSS_BACKGROUND_REPEAT_INHERIT && - apply->background_repeat != - CSS_BACKGROUND_REPEAT_NOT_SET) - style->background_repeat = apply->background_repeat; - if (apply->border_collapse != CSS_BORDER_COLLAPSE_INHERIT && - apply->border_collapse != CSS_BORDER_COLLAPSE_NOT_SET) - style->border_collapse = apply->border_collapse; - if (apply->border_spacing.border_spacing != - CSS_BORDER_SPACING_INHERIT && - apply->border_spacing.border_spacing != - CSS_BORDER_SPACING_NOT_SET) - style->border_spacing = apply->border_spacing; - if (apply->caption_side != CSS_CAPTION_SIDE_INHERIT && - apply->caption_side != CSS_CAPTION_SIDE_NOT_SET) - style->caption_side = apply->caption_side; - if (apply->clear != CSS_CLEAR_INHERIT && - apply->clear != CSS_CLEAR_NOT_SET) - style->clear = apply->clear; - if (apply->color != CSS_COLOR_INHERIT && - apply->color != CSS_COLOR_NOT_SET) - style->color = apply->color; - if (apply->content.type != CSS_CONTENT_INHERIT && - apply->content.type != CSS_CONTENT_NOT_SET) - style->content = apply->content; - if (apply->counter_reset.type != CSS_COUNTER_RESET_INHERIT && - apply->counter_reset.type != CSS_COUNTER_RESET_NOT_SET) - style->counter_reset = apply->counter_reset; - if (apply->counter_increment.type != CSS_COUNTER_INCREMENT_INHERIT && - apply->counter_increment.type != CSS_COUNTER_INCREMENT_NOT_SET) - style->counter_increment = apply->counter_increment; - if (apply->cursor != CSS_CURSOR_INHERIT && - apply->cursor != CSS_CURSOR_NOT_SET) - style->cursor = apply->cursor; - if (apply->direction != CSS_DIRECTION_INHERIT && - apply->direction != CSS_DIRECTION_NOT_SET) - style->direction = apply->direction; - if (apply->display != CSS_DISPLAY_INHERIT && - apply->display != CSS_DISPLAY_NOT_SET) - style->display = apply->display; - if (apply->empty_cells != CSS_EMPTY_CELLS_INHERIT && - apply->empty_cells != CSS_EMPTY_CELLS_NOT_SET) - style->empty_cells = apply->empty_cells; - if (apply->float_ != CSS_FLOAT_INHERIT && - apply->float_ != CSS_FLOAT_NOT_SET) - style->float_ = apply->float_; - if (apply->font_family != CSS_FONT_FAMILY_INHERIT && - apply->font_family != CSS_FONT_FAMILY_NOT_SET) - style->font_family = apply->font_family; - if (apply->font_style != CSS_FONT_STYLE_INHERIT && - apply->font_style != CSS_FONT_STYLE_NOT_SET) - style->font_style = apply->font_style; - if (apply->font_variant != CSS_FONT_VARIANT_INHERIT && - apply->font_variant != CSS_FONT_VARIANT_NOT_SET) - style->font_variant = apply->font_variant; - if (apply->font_weight != CSS_FONT_WEIGHT_INHERIT && - apply->font_weight != CSS_FONT_WEIGHT_NOT_SET) - style->font_weight = apply->font_weight; - if (apply->height.height != CSS_HEIGHT_INHERIT && - apply->height.height != CSS_HEIGHT_NOT_SET) - style->height = apply->height; - if (apply->letter_spacing.letter_spacing != - CSS_LETTER_SPACING_INHERIT && - apply->letter_spacing.letter_spacing != - CSS_LETTER_SPACING_NOT_SET) - style->letter_spacing = apply->letter_spacing; - if (apply->line_height.size != CSS_LINE_HEIGHT_INHERIT && - apply->line_height.size != CSS_LINE_HEIGHT_NOT_SET) - style->line_height = apply->line_height; - if (apply->list_style_image.type != CSS_LIST_STYLE_IMAGE_INHERIT && - apply->list_style_image.type != - CSS_LIST_STYLE_IMAGE_NOT_SET) - style->list_style_image = apply->list_style_image; - if (apply->list_style_position != CSS_LIST_STYLE_POSITION_INHERIT && - apply->list_style_position != - CSS_LIST_STYLE_POSITION_NOT_SET) - style->list_style_position = apply->list_style_position; - if (apply->list_style_type != CSS_LIST_STYLE_TYPE_INHERIT && - apply->list_style_type != CSS_LIST_STYLE_TYPE_NOT_SET) - style->list_style_type = apply->list_style_type; - if (apply->max_height.max_height != CSS_MAX_HEIGHT_INHERIT && - apply->max_height.max_height != CSS_MAX_HEIGHT_NOT_SET) - style->max_height = apply->max_height; - if (apply->max_width.max_width != CSS_MAX_WIDTH_INHERIT && - apply->max_width.max_width != CSS_MAX_WIDTH_NOT_SET) - style->max_width = apply->max_width; - if (apply->min_height.min_height != CSS_MIN_HEIGHT_INHERIT && - apply->min_height.min_height != CSS_MIN_HEIGHT_NOT_SET) - style->min_height = apply->min_height; - if (apply->min_width.min_width != CSS_MIN_WIDTH_INHERIT && - apply->min_width.min_width != CSS_MIN_WIDTH_NOT_SET) - style->min_width = apply->min_width; - if (apply->orphans.orphans != CSS_ORPHANS_INHERIT && - apply->orphans.orphans != CSS_ORPHANS_NOT_SET) - style->orphans = apply->orphans; - if (apply->overflow != CSS_OVERFLOW_INHERIT && - apply->overflow != CSS_OVERFLOW_NOT_SET) - style->overflow = apply->overflow; - if (apply->page_break_after != CSS_PAGE_BREAK_AFTER_INHERIT && - apply->page_break_after != - CSS_PAGE_BREAK_AFTER_NOT_SET) - style->page_break_after = apply->page_break_after; - if (apply->page_break_before != CSS_PAGE_BREAK_BEFORE_INHERIT && - apply->page_break_before != - CSS_PAGE_BREAK_BEFORE_NOT_SET) - style->page_break_before = apply->page_break_before; - if (apply->page_break_inside != CSS_PAGE_BREAK_INSIDE_INHERIT && - apply->page_break_inside != - CSS_PAGE_BREAK_INSIDE_NOT_SET) - style->page_break_inside = apply->page_break_inside; - if (apply->position != CSS_POSITION_INHERIT && - apply->position != CSS_POSITION_NOT_SET) - style->position = apply->position; - if (apply->table_layout != CSS_TABLE_LAYOUT_INHERIT && - apply->table_layout != CSS_TABLE_LAYOUT_NOT_SET) - style->table_layout = apply->table_layout; - if (apply->text_align != CSS_TEXT_ALIGN_INHERIT && - apply->text_align != CSS_TEXT_ALIGN_NOT_SET) - style->text_align = apply->text_align; - /* text-decoration: approximate CSS 2.1 by inheriting into inline elements */ - if (apply->text_decoration != CSS_TEXT_DECORATION_INHERIT && - apply->text_decoration != CSS_TEXT_DECORATION_NOT_SET) - style->text_decoration = apply->text_decoration; - if (apply->text_indent.size != CSS_TEXT_INDENT_INHERIT && - apply->text_indent.size != CSS_TEXT_INDENT_NOT_SET) - style->text_indent = apply->text_indent; - if (apply->text_transform != CSS_TEXT_TRANSFORM_INHERIT && - apply->text_transform != CSS_TEXT_TRANSFORM_NOT_SET) - style->text_transform = apply->text_transform; - if (apply->unicode_bidi != CSS_UNICODE_BIDI_INHERIT && - apply->unicode_bidi != CSS_UNICODE_BIDI_NOT_SET) - style->unicode_bidi = apply->unicode_bidi; - if (apply->vertical_align.type != CSS_VERTICAL_ALIGN_INHERIT && - apply->vertical_align.type != - CSS_VERTICAL_ALIGN_NOT_SET) - style->vertical_align = apply->vertical_align; - if (apply->visibility != CSS_VISIBILITY_INHERIT && - apply->visibility != CSS_VISIBILITY_NOT_SET) - style->visibility = apply->visibility; - if (apply->white_space != CSS_WHITE_SPACE_INHERIT && - apply->white_space != CSS_WHITE_SPACE_NOT_SET) - style->white_space = apply->white_space; - if (apply->widows.widows != CSS_WIDOWS_INHERIT && - apply->widows.widows != CSS_WIDOWS_NOT_SET) - style->widows = apply->widows; - if (apply->width.width != CSS_WIDTH_INHERIT && - apply->width.width != CSS_WIDTH_NOT_SET) - style->width = apply->width; - if (apply->word_spacing.word_spacing != CSS_WORD_SPACING_INHERIT && - apply->word_spacing.word_spacing != - CSS_WORD_SPACING_NOT_SET) - style->word_spacing = apply->word_spacing; - if (apply->z_index.z_index != CSS_Z_INDEX_INHERIT && - apply->z_index.z_index != CSS_Z_INDEX_NOT_SET) - style->z_index = apply->z_index; - - - /* clip */ - if (apply->clip.clip != CSS_CLIP_INHERIT && - apply->clip.clip != CSS_CLIP_NOT_SET) { - for (i = 0; i != 4; i++) { - style->clip.rect[i] = apply->clip.rect[i]; - } - } - - - /* background-position */ - if (apply->background_position.horz.pos != - CSS_BACKGROUND_POSITION_INHERIT && - apply->background_position.horz.pos != - CSS_BACKGROUND_POSITION_NOT_SET) { - style->background_position.horz = - apply->background_position.horz; - } - if (apply->background_position.vert.pos != - CSS_BACKGROUND_POSITION_INHERIT && - apply->background_position.vert.pos != - CSS_BACKGROUND_POSITION_NOT_SET) { - style->background_position.vert = - apply->background_position.vert; - } - - /* font-size */ - f = apply->font_size.value.percent / 100; - switch (apply->font_size.size) { - case CSS_FONT_SIZE_ABSOLUTE: - style->font_size = apply->font_size; - break; - case CSS_FONT_SIZE_LENGTH: - switch (apply->font_size.value.length.unit) { - case CSS_UNIT_EM: - f = apply->font_size.value.length.value; - break; - case CSS_UNIT_EX: - f = apply->font_size.value.length.value * 0.6 /*?*/; - break; - default: - style->font_size = apply->font_size; - } - if ((apply->font_size.value.length.unit != CSS_UNIT_EM) && - (apply->font_size.value.length.unit != CSS_UNIT_EX)) - break; - /* drop through if EM or EX */ - case CSS_FONT_SIZE_PERCENT: - switch (style->font_size.size) { - case CSS_FONT_SIZE_ABSOLUTE: - style->font_size.value.absolute *= f; - break; - case CSS_FONT_SIZE_LENGTH: - style->font_size.value.length.value *= f; - break; - default: - die("attempting percentage of unknown font-size"); - } - break; - case CSS_FONT_SIZE_INHERIT: - case CSS_FONT_SIZE_NOT_SET: - default: /* leave unchanged */ - break; - } - - /* outline */ - if (apply->outline.color.color != CSS_OUTLINE_COLOR_INHERIT && - apply->outline.color.color != - CSS_OUTLINE_COLOR_NOT_SET) - style->outline.color = apply->outline.color; - if (apply->outline.width.width != CSS_BORDER_WIDTH_INHERIT && - apply->outline.width.width != CSS_BORDER_WIDTH_NOT_SET) - style->outline.width = apply->outline.width; - if (apply->outline.style != CSS_BORDER_STYLE_INHERIT && - apply->outline.style != CSS_BORDER_STYLE_NOT_SET) - style->outline.style = apply->outline.style; - - /* borders, margins, padding and box position */ - for (i = 0; i != 4; i++) { - if (apply->border[i].color != CSS_COLOR_INHERIT && - apply->border[i].color != CSS_COLOR_NOT_SET) - style->border[i].color = apply->border[i].color; - if (apply->border[i].width.width != - CSS_BORDER_WIDTH_INHERIT && - apply->border[i].width.width != - CSS_BORDER_WIDTH_NOT_SET) - style->border[i].width = apply->border[i].width; - if (apply->border[i].style != CSS_BORDER_STYLE_INHERIT && - apply->border[i].style != - CSS_BORDER_STYLE_NOT_SET) - style->border[i].style = apply->border[i].style; - - if (apply->margin[i].margin != CSS_MARGIN_INHERIT && - apply->margin[i].margin != CSS_MARGIN_NOT_SET) - style->margin[i] = apply->margin[i]; - - if (apply->padding[i].padding != CSS_PADDING_INHERIT && - apply->padding[i].padding != - CSS_PADDING_NOT_SET) - style->padding[i] = apply->padding[i]; - - if (apply->pos[i].pos != CSS_POS_INHERIT && - apply->pos[i].pos != CSS_POS_NOT_SET) - style->pos[i] = apply->pos[i]; - } - - /* Set author level CSS importance (used for HTML style attribute) */ - if (author) { - if (apply->background_color != CSS_COLOR_NOT_SET) - author->background_color = true; - if (apply->background_image.type != - CSS_BACKGROUND_IMAGE_NOT_SET) - author->background_image = true; - if (apply->border_spacing.border_spacing != - CSS_BORDER_SPACING_NOT_SET) - author->border_spacing = true; - if (apply->color != CSS_COLOR_NOT_SET) - author->color = true; - if (apply->height.height != CSS_HEIGHT_NOT_SET) - author->height = true; - if (apply->width.width != CSS_WIDTH_NOT_SET) - author->width = true; - - for (i = 0; i != 4; i++) { - if (apply->border[i].color != CSS_COLOR_NOT_SET) - author->border_color[i] = true; - if (apply->border[i].width.width != - CSS_BORDER_WIDTH_NOT_SET) - author->border_width[i] = true; - if (apply->border[i].style != CSS_BORDER_STYLE_NOT_SET) - author->border_style[i] = true; - - if (apply->margin[i].margin != CSS_MARGIN_NOT_SET) - author->margin[i] = true; - - if (apply->padding[i].padding != CSS_PADDING_NOT_SET) - author->padding[i] = true; - } - } -} - - -/** - * Merge styles. - * - * \param style css_style to modify - * \param apply css_style to merge onto style - * \param specificity specificity of current CSS rule - * \param author updated to indicate which properties have greater than - * author level CSS importance - * - * Attributes which have the value 'unset' in apply are unchanged in style. - * Other attributes are copied to style, overwriting it. - */ - -void css_merge(struct css_style * const style, - const struct css_style * const apply, - const unsigned long specificity, - struct css_importance * const author) -{ - unsigned int i; - - if (apply->background_attachment != CSS_BACKGROUND_ATTACHMENT_NOT_SET) - style->background_attachment = apply->background_attachment; - if (apply->background_color != CSS_COLOR_NOT_SET) { - style->background_color = apply->background_color; - if (specificity >= CSS_SPECIFICITY_AUTHOR) - author->background_color = true; - } - if (apply->background_image.type != CSS_BACKGROUND_IMAGE_NOT_SET) { - style->background_image = apply->background_image; - if (specificity >= CSS_SPECIFICITY_AUTHOR) - author->background_image = true; - } - if (apply->background_repeat != CSS_BACKGROUND_REPEAT_NOT_SET) - style->background_repeat = apply->background_repeat; - if (apply->border_collapse != CSS_BORDER_COLLAPSE_NOT_SET) - style->border_collapse = apply->border_collapse; - if (apply->border_spacing.border_spacing != CSS_BORDER_SPACING_NOT_SET){ - style->border_spacing = apply->border_spacing; - if (specificity >= CSS_SPECIFICITY_AUTHOR) - author->border_spacing = true; - } - if (apply->caption_side != CSS_CAPTION_SIDE_NOT_SET) - style->caption_side = apply->caption_side; - if (apply->clear != CSS_CLEAR_NOT_SET) - style->clear = apply->clear; - if (apply->color != CSS_COLOR_NOT_SET) { - style->color = apply->color; - if (specificity >= CSS_SPECIFICITY_AUTHOR) - author->color = true; - } - if (apply->content.type != CSS_CONTENT_NOT_SET) - style->content = apply->content; - if (apply->counter_reset.type != CSS_COUNTER_RESET_NOT_SET) - style->counter_reset = apply->counter_reset; - if (apply->counter_increment.type != CSS_COUNTER_INCREMENT_NOT_SET) - style->counter_increment = apply->counter_increment; - if (apply->cursor != CSS_CURSOR_NOT_SET) - style->cursor = apply->cursor; - if (apply->direction != CSS_DIRECTION_NOT_SET) - style->direction = apply->direction; - if (apply->display != CSS_DISPLAY_NOT_SET) - style->display = apply->display; - if (apply->empty_cells != CSS_EMPTY_CELLS_NOT_SET) - style->empty_cells = apply->empty_cells; - if (apply->float_ != CSS_FLOAT_NOT_SET) - style->float_ = apply->float_; - if (apply->font_family != CSS_FONT_FAMILY_NOT_SET) - style->font_family = apply->font_family; - if (apply->font_size.size != CSS_FONT_SIZE_NOT_SET) - style->font_size = apply->font_size; - if (apply->font_style != CSS_FONT_STYLE_NOT_SET) - style->font_style = apply->font_style; - if (apply->font_variant != CSS_FONT_VARIANT_NOT_SET) - style->font_variant = apply->font_variant; - if (apply->font_weight != CSS_FONT_WEIGHT_NOT_SET) - style->font_weight = apply->font_weight; - if (apply->height.height != CSS_HEIGHT_NOT_SET) { - style->height = apply->height; - if (specificity >= CSS_SPECIFICITY_AUTHOR) - author->height = true; - } - if (apply->letter_spacing.letter_spacing != CSS_LETTER_SPACING_NOT_SET) - style->letter_spacing = apply->letter_spacing; - if (apply->line_height.size != CSS_LINE_HEIGHT_NOT_SET) - style->line_height = apply->line_height; - if (apply->list_style_image.type != CSS_LIST_STYLE_IMAGE_NOT_SET) - style->list_style_image = apply->list_style_image; - if (apply->list_style_position != CSS_LIST_STYLE_POSITION_NOT_SET) - style->list_style_position = apply->list_style_position; - if (apply->list_style_type != CSS_LIST_STYLE_TYPE_NOT_SET) - style->list_style_type = apply->list_style_type; - if (apply->max_height.max_height != CSS_MAX_HEIGHT_NOT_SET) - style->max_height = apply->max_height; - if (apply->max_width.max_width != CSS_MAX_WIDTH_NOT_SET) - style->max_width = apply->max_width; - if (apply->min_height.min_height != CSS_MIN_HEIGHT_NOT_SET) - style->min_height = apply->min_height; - if (apply->min_width.min_width != CSS_MIN_WIDTH_NOT_SET) - style->min_width = apply->min_width; - if (apply->orphans.orphans != CSS_ORPHANS_NOT_SET) - style->orphans = apply->orphans; - if (apply->overflow != CSS_OVERFLOW_NOT_SET) - style->overflow = apply->overflow; - if (apply->page_break_after != CSS_PAGE_BREAK_AFTER_NOT_SET) - style->page_break_after = apply->page_break_after; - if (apply->page_break_before != CSS_PAGE_BREAK_BEFORE_NOT_SET) - style->page_break_before = apply->page_break_before; - if (apply->page_break_inside != CSS_PAGE_BREAK_INSIDE_NOT_SET) - style->page_break_inside = apply->page_break_inside; - if (apply->position != CSS_POSITION_NOT_SET) - style->position = apply->position; - if (apply->table_layout != CSS_TABLE_LAYOUT_NOT_SET) - style->table_layout = apply->table_layout; - if (apply->text_align != CSS_TEXT_ALIGN_NOT_SET) - style->text_align = apply->text_align; - /* text-decoration: approximate CSS 2.1 by inheriting into inline elements */ - if (apply->text_decoration != CSS_TEXT_DECORATION_NOT_SET) - style->text_decoration = apply->text_decoration; - if (apply->text_indent.size != CSS_TEXT_INDENT_NOT_SET) - style->text_indent = apply->text_indent; - if (apply->text_transform != CSS_TEXT_TRANSFORM_NOT_SET) - style->text_transform = apply->text_transform; - if (apply->unicode_bidi != CSS_UNICODE_BIDI_NOT_SET) - style->unicode_bidi = apply->unicode_bidi; - if (apply->vertical_align.type != CSS_VERTICAL_ALIGN_NOT_SET) - style->vertical_align = apply->vertical_align; - if (apply->visibility != CSS_VISIBILITY_NOT_SET) - style->visibility = apply->visibility; - if (apply->white_space != CSS_WHITE_SPACE_NOT_SET) - style->white_space = apply->white_space; - if (apply->widows.widows != CSS_WIDOWS_NOT_SET) - style->widows = apply->widows; - if (apply->width.width != CSS_WIDTH_NOT_SET) { - style->width = apply->width; - if (specificity >= CSS_SPECIFICITY_AUTHOR) - author->width = true; - } - if (apply->word_spacing.word_spacing != CSS_WORD_SPACING_NOT_SET) - style->word_spacing = apply->word_spacing; - if (apply->z_index.z_index != CSS_Z_INDEX_NOT_SET) - style->z_index = apply->z_index; - - - /* clip */ - if (apply->clip.clip != CSS_CLIP_NOT_SET) { - for (i = 0; i != 4; i++) { - style->clip.rect[i] = apply->clip.rect[i]; - } - } - - /* background-position */ - if (apply->background_position.horz.pos != - CSS_BACKGROUND_POSITION_NOT_SET) { - style->background_position.horz = - apply->background_position.horz; - } - if (apply->background_position.vert.pos != - CSS_BACKGROUND_POSITION_NOT_SET) { - style->background_position.vert = - apply->background_position.vert; - } - - /* outline */ - if (apply->outline.color.color != CSS_OUTLINE_COLOR_NOT_SET) - style->outline.color = apply->outline.color; - if (apply->outline.width.width != CSS_BORDER_WIDTH_NOT_SET) - style->outline.width = apply->outline.width; - if (apply->outline.style != CSS_BORDER_STYLE_NOT_SET) - style->outline.style = apply->outline.style; - - /* borders, margins, padding and box position */ - for (i = 0; i != 4; i++) { - if (apply->border[i].color != CSS_COLOR_NOT_SET) { - style->border[i].color = apply->border[i].color; - if (specificity >= CSS_SPECIFICITY_AUTHOR) - author->border_color[i] = true; - } - if (apply->border[i].width.width != CSS_BORDER_WIDTH_NOT_SET) { - style->border[i].width = apply->border[i].width; - if (specificity >= CSS_SPECIFICITY_AUTHOR) - author->border_width[i] = true; - } - if (apply->border[i].style != CSS_BORDER_STYLE_NOT_SET) { - style->border[i].style = apply->border[i].style; - if (specificity >= CSS_SPECIFICITY_AUTHOR) - author->border_style[i] = true; - } - - if (apply->margin[i].margin != CSS_MARGIN_NOT_SET) { - style->margin[i] = apply->margin[i]; - if (specificity >= CSS_SPECIFICITY_AUTHOR) - author->margin[i] = true; - } - - if (apply->padding[i].padding != CSS_PADDING_NOT_SET) { - style->padding[i] = apply->padding[i]; - if (specificity >= CSS_SPECIFICITY_AUTHOR) - author->padding[i] = true; - } - - if (apply->pos[i].pos != CSS_POS_NOT_SET) - style->pos[i] = apply->pos[i]; - } -} - - -/** - * Calculate a hash for an element name. - * - * The hash is case-insensitive. - */ - -unsigned int css_hash(const char *s, int length) -{ - int i; - unsigned int z = 0; - if (s == 0) - return 0; - for (i = 0; i != length; i++) - z += s[i] & 0x1f; /* lower 5 bits, case insensitive */ - return (z % (HASH_SIZE - 1)) + 1; -} - - -/** - * Convert a struct css_length to pixels. - * - * \param length css_length to convert - * \param style css_style applying to length. may be 0 if the length's - * unit is em or ex - * \return length in pixels - * - * If a length's unit is em or ex, the returned length is subject to the - * configured option_font_min_size. - */ - -float css_len2px(const struct css_length *length, - const struct css_style *style) -{ - struct css_length font; - font.unit = CSS_UNIT_PT; - - assert(!((length->unit == CSS_UNIT_EM || length->unit == CSS_UNIT_EX) && - style == 0)); - switch (length->unit) { - case CSS_UNIT_EM: - if ((font.value = css_len2pt(&style-> - font_size.value.length, style)) < - option_font_min_size / 10) { - /* min font size is greater than given length so - * use min font size for conversion to px */ - font.value = option_font_min_size / 10; - return length->value * css_len2px(&font, style); - } else - /* use given length for conversion to px */ - return length->value * css_len2px(&style-> - font_size.value.length, 0); - case CSS_UNIT_EX: - if ((font.value = css_len2pt(&style-> - font_size.value.length, style)) < - option_font_min_size / 10) { - /* min font size is greater than given length so - * use min font size for conversion to px */ - font.value = option_font_min_size / 10; - return length->value * css_len2px(&font, - style) * 0.6; - } else - /* use given length for conversion to px */ - return length->value * css_len2px(&style-> - font_size.value.length, 0) * - 0.6; - case CSS_UNIT_PX: return length->value; - /* We assume the screen and any other output has the same dpi */ - case CSS_UNIT_IN: return length->value * css_screen_dpi; - case CSS_UNIT_CM: return length->value * css_screen_dpi / 2.54; - case CSS_UNIT_MM: return length->value * css_screen_dpi / 25.4; - /* 1pt = 1in/72 */ - case CSS_UNIT_PT: return length->value * css_screen_dpi / 72; - /* 1pc = 1pt * 12 */ - case CSS_UNIT_PC: return length->value * css_screen_dpi / 6; - default: break; - } - return 0; -} - -/** - * Convert a struct css_length to points. - * - * \param length css_length to convert - * \param style css_style applying to length. may be 0 if the length's - * unit is em or ex - * \return length in points - */ - -float css_len2pt(const struct css_length *length, - const struct css_style *style) -{ - assert(!((length->unit == CSS_UNIT_EM || length->unit == CSS_UNIT_EX) && - style == 0)); - switch (length->unit) { - case CSS_UNIT_EM: - return length->value * - css_len2pt(&style->font_size.value.length, 0); - case CSS_UNIT_EX: - return length->value * - css_len2pt(&style->font_size.value.length, 0) * - 0.6; - /* We assume the screen and any other output has the same dpi */ - case CSS_UNIT_PX: return length->value * 72 / css_screen_dpi; - /* 1pt = 1in/72 */ - case CSS_UNIT_IN: return length->value * 72; - case CSS_UNIT_CM: return length->value * 28.452756; - case CSS_UNIT_MM: return length->value * 2.8452756; - case CSS_UNIT_PT: return length->value; - /* 1pc = 1pt * 12 */ - case CSS_UNIT_PC: return length->value * 12.0; - default: break; - } - return 0; -} - -/** - * Return the most 'eyecatching' border. - * - * \return the most eyecatching border, favoured towards test2 - */ - -struct css_border *css_eyecatching_border(struct css_border *test1, - struct css_style *style1, struct css_border *test2, - struct css_style *style2) -{ - float width1, width2; - int impact = 0; - - assert(test1); - assert(style1); - assert(test2); - assert(style2); - - /* hidden border styles always win, none always loses */ - if ((test1->style == CSS_BORDER_STYLE_HIDDEN) || - (test2->style == CSS_BORDER_STYLE_NONE)) - return test1; - if ((test2->style == CSS_BORDER_STYLE_HIDDEN) || - (test1->style == CSS_BORDER_STYLE_NONE)) - return test2; - - /* the widest border wins */ - width1 = css_len2px(&test1->width.value, style1); - width2 = css_len2px(&test2->width.value, style2); - if (width1 > width2) - return test1; - if (width2 > width1) - return test2; - - /* the closest to a solid line wins */ - switch (test1->style) { - case CSS_BORDER_STYLE_DOUBLE: - impact++; - case CSS_BORDER_STYLE_SOLID: - impact++; - case CSS_BORDER_STYLE_DASHED: - impact++; - case CSS_BORDER_STYLE_DOTTED: - impact++; - case CSS_BORDER_STYLE_RIDGE: - impact++; - case CSS_BORDER_STYLE_OUTSET: - impact++; - case CSS_BORDER_STYLE_GROOVE: - impact++; - case CSS_BORDER_STYLE_INSET: - impact++; - default: - break; - } - switch (test2->style) { - case CSS_BORDER_STYLE_DOUBLE: - impact--; - case CSS_BORDER_STYLE_SOLID: - impact--; - case CSS_BORDER_STYLE_DASHED: - impact--; - case CSS_BORDER_STYLE_DOTTED: - impact--; - case CSS_BORDER_STYLE_RIDGE: - impact--; - case CSS_BORDER_STYLE_OUTSET: - impact--; - case CSS_BORDER_STYLE_GROOVE: - impact--; - case CSS_BORDER_STYLE_INSET: - impact--; - default: - break; - } - if (impact > 0) - return test1; - return test2; -} - - diff --git a/css/css.h b/css/css.h index 11aa6a195..e20d2dd9f 100644 --- a/css/css.h +++ b/css/css.h @@ -1,6 +1,5 @@ /* - * Copyright 2004 James Bursa - * Copyright 2004 John M Bell + * Copyright 2009 John-Mark Bell * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -17,700 +16,34 @@ * along with this program. If not, see . */ -/** \file - * CSS handling (interface). - * - * This module aims to implement CSS 2.1. - * - * CSS stylesheets are held in a struct ::content with type CONTENT_CSS. - * Creation and parsing should be carried out via the content_* functions. - * - * Styles are stored in a struct ::css_style, which can be retrieved from a - * content using css_get_style(). - * - * css_parse_property_list() constructs a struct ::css_style from a CSS - * property list, as found in HTML style attributes. - */ - -#ifndef _NETSURF_CSS_CSS_H_ -#define _NETSURF_CSS_CSS_H_ - -#include -#include -#include -#include "css/css_enum.h" - - -typedef uint32_t colour; /* 0xbbggrr */ -#define NS_TRANSPARENT 0x1000000 -#define CSS_COLOR_INHERIT 0x2000000 -#define CSS_COLOR_NONE 0x3000000 -#define CSS_COLOR_NOT_SET 0x4000000 -#define TOP 0 -#define RIGHT 1 -#define BOTTOM 2 -#define LEFT 3 -#define CSS_SPECIFICITY_UA 0x0000000 -#define CSS_SPECIFICITY_USER 0x1000000 -#define CSS_SPECIFICITY_AUTHOR 0x2000000 -#define CSS_SPECIFICITY_ID 0x10000 -#define CSS_SPECIFICITY_CLASS 0x100 -#define CSS_SPECIFICITY_ATTR 0x100 -#define CSS_SPECIFICITY_ELEMENT 0x1 - - -struct css_working_stylesheet; - - -/** Representation of a CSS 2 length. */ -struct css_length { - float value; - css_unit unit; -}; - -typedef enum { - CSS_TEXT_DECORATION_NONE = 0x0, - CSS_TEXT_DECORATION_INHERIT = 0x1, - CSS_TEXT_DECORATION_UNDERLINE = 0x2, - CSS_TEXT_DECORATION_BLINK = 0x4, - CSS_TEXT_DECORATION_LINE_THROUGH = 0x8, - CSS_TEXT_DECORATION_OVERLINE = 0x10, - CSS_TEXT_DECORATION_UNKNOWN = 0x1000, - CSS_TEXT_DECORATION_NOT_SET = 0x2000 -} css_text_decoration; - -typedef enum { - CSS_BACKGROUND_IMAGE_NONE, - CSS_BACKGROUND_IMAGE_INHERIT, - CSS_BACKGROUND_IMAGE_URI, - CSS_BACKGROUND_IMAGE_NOT_SET -} css_background_image_type; - -/** Part of struct css_style, for convenience. */ -struct css_background_position { - enum { - CSS_BACKGROUND_POSITION_LENGTH, - CSS_BACKGROUND_POSITION_PERCENT, - CSS_BACKGROUND_POSITION_INHERIT, - CSS_BACKGROUND_POSITION_NOT_SET - } pos; - union { - float percent; - struct css_length length; - } value; -}; - -struct css_border_width { - enum { CSS_BORDER_WIDTH_INHERIT, - CSS_BORDER_WIDTH_LENGTH, - CSS_BORDER_WIDTH_NOT_SET } width; - struct css_length value; -}; - -struct css_border { - colour color; - struct css_border_width width; - css_border_style style; -}; - -typedef enum { - CSS_CONTENT_STRING, - CSS_CONTENT_URI, - CSS_CONTENT_COUNTER, - CSS_CONTENT_ATTR, - CSS_CONTENT_OPEN_QUOTE, - CSS_CONTENT_CLOSE_QUOTE, - CSS_CONTENT_NO_OPEN_QUOTE, - CSS_CONTENT_NO_CLOSE_QUOTE -} css_content_type_generated; - -typedef enum { - CSS_LIST_STYLE_IMAGE_INHERIT, - CSS_LIST_STYLE_IMAGE_NONE, - CSS_LIST_STYLE_IMAGE_URI, - CSS_LIST_STYLE_IMAGE_NOT_SET -} css_list_style_image_type; - -typedef enum { - CSS_OUTLINE_COLOR_INHERIT, - CSS_OUTLINE_COLOR_COLOR, - CSS_OUTLINE_COLOR_INVERT, - CSS_OUTLINE_COLOR_NOT_SET -} css_outline_color_type; - -typedef enum { - CSS_VERTICAL_ALIGN_INHERIT, - CSS_VERTICAL_ALIGN_BASELINE, - CSS_VERTICAL_ALIGN_SUB, - CSS_VERTICAL_ALIGN_SUPER, - CSS_VERTICAL_ALIGN_TOP, - CSS_VERTICAL_ALIGN_TEXT_TOP, - CSS_VERTICAL_ALIGN_MIDDLE, - CSS_VERTICAL_ALIGN_BOTTOM, - CSS_VERTICAL_ALIGN_TEXT_BOTTOM, - CSS_VERTICAL_ALIGN_LENGTH, - CSS_VERTICAL_ALIGN_PERCENT, - CSS_VERTICAL_ALIGN_NOT_SET -} css_vertical_align_type; - -typedef enum { - CSS_FONT_SIZE_INHERIT, - CSS_FONT_SIZE_ABSOLUTE, - CSS_FONT_SIZE_LENGTH, - CSS_FONT_SIZE_PERCENT, - CSS_FONT_SIZE_NOT_SET -} css_font_size_type; - -struct css_counter_control { - char *name; - int value; - struct css_counter_control *next; -}; - -struct css_counter { - char *name; - css_list_style_type style; - char *separator; /** NULL for counter() */ -}; - -struct css_content { - css_content_type_generated type; - union { - char *string; - char *uri; - struct css_counter counter; - char *attr; - } data; - struct css_content *next; -}; - -/** Representation of a complete CSS 2 style. */ -struct css_style { - /* background properties */ - css_background_attachment background_attachment; - colour background_color; - struct { - css_background_image_type type; - char *uri; - } background_image; - struct { - struct css_background_position horz; - struct css_background_position vert; - } background_position; - css_background_repeat background_repeat; - - /* borders */ - struct css_border border[4]; /**< top, right, bottom, left */ - css_border_collapse border_collapse; - struct { - enum { CSS_BORDER_SPACING_INHERIT, - CSS_BORDER_SPACING_LENGTH, - CSS_BORDER_SPACING_NOT_SET } border_spacing; - struct css_length horz; - struct css_length vert; - } border_spacing; - - css_caption_side caption_side; - css_clear clear; - - struct { - enum { CSS_CLIP_INHERIT, - CSS_CLIP_AUTO, - CSS_CLIP_RECT, - CSS_CLIP_NOT_SET } clip; - struct { - enum { CSS_CLIP_RECT_AUTO, - CSS_CLIP_RECT_LENGTH } rect; - struct css_length value; - } rect[4]; /**< top, right, bottom, left */ - } clip; - - colour color; - - /* generated content */ - struct { - enum { - CSS_CONTENT_NORMAL, - CSS_CONTENT_INHERIT, - CSS_CONTENT_INTERPRET, - CSS_CONTENT_NOT_SET } type; - struct css_content *content; - } content; - - /* counter controls */ - struct { - enum { - CSS_COUNTER_RESET_NONE, - CSS_COUNTER_RESET_INHERIT, - CSS_COUNTER_RESET_INTERPRET, - CSS_COUNTER_RESET_NOT_SET } type; - struct css_counter_control *data; - } counter_reset; - struct { - enum { - CSS_COUNTER_INCREMENT_NONE, - CSS_COUNTER_INCREMENT_INHERIT, - CSS_COUNTER_INCREMENT_INTERPRET, - CSS_COUNTER_INCREMENT_NOT_SET } type; - struct css_counter_control *data; - } counter_increment; - - css_cursor cursor; - css_direction direction; - css_display display; - css_empty_cells empty_cells; - css_float float_; - - /* font properties */ - css_font_family font_family; - struct { - css_font_size_type size; - union { - struct css_length length; - float absolute; - float percent; - } value; - } font_size; - css_font_style font_style; - css_font_variant font_variant; - css_font_weight font_weight; - - struct { - enum { CSS_HEIGHT_INHERIT, - CSS_HEIGHT_AUTO, - CSS_HEIGHT_LENGTH, - CSS_HEIGHT_PERCENT, - CSS_HEIGHT_NOT_SET } height; - union { - struct css_length length; - float percent; - } value; - } height; - - struct { - enum { CSS_LETTER_SPACING_INHERIT, - CSS_LETTER_SPACING_NORMAL, - CSS_LETTER_SPACING_LENGTH, - CSS_LETTER_SPACING_NOT_SET } letter_spacing; - struct css_length length; - } letter_spacing; - - struct { - enum { CSS_LINE_HEIGHT_INHERIT, - CSS_LINE_HEIGHT_ABSOLUTE, - CSS_LINE_HEIGHT_LENGTH, - CSS_LINE_HEIGHT_PERCENT, - CSS_LINE_HEIGHT_NOT_SET } size; - union { - float absolute; - struct css_length length; - float percent; - } value; - } line_height; - - /* list properties */ - struct { - css_list_style_image_type type; - char *uri; - } list_style_image; - css_list_style_position list_style_position; - css_list_style_type list_style_type; - - /* margins */ - struct { - enum { CSS_MARGIN_INHERIT, - CSS_MARGIN_LENGTH, - CSS_MARGIN_PERCENT, - CSS_MARGIN_AUTO, - CSS_MARGIN_NOT_SET } margin; - union { - struct css_length length; - float percent; - } value; - } margin[4]; /**< top, right, bottom, left */ - - /* min/max width/height */ - struct { - enum { CSS_MAX_HEIGHT_INHERIT, - CSS_MAX_HEIGHT_NONE, - CSS_MAX_HEIGHT_LENGTH, - CSS_MAX_HEIGHT_PERCENT, - CSS_MAX_HEIGHT_NOT_SET } max_height; - union { - struct css_length length; - float percent; - } value; - } max_height; - struct { - enum { CSS_MAX_WIDTH_INHERIT, - CSS_MAX_WIDTH_NONE, - CSS_MAX_WIDTH_LENGTH, - CSS_MAX_WIDTH_PERCENT, - CSS_MAX_WIDTH_NOT_SET } max_width; - union { - struct css_length length; - float percent; - } value; - } max_width; - struct { - enum { CSS_MIN_HEIGHT_INHERIT, - CSS_MIN_HEIGHT_LENGTH, - CSS_MIN_HEIGHT_PERCENT, - CSS_MIN_HEIGHT_NOT_SET } min_height; - union { - struct css_length length; - float percent; - } value; - } min_height; - struct { - enum { CSS_MIN_WIDTH_INHERIT, - CSS_MIN_WIDTH_LENGTH, - CSS_MIN_WIDTH_PERCENT, - CSS_MIN_WIDTH_NOT_SET } min_width; - union { - struct css_length length; - float percent; - } value; - } min_width; - - struct { - enum { CSS_ORPHANS_INHERIT, - CSS_ORPHANS_INTEGER, - CSS_ORPHANS_NOT_SET } orphans; - int value; - } orphans; - - struct { - struct { - css_outline_color_type color; - colour value; - } color; - struct css_border_width width; - css_border_style style; - } outline; - - css_overflow overflow; - - /* padding */ - struct { - enum { CSS_PADDING_INHERIT, - CSS_PADDING_LENGTH, - CSS_PADDING_PERCENT, - CSS_PADDING_NOT_SET } padding; - union { - struct css_length length; - float percent; - } value; - } padding[4]; /**< top, right, bottom, left */ +#ifndef netsurf_css_css_h_ +#define netsurf_css_css_h_ - css_page_break_after page_break_after; - css_page_break_before page_break_before; - css_page_break_inside page_break_inside; +#include - struct { - enum { CSS_POS_INHERIT, - CSS_POS_AUTO, - CSS_POS_PERCENT, - CSS_POS_LENGTH, - CSS_POS_NOT_SET } pos; - union { - struct css_length length; - float percent; - } value; - } pos[4]; /**< top, right, bottom, left */ - - css_position position; - - /** \todo quotes */ - - css_table_layout table_layout; - - /* text properties */ - css_text_align text_align; - css_text_decoration text_decoration; - struct { - enum { CSS_TEXT_INDENT_INHERIT, - CSS_TEXT_INDENT_LENGTH, - CSS_TEXT_INDENT_PERCENT, - CSS_TEXT_INDENT_NOT_SET } size; - union { - struct css_length length; - float percent; - } value ; - } text_indent; - css_text_transform text_transform; - - css_unicode_bidi unicode_bidi; - - struct { - css_vertical_align_type type; - union { - struct css_length length; - float percent; - } value; - } vertical_align; - - css_visibility visibility; - - css_white_space white_space; - - struct { - enum { CSS_WIDOWS_INHERIT, - CSS_WIDOWS_INTEGER, - CSS_WIDOWS_NOT_SET } widows; - int value; - } widows; - - struct { - enum { CSS_WIDTH_INHERIT, - CSS_WIDTH_AUTO, - CSS_WIDTH_LENGTH, - CSS_WIDTH_PERCENT, - CSS_WIDTH_NOT_SET } width; - union { - struct css_length length; - float percent; - } value; - } width; - - struct { - enum { CSS_WORD_SPACING_INHERIT, - CSS_WORD_SPACING_NORMAL, - CSS_WORD_SPACING_LENGTH, - CSS_WORD_SPACING_NOT_SET } word_spacing; - struct css_length length; - } word_spacing; - - struct { - enum { CSS_Z_INDEX_INHERIT, - CSS_Z_INDEX_AUTO, - CSS_Z_INDEX_INTEGER, - CSS_Z_INDEX_NOT_SET } z_index; - int value; - } z_index; -}; - -/** Author level CSS importance info for properties that may be set in HTML */ -struct css_importance { - /* background properties */ - bool background_color; - bool background_image; - - /* borders */ - bool border_style[4]; /**< top, right, bottom, left */ - bool border_color[4]; - bool border_width[4]; - bool border_spacing; - - bool color; - - bool height; - - /* margins */ - bool margin[4]; /**< top, right, bottom, left */ - - /* padding */ - bool padding[4]; /**< top, right, bottom, left */ - - bool width; -}; - -struct css_stylesheet; - -typedef enum { - CSS_ORIGIN_AUTHOR, - CSS_ORIGIN_USER, - CSS_ORIGIN_UA -} css_origin; - -/** Data specific to CONTENT_CSS. */ -struct content_css_data { - struct css_stylesheet *css; /**< Opaque stylesheet data. */ - unsigned int import_count; /**< Number of entries in import_url. */ - struct content **import_content; /**< Imported stylesheet contents. */ - css_origin origin; /**< Origin of stylesheet. */ -}; - - -extern const struct css_style css_base_style; -extern const struct css_style css_empty_style; -extern const struct css_style css_blank_style; -extern float css_screen_dpi; - - -#ifdef CSS_INTERNALS - -/** Type of a css_selector. */ -typedef enum { - CSS_SELECTOR_ELEMENT, - CSS_SELECTOR_ID, - CSS_SELECTOR_CLASS, - CSS_SELECTOR_ATTRIB, - CSS_SELECTOR_ATTRIB_EQ, - CSS_SELECTOR_ATTRIB_INC, - CSS_SELECTOR_ATTRIB_DM, - CSS_SELECTOR_ATTRIB_PRE, - CSS_SELECTOR_ATTRIB_SUF, - CSS_SELECTOR_ATTRIB_SUB, - CSS_SELECTOR_PSEUDO, -} css_selector_type; - -/** Relationship to combiner in a css_selector. */ -typedef enum { - CSS_COMB_NONE, - CSS_COMB_ANCESTOR, - CSS_COMB_PARENT, - CSS_COMB_PRECEDED, -} css_combinator; - -/** Representation of a CSS selector. */ -struct css_selector { - css_selector_type type; - const char *data; - unsigned int data_length; - const char *data2; - unsigned int data2_length; - struct css_selector *detail; - struct css_selector *combiner; - struct css_selector *next; - css_combinator comb; - struct css_style *style; - unsigned long specificity; -}; - -/** Type of a css_node. */ -typedef enum { - CSS_NODE_DECLARATION, - CSS_NODE_IDENT, - CSS_NODE_NUMBER, - CSS_NODE_PERCENTAGE, - CSS_NODE_DIMENSION, - CSS_NODE_STRING, - CSS_NODE_DELIM, - CSS_NODE_URI, - CSS_NODE_HASH, - CSS_NODE_UNICODE_RANGE, - CSS_NODE_INCLUDES, - CSS_NODE_FUNCTION, - CSS_NODE_DASHMATCH, - CSS_NODE_PREFIX, - CSS_NODE_SUFFIX, - CSS_NODE_SUBSTR, - CSS_NODE_COLON, - CSS_NODE_COMMA, - CSS_NODE_DOT, - CSS_NODE_PLUS, - CSS_NODE_GT, - CSS_NODE_PAREN, - CSS_NODE_BRAC, -} css_node_type; - -/** A node in a CSS parse tree. */ -struct css_node { - css_node_type type; - const char *data; - unsigned int data_length; - struct css_node *value; - struct css_node *next; - css_combinator comb; - struct css_style *style; - unsigned long specificity; - struct content *stylesheet; -}; - - -#define HASH_SIZE (47 + 1) +struct content; -/** Representation of a CSS 2 style sheet. */ -struct css_stylesheet { - struct css_selector *rule[HASH_SIZE]; -}; +/** + * CSS content data + */ +struct content_css_data +{ + lwc_context *dict; /**< Dictionary to intern strings in */ -/** Parameters to and results from the CSS parser. */ -struct css_parser_params { - bool ruleset_only; - struct content *stylesheet; - struct css_node *declaration; - bool syntax_error; - bool memory_error; - bool had_ruleset; -}; + css_stylesheet *sheet; /**< Stylesheet object */ -/** Token type for the CSS parser. */ -struct css_parser_token { - const char *text; - unsigned int length; + uint32_t import_count; /**< Number of sheets imported */ + struct content **imports; /**< Array of imported sheets */ }; -#endif - - -struct content; +bool nscss_create(struct content *c, struct content *parent, + const char *params[]); -bool css_convert(struct content *c, int width, int height); -void css_destroy(struct content *c); +bool nscss_process_data(struct content *c, char *data, unsigned int size); -#ifdef CSS_INTERNALS +bool nscss_convert(struct content *c, int w, int h); -struct css_node * css_new_node(struct content *stylesheet, - css_node_type type, - const char *data, unsigned int data_length); -void css_free_node(struct css_node *node); -struct css_selector * css_new_selector(css_selector_type type, - const char *data, unsigned int data_length); -void css_free_selector(struct css_selector *node); -void css_atimport(struct content *c, struct css_node *node); -void css_add_ruleset(struct content *c, - struct css_selector *selector, - struct css_node *declaration); -void css_add_declarations(struct css_style *style, - struct css_node *declaration); -unsigned int css_hash(const char *s, int length); - -int css_tokenise(unsigned char **buffer, unsigned char *end, - unsigned char **token_text); - -void css_parser_Trace(FILE *TraceFILE, char *zTracePrompt); -void *css_parser_Alloc(void *(*mallocProc)(size_t)); -void css_parser_Free(void *p, void (*freeProc)(void*)); -void css_parser_(void *yyp, int yymajor, struct css_parser_token yyminor, - struct css_parser_params *param); -const char *css_parser_TokenName(int tokenType); +void nscss_destroy(struct content *c); #endif -void css_set_origin(struct content *c, css_origin origin); -struct css_working_stylesheet *css_make_working_stylesheet( - struct content **stylesheet_content, - unsigned int stylesheet_count); -void css_get_style(struct css_working_stylesheet *working_stylesheet, - xmlNode *element, struct css_style *style, - struct css_importance *author); -struct css_style *css_duplicate_style(const struct css_style * const style); -void css_free_style(struct css_style *style); -void css_deep_free_content(struct css_content *content); -void css_deep_free_counter_control(struct css_counter_control *control); -void css_cascade(struct css_style * const style, - const struct css_style * const apply, - struct css_importance * const author); -void css_merge(struct css_style * const style, - const struct css_style * const apply, - const unsigned long specificity, - struct css_importance * const author); -void css_parse_property_list(struct content *c, struct css_style * style, - char * str); -colour named_colour(const char *name); -colour hex_colour(const char *text, int length); - -void css_dump_style(FILE *stream, const struct css_style * const style); -void css_dump_stylesheet(const struct css_stylesheet * stylesheet); - -float css_len2px(const struct css_length *length, - const struct css_style *style); -float css_len2pt(const struct css_length *length, - const struct css_style *style); -struct css_border *css_eyecatching_border(struct css_border *test1, - struct css_style *style1, struct css_border *test2, - struct css_style *style2); - -#endif diff --git a/css/css_enums b/css/css_enums deleted file mode 100644 index ff41f08b1..000000000 --- a/css/css_enums +++ /dev/null @@ -1,29 +0,0 @@ -css_unit em ex px in cm mm pt pc -css_background_attachment inherit fixed scroll -css_background_repeat inherit repeat repeat-x repeat-y no-repeat -css_border_collapse inherit collapse separate -css_border_style inherit none hidden dotted dashed solid double groove ridge inset outset -css_caption_side inherit top bottom -css_clear inherit none both left right -css_cursor inherit auto crosshair default pointer move e-resize ne-resize nw-resize n-resize se-resize sw-resize s-resize w-resize text wait help no-drop not-allowed progress -css_direction inherit ltr rtl -css_display inherit inline block list-item run-in inline-block table inline-table table-row-group table-header-group table-footer-group table-row table-column-group table-column table-cell table-caption none -css_empty_cells inherit show hide -css_float inherit none left right -css_font_family inherit sans-serif serif monospace cursive fantasy -css_font_style inherit normal italic oblique -css_font_variant inherit normal small-caps -css_font_weight inherit normal bold bolder lighter 100 200 300 400 500 600 700 800 900 -css_list_style_position inherit outside inside -css_list_style_type inherit disc circle square decimal lower-alpha lower-roman upper-alpha upper-roman none -css_overflow inherit visible hidden scroll auto -css_page_break_after inherit auto always avoid left right -css_page_break_before inherit auto always avoid left right -css_page_break_inside inherit avoid auto -css_position inherit static relative absolute fixed -css_table_layout inherit auto fixed -css_text_align inherit left right center justify -css_text_transform inherit none capitalize lowercase uppercase -css_unicode_bidi inherit normal embed bidi-override -css_visibility inherit visible hidden collapse -css_white_space inherit normal nowrap pre pre-wrap pre-line diff --git a/css/dump.c b/css/dump.c new file mode 100644 index 000000000..430471382 --- /dev/null +++ b/css/dump.c @@ -0,0 +1,1791 @@ +/* + * Copyright 2009 John-Mark Bell + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "css/dump.h" + +static void dump_css_fixed(FILE *stream, css_fixed f); +static void dump_css_number(FILE *stream, css_fixed val); +static void dump_css_unit(FILE *stream, css_fixed val, css_unit unit); + +/** + * Dump a computed style \a style to the give file handle \a stream. + * + * \param stream Stream to write to + * \param style Computed style to dump + */ +void nscss_dump_computed_style(FILE *stream, const css_computed_style *style) +{ + uint8_t val; + css_color color = 0; + lwc_string *url = NULL; + css_fixed len1 = 0, len2 = 0; + css_unit unit1 = CSS_UNIT_PX, unit2 = CSS_UNIT_PX; + css_computed_clip_rect rect = { 0, 0, 0, 0, CSS_UNIT_PX, CSS_UNIT_PX, + CSS_UNIT_PX, CSS_UNIT_PX, true, true, + true, true }; + const css_computed_content_item *content = NULL; + const css_computed_counter *counter = NULL; + lwc_string **string_list = NULL; + int32_t zindex = 0; + + fprintf(stream, "{ "); + + /* background-attachment */ + val = css_computed_background_attachment(style); + switch (val) { + case CSS_BACKGROUND_ATTACHMENT_FIXED: + fprintf(stream, "background-attachment: fixed "); + break; + case CSS_BACKGROUND_ATTACHMENT_SCROLL: + fprintf(stream, "background-attachment: scroll "); + break; + default: + break; + } + + /* background-color */ + val = css_computed_background_color(style, &color); + switch (val) { + case CSS_BACKGROUND_COLOR_TRANSPARENT: + fprintf(stream, "background-color: transparent "); + break; + case CSS_BACKGROUND_COLOR_COLOR: + fprintf(stream, "background-color: #%08x ", color); + break; + default: + break; + } + + /* background-image */ + val = css_computed_background_image(style, &url); + if (val == CSS_BACKGROUND_IMAGE_IMAGE && url != NULL) { + fprintf(stream, "background-image: url('%.*s') ", + (int) lwc_string_length(url), + lwc_string_data(url)); + } else if (val == CSS_BACKGROUND_IMAGE_NONE) { + fprintf(stream, "background-image: none "); + } + + /* background-position */ + val = css_computed_background_position(style, &len1, &unit1, + &len2, &unit2); + if (val == CSS_BACKGROUND_POSITION_SET) { + fprintf(stream, "background-position: "); + dump_css_unit(stream, len1, unit1); + fprintf(stream, " "); + dump_css_unit(stream, len2, unit2); + fprintf(stream, " "); + } + + /* background-repeat */ + val = css_computed_background_repeat(style); + switch (val) { + case CSS_BACKGROUND_REPEAT_REPEAT_X: + fprintf(stream, "background-repeat: repeat-x "); + break; + case CSS_BACKGROUND_REPEAT_REPEAT_Y: + fprintf(stream, "background-repeat: repeat-y "); + break; + case CSS_BACKGROUND_REPEAT_REPEAT: + fprintf(stream, "background-repeat: repeat "); + break; + case CSS_BACKGROUND_REPEAT_NO_REPEAT: + fprintf(stream, "background-repeat: no-repeat "); + break; + default: + break; + } + + /* border-collapse */ + val = css_computed_border_collapse(style); + switch (val) { + case CSS_BORDER_COLLAPSE_SEPARATE: + fprintf(stream, "border-collapse: separate "); + break; + case CSS_BORDER_COLLAPSE_COLLAPSE: + fprintf(stream, "border-collapse: collapse "); + break; + default: + + break; + } + + /* border-spacing */ + val = css_computed_border_spacing(style, &len1, &unit1, &len2, &unit2); + if (val == CSS_BORDER_SPACING_SET) { + fprintf(stream, "border-spacing: "); + dump_css_unit(stream, len1, unit1); + fprintf(stream, " "); + dump_css_unit(stream, len2, unit2); + fprintf(stream, " "); + } + + /* border-top-color */ + val = css_computed_border_top_color(style, &color); + switch (val) { + case CSS_BORDER_COLOR_INITIAL: + fprintf(stream, "border-top-color: initial "); + break; + case CSS_BORDER_COLOR_TRANSPARENT: + fprintf(stream, "border-top-color: transparent "); + break; + case CSS_BORDER_COLOR_COLOR: + fprintf(stream, "border-top-color: #%08x ", color); + break; + default: + break; + } + + /* border-right-color */ + val = css_computed_border_right_color(style, &color); + switch (val) { + case CSS_BORDER_COLOR_INITIAL: + fprintf(stream, "border-right-color: initial "); + break; + case CSS_BORDER_COLOR_TRANSPARENT: + fprintf(stream, "border-right-color: transparent "); + break; + case CSS_BORDER_COLOR_COLOR: + fprintf(stream, "border-right-color: #%08x ", color); + break; + default: + break; + } + + /* border-bottom-color */ + val = css_computed_border_bottom_color(style, &color); + switch (val) { + case CSS_BORDER_COLOR_INITIAL: + fprintf(stream, "border-bottom-color: initial "); + break; + case CSS_BORDER_COLOR_TRANSPARENT: + fprintf(stream, "border-bottom-color: transparent "); + break; + case CSS_BORDER_COLOR_COLOR: + fprintf(stream, "border-bottom-color: #%08x ", color); + break; + default: + break; + } + + /* border-left-color */ + val = css_computed_border_left_color(style, &color); + switch (val) { + case CSS_BORDER_COLOR_INITIAL: + fprintf(stream, "border-left-color: initial "); + break; + case CSS_BORDER_COLOR_TRANSPARENT: + fprintf(stream, "border-left-color: transparent "); + break; + case CSS_BORDER_COLOR_COLOR: + fprintf(stream, "border-left-color: #%08x ", color); + break; + default: + break; + } + + /* border-top-style */ + val = css_computed_border_top_style(style); + switch (val) { + case CSS_BORDER_STYLE_NONE: + fprintf(stream, "border-top-style: none "); + break; + case CSS_BORDER_STYLE_HIDDEN: + fprintf(stream, "border-top-style: hidden "); + break; + case CSS_BORDER_STYLE_DOTTED: + fprintf(stream, "border-top-style: dotted "); + break; + case CSS_BORDER_STYLE_DASHED: + fprintf(stream, "border-top-style: dashed "); + break; + case CSS_BORDER_STYLE_SOLID: + fprintf(stream, "border-top-style: solid "); + break; + case CSS_BORDER_STYLE_DOUBLE: + fprintf(stream, "border-top-style: double "); + break; + case CSS_BORDER_STYLE_GROOVE: + fprintf(stream, "border-top-style: groove "); + break; + case CSS_BORDER_STYLE_RIDGE: + fprintf(stream, "border-top-style: ridge "); + break; + case CSS_BORDER_STYLE_INSET: + fprintf(stream, "border-top-style: inset "); + break; + case CSS_BORDER_STYLE_OUTSET: + fprintf(stream, "border-top-style: outset "); + break; + default: + break; + } + + /* border-right-style */ + val = css_computed_border_right_style(style); + switch (val) { + case CSS_BORDER_STYLE_NONE: + fprintf(stream, "border-right-style: none "); + break; + case CSS_BORDER_STYLE_HIDDEN: + fprintf(stream, "border-right-style: hidden "); + break; + case CSS_BORDER_STYLE_DOTTED: + fprintf(stream, "border-right-style: dotted "); + break; + case CSS_BORDER_STYLE_DASHED: + fprintf(stream, "border-right-style: dashed "); + break; + case CSS_BORDER_STYLE_SOLID: + fprintf(stream, "border-right-style: solid "); + break; + case CSS_BORDER_STYLE_DOUBLE: + fprintf(stream, "border-right-style: double "); + break; + case CSS_BORDER_STYLE_GROOVE: + fprintf(stream, "border-right-style: groove "); + break; + case CSS_BORDER_STYLE_RIDGE: + fprintf(stream, "border-right-style: ridge "); + break; + case CSS_BORDER_STYLE_INSET: + fprintf(stream, "border-right-style: inset "); + break; + case CSS_BORDER_STYLE_OUTSET: + fprintf(stream, "border-right-style: outset "); + break; + default: + break; + } + + /* border-bottom-style */ + val = css_computed_border_bottom_style(style); + switch (val) { + case CSS_BORDER_STYLE_NONE: + fprintf(stream, "border-bottom-style: none "); + break; + case CSS_BORDER_STYLE_HIDDEN: + fprintf(stream, "border-bottom-style: hidden "); + break; + case CSS_BORDER_STYLE_DOTTED: + fprintf(stream, "border-bottom-style: dotted "); + break; + case CSS_BORDER_STYLE_DASHED: + fprintf(stream, "border-bottom-style: dashed "); + break; + case CSS_BORDER_STYLE_SOLID: + fprintf(stream, "border-bottom-style: solid "); + break; + case CSS_BORDER_STYLE_DOUBLE: + fprintf(stream, "border-bottom-style: double "); + break; + case CSS_BORDER_STYLE_GROOVE: + fprintf(stream, "border-bottom-style: groove "); + break; + case CSS_BORDER_STYLE_RIDGE: + fprintf(stream, "border-bottom-style: ridge "); + break; + case CSS_BORDER_STYLE_INSET: + fprintf(stream, "border-bottom-style: inset "); + break; + case CSS_BORDER_STYLE_OUTSET: + fprintf(stream, "border-bottom-style: outset "); + break; + default: + break; + } + + /* border-left-style */ + val = css_computed_border_left_style(style); + switch (val) { + case CSS_BORDER_STYLE_NONE: + fprintf(stream, "border-left-style: none "); + break; + case CSS_BORDER_STYLE_HIDDEN: + fprintf(stream, "border-left-style: hidden "); + break; + case CSS_BORDER_STYLE_DOTTED: + fprintf(stream, "border-left-style: dotted "); + break; + case CSS_BORDER_STYLE_DASHED: + fprintf(stream, "border-left-style: dashed "); + break; + case CSS_BORDER_STYLE_SOLID: + fprintf(stream, "border-left-style: solid "); + break; + case CSS_BORDER_STYLE_DOUBLE: + fprintf(stream, "border-left-style: double "); + break; + case CSS_BORDER_STYLE_GROOVE: + fprintf(stream, "border-left-style: groove "); + break; + case CSS_BORDER_STYLE_RIDGE: + fprintf(stream, "border-left-style: ridge "); + break; + case CSS_BORDER_STYLE_INSET: + fprintf(stream, "border-left-style: inset "); + break; + case CSS_BORDER_STYLE_OUTSET: + fprintf(stream, "border-left-style: outset "); + break; + default: + break; + } + + /* border-top-width */ + val = css_computed_border_top_width(style, &len1, &unit1); + switch (val) { + case CSS_BORDER_WIDTH_THIN: + fprintf(stream, "border-top-width: thin "); + break; + case CSS_BORDER_WIDTH_MEDIUM: + fprintf(stream, "border-top-width: medium "); + break; + case CSS_BORDER_WIDTH_THICK: + fprintf(stream, "border-top-width: thick "); + break; + case CSS_BORDER_WIDTH_WIDTH: + fprintf(stream, "border-top-width: "); + dump_css_unit(stream, len1, unit1); + fprintf(stream, " "); + break; + default: + break; + } + + /* border-right-width */ + val = css_computed_border_right_width(style, &len1, &unit1); + switch (val) { + case CSS_BORDER_WIDTH_THIN: + fprintf(stream, "border-right-width: thin "); + break; + case CSS_BORDER_WIDTH_MEDIUM: + fprintf(stream, "border-right-width: medium "); + break; + case CSS_BORDER_WIDTH_THICK: + fprintf(stream, "border-right-width: thick "); + break; + case CSS_BORDER_WIDTH_WIDTH: + fprintf(stream, "border-right-width: "); + dump_css_unit(stream, len1, unit1); + fprintf(stream, " "); + break; + default: + break; + } + + /* border-bottom-width */ + val = css_computed_border_bottom_width(style, &len1, &unit1); + switch (val) { + case CSS_BORDER_WIDTH_THIN: + fprintf(stream, "border-bottom-width: thin "); + break; + case CSS_BORDER_WIDTH_MEDIUM: + fprintf(stream, "border-bottom-width: medium "); + break; + case CSS_BORDER_WIDTH_THICK: + fprintf(stream, "border-bottom-width: thick "); + break; + case CSS_BORDER_WIDTH_WIDTH: + fprintf(stream, "border-bottom-width: "); + dump_css_unit(stream, len1, unit1); + fprintf(stream, " "); + break; + default: + break; + } + + /* border-left-width */ + val = css_computed_border_left_width(style, &len1, &unit1); + switch (val) { + case CSS_BORDER_WIDTH_THIN: + fprintf(stream, "border-left-width: thin "); + break; + case CSS_BORDER_WIDTH_MEDIUM: + fprintf(stream, "border-left-width: medium "); + break; + case CSS_BORDER_WIDTH_THICK: + fprintf(stream, "border-left-width: thick "); + break; + case CSS_BORDER_WIDTH_WIDTH: + fprintf(stream, "border-left-width: "); + dump_css_unit(stream, len1, unit1); + fprintf(stream, " "); + break; + default: + break; + } + + /* bottom */ + val = css_computed_bottom(style, &len1, &unit1); + switch (val) { + case CSS_BOTTOM_AUTO: + fprintf(stream, "bottom: auto "); + break; + case CSS_BOTTOM_SET: + fprintf(stream, "bottom: "); + dump_css_unit(stream, len1, unit1); + fprintf(stream, " "); + break; + default: + break; + } + + /* caption-side */ + val = css_computed_caption_side(style); + switch (val) { + case CSS_CAPTION_SIDE_TOP: + fprintf(stream, "caption_side: top "); + break; + case CSS_CAPTION_SIDE_BOTTOM: + fprintf(stream, "caption_side: bottom "); + break; + default: + break; + } + + /* clear */ + val = css_computed_clear(style); + switch (val) { + case CSS_CLEAR_NONE: + fprintf(stream, "clear: none "); + break; + case CSS_CLEAR_LEFT: + fprintf(stream, "clear: left "); + break; + case CSS_CLEAR_RIGHT: + fprintf(stream, "clear: right "); + break; + case CSS_CLEAR_BOTH: + fprintf(stream, "clear: both "); + break; + default: + break; + } + + /* clip */ + val = css_computed_clip(style, &rect); + switch (val) { + case CSS_CLIP_AUTO: + fprintf(stream, "clip: auto "); + break; + case CSS_CLIP_RECT: + fprintf(stream, "clip: rect( "); + + if (rect.top_auto) + fprintf(stream, "auto"); + else + dump_css_unit(stream, rect.top, rect.tunit); + fprintf(stream, ", "); + + if (rect.right_auto) + fprintf(stream, "auto"); + else + dump_css_unit(stream, rect.right, rect.runit); + fprintf(stream, ", "); + + if (rect.bottom_auto) + fprintf(stream, "auto"); + else + dump_css_unit(stream, rect.bottom, rect.bunit); + fprintf(stream, ", "); + + if (rect.left_auto) + fprintf(stream, "auto"); + else + dump_css_unit(stream, rect.left, rect.lunit); + fprintf(stream, ") "); + break; + default: + break; + } + + /* color */ + val = css_computed_color(style, &color); + if (val == CSS_COLOR_COLOR) { + fprintf(stream, "color: #%08x ", color); + } + + /* content */ + val = css_computed_content(style, &content); + switch (val) { + case CSS_CONTENT_NONE: + fprintf(stream, "content: none "); + break; + case CSS_CONTENT_NORMAL: + fprintf(stream, "content: normal "); + break; + case CSS_CONTENT_SET: + fprintf(stream, "content:"); + + while (content->type != CSS_COMPUTED_CONTENT_NONE) { + fprintf(stream, " "); + + switch (content->type) { + case CSS_COMPUTED_CONTENT_STRING: + fprintf(stream, "\"%.*s\"", + (int) lwc_string_length( + content->data.string), + lwc_string_data( + content->data.string)); + break; + case CSS_COMPUTED_CONTENT_URI: + fprintf(stream, "uri(\"%.*s\")", + (int) lwc_string_length( + content->data.uri), + lwc_string_data( + content->data.uri)); + break; + case CSS_COMPUTED_CONTENT_COUNTER: + fprintf(stream, "counter(%.*s)", + (int) lwc_string_length( + content->data.counter.name), + lwc_string_data( + content->data.counter.name)); + break; + case CSS_COMPUTED_CONTENT_COUNTERS: + fprintf(stream, "counters(%.*s, \"%.*s\")", + (int) lwc_string_length( + content->data.counters.name), + lwc_string_data( + content->data.counters.name), + (int) lwc_string_length( + content->data.counters.sep), + lwc_string_data( + content->data.counters.sep)); + break; + case CSS_COMPUTED_CONTENT_ATTR: + fprintf(stream, "attr(%.*s)", + (int) lwc_string_length( + content->data.attr), + lwc_string_data( + content->data.attr)); + break; + case CSS_COMPUTED_CONTENT_OPEN_QUOTE: + fprintf(stream, "open-quote"); + break; + case CSS_COMPUTED_CONTENT_CLOSE_QUOTE: + fprintf(stream, "close-quote"); + break; + case CSS_COMPUTED_CONTENT_NO_OPEN_QUOTE: + fprintf(stream, "no-open-quote"); + break; + case CSS_COMPUTED_CONTENT_NO_CLOSE_QUOTE: + fprintf(stream, "no-close-quote"); + break; + } + + content++; + } + + fprintf(stream, " "); + break; + default: + break; + } + + /* counter-increment */ + val = css_computed_counter_increment(style, &counter); + if (counter == NULL) { + fprintf(stream, "counter-increment: none "); + } else { + fprintf(stream, "counter-increment:"); + + while (counter->name != NULL) { + fprintf(stream, " %.*s ", + (int) lwc_string_length(counter->name), + lwc_string_data(counter->name)); + + dump_css_fixed(stream, counter->value); + + counter++; + } + + fprintf(stream, " "); + } + + /* counter-reset */ + val = css_computed_counter_reset(style, &counter); + if (counter == NULL) { + fprintf(stream, "counter-reset: none "); + } else { + fprintf(stream, "counter-reset:"); + + while (counter->name != NULL) { + fprintf(stream, " %.*s ", + (int) lwc_string_length(counter->name), + lwc_string_data(counter->name)); + + dump_css_fixed(stream, counter->value); + + counter++; + } + + fprintf(stream, " "); + } + + /* cursor */ + val = css_computed_cursor(style, &string_list); + fprintf(stream, "cursor:"); + + if (string_list != NULL) { + while (*string_list != NULL) { + fprintf(stream, " url\"%.*s\")", + (int) lwc_string_length(*string_list), + lwc_string_data(*string_list)); + + string_list++; + } + } + switch (val) { + case CSS_CURSOR_AUTO: + fprintf(stream, " auto "); + break; + case CSS_CURSOR_CROSSHAIR: + fprintf(stream, " crosshair "); + break; + case CSS_CURSOR_DEFAULT: + fprintf(stream, " default "); + break; + case CSS_CURSOR_POINTER: + fprintf(stream, " pointer "); + break; + case CSS_CURSOR_MOVE: + fprintf(stream, " move "); + break; + case CSS_CURSOR_E_RESIZE: + fprintf(stream, " e-resize "); + break; + case CSS_CURSOR_NE_RESIZE: + fprintf(stream, " ne-resize "); + break; + case CSS_CURSOR_NW_RESIZE: + fprintf(stream, " nw-resize "); + break; + case CSS_CURSOR_N_RESIZE: + fprintf(stream, " n-resize "); + break; + case CSS_CURSOR_SE_RESIZE: + fprintf(stream, " se-resize "); + break; + case CSS_CURSOR_SW_RESIZE: + fprintf(stream, " sw-resize "); + break; + case CSS_CURSOR_S_RESIZE: + fprintf(stream, " s-resize "); + break; + case CSS_CURSOR_W_RESIZE: + fprintf(stream, " w-resize "); + break; + case CSS_CURSOR_TEXT: + fprintf(stream, " text "); + break; + case CSS_CURSOR_WAIT: + fprintf(stream, " wait "); + break; + case CSS_CURSOR_HELP: + fprintf(stream, " help "); + break; + case CSS_CURSOR_PROGRESS: + fprintf(stream, " progress "); + break; + default: + break; + } + + /* direction */ + val = css_computed_direction(style); + switch (val) { + case CSS_DIRECTION_LTR: + fprintf(stream, "direction: ltr "); + break; + case CSS_DIRECTION_RTL: + fprintf(stream, "direction: rtl "); + break; + default: + break; + } + + /* display */ + val = css_computed_display_static(style); + switch (val) { + case CSS_DISPLAY_INLINE: + fprintf(stream, "display: inline "); + break; + case CSS_DISPLAY_BLOCK: + fprintf(stream, "display: block "); + break; + case CSS_DISPLAY_LIST_ITEM: + fprintf(stream, "display: list-item "); + break; + case CSS_DISPLAY_RUN_IN: + fprintf(stream, "display: run-in "); + break; + case CSS_DISPLAY_INLINE_BLOCK: + fprintf(stream, "display: inline-block "); + break; + case CSS_DISPLAY_TABLE: + fprintf(stream, "display: table "); + break; + case CSS_DISPLAY_INLINE_TABLE: + fprintf(stream, "display: inline-table "); + break; + case CSS_DISPLAY_TABLE_ROW_GROUP: + fprintf(stream, "display: table-row-group "); + break; + case CSS_DISPLAY_TABLE_HEADER_GROUP: + fprintf(stream, "display: table-header-group "); + break; + case CSS_DISPLAY_TABLE_FOOTER_GROUP: + fprintf(stream, "display: table-footer-group "); + break; + case CSS_DISPLAY_TABLE_ROW: + fprintf(stream, "display: table-row "); + break; + case CSS_DISPLAY_TABLE_COLUMN_GROUP: + fprintf(stream, "display: table-column-group "); + break; + case CSS_DISPLAY_TABLE_COLUMN: + fprintf(stream, "display: table-column "); + break; + case CSS_DISPLAY_TABLE_CELL: + fprintf(stream, "display: table-cell "); + break; + case CSS_DISPLAY_TABLE_CAPTION: + fprintf(stream, "display: table-caption "); + break; + case CSS_DISPLAY_NONE: + fprintf(stream, "display: none "); + break; + default: + break; + } + + /* empty-cells */ + val = css_computed_empty_cells(style); + switch (val) { + case CSS_EMPTY_CELLS_SHOW: + fprintf(stream, "empty-cells: show "); + break; + case CSS_EMPTY_CELLS_HIDE: + fprintf(stream, "empty-cells: hide "); + break; + default: + break; + } + + /* float */ + val = css_computed_float(style); + switch (val) { + case CSS_FLOAT_LEFT: + fprintf(stream, "float: left "); + break; + case CSS_FLOAT_RIGHT: + fprintf(stream, "float: right "); + break; + case CSS_FLOAT_NONE: + fprintf(stream, "float: none "); + break; + default: + break; + } + + /* font-family */ + val = css_computed_font_family(style, &string_list); + if (val != CSS_FONT_FAMILY_INHERIT) { + fprintf(stream, "font-family:"); + + if (string_list != NULL) { + while (*string_list != NULL) { + fprintf(stream, " \"%.*s\"", + (int) lwc_string_length(*string_list), + lwc_string_data(*string_list)); + + string_list++; + } + } + switch (val) { + case CSS_FONT_FAMILY_SERIF: + fprintf(stream, " serif "); + break; + case CSS_FONT_FAMILY_SANS_SERIF: + fprintf(stream, " sans-serif "); + break; + case CSS_FONT_FAMILY_CURSIVE: + fprintf(stream, " cursive "); + break; + case CSS_FONT_FAMILY_FANTASY: + fprintf(stream, " fantasy "); + break; + case CSS_FONT_FAMILY_MONOSPACE: + fprintf(stream, " monospace "); + break; + } + } + + /* font-size */ + val = css_computed_font_size(style, &len1, &unit1); + switch (val) { + case CSS_FONT_SIZE_XX_SMALL: + fprintf(stream, "font-size: xx-small "); + break; + case CSS_FONT_SIZE_X_SMALL: + fprintf(stream, "font-size: x-small "); + break; + case CSS_FONT_SIZE_SMALL: + fprintf(stream, "font-size: small "); + break; + case CSS_FONT_SIZE_MEDIUM: + fprintf(stream, "font-size: medium "); + break; + case CSS_FONT_SIZE_LARGE: + fprintf(stream, "font-size: large "); + break; + case CSS_FONT_SIZE_X_LARGE: + fprintf(stream, "font-size: x-large "); + break; + case CSS_FONT_SIZE_XX_LARGE: + fprintf(stream, "font-size: xx-large "); + break; + case CSS_FONT_SIZE_LARGER: + fprintf(stream, "font-size: larger "); + break; + case CSS_FONT_SIZE_SMALLER: + fprintf(stream, "font-size: smaller "); + break; + case CSS_FONT_SIZE_DIMENSION: + fprintf(stream, "font-size: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* font-style */ + val = css_computed_font_style(style); + switch (val) { + case CSS_FONT_STYLE_NORMAL: + fprintf(stream, "font-style: normal "); + break; + case CSS_FONT_STYLE_ITALIC: + fprintf(stream, "font-style: italic "); + break; + case CSS_FONT_STYLE_OBLIQUE: + fprintf(stream, "font-style: oblique "); + break; + default: + break; + } + + /* font-variant */ + val = css_computed_font_variant(style); + switch (val) { + case CSS_FONT_VARIANT_NORMAL: + fprintf(stream, "font-variant: normal "); + break; + case CSS_FONT_VARIANT_SMALL_CAPS: + fprintf(stream, "font-variant: small-caps "); + break; + default: + break; + } + + /* font-weight */ + val = css_computed_font_weight(style); + switch (val) { + case CSS_FONT_WEIGHT_NORMAL: + fprintf(stream, "font-weight: normal "); + break; + case CSS_FONT_WEIGHT_BOLD: + fprintf(stream, "font-weight: bold "); + break; + case CSS_FONT_WEIGHT_BOLDER: + fprintf(stream, "font-weight: bolder "); + break; + case CSS_FONT_WEIGHT_LIGHTER: + fprintf(stream, "font-weight: lighter "); + break; + case CSS_FONT_WEIGHT_100: + fprintf(stream, "font-weight: 100 "); + break; + case CSS_FONT_WEIGHT_200: + fprintf(stream, "font-weight: 200 "); + break; + case CSS_FONT_WEIGHT_300: + fprintf(stream, "font-weight: 300 "); + break; + case CSS_FONT_WEIGHT_400: + fprintf(stream, "font-weight: 400 "); + break; + case CSS_FONT_WEIGHT_500: + fprintf(stream, "font-weight: 500 "); + break; + case CSS_FONT_WEIGHT_600: + fprintf(stream, "font-weight: 600 "); + break; + case CSS_FONT_WEIGHT_700: + fprintf(stream, "font-weight: 700 "); + break; + case CSS_FONT_WEIGHT_800: + fprintf(stream, "font-weight: 800 "); + break; + case CSS_FONT_WEIGHT_900: + fprintf(stream, "font-weight: 900 "); + break; + default: + break; + } + + /* height */ + val = css_computed_height(style, &len1, &unit1); + switch (val) { + case CSS_HEIGHT_AUTO: + fprintf(stream, "height: auto "); + break; + case CSS_HEIGHT_SET: + fprintf(stream, "height: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* left */ + val = css_computed_left(style, &len1, &unit1); + switch (val) { + case CSS_LEFT_AUTO: + fprintf(stream, "left: auto "); + break; + case CSS_LEFT_SET: + fprintf(stream, "left: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* letter-spacing */ + val = css_computed_letter_spacing(style, &len1, &unit1); + switch (val) { + case CSS_LETTER_SPACING_NORMAL: + fprintf(stream, "letter-spacing: normal "); + break; + case CSS_LETTER_SPACING_SET: + fprintf(stream, "letter-spacing: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* line-height */ + val = css_computed_line_height(style, &len1, &unit1); + switch (val) { + case CSS_LINE_HEIGHT_NORMAL: + fprintf(stream, "line-height: normal "); + break; + case CSS_LINE_HEIGHT_NUMBER: + fprintf(stream, "line-height: "); + + dump_css_fixed(stream, len1); + + fprintf(stream, " "); + break; + case CSS_LINE_HEIGHT_DIMENSION: + fprintf(stream, "line-height: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* list-style-image */ + val = css_computed_list_style_image(style, &url); + if (url != NULL) { + fprintf(stream, "list-style-image: url('%.*s') ", + (int) lwc_string_length(url), + lwc_string_data(url)); + } else if (val == CSS_LIST_STYLE_IMAGE_NONE) { + fprintf(stream, "list-style-image: none "); + } + + /* list-style-position */ + val = css_computed_list_style_position(style); + switch (val) { + case CSS_LIST_STYLE_POSITION_INSIDE: + fprintf(stream, "list-style-position: inside "); + break; + case CSS_LIST_STYLE_POSITION_OUTSIDE: + fprintf(stream, "list-style-position: outside "); + break; + default: + break; + } + + /* list-style-type */ + val = css_computed_list_style_type(style); + switch (val) { + case CSS_LIST_STYLE_TYPE_DISC: + fprintf(stream, "list-style-type: disc "); + break; + case CSS_LIST_STYLE_TYPE_CIRCLE: + fprintf(stream, "list-style-type: circle "); + break; + case CSS_LIST_STYLE_TYPE_SQUARE: + fprintf(stream, "list-style-type: square "); + break; + case CSS_LIST_STYLE_TYPE_DECIMAL: + fprintf(stream, "list-style-type: decimal "); + break; + case CSS_LIST_STYLE_TYPE_DECIMAL_LEADING_ZERO: + fprintf(stream, "list-style-type: decimal-leading-zero "); + break; + case CSS_LIST_STYLE_TYPE_LOWER_ROMAN: + fprintf(stream, "list-style-type: lower-roman "); + break; + case CSS_LIST_STYLE_TYPE_UPPER_ROMAN: + fprintf(stream, "list-style-type: upper-roman "); + break; + case CSS_LIST_STYLE_TYPE_LOWER_GREEK: + fprintf(stream, "list-style-type: lower-greek "); + break; + case CSS_LIST_STYLE_TYPE_LOWER_LATIN: + fprintf(stream, "list-style-type: lower-latin "); + break; + case CSS_LIST_STYLE_TYPE_UPPER_LATIN: + fprintf(stream, "list-style-type: upper-latin "); + break; + case CSS_LIST_STYLE_TYPE_ARMENIAN: + fprintf(stream, "list-style-type: armenian "); + break; + case CSS_LIST_STYLE_TYPE_GEORGIAN: + fprintf(stream, "list-style-type: georgian "); + break; + case CSS_LIST_STYLE_TYPE_LOWER_ALPHA: + fprintf(stream, "list-style-type: lower-alpha "); + break; + case CSS_LIST_STYLE_TYPE_UPPER_ALPHA: + fprintf(stream, "list-style-type: upper-alpha "); + break; + case CSS_LIST_STYLE_TYPE_NONE: + fprintf(stream, "list-style-type: none "); + break; + default: + break; + } + + /* margin-top */ + val = css_computed_margin_top(style, &len1, &unit1); + switch (val) { + case CSS_MARGIN_AUTO: + fprintf(stream, "margin-top: auto "); + break; + case CSS_MARGIN_SET: + fprintf(stream, "margin-top: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* margin-right */ + val = css_computed_margin_right(style, &len1, &unit1); + switch (val) { + case CSS_MARGIN_AUTO: + fprintf(stream, "margin-right: auto "); + break; + case CSS_MARGIN_SET: + fprintf(stream, "margin-right: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* margin-bottom */ + val = css_computed_margin_bottom(style, &len1, &unit1); + switch (val) { + case CSS_MARGIN_AUTO: + fprintf(stream, "margin-bottom: auto "); + break; + case CSS_MARGIN_SET: + fprintf(stream, "margin-bottom: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* margin-left */ + val = css_computed_margin_left(style, &len1, &unit1); + switch (val) { + case CSS_MARGIN_AUTO: + fprintf(stream, "margin-left: auto "); + break; + case CSS_MARGIN_SET: + fprintf(stream, "margin-left: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* max-height */ + val = css_computed_max_height(style, &len1, &unit1); + switch (val) { + case CSS_MAX_HEIGHT_NONE: + fprintf(stream, "max-height: none "); + break; + case CSS_MAX_HEIGHT_SET: + fprintf(stream, "max-height: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* max-width */ + val = css_computed_max_width(style, &len1, &unit1); + switch (val) { + case CSS_MAX_WIDTH_NONE: + fprintf(stream, "max-width: none "); + break; + case CSS_MAX_WIDTH_SET: + fprintf(stream, "max-width: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* min-height */ + val = css_computed_min_height(style, &len1, &unit1); + switch (val) { + case CSS_MIN_HEIGHT_SET: + fprintf(stream, "min-height: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* min-width */ + val = css_computed_min_width(style, &len1, &unit1); + switch (val) { + case CSS_MIN_WIDTH_SET: + fprintf(stream, "min-width: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* outline-color */ + val = css_computed_outline_color(style, &color); + switch (val) { + case CSS_OUTLINE_COLOR_INVERT: + fprintf(stream, "outline-color: invert "); + break; + case CSS_OUTLINE_COLOR_COLOR: + fprintf(stream, "outline-color: #%08x ", color); + break; + default: + break; + } + + /* outline-style */ + val = css_computed_outline_style(style); + switch (val) { + case CSS_OUTLINE_STYLE_NONE: + fprintf(stream, "outline-style: none "); + break; + case CSS_OUTLINE_STYLE_DOTTED: + fprintf(stream, "outline-style: dotted "); + break; + case CSS_OUTLINE_STYLE_DASHED: + fprintf(stream, "outline-style: dashed "); + break; + case CSS_OUTLINE_STYLE_SOLID: + fprintf(stream, "outline-style: solid "); + break; + case CSS_OUTLINE_STYLE_DOUBLE: + fprintf(stream, "outline-style: double "); + break; + case CSS_OUTLINE_STYLE_GROOVE: + fprintf(stream, "outline-style: groove "); + break; + case CSS_OUTLINE_STYLE_RIDGE: + fprintf(stream, "outline-style: ridge "); + break; + case CSS_OUTLINE_STYLE_INSET: + fprintf(stream, "outline-style: inset "); + break; + case CSS_OUTLINE_STYLE_OUTSET: + fprintf(stream, "outline-style: outset "); + break; + default: + break; + } + + /* outline-width */ + val = css_computed_outline_width(style, &len1, &unit1); + switch (val) { + case CSS_OUTLINE_WIDTH_THIN: + fprintf(stream, "outline-width: thin "); + break; + case CSS_OUTLINE_WIDTH_MEDIUM: + fprintf(stream, "outline-width: medium "); + break; + case CSS_OUTLINE_WIDTH_THICK: + fprintf(stream, "outline-width: thick "); + break; + case CSS_OUTLINE_WIDTH_WIDTH: + fprintf(stream, "outline-width: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* overflow */ + val = css_computed_overflow(style); + switch (val) { + case CSS_OVERFLOW_VISIBLE: + fprintf(stream, "overflow: visible "); + break; + case CSS_OVERFLOW_HIDDEN: + fprintf(stream, "overflow: hidden "); + break; + case CSS_OVERFLOW_SCROLL: + fprintf(stream, "overflow: scroll "); + break; + case CSS_OVERFLOW_AUTO: + fprintf(stream, "overflow: auto "); + break; + default: + break; + } + + /* padding-top */ + val = css_computed_padding_top(style, &len1, &unit1); + switch (val) { + case CSS_PADDING_SET: + fprintf(stream, "padding-top: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* padding-right */ + val = css_computed_padding_right(style, &len1, &unit1); + switch (val) { + case CSS_PADDING_SET: + fprintf(stream, "padding-right: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* padding-bottom */ + val = css_computed_padding_bottom(style, &len1, &unit1); + switch (val) { + case CSS_PADDING_SET: + fprintf(stream, "padding-bottom: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* padding-left */ + val = css_computed_padding_left(style, &len1, &unit1); + switch (val) { + case CSS_PADDING_SET: + fprintf(stream, "padding-left: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* position */ + val = css_computed_position(style); + switch (val) { + case CSS_POSITION_STATIC: + fprintf(stream, "position: static "); + break; + case CSS_POSITION_RELATIVE: + fprintf(stream, "position: relative "); + break; + case CSS_POSITION_ABSOLUTE: + fprintf(stream, "position: absolute "); + break; + case CSS_POSITION_FIXED: + fprintf(stream, "position: fixed "); + break; + default: + break; + } + + /* quotes */ + val = css_computed_quotes(style, &string_list); + if (val == CSS_QUOTES_STRING && string_list != NULL) { + fprintf(stream, "quotes:"); + + while (*string_list != NULL) { + fprintf(stream, " \"%.*s\"", + (int) lwc_string_length(*string_list), + lwc_string_data(*string_list)); + + string_list++; + } + + fprintf(stream, " "); + } else { + switch (val) { + case CSS_QUOTES_NONE: + fprintf(stream, "quotes: none "); + break; + default: + break; + } + } + + /* right */ + val = css_computed_right(style, &len1, &unit1); + switch (val) { + case CSS_RIGHT_AUTO: + fprintf(stream, "right: auto "); + break; + case CSS_RIGHT_SET: + fprintf(stream, "right: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* table-layout */ + val = css_computed_table_layout(style); + switch (val) { + case CSS_TABLE_LAYOUT_AUTO: + fprintf(stream, "table-layout: auto "); + break; + case CSS_TABLE_LAYOUT_FIXED: + fprintf(stream, "table-layout: fixed "); + break; + default: + break; + } + + /* text-align */ + val = css_computed_text_align(style); + switch (val) { + case CSS_TEXT_ALIGN_LEFT: + fprintf(stream, "text-align: left "); + break; + case CSS_TEXT_ALIGN_RIGHT: + fprintf(stream, "text-align: right "); + break; + case CSS_TEXT_ALIGN_CENTER: + fprintf(stream, "text-align: center "); + break; + case CSS_TEXT_ALIGN_JUSTIFY: + fprintf(stream, "text-align: justify "); + break; + case CSS_TEXT_ALIGN_DEFAULT: + fprintf(stream, "text-align: default "); + break; + default: + break; + } + + /* text-decoration */ + val = css_computed_text_decoration(style); + if (val == CSS_TEXT_DECORATION_NONE) { + fprintf(stream, "text-decoration: none "); + } else { + fprintf(stream, "text-decoration:"); + + if (val & CSS_TEXT_DECORATION_BLINK) { + fprintf(stream, " blink"); + } + if (val & CSS_TEXT_DECORATION_LINE_THROUGH) { + fprintf(stream, " line-through"); + } + if (val & CSS_TEXT_DECORATION_OVERLINE) { + fprintf(stream, " overline"); + } + if (val & CSS_TEXT_DECORATION_UNDERLINE) { + fprintf(stream, " underline"); + } + + fprintf(stream, " "); + } + + /* text-indent */ + val = css_computed_text_indent(style, &len1, &unit1); + switch (val) { + case CSS_TEXT_INDENT_SET: + fprintf(stream, "text-indent: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* text-transform */ + val = css_computed_text_transform(style); + switch (val) { + case CSS_TEXT_TRANSFORM_CAPITALIZE: + fprintf(stream, "text-transform: capitalize "); + break; + case CSS_TEXT_TRANSFORM_UPPERCASE: + fprintf(stream, "text-transform: uppercase "); + break; + case CSS_TEXT_TRANSFORM_LOWERCASE: + fprintf(stream, "text-transform: lowercase "); + break; + case CSS_TEXT_TRANSFORM_NONE: + fprintf(stream, "text-transform: none "); + break; + default: + break; + } + + /* top */ + val = css_computed_top(style, &len1, &unit1); + switch (val) { + case CSS_TOP_AUTO: + fprintf(stream, "top: auto "); + break; + case CSS_TOP_SET: + fprintf(stream, "top: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* unicode-bidi */ + val = css_computed_unicode_bidi(style); + switch (val) { + case CSS_UNICODE_BIDI_NORMAL: + fprintf(stream, "unicode-bidi: normal "); + break; + case CSS_UNICODE_BIDI_EMBED: + fprintf(stream, "unicode-bidi: embed "); + break; + case CSS_UNICODE_BIDI_BIDI_OVERRIDE: + fprintf(stream, "unicode-bidi: bidi-override "); + break; + default: + break; + } + + /* vertical-align */ + val = css_computed_vertical_align(style, &len1, &unit1); + switch (val) { + case CSS_VERTICAL_ALIGN_BASELINE: + fprintf(stream, "vertical-align: baseline "); + break; + case CSS_VERTICAL_ALIGN_SUB: + fprintf(stream, "vertical-align: sub "); + break; + case CSS_VERTICAL_ALIGN_SUPER: + fprintf(stream, "vertical-align: super "); + break; + case CSS_VERTICAL_ALIGN_TOP: + fprintf(stream, "vertical-align: top "); + break; + case CSS_VERTICAL_ALIGN_TEXT_TOP: + fprintf(stream, "vertical-align: text-top "); + break; + case CSS_VERTICAL_ALIGN_MIDDLE: + fprintf(stream, "vertical-align: middle "); + break; + case CSS_VERTICAL_ALIGN_BOTTOM: + fprintf(stream, "vertical-align: bottom "); + break; + case CSS_VERTICAL_ALIGN_TEXT_BOTTOM: + fprintf(stream, "vertical-align: text-bottom "); + break; + case CSS_VERTICAL_ALIGN_SET: + fprintf(stream, "vertical-align: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* visibility */ + val = css_computed_visibility(style); + switch (val) { + case CSS_VISIBILITY_VISIBLE: + fprintf(stream, "visibility: visible "); + break; + case CSS_VISIBILITY_HIDDEN: + fprintf(stream, "visibility: hidden "); + break; + case CSS_VISIBILITY_COLLAPSE: + fprintf(stream, "visibility: collapse "); + break; + default: + break; + } + + /* white-space */ + val = css_computed_white_space(style); + switch (val) { + case CSS_WHITE_SPACE_NORMAL: + fprintf(stream, "white-space: normal "); + break; + case CSS_WHITE_SPACE_PRE: + fprintf(stream, "white-space: pre "); + break; + case CSS_WHITE_SPACE_NOWRAP: + fprintf(stream, "white-space: nowrap "); + break; + case CSS_WHITE_SPACE_PRE_WRAP: + fprintf(stream, "white-space: pre-wrap "); + break; + case CSS_WHITE_SPACE_PRE_LINE: + fprintf(stream, "white-space: pre-line "); + break; + default: + break; + } + + /* width */ + val = css_computed_width(style, &len1, &unit1); + switch (val) { + case CSS_WIDTH_AUTO: + fprintf(stream, "width: auto "); + break; + case CSS_WIDTH_SET: + fprintf(stream, "width: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* word-spacing */ + val = css_computed_word_spacing(style, &len1, &unit1); + switch (val) { + case CSS_WORD_SPACING_NORMAL: + fprintf(stream, "word-spacing: normal "); + break; + case CSS_WORD_SPACING_SET: + fprintf(stream, "word-spacing: "); + + dump_css_unit(stream, len1, unit1); + + fprintf(stream, " "); + break; + default: + break; + } + + /* z-index */ + val = css_computed_z_index(style, &zindex); + switch (val) { + case CSS_Z_INDEX_AUTO: + fprintf(stream, "z-index: auto "); + break; + case CSS_Z_INDEX_SET: + fprintf(stream, "z-index: %d ", zindex); + break; + default: + break; + } + + fprintf(stream, "}"); +} + +/****************************************************************************** + * Helper functions for nscss_dump_computed_style * + ******************************************************************************/ + +/** + * Dump a fixed point value to the stream in a textual form. + * + * \param stream Stream to write to + * \param f Value to write + */ +void dump_css_fixed(FILE *stream, css_fixed f) +{ +#define NSCSS_ABS(x) (uint32_t)((x) < 0 ? -(x) : (x)) + uint32_t uintpart = FIXTOINT(NSCSS_ABS(f)); + /* + 500 to ensure round to nearest (division will truncate) */ + uint32_t fracpart = ((NSCSS_ABS(f) & 0x3ff) * 1000 + 500) / (1 << 10); +#undef NSCSS_ABS + + fprintf(stream, "%s%d.%03d", f < 0 ? "-" : "", uintpart, fracpart); +} + +/** + * Dump a numeric value to the stream in a textual form. + * + * \param stream Stream to write to + * \param val Value to write + */ +void dump_css_number(FILE *stream, css_fixed val) +{ + if (INTTOFIX(FIXTOINT(val)) == val) + fprintf(stream, "%d", FIXTOINT(val)); + else + dump_css_fixed(stream, val); +} + +/** + * Dump a dimension to the stream in a textual form. + * + * \param stream Stream to write to + * \param val Value to write + * \param unit Unit to write + */ +void dump_css_unit(FILE *stream, css_fixed val, css_unit unit) +{ + dump_css_number(stream, val); + + switch (unit) { + case CSS_UNIT_PX: + fprintf(stream, "px"); + break; + case CSS_UNIT_EX: + fprintf(stream, "ex"); + break; + case CSS_UNIT_EM: + fprintf(stream, "em"); + break; + case CSS_UNIT_IN: + fprintf(stream, "in"); + break; + case CSS_UNIT_CM: + fprintf(stream, "cm"); + break; + case CSS_UNIT_MM: + fprintf(stream, "mm"); + break; + case CSS_UNIT_PT: + fprintf(stream, "pt"); + break; + case CSS_UNIT_PC: + fprintf(stream, "pc"); + break; + case CSS_UNIT_PCT: + fprintf(stream, "%%"); + break; + case CSS_UNIT_DEG: + fprintf(stream, "deg"); + break; + case CSS_UNIT_GRAD: + fprintf(stream, "grad"); + break; + case CSS_UNIT_RAD: + fprintf(stream, "rad"); + break; + case CSS_UNIT_MS: + fprintf(stream, "ms"); + break; + case CSS_UNIT_S: + fprintf(stream, "s"); + break; + case CSS_UNIT_HZ: + fprintf(stream, "Hz"); + break; + case CSS_UNIT_KHZ: + fprintf(stream, "kHz"); + break; + } +} + diff --git a/css/dump.h b/css/dump.h new file mode 100644 index 000000000..25d283e9e --- /dev/null +++ b/css/dump.h @@ -0,0 +1,26 @@ +/* + * Copyright 2009 John-Mark Bell + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETSURF_CSS_DUMP_H_ +#define NETSURF_CSS_DUMP_H_ + +#include "css/css.h" + +void nscss_dump_computed_style(FILE *stream, const css_computed_style *style); + +#endif diff --git a/css/internal.c b/css/internal.c new file mode 100644 index 000000000..fd22af628 --- /dev/null +++ b/css/internal.c @@ -0,0 +1,82 @@ +/* + * Copyright 2009 John-Mark Bell + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "css/internal.h" + +#include "utils/url.h" + +/** + * URL resolution callback for libcss + * + * \param pw Resolution context + * \param ctx Dictionary to intern result in + * \param base Base URI + * \param rel Relative URL + * \param abs Pointer to location to receive resolved URL + * \return CSS_OK on success, + * CSS_NOMEM on memory exhaustion, + * CSS_INVALID if resolution failed. + */ +css_error nscss_resolve_url(void *pw, lwc_context *ctx, + const char *base, lwc_string *rel, lwc_string **abs) +{ + lwc_error lerror; + char *rel_url, *abs_url, *norm_url; + url_func_result res; + + /* Copy relative URL and ensure it's NUL terminated */ + rel_url = malloc(lwc_string_length(rel) + 1); + if (rel_url == NULL) + return CSS_NOMEM; + + memcpy(rel_url, lwc_string_data(rel), lwc_string_length(rel)); + rel_url[lwc_string_length(rel)] = '\0'; + + /* Resolve URI */ + res = url_join(rel_url, base, &abs_url); + if (res != URL_FUNC_OK) { + free(rel_url); + return res == URL_FUNC_NOMEM ? CSS_NOMEM : CSS_INVALID; + } + + free(rel_url); + + /* Normalise it */ + res = url_normalize(abs_url, &norm_url); + if (res != URL_FUNC_OK) { + free(abs_url); + return res == URL_FUNC_NOMEM ? CSS_NOMEM : CSS_INVALID; + } + + free(abs_url); + + /* Intern it */ + lerror = lwc_context_intern(ctx, norm_url, strlen(norm_url), abs); + if (lerror != lwc_error_ok) { + *abs = NULL; + free(norm_url); + return lerror == lwc_error_oom ? CSS_NOMEM : CSS_INVALID; + } + + free(norm_url); + + return CSS_OK; +} + diff --git a/css/internal.h b/css/internal.h new file mode 100644 index 000000000..e675a4876 --- /dev/null +++ b/css/internal.h @@ -0,0 +1,27 @@ +/* + * Copyright 2009 John-Mark Bell + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETSURF_CSS_INTERNAL_H_ +#define NETSURF_CSS_INTERNAL_H_ + +#include "css/css.h" + +css_error nscss_resolve_url(void *pw, lwc_context *ctx, + const char *base, lwc_string *rel, lwc_string **abs); + +#endif diff --git a/css/makeenum b/css/makeenum deleted file mode 100755 index 54c513d06..000000000 --- a/css/makeenum +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/perl -W -# -# This file is part of NetSurf, http://netsurf-browser.org/ -# Licensed under the GNU General Public License, -# http://www.opensource.org/licenses/gpl-license -# Copyright 2003 James Bursa -# - -$out = shift or die "usage: makeenum leafname"; - -open H, ">$out.h" or die "open 'enum.h' failed"; -open C, ">$out.c" or die "open 'enum.c' failed"; - -print C "#include \n"; -print C "#include \"$out.h\"\n\n"; - -while (<>) { - chomp; - @enum = split; - $name = shift @enum; - - @uc_enum = map uc, @enum; - s/-/_/g foreach (@uc_enum); - $uc_name = uc $name; - - print H "extern const char * const ${name}_name[];\n"; - print H "typedef enum {\n ${uc_name}_"; - print H join ",\n ${uc_name}_", @uc_enum; - print H ",\n ${uc_name}_UNKNOWN"; - print H ",\n ${uc_name}_NOT_SET\n"; - print H "} $name;\n"; - print H "$name ${name}_parse(const char * const s, int length);\n\n"; - - print C "/**\n * $name\n */\n\n"; - print C "const char * const ${name}_name[] = {\n \""; - print C join "\",\n \"", @enum; - print C "\"\n};\n\n"; - print C "$name ${name}_parse(const char * const s, int length)\n{\n"; - foreach $x (@enum) { - $ux = uc $x; - $ux =~ s/-/_/g; - $len = length $x; - print C " if (length == $len && strncasecmp(s, \"$x\", $len) == 0) return ${uc_name}_$ux;\n"; - } - print C " return ${uc_name}_UNKNOWN;\n}\n\n"; -} diff --git a/css/parser.y b/css/parser.y deleted file mode 100644 index 77ab604a5..000000000 --- a/css/parser.y +++ /dev/null @@ -1,441 +0,0 @@ -/* - * This file is part of NetSurf, http://netsurf-browser.org/ - * Licensed under the GNU General Public License, - * http://www.opensource.org/licenses/gpl-license - * Copyright 2004 James Bursa - */ - -/** \file - -CSS parser using the lemon parser generator. - -see CSS2.1 Specification, chapter 4 -http://www.w3.org/TR/CSS21/syndata.html - -stylesheet : [ CDO | CDC | S | statement ]*; -statement : ruleset | at-rule; -at-rule : ATKEYWORD S* any* [ block | ';' S* ]; -block : '{' S* [ any | block | ATKEYWORD S* | ';' S* ]* '}' S*; -ruleset : selector? '{' S* declaration? [ ';' S* declaration? ]* '}' S*; -selector : any+; -declaration : DELIM? property S* ':' S* value S*; -property : IDENT; -value : [ any | block | ATKEYWORD S* ]+; -any : [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING - | DELIM | URI | HASH | UNICODE-RANGE | INCLUDES - | DASHMATCH | FUNCTION S* any* ')' - | '(' S* any* ')' | '[' S* any* ']' ] S*; - -Note: CDO, CDC will be stripped out by the scanner -*/ - -stylesheet ::= ws statement_list. - -ws ::= . -ws ::= ws_1. - -ws_1 ::= S. -ws_1 ::= ws_1 S. - -statement_list ::= . -statement_list ::= statement_list statement. - -statement ::= ws_1. -statement ::= ruleset. -statement ::= at_rule. - -at_rule ::= ATKEYWORD ws any_list block. -at_rule ::= ATKEYWORD(A) ws any_list(B) SEMI ws. - { if ((A.length == 7) && (strncasecmp(A.text, "@import", 7) == 0) - && B && !param->had_ruleset) - css_atimport(param->stylesheet, B); - css_free_node(B); } - -block ::= LBRACE ws block_body RBRACE ws. -block_body ::= . -block_body ::= block_body any ws. -block_body ::= block_body block. -block_body ::= block_body ATKEYWORD ws. -block_body ::= block_body SEMI ws. - -ruleset ::= selector_list(A) LBRACE ws declaration_list(B) RBRACE ws. - { if (A && B) { - /*param->had_ruleset = true;*/ - css_add_ruleset(param->stylesheet, A, B); - } - else - css_free_selector(A); - css_free_node(B); } -ruleset ::= LBRACE declaration_list(A) RBRACE. - /* this form of ruleset not used in CSS2 - used to parse style attributes (ruleset_only = 1) */ - { if (param->ruleset_only) param->declaration = A; - else css_free_node(A); } -ruleset ::= any_list_1(A) LBRACE declaration_list(B) RBRACE. - { css_free_node(A); css_free_node(B); } /* not CSS2 */ - -selector_list(A) ::= selector(B) ws. - { A = B; } -selector_list(A) ::= selector_list(B) COMMA ws selector(C) ws. - { if (B && C) { - C->next = B; - A = C; - } else { - css_free_selector(B); - css_free_selector(C); - A = 0; - } } - -selector(A) ::= simple_selector(B). - { A = B; } -selector(A) ::= selector(B) css_combinator(C) simple_selector(D). - { if (B && D) { - D->combiner = B; - D->comb = C; - D->specificity += B->specificity; - A = D; - } else { - css_free_selector(B); - css_free_selector(D); - A = 0; - } } - -css_combinator(A) ::= ws PLUS ws. - { A = CSS_COMB_PRECEDED; } -css_combinator(A) ::= ws GT ws. - { A = CSS_COMB_PARENT; } -css_combinator(A) ::= ws_1. - { A = CSS_COMB_ANCESTOR; } - -simple_selector(A) ::= element_name(B) detail_list(C). - { if (C && (A = css_new_selector(CSS_SELECTOR_ELEMENT, - B.text, B.length))) { - A->detail = C; - A->specificity = 1 + C->specificity; - } else { - param->memory_error = true; - css_free_selector(C); - A = 0; - } } -simple_selector(A) ::= element_name(B). - { if ((A = css_new_selector(CSS_SELECTOR_ELEMENT, - B.text, B.length))) - A->specificity = CSS_SPECIFICITY_ELEMENT; - else - param->memory_error = true; - } -simple_selector(A) ::= detail_list(C). - { if (C && (A = css_new_selector(CSS_SELECTOR_ELEMENT, 0, 0))) { - A->detail = C; - A->specificity = C->specificity; - } else { - param->memory_error = true; - css_free_selector(C); - A = 0; - } } - -element_name(A) ::= IDENT(B). - { A = B; } -element_name(A) ::= ASTERISK. - { A.text = 0; } - -detail_list(A) ::= detail(B). - { A = B; } -detail_list(A) ::= detail(B) detail_list(C). - { if (B && C) { - B->specificity += C->specificity; - B->next = C; - A = B; - } else { - css_free_selector(B); - css_free_selector(C); - A = 0; - } } - -detail(A) ::= HASH(B). - { A = css_new_selector(CSS_SELECTOR_ID, B.text+1, B.length-1); - if (A) A->specificity = CSS_SPECIFICITY_ID; - else param->memory_error = true; } -detail(A) ::= DOT IDENT(B). - { A = css_new_selector(CSS_SELECTOR_CLASS, B.text, B.length); - if (A) A->specificity = CSS_SPECIFICITY_CLASS; - else param->memory_error = true; } -detail(A) ::= LBRAC ws IDENT(B) ws RBRAC. - { A = css_new_selector(CSS_SELECTOR_ATTRIB, B.text, B.length); - if (A) A->specificity = CSS_SPECIFICITY_ATTR; - else param->memory_error = true; } -detail(A) ::= LBRAC ws IDENT(B) ws EQUALS ws IDENT(C) ws RBRAC. - { A = css_new_selector(CSS_SELECTOR_ATTRIB_EQ, B.text, B.length); - if (A) { A->data2 = C.text; A->data2_length = C.length; - A->specificity = CSS_SPECIFICITY_ATTR; } - else param->memory_error = true; } -detail(A) ::= LBRAC ws IDENT(B) ws EQUALS ws STRING(C) ws RBRAC. - { A = css_new_selector(CSS_SELECTOR_ATTRIB_EQ, B.text, B.length); - if (A) { A->data2 = C.text + 1; A->data2_length = C.length - 2; - A->specificity = CSS_SPECIFICITY_ATTR; } - else param->memory_error = true; } -detail(A) ::= LBRAC ws IDENT(B) ws INCLUDES ws IDENT(C) ws RBRAC. - { A = css_new_selector(CSS_SELECTOR_ATTRIB_INC, B.text, B.length); - if (A) { A->data2 = C.text; A->data2_length = C.length; - A->specificity = CSS_SPECIFICITY_ATTR; } - else param->memory_error = true; } -detail(A) ::= LBRAC ws IDENT(B) ws INCLUDES ws STRING(C) ws RBRAC. - { A = css_new_selector(CSS_SELECTOR_ATTRIB_INC, B.text, B.length); - if (A) { A->data2 = C.text + 1; A->data2_length = C.length - 2; - A->specificity = CSS_SPECIFICITY_ATTR; } - else param->memory_error = true; } -detail(A) ::= LBRAC ws IDENT(B) ws DASHMATCH ws IDENT(C) ws RBRAC. - { A = css_new_selector(CSS_SELECTOR_ATTRIB_DM, B.text, B.length); - if (A) { A->data2 = C.text; A->data2_length = C.length; - A->specificity = CSS_SPECIFICITY_ATTR; } - else param->memory_error = true; } -detail(A) ::= LBRAC ws IDENT(B) ws DASHMATCH ws STRING(C) ws RBRAC. - { A = css_new_selector(CSS_SELECTOR_ATTRIB_DM, B.text, B.length); - if (A) { A->data2 = C.text + 1; A->data2_length = C.length - 2; - A->specificity = CSS_SPECIFICITY_ATTR; } - else param->memory_error = true; } -detail(A) ::= LBRAC ws IDENT(B) ws PREFIX ws IDENT(C) ws RBRAC. - { A = css_new_selector(CSS_SELECTOR_ATTRIB_PRE, B.text, B.length); - if (A) { A->data2 = C.text; A->data2_length = C.length; - A->specificity = CSS_SPECIFICITY_ATTR; } - else param->memory_error = true; } -detail(A) ::= LBRAC ws IDENT(B) ws PREFIX ws STRING(C) ws RBRAC. - { A = css_new_selector(CSS_SELECTOR_ATTRIB_PRE, B.text, B.length); - if (A) { A->data2 = C.text + 1; A->data2_length = C.length - 2; - A->specificity = CSS_SPECIFICITY_ATTR; } - else param->memory_error = true; } -detail(A) ::= LBRAC ws IDENT(B) ws SUFFIX ws IDENT(C) ws RBRAC. - { A = css_new_selector(CSS_SELECTOR_ATTRIB_SUF, B.text, B.length); - if (A) { A->data2 = C.text; A->data2_length = C.length; - A->specificity = CSS_SPECIFICITY_ATTR; } - else param->memory_error = true; } -detail(A) ::= LBRAC ws IDENT(B) ws SUFFIX ws STRING(C) ws RBRAC. - { A = css_new_selector(CSS_SELECTOR_ATTRIB_SUF, B.text, B.length); - if (A) { A->data2 = C.text + 1; A->data2_length = C.length - 2; - A->specificity = CSS_SPECIFICITY_ATTR; } - else param->memory_error = true; } -detail(A) ::= LBRAC ws IDENT(B) ws SUBSTR ws IDENT(C) ws RBRAC. - { A = css_new_selector(CSS_SELECTOR_ATTRIB_SUB, B.text, B.length); - if (A) { A->data2 = C.text; A->data2_length = C.length; - A->specificity = CSS_SPECIFICITY_ATTR; } - else param->memory_error = true; } -detail(A) ::= LBRAC ws IDENT(B) ws SUBSTR ws STRING(C) ws RBRAC. - { A = css_new_selector(CSS_SELECTOR_ATTRIB_SUB, B.text, B.length); - if (A) { A->data2 = C.text + 1; A->data2_length = C.length - 2; - A->specificity = CSS_SPECIFICITY_ATTR; } - else param->memory_error = true; } -detail(A) ::= COLON IDENT(B). - { if (B.length == 4 && strncasecmp(B.text, "link", 4) == 0) { - A = css_new_selector(CSS_SELECTOR_ATTRIB, "href", 4); - if (A) A->specificity = CSS_SPECIFICITY_ATTR; - else param->memory_error = true; - } else { - A = css_new_selector(CSS_SELECTOR_PSEUDO, B.text, B.length); - if (A) A->specificity = CSS_SPECIFICITY_ATTR; - else param->memory_error = true; - } } -detail(A) ::= COLON FUNCTION(B) ws IDENT ws RPAREN. - { A = css_new_selector(CSS_SELECTOR_PSEUDO, B.text, B.length); - if (A) A->specificity = CSS_SPECIFICITY_ATTR; - else param->memory_error = true; } -detail(A) ::= COLON FUNCTION(B) ws RPAREN. - { A = css_new_selector(CSS_SELECTOR_PSEUDO, B.text, B.length); - if (A) A->specificity = CSS_SPECIFICITY_ATTR; - else param->memory_error = true; } - -declaration_list(A) ::= . - { A = 0; } -declaration_list(A) ::= declaration(B). - { A = B; } -declaration_list(A) ::= declaration_list(B) SEMI. - { A = B; } -declaration_list(A) ::= declaration(B) SEMI ws declaration_list(C). - { if (B) { B->next = C; A = B; } else { A = C; } } - -declaration ::= DELIM property ws COLON ws value ws. - /* ignore this as it has no meaning in CSS2 */ - -declaration(A) ::= property(B) ws COLON ws value(C) ws. - { if (C && (A = css_new_node(param->stylesheet, - CSS_NODE_DECLARATION, - B.text, B.length))) { - A->value = C; - } else { - param->memory_error = true; - css_free_node(C); - A = 0; - } } -declaration(A) ::= any_list_1(B). /* malformed declaration: ignore */ - { A = 0; css_free_node(B); } - -property(A) ::= IDENT(B). - { A = B; } - -value(A) ::= any(B) ws. - { A = B; } -value(A) ::= any(B) ws value(C). - { if (B && C) { B->next = C; A = B; } - else { css_free_node(B); css_free_node(C); A = 0; } } -value(A) ::= value(B) ws block. - { A = B; } -value(A) ::= value(B) ws ATKEYWORD ws. - { A = B; } - - -any_list(A) ::= . - { A = 0; } -any_list(A) ::= any(B) ws any_list(C). - { if (B) { B->next = C; A = B; } - else { css_free_node(B); css_free_node(C); A = 0; } } -any_list_1(A) ::= any(B) ws any_list(C). - { if (B) { B->next = C; A = B; } - else { css_free_node(B); css_free_node(C); A = 0; } } -any(A) ::= IDENT(B). - { A = css_new_node(param->stylesheet, CSS_NODE_IDENT, - B.text, B.length); - if (!A) param->memory_error = true; } -any(A) ::= NUMBER(B). - { A = css_new_node(param->stylesheet, CSS_NODE_NUMBER, - B.text, B.length); - if (!A) param->memory_error = true; } -any(A) ::= PERCENTAGE(B). - { A = css_new_node(param->stylesheet, CSS_NODE_PERCENTAGE, - B.text, B.length); - if (!A) param->memory_error = true; } -any(A) ::= DIMENSION(B). - { A = css_new_node(param->stylesheet, CSS_NODE_DIMENSION, - B.text, B.length); - if (!A) param->memory_error = true; } -any(A) ::= STRING(B). - { A = css_new_node(param->stylesheet, CSS_NODE_STRING, - B.text + 1, B.length - 2); - if (!A) param->memory_error = true; } -any(A) ::= DELIM(B). - { A = css_new_node(param->stylesheet, CSS_NODE_DELIM, - B.text, B.length); - if (!A) param->memory_error = true; } -any(A) ::= URI(B). - { A = css_new_node(param->stylesheet, CSS_NODE_URI, - B.text, B.length); - if (!A) param->memory_error = true; } -any(A) ::= HASH(B). - { A = css_new_node(param->stylesheet, CSS_NODE_HASH, - B.text, B.length); - if (!A) param->memory_error = true; } -any(A) ::= UNICODE_RANGE(B). - { A = css_new_node(param->stylesheet, CSS_NODE_UNICODE_RANGE, - B.text, B.length); - if (!A) param->memory_error = true; } -any(A) ::= INCLUDES. - { A = css_new_node(param->stylesheet, CSS_NODE_INCLUDES, - 0, 0); - if (!A) param->memory_error = true; } -any(A) ::= FUNCTION(B) ws any_list(C) RPAREN. - { if ((A = css_new_node(param->stylesheet, CSS_NODE_FUNCTION, - B.text, B.length))) - A->value = C; - else { - param->memory_error = true; - css_free_node(C); - A = 0; - } } -any(A) ::= DASHMATCH. - { A = css_new_node(param->stylesheet, CSS_NODE_DASHMATCH, - 0, 0); - if (!A) param->memory_error = true; } -any(A) ::= PREFIX. - { A = css_new_node(param->stylesheet, CSS_NODE_PREFIX, - 0, 0); - if (!A) param->memory_error = true; } -any(A) ::= SUFFIX. - { A = css_new_node(param->stylesheet, CSS_NODE_SUFFIX, - 0, 0); - if (!A) param->memory_error = true; } -any(A) ::= SUBSTR. - { A = css_new_node(param->stylesheet, CSS_NODE_SUBSTR, - 0, 0); - if (!A) param->memory_error = true; } -any(A) ::= COLON. - { A = css_new_node(param->stylesheet, CSS_NODE_COLON, 0, 0); - if (!A) param->memory_error = true; } -any(A) ::= COMMA. - { A = css_new_node(param->stylesheet, CSS_NODE_COMMA, 0, 0); - if (!A) param->memory_error = true; } -any(A) ::= DOT. - { A = css_new_node(param->stylesheet, CSS_NODE_DOT, 0, 0); - if (!A) param->memory_error = true; } -any(A) ::= PLUS. - { A = css_new_node(param->stylesheet, CSS_NODE_PLUS, 0, 0); - if (!A) param->memory_error = true; } -any(A) ::= GT. - { A = css_new_node(param->stylesheet, CSS_NODE_GT, 0, 0); - if (!A) param->memory_error = true; } -any(A) ::= LPAREN ws any_list(B) RPAREN. - { if ((A = css_new_node(param->stylesheet, CSS_NODE_PAREN, - 0, 0))) - A->value = B; - else { - param->memory_error = true; - css_free_node(B); - A = 0; - } } -any(A) ::= LBRAC ws any_list(B) RBRAC. - { if ((A = css_new_node(param->stylesheet, CSS_NODE_BRAC, - 0, 0))) - A->value = B; - else { - param->memory_error = true; - css_free_node(B); - A = 0; - } } -any(A) ::= ASTERISK(B). - { A = css_new_node(param->stylesheet, CSS_NODE_DELIM, - B.text, B.length); - if (!A) param->memory_error = true; } - - -/* lemon directives */ - -%extra_argument { struct css_parser_params *param } -%include { -#include -#include -#include -#define CSS_INTERNALS -#include "css/css.h" -#include "utils/utils.h" } -%name css_parser_ - -%token_type { struct css_parser_token } - -%type selector_list { struct css_selector * } -%type selector { struct css_selector * } -%type css_combinator { css_combinator } -%type simple_selector { struct css_selector * } -%type detail_list { struct css_selector * } -%type detail { struct css_selector * } -%type declaration_list { struct css_node * } -%type declaration { struct css_node * } -%type value { struct css_node * } -%type any_list { struct css_node * } -%type any_list_1 { struct css_node * } -%type any { struct css_node * } - -%destructor selector_list { css_free_selector($$); } -%destructor selector { css_free_selector($$); } -%destructor simple_selector { css_free_selector($$); } -%destructor detail_list { css_free_selector($$); } -%destructor detail { css_free_selector($$); } -%destructor declaration_list { css_free_node($$); } -%destructor declaration { css_free_node($$); } -%destructor value { css_free_node($$); } -%destructor any_list { css_free_node($$); } -%destructor any_list_1 { css_free_node($$); } -%destructor any { css_free_node($$); } - -%left COLON COMMA GT HASH LBRAC PLUS. -%left DOT. -%left IDENT. -%left LBRACE. - -%syntax_error { param->syntax_error = true; } diff --git a/css/ruleset.c b/css/ruleset.c deleted file mode 100644 index 46bc5e2a5..000000000 --- a/css/ruleset.c +++ /dev/null @@ -1,3152 +0,0 @@ -/* - * Copyright 2004 James Bursa - * Copyright 2004 John M Bell - * - * This file is part of NetSurf, http://www.netsurf-browser.org/ - * - * NetSurf is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * NetSurf is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/** \file - * CSS ruleset parsing. - * - * This file implements the last stage of CSS parsing. It converts trees of - * struct css_node produced by the parser into struct style, and adds them to a - * stylesheet. - * - * This code is complicated by the CSS error handling rules. According to - * CSS 2.1 4.2 "Illegal values", the whole of a declaration must be legal for - * any of it to be used. - */ - -#define _GNU_SOURCE /* for strndup */ -#include -#include -#include -#include -#include -#include -#define CSS_INTERNALS -#include "css/css.h" -#include "content/content.h" -#include "desktop/options.h" -#define NDEBUG -#include "utils/log.h" -#undef NDEBUG -#include "utils/url.h" -#include "utils/utils.h" - -static bool css_compare_selectors(const struct css_selector *n0, - const struct css_selector *n1); -static int parse_length(struct css_length * const length, - const struct css_node * const v, bool non_negative); -static colour parse_colour(const struct css_node * const v); -static colour css_parse_rgb(struct css_node *v); -static bool parse_uri(const struct css_node *v, char **uri); -static struct css_content *parse_content_new(struct css_content **current, css_content_type_generated generated); -static bool parse_content_counter(struct css_content **current, struct css_node *t, bool counters); -bool parse_counter_control_data(struct css_counter_control **current, const struct css_node * v, int empty); -struct css_counter_control *parse_counter_control_new(struct css_counter_control **current); - -static void parse_background(struct css_style * const s, - const struct css_node * v); -static void parse_background_attachment(struct css_style * const s, const struct css_node * const v); -static void parse_background_color(struct css_style * const s, const struct css_node * const v); -static void parse_background_image(struct css_style * const s, - const struct css_node * const v); -static bool css_background_image_parse(const struct css_node *v, - css_background_image_type *type, char **uri); -static struct css_background_entry *css_background_lookup( - const struct css_node *v); -static void parse_background_position(struct css_style * const s, - const struct css_node * const v); -static bool css_background_position_parse(const struct css_node **node, - struct css_background_position *horz, - struct css_background_position *vert); -static void parse_background_repeat(struct css_style * const s, const struct css_node * const v); -static void parse_border(struct css_style * const s, const struct css_node * v); -static void parse_border_bottom(struct css_style * const s, const struct css_node * v); -static void parse_border_bottom_color(struct css_style * const s, const struct css_node * v); -static void parse_border_bottom_style(struct css_style * const s, const struct css_node * v); -static void parse_border_bottom_width(struct css_style * const s, const struct css_node * v); -static void parse_border_collapse(struct css_style * const s, const struct css_node * v); -static void parse_border_color(struct css_style * const s, const struct css_node * v); -static void parse_border_color_side(struct css_style * const s, - const struct css_node * const v, unsigned int i); -static void parse_border_left(struct css_style * const s, const struct css_node * v); -static void parse_border_left_color(struct css_style * const s, const struct css_node * v); -static void parse_border_left_style(struct css_style * const s, const struct css_node * v); -static void parse_border_left_width(struct css_style * const s, const struct css_node * v); -static void parse_border_right(struct css_style * const s, const struct css_node * v); -static void parse_border_right_color(struct css_style * const s, const struct css_node * v); -static void parse_border_right_style(struct css_style * const s, const struct css_node * v); -static void parse_border_right_width(struct css_style * const s, const struct css_node * v); -static void parse_border_side(struct css_style * const s, - const struct css_node *v, unsigned int i); -static void parse_border_spacing(struct css_style * const s, const struct css_node * v); -static void parse_border_style(struct css_style * const s, const struct css_node * v); -static void parse_border_style_side(struct css_style * const s, - const struct css_node * const v, unsigned int i); -static void parse_border_top(struct css_style * const s, const struct css_node * v); -static void parse_border_top_color(struct css_style * const s, const struct css_node * v); -static void parse_border_top_style(struct css_style * const s, const struct css_node * v); -static void parse_border_top_width(struct css_style * const s, const struct css_node * v); -static void parse_border_width(struct css_style * const s, const struct css_node * v); -static void parse_border_width_side(struct css_style * const s, - const struct css_node * const v, unsigned int i); -static void parse_bottom(struct css_style * const s, const struct css_node * v); -static void parse_caption_side(struct css_style * const s, const struct css_node * v); -static void parse_clear(struct css_style * const s, const struct css_node * const v); -static void parse_clip(struct css_style * const s, const struct css_node * v); -static void parse_color(struct css_style * const s, const struct css_node * const v); -static void parse_content(struct css_style * const s, const struct css_node * v); -static void parse_counter_increment(struct css_style * const s, const struct css_node * v); -static void parse_counter_reset(struct css_style * const s, const struct css_node * v); -static void parse_cursor(struct css_style * const s, const struct css_node * v); -static void parse_direction(struct css_style * const s, const struct css_node * v); -static void parse_display(struct css_style * const s, const struct css_node * const v); -static void parse_empty_cells(struct css_style * const s, const struct css_node * v); -static void parse_float(struct css_style * const s, const struct css_node * const v); -static void parse_font(struct css_style * const s, const struct css_node * v); -static void parse_font_family(struct css_style * const s, const struct css_node * v); -static void parse_font_size(struct css_style * const s, const struct css_node * const v); -static void parse_font_style(struct css_style * const s, const struct css_node * const v); -static void parse_font_variant(struct css_style * const s, const struct css_node * const v); -static void parse_font_weight(struct css_style * const s, const struct css_node * const v); -static void parse_height(struct css_style * const s, const struct css_node * const v); -static void parse_left(struct css_style * const s, const struct css_node * v); -static void parse_letter_spacing(struct css_style * const s, const struct css_node * v); -static void parse_line_height(struct css_style * const s, const struct css_node * const v); -static void parse_list_style(struct css_style * const s, const struct css_node * v); -static void parse_list_style_image(struct css_style * const s, const struct css_node * v); -static bool css_list_style_image_parse(const struct css_node *v, - css_list_style_image_type *type, char **uri); -static void parse_list_style_position(struct css_style * const s, const struct css_node * v); -static void parse_list_style_type(struct css_style * const s, const struct css_node * v); -static void parse_margin(struct css_style * const s, const struct css_node * const v); -static void parse_margin_bottom(struct css_style * const s, const struct css_node * const v); -static void parse_margin_left(struct css_style * const s, const struct css_node * const v); -static void parse_margin_right(struct css_style * const s, const struct css_node * const v); -static void parse_margin_top(struct css_style * const s, const struct css_node * const v); -static void parse_margin_side(struct css_style * const s, const struct css_node * const v, - unsigned int i); -static void parse_max_height(struct css_style *const s, const struct css_node * v); -static void parse_max_width(struct css_style *const s, const struct css_node * v); -static void parse_min_height(struct css_style *const s, const struct css_node * v); -static void parse_min_width(struct css_style *const s, const struct css_node * v); -static void parse_orphans(struct css_style * const s, const struct css_node * const v); -static void parse_outline(struct css_style * const s, const struct css_node * v); -static void parse_outline_color(struct css_style * const s, const struct css_node * const v); -static void parse_outline_style(struct css_style * const s, const struct css_node * const v); -static void parse_outline_width(struct css_style * const s, const struct css_node * const v); -static bool css_outline_width_parse(const struct css_node * v, struct css_border_width * w); -static void parse_overflow(struct css_style * const s, const struct css_node * const v); -static void parse_padding(struct css_style * const s, const struct css_node * const v); -static void parse_padding_bottom(struct css_style * const s, const struct css_node * const v); -static void parse_padding_left(struct css_style * const s, const struct css_node * const v); -static void parse_padding_right(struct css_style * const s, const struct css_node * const v); -static void parse_padding_top(struct css_style * const s, const struct css_node * const v); -static void parse_padding_side(struct css_style * const s, const struct css_node * const v, - unsigned int i); -static void parse_page_break_after(struct css_style * const s, const struct css_node * v); -static void parse_page_break_before(struct css_style * const s, const struct css_node * v); -static void parse_page_break_inside(struct css_style * const s, const struct css_node * v); -static void parse_pos(struct css_style * const s, const struct css_node * v, unsigned int i); -static void parse_position(struct css_style * const s, const struct css_node * v); -static void parse_right(struct css_style * const s, const struct css_node * v); -static void parse_table_layout(struct css_style * const s, const struct css_node * v); -static void parse_text_align(struct css_style * const s, const struct css_node * const v); -static void parse_text_decoration(struct css_style * const s, const struct css_node * const v); -static void parse_text_indent(struct css_style * const s, const struct css_node * const v); -static void parse_text_transform(struct css_style * const s, const struct css_node * const v); -static void parse_top(struct css_style * const s, const struct css_node * v); -static void parse_unicode_bidi(struct css_style * const s, const struct css_node * const v); -static void parse_vertical_align(struct css_style * const s, const struct css_node * v); -static void parse_visibility(struct css_style * const s, const struct css_node * const v); -static void parse_widows(struct css_style * const s, const struct css_node * const v); -static void parse_width(struct css_style * const s, const struct css_node * const v); -static void parse_white_space(struct css_style * const s, const struct css_node * const v); -static void parse_word_spacing(struct css_style * const s, const struct css_node * v); -static void parse_z_index(struct css_style * const s, const struct css_node * const v); -static css_text_decoration css_text_decoration_parse(const char * const s, - int length); - - -/** Invalid hex */ -#define IH 0xffffffff -/** ASCII to hexadeximal conversion */ -static const unsigned int ascii_to_hex[] = { - IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0x00 - 0x0f */ - IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0x10 - 0x1f */ - IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0x20 - 0x2f */ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, IH, IH, IH, IH, IH, IH, /* 0x30 - 0x3f */ - IH, 10, 11, 12, 13, 14, 15, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0x40 - 0x4f */ - IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0x50 - 0x5f */ - IH, 10, 11, 12, 13, 14, 15, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0x60 - 0x6f */ - IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0x70 - 0x7f */ - IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0x80 - 0x8f */ - IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0x90 - 0x9f */ - IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0xa0 - 0xaf */ - IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0xb0 - 0xbf */ - IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0xc0 - 0xcf */ - IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0xd0 - 0xdf */ - IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, /* 0xe0 - 0xef */ - IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH, IH /* 0xf0 - 0xff */ -}; - - -/** An entry in css_property_table. */ -struct css_property_entry { - const char name[25]; - void (*parse) (struct css_style * const s, - const struct css_node * const v); -}; - -/** Table of property parsers. MUST be sorted by property name. */ -static const struct css_property_entry css_property_table[] = { - { "background", parse_background }, - { "background-attachment", parse_background_attachment }, - { "background-color", parse_background_color }, - { "background-image", parse_background_image }, - { "background-position", parse_background_position }, - { "background-repeat", parse_background_repeat }, - { "border", parse_border }, - { "border-bottom", parse_border_bottom }, - { "border-bottom-color", parse_border_bottom_color }, - { "border-bottom-style", parse_border_bottom_style }, - { "border-bottom-width", parse_border_bottom_width }, - { "border-collapse", parse_border_collapse }, - { "border-color", parse_border_color }, - { "border-left", parse_border_left }, - { "border-left-color", parse_border_left_color }, - { "border-left-style", parse_border_left_style }, - { "border-left-width", parse_border_left_width }, - { "border-right", parse_border_right }, - { "border-right-color", parse_border_right_color }, - { "border-right-style", parse_border_right_style }, - { "border-right-width", parse_border_right_width }, - { "border-spacing", parse_border_spacing }, - { "border-style", parse_border_style }, - { "border-top", parse_border_top }, - { "border-top-color", parse_border_top_color }, - { "border-top-style", parse_border_top_style }, - { "border-top-width", parse_border_top_width }, - { "border-width", parse_border_width }, - { "bottom", parse_bottom }, - { "caption-side", parse_caption_side }, - { "clear", parse_clear }, - { "clip", parse_clip }, - { "color", parse_color }, - { "content", parse_content }, - { "counter-increment", parse_counter_increment }, - { "counter-reset", parse_counter_reset }, - { "cursor", parse_cursor }, - { "direction", parse_direction }, - { "display", parse_display }, - { "empty-cells", parse_empty_cells }, - { "float", parse_float }, - { "font", parse_font }, - { "font-family", parse_font_family }, - { "font-size", parse_font_size }, - { "font-style", parse_font_style }, - { "font-variant", parse_font_variant }, - { "font-weight", parse_font_weight }, - { "height", parse_height }, - { "left", parse_left }, - { "letter-spacing", parse_letter_spacing }, - { "line-height", parse_line_height }, - { "list-style", parse_list_style }, - { "list-style-image", parse_list_style_image }, - { "list-style-position", parse_list_style_position }, - { "list-style-type", parse_list_style_type }, - { "margin", parse_margin }, - { "margin-bottom", parse_margin_bottom }, - { "margin-left", parse_margin_left }, - { "margin-right", parse_margin_right }, - { "margin-top", parse_margin_top }, - { "max-height", parse_max_height }, - { "max-width", parse_max_width }, - { "min-height", parse_min_height }, - { "min-width", parse_min_width }, - { "orphans", parse_orphans }, - { "outline", parse_outline }, - { "outline-color", parse_outline_color }, - { "outline-style", parse_outline_style }, - { "outline-width", parse_outline_width }, - { "overflow", parse_overflow }, - { "padding", parse_padding }, - { "padding-bottom", parse_padding_bottom }, - { "padding-left", parse_padding_left }, - { "padding-right", parse_padding_right }, - { "padding-top", parse_padding_top }, - { "page-break-after", parse_page_break_after }, - { "page-break-before", parse_page_break_before }, - { "page-break-inside", parse_page_break_inside }, - { "position", parse_position }, - { "right", parse_right }, - { "table-layout", parse_table_layout }, - { "text-align", parse_text_align }, - { "text-decoration", parse_text_decoration }, - { "text-indent", parse_text_indent }, - { "text-transform", parse_text_transform }, - { "top", parse_top }, - { "unicode-bidi", parse_unicode_bidi }, - { "vertical-align", parse_vertical_align }, - { "visibility", parse_visibility }, - { "white-space", parse_white_space }, - { "widows", parse_widows }, - { "width", parse_width }, - { "word-spacing", parse_word_spacing }, - { "z-index", parse_z_index } -}; - - -/** An entry in css_colour_table. */ -struct css_colour_entry { - const char name[21]; - colour col; -}; - -/* Table of standard colour names. MUST be sorted by colour name. - * Note: colour is 0xbbggrr. */ -static const struct css_colour_entry css_colour_table[] = { - { "aliceblue", 0xfff8f0 }, - { "antiquewhite", 0xd7ebfa }, - { "aqua", 0xffff00 }, - { "aquamarine", 0xd4ff7f }, - { "azure", 0xfffff0 }, - { "beige", 0xdcf5f5 }, - { "bisque", 0xc4e4ff }, - { "black", 0x000000 }, - { "blanchedalmond", 0xcdebff }, - { "blue", 0xff0000 }, - { "blueviolet", 0xe22b8a }, - { "brown", 0x2a2aa5 }, - { "burlywood", 0x87b8de }, - { "cadetblue", 0xa09e5f }, - { "chartreuse", 0x00ff7f }, - { "chocolate", 0x1e69d2 }, - { "coral", 0x507fff }, - { "cornflowerblue", 0xed9564 }, - { "cornsilk", 0xdcf8ff }, - { "crimson", 0x3c14dc }, - { "cyan", 0xffff00 }, - { "darkblue", 0x8b0000 }, - { "darkcyan", 0x8b8b00 }, - { "darkgoldenrod", 0x0b86b8 }, - { "darkgray", 0xa9a9a9 }, - { "darkgreen", 0x006400 }, - { "darkgrey", 0xa9a9a9 }, - { "darkkhaki", 0x6bb7bd }, - { "darkmagenta", 0x8b008b }, - { "darkolivegreen", 0x2f6b55 }, - { "darkorange", 0x008cff }, - { "darkorchid", 0xcc3299 }, - { "darkred", 0x00008b }, - { "darksalmon", 0x7a96e9 }, - { "darkseagreen", 0x8fbc8f }, - { "darkslateblue", 0x8b3d48 }, - { "darkslategray", 0x4f4f2f }, - { "darkslategrey", 0x4f4f2f }, - { "darkturquoise", 0xd1ce00 }, - { "darkviolet", 0xd30094 }, - { "deeppink", 0x9314ff }, - { "deepskyblue", 0xffbf00 }, - { "dimgray", 0x696969 }, - { "dimgrey", 0x696969 }, - { "dodgerblue", 0xff901e }, - { "feldspar", 0x7592d1 }, /* not SVG-1.0 */ - { "firebrick", 0x2222b2 }, - { "floralwhite", 0xf0faff }, - { "forestgreen", 0x228b22 }, - { "fuchsia", 0xff00ff }, - { "gainsboro", 0xdcdcdc }, - { "ghostwhite", 0xfff8f8 }, - { "gold", 0x00d7ff }, - { "goldenrod", 0x20a5da }, - { "gray", 0x808080 }, - { "green", 0x008000 }, - { "greenyellow", 0x2fffad }, - { "grey", 0x808080 }, - { "honeydew", 0xf0fff0 }, - { "hotpink", 0xb469ff }, - { "indianred", 0x5c5ccd }, - { "indigo", 0x82004b }, - { "ivory", 0xf0ffff }, - { "khaki", 0x8ce6f0 }, - { "lavender", 0xfae6e6 }, - { "lavenderblush", 0xf5f0ff }, - { "lawngreen", 0x00fc7c }, - { "lemonchiffon", 0xcdfaff }, - { "lightblue", 0xe6d8ad }, - { "lightcoral", 0x8080f0 }, - { "lightcyan", 0xffffe0 }, - { "lightgoldenrodyellow", 0xd2fafa }, - { "lightgray", 0xd3d3d3 }, - { "lightgreen", 0x90ee90 }, - { "lightgrey", 0xd3d3d3 }, - { "lightpink", 0xc1b6ff }, - { "lightsalmon", 0x7aa0ff }, - { "lightseagreen", 0xaab220 }, - { "lightskyblue", 0xface87 }, - { "lightslateblue", 0xff7084 }, /* not SVG-1.0*/ - { "lightslategray", 0x998877 }, - { "lightslategrey", 0x998877 }, - { "lightsteelblue", 0xdec4b0 }, - { "lightyellow", 0xe0ffff }, - { "lime", 0x00ff00 }, - { "limegreen", 0x32cd32 }, - { "linen", 0xe6f0fa }, - { "magenta", 0xff00ff }, - { "maroon", 0x000080 }, - { "mediumaquamarine", 0xaacd66 }, - { "mediumblue", 0xcd0000 }, - { "mediumorchid", 0xd355ba }, - { "mediumpurple", 0xdb7093 }, - { "mediumseagreen", 0x71b33c }, - { "mediumslateblue", 0xee687b }, - { "mediumspringgreen", 0x9afa00 }, - { "mediumturquoise", 0xccd148 }, - { "mediumvioletred", 0x8515c7 }, - { "midnightblue", 0x701919 }, - { "mintcream", 0xfafff5 }, - { "mistyrose", 0xe1e4ff }, - { "moccasin", 0xb5e4ff }, - { "navajowhite", 0xaddeff }, - { "navy", 0x800000 }, - { "oldlace", 0xe6f5fd }, - { "olive", 0x008080 }, - { "olivedrab", 0x238e6b }, - { "orange", 0x00a5ff }, - { "orangered", 0x0045ff }, - { "orchid", 0xd670da }, - { "palegoldenrod", 0xaae8ee }, - { "palegreen", 0x98fb98 }, - { "paleturquoise", 0xeeeeaf }, - { "palevioletred", 0x9370db }, - { "papayawhip", 0xd5efff }, - { "peachpuff", 0xb9daff }, - { "peru", 0x3f85cd }, - { "pink", 0xcbc0ff }, - { "plum", 0xdda0dd }, - { "powderblue", 0xe6e0b0 }, - { "purple", 0x800080 }, - { "red", 0x0000ff }, - { "rosybrown", 0x8f8fbc }, - { "royalblue", 0xe16941 }, - { "saddlebrown", 0x13458b }, - { "salmon", 0x7280fa }, - { "sandybrown", 0x60a4f4 }, - { "seagreen", 0x578b2e }, - { "seashell", 0xeef5ff }, - { "sienna", 0x2d52a0 }, - { "silver", 0xc0c0c0 }, - { "skyblue", 0xebce87 }, - { "slateblue", 0xcd5a6a }, - { "slategray", 0x908070 }, - { "slategrey", 0x908070 }, - { "snow", 0xfafaff }, - { "springgreen", 0x7fff00 }, - { "steelblue", 0xb48246 }, - { "tan", 0x8cb4d2 }, - { "teal", 0x808000 }, - { "thistle", 0xd8bfd8 }, - { "tomato", 0x4763ff }, - { "transparent", NS_TRANSPARENT }, - { "turquoise", 0xd0e040 }, - { "violet", 0xee82ee }, - { "violetred", 0x9020d0 }, /* not SVG-1.0*/ - { "wheat", 0xb3def5 }, - { "white", 0xffffff }, - { "whitesmoke", 0xf5f5f5 }, - { "yellow", 0x00ffff }, - { "yellowgreen", 0x32cd9a }, -}; - - -/** An entry in css_font_size_table. */ -struct css_font_size_entry { - const char name[10]; - float size; -}; - -/** Table of font sizes. MUST be sorted by name. */ -#define SIZE_FACTOR 1.2 -static const struct css_font_size_entry css_font_size_table[] = { - { "large", 1.0 * SIZE_FACTOR }, - { "medium", 1.0 }, - { "small", 1.0 / SIZE_FACTOR }, - { "x-large", 1.0 * SIZE_FACTOR * SIZE_FACTOR }, - { "x-small", 1.0 / (SIZE_FACTOR * SIZE_FACTOR) }, - { "xx-large", 1.0 * SIZE_FACTOR * SIZE_FACTOR * SIZE_FACTOR }, - { "xx-small", 1.0 / (SIZE_FACTOR * SIZE_FACTOR * SIZE_FACTOR) }, -}; - - -/** - * Add a ruleset to a stylesheet. - */ - -void css_add_ruleset(struct content *c, - struct css_selector *selector, - struct css_node *declaration) -{ - bool found; - struct css_stylesheet *stylesheet = c->data.css.css; - struct css_selector *n, *sel, *next_sel, *prev; - struct css_style *style; - unsigned int hash; - - for (sel = selector; sel != 0; sel = next_sel) { - next_sel = sel->next; - - /* check if this selector is already present */ - found = false; - prev = 0; - hash = css_hash(sel->data, sel->data_length); - /* selectors are ordered by specificity in the hash chain */ - for (n = stylesheet->rule[hash]; - n && n->specificity < sel->specificity; - n = n->next) - prev = n; - for ( ; n && n->specificity == sel->specificity; - n = n->next) { - prev = n; - if (css_compare_selectors(sel, n)) { - found = true; - break; - } - } - if (!found) { - /* not present: construct a new struct css_style */ - LOG(("constructing new style")); - style = css_duplicate_style(&css_empty_style); - if (!style) { - /** \todo report to user */ - css_free_selector(sel); - return; - } - sel->style = style; - sel->next = n; - if (prev) - prev->next = sel; - else - stylesheet->rule[hash] = sel; - c->size += sizeof(*style); - } else { - /* already exists: augument existing style */ - LOG(("augumenting existing style")); - style = n->style; - sel->next = 0; - css_free_selector(sel); - } - - /* fill in the declarations */ - css_add_declarations(style, declaration); - } -} - - -/** - * Add declarations to a style. - */ - -void css_add_declarations(struct css_style *style, struct css_node *declaration) -{ - char name[25]; /* this must be the same length as p->name */ - struct css_node *n; - for (n = declaration; n != 0; n = n->next) { - struct css_property_entry *p; - assert(n->type == CSS_NODE_DECLARATION && n->data && n->value); - if (24 < n->data_length) - continue; - strncpy(name, n->data, n->data_length); - name[n->data_length] = 0; - p = bsearch(name, css_property_table, - sizeof css_property_table / - sizeof css_property_table[0], - sizeof css_property_table[0], - (int (*)(const void *, const void *)) - strcasecmp); - if (p == 0) - continue; - p->parse(style, n->value); - } -} - - -/** - * Compare two css_selectors. - */ - -bool css_compare_selectors(const struct css_selector *n0, - const struct css_selector *n1) -{ - struct css_selector *m0, *m1; - unsigned int count0 = 0, count1 = 0; - - /* compare element name */ - if (!((n0->data == 0 && n1->data == 0) || - (n0->data != 0 && n1->data != 0 && - n0->data_length == n1->data_length && - strncmp(n0->data, n1->data, n0->data_length) == 0))) - return false; - - if (n0->comb != n1->comb) - return false; - - /* compare classes and ids */ - for (m0 = n0->detail; m0 != 0; m0 = m0->next) - count0++; - for (m1 = n1->detail; m1 != 0; m1 = m1->next) - count1++; - if (count0 != count1) - return false; - for (m0 = n0->detail; m0 != 0; m0 = m0->next) { - bool found = false; - for (m1 = n1->detail; m1 != 0; m1 = m1->next) { - /* TODO: should this be case sensitive for IDs? */ - if (m0->type == m1->type && - m0->data_length == m1->data_length && - strncasecmp(m0->data, m1->data, - m0->data_length) == 0 && - ((m0->data2 == 0 && m1->data2 == 0) || - (m0->data2_length == m1->data2_length && - strncasecmp(m0->data2, m1->data2, - m0->data2_length) == 0))) { - found = true; - break; - } - } - if (!found) - return false; - } - - /* compare ancestors */ - if (n0->comb == CSS_COMB_NONE) - return true; - - return css_compare_selectors(n0->combiner, n1->combiner); -} - - -/* - * Property parsers. - */ - -int parse_length(struct css_length * const length, - const struct css_node * const v, bool non_negative) -{ - css_unit u; - float value; - int num_length; - - if (v->type == CSS_NODE_NUMBER && fabs(atof(v->data)) < 0.0001) { - length->unit = CSS_UNIT_PX; - length->value = 0; - return 0; - } - - if (v->type != CSS_NODE_DIMENSION && v->type != CSS_NODE_NUMBER) - return 1; - - num_length = strspn(v->data, "0123456789+-."); - - if (v->type == CSS_NODE_DIMENSION) { - u = css_unit_parse(v->data + num_length, - v->data_length - num_length); - if (u == CSS_UNIT_UNKNOWN) { - return 1; - } - } else { - u = CSS_UNIT_PX; - } - value = atof(v->data); - if (non_negative && value < 0) - return 1; - length->unit = u; - length->value = value; - return 0; -} - - -colour named_colour(const char *name) -{ - struct css_colour_entry *col; - int length; - - col = bsearch(name, css_colour_table, - sizeof css_colour_table / sizeof css_colour_table[0], - sizeof css_colour_table[0], - (int (*)(const void *, const void *)) strcasecmp); - if (col != 0) - return col->col; - - /* A common error is the omission of the '#' from the - * start of a colour specified in #rrggbb or #rgb format. - * This attempts to detect and recover from this. - */ - length = strlen(name); - if ((length == 3) || (length == 6)) - return hex_colour(name, length); - return CSS_COLOR_NONE; -} - - -colour parse_colour(const struct css_node * const v) -{ - colour c = CSS_COLOR_NONE; - struct css_colour_entry *col; - char colour_name[21]; - - switch (v->type) { - case CSS_NODE_HASH: - if ((v->data_length == 4) || (v->data_length == 7)) - c = hex_colour(v->data + 1, v->data_length - 1); - break; - - case CSS_NODE_FUNCTION: - if (v->data_length == 4 && - strncasecmp(v->data, "rgb", 3) == 0) - c = css_parse_rgb(v->value); - break; - - case CSS_NODE_IDENT: - if (20 < v->data_length) - break; - strncpy(colour_name, v->data, v->data_length); - colour_name[v->data_length] = 0; - col = bsearch(colour_name, css_colour_table, - sizeof css_colour_table / - sizeof css_colour_table[0], - sizeof css_colour_table[0], - (int (*)(const void *, const void *)) - strcasecmp); - if (col != 0) - c = col->col; - break; - - default: - break; - } - - /* Hex colour values without a preceding # are invalid but it is a - * common omission that other browsers cater for. */ - if (c == CSS_COLOR_NONE && (v->type == CSS_NODE_DELIM || - v->type == CSS_NODE_IDENT || v->type == CSS_NODE_NUMBER || - v->type == CSS_NODE_DIMENSION)) { - if ((v->data_length == 3) || (v->data_length == 6)) - return hex_colour(v->data, v->data_length); - } - return c; -} - - -/** - * Parse an RGB value in hexadecimal notation. - */ -colour hex_colour(const char *text, int length) -{ - colour c; - - /* parse RGB */ - if (length == 3) { - c = ascii_to_hex[(int)text[0]] | (ascii_to_hex[(int)text[1]] << 8) | - (ascii_to_hex[(int)text[2]] << 16); - if (c & (0xff << 24)) - return CSS_COLOR_NONE; - return c | (c << 4); - } - - /* parse RRGGBB */ - if (length == 6) { - c = ascii_to_hex[(int)text[1]] | (ascii_to_hex[(int)text[0]] << 4) | - (ascii_to_hex[(int)text[3]] << 8) | (ascii_to_hex[(int)text[2]] << 12) | - (ascii_to_hex[(int)text[5]] << 16) | (ascii_to_hex[(int)text[4]] << 20); - if (c & (0xff << 24)) - return CSS_COLOR_NONE; - return c; - } - return CSS_COLOR_NONE; -} - - -/** - * Parse an RGB value in functional notation. - */ - -colour css_parse_rgb(struct css_node *v) -{ - unsigned int i; - int c[3]; - - /* we expect exactly the nodes - * X COMMA X COMMA X - * where X is NUMBER or PERCENTAGE - */ - - for (i = 0; i != 3; i++) { - if (!v) - return CSS_COLOR_NONE; - if (v->type == CSS_NODE_NUMBER) - c[i] = atoi(v->data); - else if (v->type == CSS_NODE_PERCENTAGE) - c[i] = atoi(v->data) * 255 / 100; - else - return CSS_COLOR_NONE; - if (c[i] < 0) - c[i] = 0; - if (255 < c[i]) - c[i] = 255; - - v = v->next; - - if (i == 2) { - if (v) - return CSS_COLOR_NONE; - } else { - if (!v || v->type != CSS_NODE_COMMA) - return CSS_COLOR_NONE; - - v = v->next; - } - } - - return (c[2] << 16) | (c[1] << 8) | c[0]; -} - -/** - * Parse a uri - * - * \param v node to parse - * \param uri updated to uri, if successful - * \return true on success, false on failure - */ -bool parse_uri(const struct css_node *v, char **uri) -{ - bool string = false; - const char *u; - char *t, *url; - url_func_result res; - - switch (v->type) { - case CSS_NODE_URI: - for (u = v->data + 4; - *u == ' ' || *u == '\t' || *u == '\r' || - *u == '\n' || *u == '\f'; - u++) - ; - if (*u == '\'' || *u == '"') { - string = true; - u++; - } - url = strndup(u, v->data_length - (u - v->data)); - if (!url) - return false; - for (t = url + strlen(url) - 2; - *t == ' ' || *t == '\t' || *t == '\r' || - *t == '\n' || *t == '\f'; - t--) - ; - if (string) - *t = 0; - else - *(t + 1) = 0; - - /* for inline style attributes, the stylesheet - * content is the parent HTML content - */ - if (v->stylesheet->type == CONTENT_HTML) - res = url_join(url, v->stylesheet->data.html.base_url, uri); - else - res = url_join(url, v->stylesheet->url, uri); - free(url); - if (res != URL_FUNC_OK) - return false; - break; - case CSS_NODE_STRING: - url = strndup(v->data, v->data_length); - if (!url) - return false; - - if (v->stylesheet->type == CONTENT_HTML) - res = url_join(url, v->stylesheet->data.html.base_url, uri); - else - res = url_join(url, v->stylesheet->url, uri); - free(url); - if (res != URL_FUNC_OK) - return false; - break; - default: - return false; - } - - return true; -} - -/** - * \name Individual property parsers. - * \{ - */ - -void parse_background(struct css_style * const s, - const struct css_node * v) -{ - colour c = NS_TRANSPARENT, c2; - css_background_image_type bi = CSS_BACKGROUND_IMAGE_NONE, bi2; - char *bi_uri = 0; - css_background_repeat br = CSS_BACKGROUND_REPEAT_REPEAT, br2; - css_background_attachment ba = CSS_BACKGROUND_ATTACHMENT_SCROLL, ba2; - struct css_background_position horz = - { CSS_BACKGROUND_POSITION_PERCENT, { 0 } }; - struct css_background_position vert = - { CSS_BACKGROUND_POSITION_PERCENT, { 0 } }; - struct css_background_position horz2, vert2; - bool had_colour = false, had_image = false, had_repeat = false, - had_attachment = false, had_position = false; - - while (v) { - switch (v->type) { - case CSS_NODE_URI: - case CSS_NODE_STRING: - /* background-image */ - if (had_image) - goto error; - had_image = true; - if (!css_background_image_parse(v, &bi2, - &bi_uri)) - goto error; - bi = bi2; - v = v->next; - break; - - case CSS_NODE_DIMENSION: - case CSS_NODE_NUMBER: - case CSS_NODE_PERCENTAGE: - /* background-position */ - if (had_position) - goto error; - had_position = true; - if (!css_background_position_parse(&v, - &horz2, &vert2)) - goto error; - horz = horz2; - vert = vert2; - break; - - case CSS_NODE_IDENT: - /* could be background-image: none */ - if (v->data_length == 4 && - strncasecmp(v->data, "none", - 4) == 0) { - if (had_image) - goto error; - had_image = true; - bi = CSS_BACKGROUND_IMAGE_NONE; - v = v->next; - break; - } - - /* background-repeat */ - br2 = css_background_repeat_parse(v->data, - v->data_length); - if (br2 != CSS_BACKGROUND_REPEAT_UNKNOWN) { - if (had_repeat) - goto error; - had_repeat = true; - br = br2; - v = v->next; - break; - } - - /* background-attachment */ - ba2 = css_background_attachment_parse(v->data, - v->data_length); - if (ba2 != CSS_BACKGROUND_ATTACHMENT_UNKNOWN) { - if (had_attachment) - goto error; - had_attachment = true; - ba = ba2; - v = v->next; - break; - } - - /* background-position */ - if (css_background_position_parse(&v, - &horz2, &vert2)) { - if (had_position) - goto error; - had_position = true; - horz = horz2; - vert = vert2; - break; - } - - /* fall through */ - case CSS_NODE_HASH: - case CSS_NODE_FUNCTION: - /* background-color */ - if (had_colour) - goto error; - had_colour = true; - c2 = parse_colour(v); - if (c2 != CSS_COLOR_NONE) { - c = c2; - v = v->next; - break; - } - - /* fall through */ - default: - /* parsing failed */ - goto error; - } - } - - s->background_color = c; - s->background_image.type = bi; - if (s->background_image.type == CSS_BACKGROUND_IMAGE_URI) - free(s->background_image.uri); - s->background_image.uri = bi_uri; - s->background_repeat = br; - s->background_attachment = ba; - s->background_position.horz = horz; - s->background_position.vert = vert; - - return; - -error: - free(bi_uri); -} - - -void parse_background_attachment(struct css_style * const s, - const struct css_node * const v) -{ - css_background_attachment z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_background_attachment_parse(v->data, v->data_length); - if (z != CSS_BACKGROUND_ATTACHMENT_UNKNOWN) - s->background_attachment = z; -} - - -void parse_background_color(struct css_style * const s, - const struct css_node * const v) -{ - colour c; - if (v->next) - return; - c = parse_colour(v); - if (c != CSS_COLOR_NONE) - s->background_color = c; -} - - -void parse_background_image(struct css_style * const s, - const struct css_node * const v) -{ - css_background_image_type type; - char *uri = 0; - - if (v->next) - return; - if (!css_background_image_parse(v, &type, &uri)) - return; - - if (s->background_image.type == CSS_BACKGROUND_IMAGE_URI) - free(s->background_image.uri); - s->background_image.type = type; - s->background_image.uri = uri; -} - - - -/** - * Parse a background-image property. - * - * \param node node to parse - * \param type updated to background image type - * \param uri updated to background image uri, if type is - * CSS_BACKGROUND_IMAGE_URI - * \return true on success, false on parse failure - */ - -bool css_background_image_parse(const struct css_node *v, - css_background_image_type *type, char **uri) -{ - switch (v->type) { - case CSS_NODE_URI: - case CSS_NODE_STRING: - if (!parse_uri(v, uri)) - return false; - *type = CSS_BACKGROUND_IMAGE_URI; - break; - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - *type = CSS_BACKGROUND_IMAGE_INHERIT; - else if (v->data_length == 4 && - strncasecmp(v->data, "none", 4) == 0) - *type = CSS_BACKGROUND_IMAGE_NONE; - break; - default: - return false; - } - return true; -} - - -/** An entry in css_background_table. */ -struct css_background_entry { - const char *keyword; - unsigned int length; - float value; - bool horizontal; - bool vertical; -}; - -/** Lookup table for parsing background-postion. */ -struct css_background_entry css_background_table[] = { - { "left", 4, 0, true, false }, - { "right", 5, 100, true, false }, - { "top", 3, 0, false, true }, - { "bottom", 6, 100, false, true }, - { "center", 6, 50, false, false } /* true, true would be more - logical, but this actually simplifies the code */ -}; - -#define CSS_BACKGROUND_TABLE_ENTRIES (sizeof css_background_table / \ - sizeof css_background_table[0]) - - -/** - * Lookup a background-position keyword in css_background_table. - */ - -struct css_background_entry *css_background_lookup( - const struct css_node *v) -{ - unsigned int i; - for (i = 0; i != CSS_BACKGROUND_TABLE_ENTRIES; i++) - if (css_background_table[i].length == v->data_length && - strncasecmp(v->data, - css_background_table[i].keyword, - css_background_table[i].length) == 0) - break; - if (i == CSS_BACKGROUND_TABLE_ENTRIES) - return NULL; - return &css_background_table[i]; -} - - -void parse_background_position(struct css_style * const s, - const struct css_node * v) -{ - const struct css_node *node = v; - struct css_background_position horz, vert; - - if (v->next && v->next->next) - /* more than two nodes */ - return; - - if (!css_background_position_parse(&node, &horz, &vert)) - return; - if (node) - /* didn't parse all the nodes */ - return; - - s->background_position.horz = horz; - s->background_position.vert = vert; -} - - -/** - * Parse a background-position property. - * - * \param node list of nodes, updated to first unused node - * \param horz updated to horizontal background position - * \param vert updated to vertical background position - * \return true on success, false on parse failure - */ - -bool css_background_position_parse(const struct css_node **node, - struct css_background_position *horz, - struct css_background_position *vert) -{ - const struct css_node *v = *node; - const struct css_node *w = v->next; - struct css_background_entry *bg = NULL, *bg2 = NULL; - bool switched = false; - - if (v->type == CSS_NODE_IDENT) - bg = css_background_lookup(v); - if (w && w->type == CSS_NODE_IDENT) - bg2 = css_background_lookup(w); - - if (!(w && ((w->type == CSS_NODE_IDENT && bg2) || - w->type == CSS_NODE_PERCENTAGE || - w->type == CSS_NODE_DIMENSION || - w->type == CSS_NODE_NUMBER))) { - /* only one value specified */ - if (v->type == CSS_NODE_IDENT) { - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) - == 0) { - horz->pos = vert->pos = - CSS_BACKGROUND_POSITION_INHERIT; - return false; - } - - if (bg == NULL) - return false; - horz->pos = vert->pos = CSS_BACKGROUND_POSITION_PERCENT; - horz->value.percent = bg->horizontal ? bg->value : 50; - vert->value.percent = bg->vertical ? bg->value : 50; - } - else if (v->type == CSS_NODE_PERCENTAGE) { - horz->pos = vert->pos = CSS_BACKGROUND_POSITION_PERCENT; - horz->value.percent = atof(v->data); - vert->value.percent = 50.0; - } - else if ((v->type == CSS_NODE_DIMENSION) || - (v->type == CSS_NODE_NUMBER)) { - if (parse_length(&horz->value. - length, v, false) == 0) { - horz->pos = CSS_BACKGROUND_POSITION_LENGTH; - vert->pos = CSS_BACKGROUND_POSITION_PERCENT; - vert->value.percent = 50.0; - } - } - - *node = w; - return true; - } - - /* two values specified */ - if (v->type == CSS_NODE_IDENT && w->type == CSS_NODE_IDENT) { - /* both keywords */ - if (bg == NULL || bg2 == NULL) - return false; - if ((bg->horizontal && bg2->horizontal) || - (bg->vertical && bg2->vertical)) - return false; - horz->pos = vert->pos = CSS_BACKGROUND_POSITION_PERCENT; - horz->value.percent = vert->value.percent = 50; - if (bg->horizontal) - horz->value.percent = bg->value; - else if (bg2->horizontal) - horz->value.percent = bg2->value; - if (bg->vertical) - vert->value.percent = bg->value; - else if (bg2->vertical) - vert->value.percent = bg2->value; - - *node = w->next; - return true; - } - - /* reverse specifiers such that idents are places in h, v order */ - if ((v->type == CSS_NODE_IDENT && bg && bg->vertical) || - (w->type == CSS_NODE_IDENT && bg2 && bg2->horizontal)) { - const struct css_node *n_temp; - struct css_background_entry *b_temp; - - n_temp = v; - v = w; - w = n_temp; - - b_temp = bg; - bg = bg2; - bg2 = b_temp; - - /* Flag this so we update *node with the right thing */ - switched = true; - } - - if (v->type == CSS_NODE_IDENT) { /* horizontal value */ - if (bg == NULL || bg->vertical) - return false; - } - if (w->type == CSS_NODE_IDENT) { /* vertical value */ - if (bg2 == NULL || bg2->horizontal) - return false; - } - - if (v->type == CSS_NODE_IDENT) { /* horizontal value */ - assert(bg != NULL); - horz->pos = CSS_BACKGROUND_POSITION_PERCENT; - horz->value.percent = bg->value; - } else if (v->type == CSS_NODE_PERCENTAGE) { - horz->pos = CSS_BACKGROUND_POSITION_PERCENT; - horz->value.percent = atof(v->data); - } else if ((v->type == CSS_NODE_DIMENSION) || - (v->type == CSS_NODE_NUMBER)) { - if (parse_length(&horz->value.length, - v, false) == 0) - horz->pos = CSS_BACKGROUND_POSITION_LENGTH; - } - - if (w->type == CSS_NODE_IDENT) { /* vertical value */ - assert(bg2 != NULL); - vert->pos = CSS_BACKGROUND_POSITION_PERCENT; - vert->value.percent = bg2->value; - } else if (w->type == CSS_NODE_PERCENTAGE) { - vert->pos = CSS_BACKGROUND_POSITION_PERCENT; - vert->value.percent = atof(w->data); - } else if ((w->type == CSS_NODE_DIMENSION) || - (w->type == CSS_NODE_NUMBER)) { - if (parse_length(&vert->value.length, - w, false) == 0) - vert->pos = CSS_BACKGROUND_POSITION_LENGTH; - } - - *node = switched ? v->next : w->next; - - return true; -} - - -void parse_background_repeat(struct css_style * const s, - const struct css_node * const v) -{ - css_background_repeat z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_background_repeat_parse(v->data, v->data_length); - if (z != CSS_BACKGROUND_REPEAT_UNKNOWN) - s->background_repeat = z; -} - - -void parse_border_width(struct css_style * const s, - const struct css_node * const v) -{ - unsigned int count = 0; - const struct css_node *w; - - for (w = v; w; w = w->next, count++) - if (!((w->type == CSS_NODE_IDENT && ( - (w->data_length == 7 && - strncasecmp(w->data, "inherit", 7) == 0) || - (w->data_length == 4 && - strncasecmp(w->data, "thin", 4) == 0) || - (w->data_length == 6 && - strncasecmp(w->data, "medium", 6) == 0) || - (w->data_length == 5 && - strncasecmp(w->data, "thick", 5) == 0))) || - (w->type == CSS_NODE_DIMENSION) || - (w->type == CSS_NODE_NUMBER))) - return; - - w = v; - switch (count) { - case 1: /* one value: applies to all sides */ - parse_border_width_side(s, w, TOP); - parse_border_width_side(s, w, RIGHT); - parse_border_width_side(s, w, BOTTOM); - parse_border_width_side(s, w, LEFT); - break; - case 2: /* (top and bottom), (left and right) */ - parse_border_width_side(s, w, TOP); - parse_border_width_side(s, w, BOTTOM); - w = w->next; - parse_border_width_side(s, w, RIGHT); - parse_border_width_side(s, w, LEFT); - break; - case 3: /* top, (left and right), bottom */ - parse_border_width_side(s, w, TOP); - w = w->next; - parse_border_width_side(s, w, RIGHT); - parse_border_width_side(s, w, LEFT); - w = w->next; - parse_border_width_side(s, w, BOTTOM); - break; - case 4: /* top, right, bottom, left */ - parse_border_width_side(s, w, TOP); - w = w->next; - parse_border_width_side(s, w, RIGHT); - w = w->next; - parse_border_width_side(s, w, BOTTOM); - w = w->next; - parse_border_width_side(s, w, LEFT); - break; - } -} - -#define PARSE_BORDER_WIDTH(side, z) \ -void parse_border_ ## side ## _width(struct css_style * const s, \ - const struct css_node * const v) \ -{ \ - if (v->next != 0) \ - return; \ - parse_border_width_side(s, v, z); \ -} - -PARSE_BORDER_WIDTH(top, TOP) -PARSE_BORDER_WIDTH(right, RIGHT) -PARSE_BORDER_WIDTH(bottom, BOTTOM) -PARSE_BORDER_WIDTH(left, LEFT) - -void parse_border_width_side(struct css_style * const s, - const struct css_node * const v, unsigned int i) -{ - if (v->type == CSS_NODE_IDENT) { - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->border[i].width.width = CSS_BORDER_WIDTH_INHERIT; - else if (v->data_length == 4 && - strncasecmp(v->data, "thin", 4) == 0) { - s->border[i].width.width = CSS_BORDER_WIDTH_LENGTH; - s->border[i].width.value.value = 1; - s->border[i].width.value.unit = CSS_UNIT_PX; - } else if (v->data_length == 6 && - strncasecmp(v->data, "medium", 6) == 0) { - s->border[i].width.width = CSS_BORDER_WIDTH_LENGTH; - s->border[i].width.value.value = 2; - s->border[i].width.value.unit = CSS_UNIT_PX; - } else if (v->data_length == 5 && - strncasecmp(v->data, "thick", 5) == 0) { - s->border[i].width.width = CSS_BORDER_WIDTH_LENGTH; - s->border[i].width.value.value = 4; - s->border[i].width.value.unit = CSS_UNIT_PX; - } - } else if ((v->type == CSS_NODE_DIMENSION || - v->type == CSS_NODE_NUMBER) && - parse_length(&s->border[i].width.value, v, true) == 0) - s->border[i].width.width = CSS_BORDER_WIDTH_LENGTH; -} - -void parse_border_color(struct css_style * const s, - const struct css_node * const v) -{ - unsigned int count = 0; - const struct css_node *w; - - for (w = v; w; w = w->next, count++) - if (!(w->type == CSS_NODE_HASH || - w->type == CSS_NODE_FUNCTION || - w->type == CSS_NODE_IDENT)) - return; - - w = v; - switch (count) { - case 1: /* one value: applies to all sides */ - parse_border_color_side(s, w, TOP); - parse_border_color_side(s, w, RIGHT); - parse_border_color_side(s, w, BOTTOM); - parse_border_color_side(s, w, LEFT); - break; - case 2: /* (top and bottom), (left and right) */ - parse_border_color_side(s, w, TOP); - parse_border_color_side(s, w, BOTTOM); - w = w->next; - parse_border_color_side(s, w, RIGHT); - parse_border_color_side(s, w, LEFT); - break; - case 3: /* top, (left and right), bottom */ - parse_border_color_side(s, w, TOP); - w = w->next; - parse_border_color_side(s, w, RIGHT); - parse_border_color_side(s, w, LEFT); - w = w->next; - parse_border_color_side(s, w, BOTTOM); - break; - case 4: /* top, right, bottom, left */ - parse_border_color_side(s, w, TOP); - w = w->next; - parse_border_color_side(s, w, RIGHT); - w = w->next; - parse_border_color_side(s, w, BOTTOM); - w = w->next; - parse_border_color_side(s, w, LEFT); - break; - } -} - -#define PARSE_BORDER_COLOR(side, z) \ -void parse_border_ ## side ## _color(struct css_style * const s, \ - const struct css_node * const v) \ -{ \ - if (v->next != 0) \ - return; \ - parse_border_color_side(s, v, z); \ -} - -PARSE_BORDER_COLOR(top, TOP) -PARSE_BORDER_COLOR(right, RIGHT) -PARSE_BORDER_COLOR(bottom, BOTTOM) -PARSE_BORDER_COLOR(left, LEFT) - -void parse_border_color_side(struct css_style * const s, - const struct css_node * const v, unsigned int i) -{ - colour c = parse_colour(v); - if (c != CSS_COLOR_NONE) - s->border[i].color = c; -} - -void parse_border_style(struct css_style * const s, - const struct css_node * const v) -{ - unsigned int count = 0; - const struct css_node *w; - - for (w = v; w; w = w->next, count++) - if (w->type != CSS_NODE_IDENT) - return; - - w = v; - switch (count) { - case 1: /* one value: applies to all sides */ - parse_border_style_side(s, w, TOP); - parse_border_style_side(s, w, RIGHT); - parse_border_style_side(s, w, BOTTOM); - parse_border_style_side(s, w, LEFT); - break; - case 2: /* (top and bottom), (left and right) */ - parse_border_style_side(s, w, TOP); - parse_border_style_side(s, w, BOTTOM); - w = w->next; - parse_border_style_side(s, w, RIGHT); - parse_border_style_side(s, w, LEFT); - break; - case 3: /* top, (left and right), bottom */ - parse_border_style_side(s, w, TOP); - w = w->next; - parse_border_style_side(s, w, RIGHT); - parse_border_style_side(s, w, LEFT); - w = w->next; - parse_border_style_side(s, w, BOTTOM); - break; - case 4: /* top, right, bottom, left */ - parse_border_style_side(s, w, TOP); - w = w->next; - parse_border_style_side(s, w, RIGHT); - w = w->next; - parse_border_style_side(s, w, BOTTOM); - w = w->next; - parse_border_style_side(s, w, LEFT); - break; - } -} - -#define PARSE_BORDER_STYLE(side, z) \ -void parse_border_ ## side ## _style(struct css_style * const s, \ - const struct css_node * const v) \ -{ \ - if (v->next != 0 || v->type != CSS_NODE_IDENT) \ - return; \ - parse_border_style_side(s, v, z); \ -} - -PARSE_BORDER_STYLE(top, TOP) -PARSE_BORDER_STYLE(right, RIGHT) -PARSE_BORDER_STYLE(bottom, BOTTOM) -PARSE_BORDER_STYLE(left, LEFT) - -void parse_border_style_side(struct css_style * const s, - const struct css_node * const v, unsigned int i) -{ - css_border_style z = css_border_style_parse(v->data, v->data_length); - if (z != CSS_BORDER_STYLE_UNKNOWN) - s->border[i].style = z; -} - -void parse_border(struct css_style * const s, - const struct css_node * const v) -{ - parse_border_side(s, v, TOP); - parse_border_side(s, v, RIGHT); - parse_border_side(s, v, BOTTOM); - parse_border_side(s, v, LEFT); -} - -#define PARSE_BORDER(side, z) \ -void parse_border_ ## side(struct css_style * const s, \ - const struct css_node * const v) \ -{ \ - parse_border_side(s, v, z); \ -} - -PARSE_BORDER(top, TOP) -PARSE_BORDER(right, RIGHT) -PARSE_BORDER(bottom, BOTTOM) -PARSE_BORDER(left, LEFT) - -void parse_border_side(struct css_style * const s, - const struct css_node *v, unsigned int i) -{ - colour c; - css_border_style z; - - if (!v->next && v->type == CSS_NODE_IDENT && - v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) { - s->border[i].color = CSS_COLOR_INHERIT; - s->border[i].width.width = CSS_BORDER_WIDTH_INHERIT; - s->border[i].style = CSS_BORDER_STYLE_INHERIT; - return; - } - - for (; v; v = v->next) { - c = parse_colour(v); - if (c != CSS_COLOR_NONE) { - s->border[i].color = c; - continue; - } - - if (v->type == CSS_NODE_IDENT) { - z = css_border_style_parse(v->data, v->data_length); - if (z != CSS_BORDER_STYLE_UNKNOWN) { - s->border[i].style = z; - continue; - } - } - - parse_border_width_side(s, v, i); - } -} - -void parse_border_collapse(struct css_style * const s, const struct css_node * v) -{ - css_border_collapse z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_border_collapse_parse(v->data, v->data_length); - if (z != CSS_BORDER_COLLAPSE_UNKNOWN) - s->border_collapse = z; -} - -void parse_border_spacing(struct css_style * const s, const struct css_node * v) -{ - if (v->next && v->next->next) - /* more than two nodes */ - return; - - if (!v->next) { - /* one node */ - if (v->type == CSS_NODE_IDENT && v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->border_spacing.border_spacing = - CSS_BORDER_SPACING_INHERIT; - else if (v->type == CSS_NODE_DIMENSION || - v->type == CSS_NODE_NUMBER) { - if (parse_length(&s->border_spacing.horz, - v, true) == 0 && - parse_length(&s->border_spacing.vert, - v, true) == 0) - s->border_spacing.border_spacing = - CSS_BORDER_SPACING_LENGTH; - } - } else { - /* two nodes */ - if ((v->type == CSS_NODE_DIMENSION || - v->type == CSS_NODE_NUMBER) && - (v->next->type == CSS_NODE_DIMENSION || - v->next->type == CSS_NODE_NUMBER)) { - if (parse_length(&s->border_spacing.horz, - v, true) == 0 && - parse_length(&s->border_spacing.vert, - v->next, true) == 0) - s->border_spacing.border_spacing = - CSS_BORDER_SPACING_LENGTH; - } - } -} - -void parse_caption_side(struct css_style * const s, const struct css_node * v) -{ - css_caption_side z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_caption_side_parse(v->data, v->data_length); - if (z != CSS_CAPTION_SIDE_UNKNOWN) - s->caption_side = z; -} - -void parse_clear(struct css_style * const s, const struct css_node * const v) -{ - css_clear z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_clear_parse(v->data, v->data_length); - if (z != CSS_CLEAR_UNKNOWN) - s->clear = z; -} - -void parse_clip(struct css_style * const s, const struct css_node * v) -{ - int i; - struct css_node *t; - - if (v->next != 0) - return; - - switch (v->type) { - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->clip.clip = CSS_CLIP_INHERIT; - else if (v->data_length == 4 && - strncasecmp(v->data, "auto", 4) == 0) - s->clip.clip = CSS_CLIP_AUTO; - break; - case CSS_NODE_FUNCTION: - /* must be rect(X,X,X,X) */ - if (v->data_length == 5 && - strncasecmp(v->data, "rect", 4) == 0) { - struct { - enum { CSS_CLIP_RECT_AUTO, - CSS_CLIP_RECT_LENGTH } rect; - struct css_length value; - } rect[4]; - - t = v->value; - if (!t) - return; - - for (i = 0; i != 4; i++) { - switch (t->type) { - case CSS_NODE_IDENT: - if (t->data_length == 4 && strncasecmp(t->data, "auto", 4) == 0) { - rect[i].rect = CSS_CLIP_RECT_AUTO; - } - else - return; - break; - case CSS_NODE_DIMENSION: - case CSS_NODE_NUMBER: - if (parse_length(&rect[i].value, - t, false) != 0) - return; - rect[i].rect = CSS_CLIP_RECT_LENGTH; - break; - default: - return; - } - - /* move to comma or end */ - t = t->next; - - if (i == 3 && t) - /* excess arguments - ignore rule */ - return; - else { - if (!t || t->type != CSS_NODE_COMMA) - /* insufficient arguments or - * no comma - ignore rule */ - return; - } - - /* move to next argument */ - t = t->next; - } - - /* If we reach here, rule is valid, so apply to s */ - for (i = 0; i != 4; i++) { - s->clip.rect[i].rect = rect[i].rect; - s->clip.rect[i].value.value = - rect[i].value.value; - s->clip.rect[i].value.unit = - rect[i].value.unit; - } - s->clip.clip = CSS_CLIP_RECT; - } - break; - default: - break; - } -} - -void parse_color(struct css_style * const s, const struct css_node * const v) -{ - colour c; - if (v->next) - return; - c = parse_colour(v); - if (c != CSS_COLOR_NONE) - s->color = c; -} - -void parse_content(struct css_style * const s, const struct css_node * v) -{ - struct css_content *new_content = NULL; - struct css_content *content; - struct css_node *t; - bool first = true; - - for (; v; v = v->next) { - switch (v->type) { - case CSS_NODE_STRING: - content = parse_content_new(&new_content, CSS_CONTENT_STRING); - if (!content) - return; - content->data.string = strndup(v->data, v->data_length); - if (!content->data.string) { - css_deep_free_content(new_content); - return; - } - break; - case CSS_NODE_URI: - content = parse_content_new(&new_content, CSS_CONTENT_URI); - if (!content) - return; - if (!parse_uri(v, &content->data.uri)) { - css_deep_free_content(new_content); - return; - } - break; - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) { - if ((!first) || (v->next)) - return; - css_deep_free_content(s->content.content); - s->content.content = NULL; - s->content.type = CSS_CONTENT_INHERIT; - return; - } else if (v->data_length == 6 && - strncasecmp(v->data, "normal", 6) == 0) { - if ((!first) || (v->next)) - return; - css_deep_free_content(s->content.content); - s->content.content = NULL; - s->content.type = CSS_CONTENT_NORMAL; - return; - } else if (v->data_length == 10 && - strncasecmp(v->data, "open-quote", 10) == 0) { - if (!parse_content_new(&new_content, CSS_CONTENT_OPEN_QUOTE)) - return; - } else if (v->data_length == 11 && - strncasecmp(v->data, "close-quote", 11) == 0) { - if (!parse_content_new(&new_content, CSS_CONTENT_CLOSE_QUOTE)) - return; - } else if (v->data_length == 13 && - strncasecmp(v->data, "no-open-quote", 13) == 0) { - if (!parse_content_new(&new_content, CSS_CONTENT_NO_OPEN_QUOTE)) - return; - } else if (v->data_length == 14 && - strncasecmp(v->data, "no-close-quote", 14) == 0) { - if (!parse_content_new(&new_content, CSS_CONTENT_NO_CLOSE_QUOTE)) - return; - } else { - css_deep_free_content(new_content); - return; - } - break; - case CSS_NODE_FUNCTION: - if (v->data_length == 5 && - strncasecmp(v->data, "attr", 4) == 0) { - content = parse_content_new(&new_content, CSS_CONTENT_URI); - if (!content) - return; - t = v->value; - if ((t->type == CSS_NODE_STRING) && (!t->next)) { - content->data.string = strndup(t->data, t->data_length); - if (!content->data.string) { - css_deep_free_content(new_content); - return; - } - } else { - css_deep_free_content(new_content); - return; - } - } else if (v->data_length == 8 && - strncasecmp(v->data, "counter", 7) == 0) { - if (!parse_content_counter(&new_content, v->value, false)) - return; - } else if (v->data_length == 9 && - strncasecmp(v->data, "counters", 8) == 0) { - if (!parse_content_counter(&new_content, v->value, true)) - return; - } else { - css_deep_free_content(new_content); - return; - } - default: - css_deep_free_content(new_content); - return; - } - first = false; - } - - if (new_content) { - css_deep_free_content(s->content.content); - s->content.type = CSS_CONTENT_INTERPRET; - s->content.content = new_content; - } -} - -struct css_content *parse_content_new(struct css_content **current, css_content_type_generated generated) { - struct css_content *content; - struct css_content *link; - - content = (struct css_content *)calloc(1, sizeof(struct css_content)); - if (!content) { - css_deep_free_content(*current); - return NULL; - } - - content->type = generated; - if (!*current) { - *current = content; - } else { - for (link = *current; link->next; link = link->next); - link->next = content; - } - return content; -} - -bool parse_content_counter(struct css_content **current, struct css_node *t, bool counters) { - struct css_content *content; - css_list_style_type z; - - content = parse_content_new(current, CSS_CONTENT_COUNTER); - if ((!content) || (t->type != CSS_NODE_IDENT)) - return false; - - content->data.counter.name = strndup(t->data, t->data_length); - content->data.counter.style = CSS_LIST_STYLE_TYPE_DECIMAL; - t = t->next; - - if (counters) { - if ((!t) || (t->type != CSS_NODE_STRING)) { - css_deep_free_content(*current); - return false; - } - content->data.counter.separator = strndup(t->data, t->data_length); - t = t->next; - } - - if (!t) - return true; - - if ((t->type != CSS_NODE_IDENT) || (t->next)) { - css_deep_free_content(*current); - return false; - } - z = css_list_style_type_parse(t->data, t->data_length); - if (z != CSS_LIST_STYLE_TYPE_UNKNOWN) - content->data.counter.style = z; - return true; -} - -void parse_counter_reset(struct css_style * const s, const struct css_node * v) { - struct css_counter_control *counter = NULL; - - if (!parse_counter_control_data(&counter, v, 0)) - return; - - if (counter) { - css_deep_free_counter_control(s->counter_reset.data); - s->counter_reset.type = CSS_COUNTER_RESET_INTERPRET; - s->counter_reset.data = counter; - } -} - -void parse_counter_increment(struct css_style * const s, const struct css_node * v) { - struct css_counter_control *counter = NULL; - - if (!parse_counter_control_data(&counter, v, 1)) - return; - - if (counter) { - css_deep_free_counter_control(s->counter_increment.data); - s->counter_increment.type = CSS_COUNTER_INCREMENT_INTERPRET; - s->counter_increment.data = counter; - } -} - -bool parse_counter_control_data(struct css_counter_control **current, const struct css_node * v, int empty) { - struct css_counter_control *open = NULL; - - for (; v; v = v->next) { - switch (v->type) { - case CSS_NODE_IDENT: - open = parse_counter_control_new(current); - if (!open) - return false; - open->name = strndup(v->data, v->data_length); - open->value = empty; - if (!open->name) { - css_deep_free_counter_control(*current); - return false; - } - break; - case CSS_NODE_NUMBER: - if (!open) { - css_deep_free_counter_control(*current); - return false; - } - open->value = atoi(v->data); - open = NULL; - break; - default: - css_deep_free_counter_control(*current); - return false; - } - } - return true; -} - -struct css_counter_control *parse_counter_control_new(struct css_counter_control **current) { - struct css_counter_control *counter; - struct css_counter_control *link; - - counter = (struct css_counter_control *)calloc(1, sizeof(struct css_counter_control)); - if (!counter) { - css_deep_free_counter_control(*current); - return NULL; - } - - if (!*current) { - *current = counter; - } else { - for (link = *current; link->next; link = link->next); - link->next = counter; - } - return counter; -} - -void parse_cursor(struct css_style * const s, const struct css_node * v) -{ - css_cursor z; - for (; v; v = v->next) { - switch (v->type) { - case CSS_NODE_IDENT: - z = css_cursor_parse(v->data, v->data_length); - if (z != CSS_CURSOR_UNKNOWN) { - s->cursor = z; - return; - } - break; - default: - break; - } - } -} - -void parse_direction(struct css_style * const s, const struct css_node * v) -{ - css_direction z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_direction_parse(v->data, v->data_length); - if (z != CSS_DIRECTION_UNKNOWN) - s->direction = z; -} - -void parse_display(struct css_style * const s, const struct css_node * const v) -{ - css_display z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_display_parse(v->data, v->data_length); - if (z != CSS_DISPLAY_UNKNOWN) - s->display = z; -} - -void parse_empty_cells(struct css_style * const s, const struct css_node * v) -{ - css_empty_cells z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_empty_cells_parse(v->data, v->data_length); - if (z != CSS_EMPTY_CELLS_UNKNOWN) - s->empty_cells = z; -} - -void parse_float(struct css_style * const s, const struct css_node * const v) -{ - css_float z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_float_parse(v->data, v->data_length); - if (z != CSS_FLOAT_UNKNOWN) - s->float_ = z; -} - -void parse_font(struct css_style * const s, const struct css_node * v) -{ - css_font_family ff; - css_font_style fs; - css_font_variant fv; - css_font_weight fw; - s->font_family = CSS_FONT_FAMILY_SANS_SERIF; - s->font_style = CSS_FONT_STYLE_NORMAL; - s->font_weight = CSS_FONT_WEIGHT_NORMAL; - s->line_height.size = CSS_LINE_HEIGHT_ABSOLUTE; - s->line_height.value.absolute = 1.3; - for (; v; v = v->next) { - switch (v->type) { - case CSS_NODE_IDENT: - /* font-family */ - ff = css_font_family_parse(v->data, - v->data_length); - if (ff != CSS_FONT_FAMILY_UNKNOWN) { - s->font_family = ff; - break; - } - /* font-style, font-variant, or font-weight */ - fs = css_font_style_parse(v->data, - v->data_length); - if (fs != CSS_FONT_STYLE_UNKNOWN) { - s->font_style = fs; - break; - } - fv = css_font_variant_parse(v->data, - v->data_length); - if (fv != CSS_FONT_VARIANT_UNKNOWN) { - s->font_variant = fv; - break; - } - fw = css_font_weight_parse(v->data, - v->data_length); - if (fw != CSS_FONT_WEIGHT_UNKNOWN) { - s->font_weight = fw; - break; - } - case CSS_NODE_PERCENTAGE: - case CSS_NODE_DIMENSION: - parse_font_size(s, v); - break; - case CSS_NODE_DELIM: - if (v->data[0] == '/' && v->data_length == 1 && - v->next) { - v = v->next; - parse_line_height(s, v); - } - break; - default: - break; - } - } -} - -void parse_font_family(struct css_style * const s, const struct css_node * v) -{ - css_font_family z; - for (; v; v = v->next) { - switch (v->type) { - case CSS_NODE_IDENT: - z = css_font_family_parse(v->data, - v->data_length); - if (z != CSS_FONT_FAMILY_UNKNOWN) { - s->font_family = z; - return; - } - break; - default: - break; - } - } -} - -void parse_font_size(struct css_style * const s, const struct css_node * const v) -{ - char font_size_name[10]; - struct css_font_size_entry *fs; - switch (v->type) { - case CSS_NODE_IDENT: - if (9 < v->data_length) - break; - strncpy(font_size_name, v->data, v->data_length); - font_size_name[v->data_length] = 0; - fs = bsearch(font_size_name, css_font_size_table, - sizeof css_font_size_table / - sizeof css_font_size_table[0], - sizeof css_font_size_table[0], - (int (*)(const void *, const void *)) - strcasecmp); - if (fs != 0) { - s->font_size.size = CSS_FONT_SIZE_LENGTH; - s->font_size.value.length.unit = CSS_UNIT_PT; - s->font_size.value.length.value = fs->size * - option_font_size / 10; - } else if (v->data_length == 6 && - strncasecmp(v->data, "larger", 6) == 0) { - s->font_size.size = CSS_FONT_SIZE_PERCENT; - s->font_size.value.percent = SIZE_FACTOR * 100; - } else if (v->data_length == 7 && - strncasecmp(v->data, "smaller", 7) == 0) { - s->font_size.size = CSS_FONT_SIZE_PERCENT; - s->font_size.value.percent = 1 / SIZE_FACTOR * 100; - } - break; - - case CSS_NODE_PERCENTAGE: - s->font_size.size = CSS_FONT_SIZE_PERCENT; - s->font_size.value.percent = atof(v->data); - break; - - case CSS_NODE_DIMENSION: - case CSS_NODE_NUMBER: - if (parse_length(&s->font_size.value.length, v, true) == 0) - s->font_size.size = CSS_FONT_SIZE_LENGTH; - break; - - default: - break; - } -} - -void parse_font_style(struct css_style * const s, const struct css_node * const v) -{ - css_font_style z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_font_style_parse(v->data, v->data_length); - if (z != CSS_FONT_STYLE_UNKNOWN) - s->font_style = z; -} - -void parse_font_variant(struct css_style * const s, const struct css_node * const v) -{ - css_font_variant z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_font_variant_parse(v->data, v->data_length); - if (z != CSS_FONT_VARIANT_UNKNOWN) - s->font_variant = z; -} - -void parse_font_weight(struct css_style * const s, const struct css_node * const v) -{ - css_font_weight z; - if ((v->type != CSS_NODE_IDENT && v->type != CSS_NODE_NUMBER) || v->next != 0) - return; - z = css_font_weight_parse(v->data, v->data_length); - if (z != CSS_FONT_WEIGHT_UNKNOWN) - s->font_weight = z; -} - -void parse_height(struct css_style * const s, const struct css_node * const v) -{ - if (v->type == CSS_NODE_IDENT && v->data_length == 4 && - strncasecmp(v->data, "auto", 4) == 0) - s->height.height = CSS_HEIGHT_AUTO; - else if (v->type == CSS_NODE_PERCENTAGE) { - s->height.height = CSS_HEIGHT_PERCENT; - s->height.value.percent = atof(v->data); - } else if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && - parse_length(&s->height.value.length, v, true) == 0) - s->height.height = CSS_HEIGHT_LENGTH; -} - -void parse_letter_spacing(struct css_style * const s, const struct css_node * v) -{ - if (v->next != 0) - return; - - switch (v->type) { - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->letter_spacing.letter_spacing = CSS_LETTER_SPACING_INHERIT; - else if (v->data_length == 6 && - strncasecmp(v->data, "normal", 6) == 0) - s->letter_spacing.letter_spacing = CSS_LETTER_SPACING_NORMAL; - break; - case CSS_NODE_DIMENSION: - case CSS_NODE_NUMBER: - if (parse_length(&s->letter_spacing.length, v, false) == 0) - s->letter_spacing.letter_spacing = CSS_LETTER_SPACING_LENGTH; - break; - default: - break; - } -} - -void parse_line_height(struct css_style * const s, const struct css_node * const v) -{ - if (v->type == CSS_NODE_IDENT && v->data_length == 6 && - strncasecmp(v->data, "normal", 6) == 0) { - s->line_height.size = CSS_LINE_HEIGHT_ABSOLUTE; - s->line_height.value.absolute = 1.3; - } else if (v->type == CSS_NODE_PERCENTAGE) { - s->line_height.size = CSS_LINE_HEIGHT_PERCENT; - s->line_height.value.percent = atof(v->data); - } else if (v->type == CSS_NODE_DIMENSION && - parse_length(&s->line_height.value.length, v, true) == 0) { - s->line_height.size = CSS_LINE_HEIGHT_LENGTH; - } else if (v->type == CSS_NODE_NUMBER) { - s->line_height.size = CSS_LINE_HEIGHT_ABSOLUTE; - s->line_height.value.absolute = atof(v->data); - } -} - -void parse_list_style(struct css_style * const s, const struct css_node * v) -{ - css_list_style_type t = CSS_LIST_STYLE_TYPE_DISC, t2; - css_list_style_position p = CSS_LIST_STYLE_POSITION_OUTSIDE, p2; - css_list_style_image_type i = CSS_LIST_STYLE_IMAGE_NONE, i2; - char *lsi_uri = 0; - - while (v) { - switch (v->type) { - case CSS_NODE_IDENT: - t2 = css_list_style_type_parse(v->data, v->data_length); - if (t2 != CSS_LIST_STYLE_TYPE_UNKNOWN) { - t = t2; - v = v->next; - break; - } - - p2 = css_list_style_position_parse(v->data, v->data_length); - if (p2 != CSS_LIST_STYLE_POSITION_UNKNOWN) { - p = p2; - v = v->next; - break; - } - - /* drop through */ - case CSS_NODE_STRING: - case CSS_NODE_URI: - if (!css_list_style_image_parse(v, &i2, &lsi_uri)) - return; - i = i2; - v = v->next; - break; - default: - return; - } - } - - s->list_style_type = t; - s->list_style_position = p; - s->list_style_image.type = i; - s->list_style_image.uri = lsi_uri; -} - -void parse_list_style_image(struct css_style * const s, const struct css_node * v) -{ - css_list_style_image_type type; - char *uri; - - if (v->next != 0) - return; - if (!css_list_style_image_parse(v, &type, &uri)) - return; - - if (s->list_style_image.type == CSS_LIST_STYLE_IMAGE_URI) - free(s->list_style_image.uri); - s->list_style_image.type = type; - s->list_style_image.uri = uri; -} - -/** - * Parse a list-style-image property. - * - * \param node node to parse - * \param type updated to list-style-image type - * \param uri updated to image uri, if type is - * CSS_LIST_STYLE_IMAGE_URI - * \return true on success, false on parse failure - */ - -bool css_list_style_image_parse(const struct css_node *v, - css_list_style_image_type *type, char **uri) -{ - switch (v->type) { - case CSS_NODE_URI: - case CSS_NODE_STRING: - if (!parse_uri(v, uri)) - return false; - *type = CSS_LIST_STYLE_IMAGE_URI; - break; - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - *type = CSS_LIST_STYLE_IMAGE_INHERIT; - else if (v->data_length == 4 && - strncasecmp(v->data, "none", 4) == 0) - *type = CSS_LIST_STYLE_IMAGE_NONE; - break; - default: - return false; - } - return true; -} - -void parse_list_style_position(struct css_style * const s, const struct css_node * v) -{ - css_list_style_position z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_list_style_position_parse(v->data, v->data_length); - if (z != CSS_LIST_STYLE_POSITION_UNKNOWN) - s->list_style_position = z; -} - -void parse_list_style_type(struct css_style * const s, const struct css_node * v) -{ - css_list_style_type z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_list_style_type_parse(v->data, v->data_length); - if (z != CSS_LIST_STYLE_TYPE_UNKNOWN) - s->list_style_type = z; -} - -void parse_margin(struct css_style * const s, const struct css_node * const v) -{ - unsigned int count = 0; - const struct css_node *w; - - for (w = v; w; w = w->next, count++) - if (!((w->type == CSS_NODE_IDENT && ( - (w->data_length == 7 && - strncasecmp(w->data, "inherit", 7) == 0) || - (w->data_length == 4 && - strncasecmp(w->data, "auto", 4) == 0))) || - (w->type == CSS_NODE_PERCENTAGE) || - (w->type == CSS_NODE_DIMENSION) || - (w->type == CSS_NODE_NUMBER))) - return; - - w = v; - switch (count) { - case 1: /* one value: applies to all sides */ - parse_margin_side(s, w, TOP); - parse_margin_side(s, w, RIGHT); - parse_margin_side(s, w, BOTTOM); - parse_margin_side(s, w, LEFT); - break; - case 2: /* (top and bottom), (left and right) */ - parse_margin_side(s, w, TOP); - parse_margin_side(s, w, BOTTOM); - w = w->next; - parse_margin_side(s, w, RIGHT); - parse_margin_side(s, w, LEFT); - break; - case 3: /* top, (left and right), bottom */ - parse_margin_side(s, w, TOP); - w = w->next; - parse_margin_side(s, w, RIGHT); - parse_margin_side(s, w, LEFT); - w = w->next; - parse_margin_side(s, w, BOTTOM); - break; - case 4: /* top, right, bottom, left */ - parse_margin_side(s, w, TOP); - w = w->next; - parse_margin_side(s, w, RIGHT); - w = w->next; - parse_margin_side(s, w, BOTTOM); - w = w->next; - parse_margin_side(s, w, LEFT); - break; - } -} - -#define PARSE_MARGIN_(side, z) \ -void parse_margin_ ## side(struct css_style * const s, \ - const struct css_node * const v) \ -{ \ - if (v->next != 0) \ - return; \ - parse_margin_side(s, v, z); \ -} - -PARSE_MARGIN_(top, TOP) -PARSE_MARGIN_(right, RIGHT) -PARSE_MARGIN_(bottom, BOTTOM) -PARSE_MARGIN_(left, LEFT) - -void parse_margin_side(struct css_style * const s, const struct css_node * const v, - unsigned int i) -{ - if (v->type == CSS_NODE_IDENT && v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->margin[i].margin = CSS_MARGIN_INHERIT; - else if (v->type == CSS_NODE_IDENT && v->data_length == 4 && - strncasecmp(v->data, "auto", 4) == 0) - s->margin[i].margin = CSS_MARGIN_AUTO; - else if (v->type == CSS_NODE_PERCENTAGE) { - s->margin[i].margin = CSS_MARGIN_PERCENT; - s->margin[i].value.percent = atof(v->data); - } else if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && - parse_length(&s->margin[i].value.length, v, false) == 0) { - s->margin[i].margin = CSS_MARGIN_LENGTH; - } -} - -void parse_max_height(struct css_style *const s, const struct css_node * v) -{ - if (v->next != 0) - return; - switch (v->type) { - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->max_height.max_height = CSS_MAX_HEIGHT_INHERIT; - else if (v->data_length == 4 && - strncasecmp(v->data, "none", 4) == 0) - s->max_height.max_height = CSS_MAX_HEIGHT_NONE; - break; - case CSS_NODE_DIMENSION: - case CSS_NODE_NUMBER: - if (!parse_length(&s->max_height.value.length, v, true)) - s->max_height.max_height = CSS_MAX_HEIGHT_LENGTH; - break; - case CSS_NODE_PERCENTAGE: - s->max_height.value.percent = atof(v->data); - s->max_height.max_height = CSS_MAX_HEIGHT_PERCENT; - break; - default: - break; - } -} - -void parse_max_width(struct css_style *const s, const struct css_node * v) -{ - if (v->next != 0) - return; - switch (v->type) { - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->max_width.max_width = CSS_MAX_WIDTH_INHERIT; - else if (v->data_length == 4 && - strncasecmp(v->data, "none", 4) == 0) - s->max_width.max_width = CSS_MAX_WIDTH_NONE; - break; - case CSS_NODE_DIMENSION: - case CSS_NODE_NUMBER: - if (!parse_length(&s->max_width.value.length, v, true)) - s->max_width.max_width = CSS_MAX_WIDTH_LENGTH; - break; - case CSS_NODE_PERCENTAGE: - s->max_width.value.percent = atof(v->data); - s->max_width.max_width = CSS_MAX_WIDTH_PERCENT; - break; - default: - break; - } -} - -void parse_min_height(struct css_style *const s, const struct css_node * v) -{ - if (v->next != 0) - return; - switch (v->type) { - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->min_height.min_height = CSS_MIN_HEIGHT_INHERIT; - break; - case CSS_NODE_DIMENSION: - case CSS_NODE_NUMBER: - if (!parse_length(&s->min_height.value.length, v, true)) - s->min_height.min_height = CSS_MIN_HEIGHT_LENGTH; - break; - case CSS_NODE_PERCENTAGE: - s->min_height.value.percent = atof(v->data); - s->min_height.min_height = CSS_MIN_HEIGHT_PERCENT; - break; - default: - break; - } -} - -void parse_min_width(struct css_style *const s, const struct css_node * v) -{ - if (v->next != 0) - return; - switch (v->type) { - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->min_width.min_width = CSS_MIN_WIDTH_INHERIT; - break; - case CSS_NODE_DIMENSION: - case CSS_NODE_NUMBER: - if (!parse_length(&s->min_width.value.length, v, true)) - s->min_width.min_width = CSS_MIN_WIDTH_LENGTH; - break; - case CSS_NODE_PERCENTAGE: - s->min_width.value.percent = atof(v->data); - s->min_width.min_width = CSS_MIN_WIDTH_PERCENT; - break; - default: - break; - } -} - -void parse_orphans(struct css_style * const s, const struct css_node * const v) -{ - if (v->next != 0) - return; - switch (v->type) { - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->orphans.orphans = CSS_ORPHANS_INHERIT; - break; - case CSS_NODE_NUMBER: - s->orphans.value = atoi(v->data); - s->orphans.orphans = CSS_ORPHANS_INTEGER; - break; - default: - break; - } -} - -void parse_outline(struct css_style * const s, const struct css_node * v) -{ - css_outline_color_type c = CSS_OUTLINE_COLOR_INVERT; - colour col = 0, col2; - css_border_style b = CSS_BORDER_STYLE_NONE, b2; - struct css_border_width w = { CSS_BORDER_WIDTH_LENGTH, { 2, CSS_UNIT_PX } }; - struct css_border_width w2; - - while (v) { - switch (v->type) { - case CSS_NODE_HASH: - case CSS_NODE_FUNCTION: - case CSS_NODE_IDENT: - col2 = parse_colour(v); - if (col2 != CSS_COLOR_NONE) { - col = col2; - c = CSS_OUTLINE_COLOR_COLOR; - v = v->next; - break; - } - if (v->type == CSS_NODE_HASH || - v->type == CSS_NODE_FUNCTION) - return; - - /* could be inherit */ - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) { - c = CSS_OUTLINE_COLOR_INHERIT; - v = v->next; - break; - } - - b2 = css_border_style_parse(v->data, v->data_length); - if (b2 != CSS_BORDER_STYLE_UNKNOWN) { - b = b2; - v = v->next; - break; - } - - /* fall through */ - case CSS_NODE_DIMENSION: - case CSS_NODE_NUMBER: - if (css_outline_width_parse(v, &w2)) { - w = w2; - v = v->next; - break; - } - - /* fall through */ - default: - return; - } - } - - s->outline.color.color = c; - s->outline.color.value = col; - s->outline.width = w; - s->outline.style = b; -} - -void parse_outline_color(struct css_style * const s, const struct css_node * const v) -{ - colour c; - - if (v->next != 0) - return; - - c = parse_colour(v); - if (c == CSS_COLOR_NONE && v->type == CSS_NODE_IDENT) { - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->outline.color.color = CSS_OUTLINE_COLOR_INHERIT; - else if (v->data_length == 6 && - strncasecmp(v->data, "invert", 6) == 0) - s->outline.color.color = CSS_OUTLINE_COLOR_INVERT; - } - else { - s->outline.color.value = c; - s->outline.color.color = CSS_OUTLINE_COLOR_COLOR; - } -} - -void parse_outline_style(struct css_style * const s, const struct css_node * const v) -{ - css_border_style z; - - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_border_style_parse(v->data, v->data_length); - if (z != CSS_BORDER_STYLE_UNKNOWN) - s->outline.style = z; -} - -void parse_outline_width(struct css_style * const s, const struct css_node * const v) -{ - struct css_border_width w; - if (v->next != 0) - return; - if (!css_outline_width_parse(v, &w)) - return; - s->outline.width = w; -} - - -bool css_outline_width_parse(const struct css_node * v, struct css_border_width * w) -{ - if (v->type == CSS_NODE_IDENT) { - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) { - w->width = CSS_BORDER_WIDTH_INHERIT; - return true; - } else if (v->data_length == 4 && - strncasecmp(v->data, "thin", 4) == 0) { - w->width = CSS_BORDER_WIDTH_LENGTH; - w->value.value = 1; - w->value.unit = CSS_UNIT_PX; - return true; - } else if (v->data_length == 6 && - strncasecmp(v->data, "medium", 6) == 0) { - w->width = CSS_BORDER_WIDTH_LENGTH; - w->value.value = 2; - w->value.unit = CSS_UNIT_PX; - return true; - } else if (v->data_length == 5 && - strncasecmp(v->data, "thick", 5) == 0) { - w->width = CSS_BORDER_WIDTH_LENGTH; - w->value.value = 4; - w->value.unit = CSS_UNIT_PX; - return true; - } - } else if ((v->type == CSS_NODE_DIMENSION || - v->type == CSS_NODE_NUMBER) && - parse_length(&w->value, v, true) == 0) { - w->width = CSS_BORDER_WIDTH_LENGTH; - return true; - } - - return false; -} - -void parse_overflow(struct css_style * const s, const struct css_node * const v) -{ - css_overflow z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_overflow_parse(v->data, v->data_length); - if (z != CSS_OVERFLOW_UNKNOWN) - s->overflow = z; -} - -void parse_padding(struct css_style * const s, const struct css_node * const v) -{ - unsigned int count = 0; - const struct css_node *w; - - for (w = v; w; w = w->next, count++) - if (!((w->type == CSS_NODE_IDENT && w->data_length == 7 && - strncasecmp(w->data, "inherit", 7) == 0) || - (w->type == CSS_NODE_PERCENTAGE) || - (w->type == CSS_NODE_DIMENSION) || - (w->type == CSS_NODE_NUMBER))) - return; - - w = v; - switch (count) { - case 1: /* one value: applies to all sides */ - parse_padding_side(s, w, TOP); - parse_padding_side(s, w, RIGHT); - parse_padding_side(s, w, BOTTOM); - parse_padding_side(s, w, LEFT); - break; - case 2: /* (top and bottom), (left and right) */ - parse_padding_side(s, w, TOP); - parse_padding_side(s, w, BOTTOM); - w = w->next; - parse_padding_side(s, w, RIGHT); - parse_padding_side(s, w, LEFT); - break; - case 3: /* top, (left and right), bottom */ - parse_padding_side(s, w, TOP); - w = w->next; - parse_padding_side(s, w, RIGHT); - parse_padding_side(s, w, LEFT); - w = w->next; - parse_padding_side(s, w, BOTTOM); - break; - case 4: /* top, right, bottom, left */ - parse_padding_side(s, w, TOP); - w = w->next; - parse_padding_side(s, w, RIGHT); - w = w->next; - parse_padding_side(s, w, BOTTOM); - w = w->next; - parse_padding_side(s, w, LEFT); - break; - } -} - -#define PARSE_PADDING_(side, z) \ -void parse_padding_ ## side(struct css_style * const s, \ - const struct css_node * const v) \ -{ \ - if (v->next != 0) \ - return; \ - parse_padding_side(s, v, z); \ -} - -PARSE_PADDING_(top, TOP) -PARSE_PADDING_(right, RIGHT) -PARSE_PADDING_(bottom, BOTTOM) -PARSE_PADDING_(left, LEFT) - -void parse_padding_side(struct css_style * const s, const struct css_node * const v, - unsigned int i) -{ - if (v->type == CSS_NODE_IDENT && v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) { - s->padding[i].padding = CSS_PADDING_INHERIT; - } else if (v->type == CSS_NODE_PERCENTAGE) { - s->padding[i].padding = CSS_PADDING_PERCENT; - s->padding[i].value.percent = atof(v->data); - } else if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && - parse_length(&s->padding[i].value.length, v, true) == 0) { - s->padding[i].padding = CSS_PADDING_LENGTH; - } -} - -void parse_page_break_after(struct css_style * const s, const struct css_node * v) -{ - css_page_break_after z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_page_break_after_parse(v->data, v->data_length); - if (z != CSS_PAGE_BREAK_AFTER_UNKNOWN) - s->page_break_after = z; -} - -void parse_page_break_before(struct css_style * const s, const struct css_node * v) -{ - css_page_break_before z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_page_break_before_parse(v->data, v->data_length); - if (z != CSS_PAGE_BREAK_BEFORE_UNKNOWN) - s->page_break_before = z; -} - -void parse_page_break_inside(struct css_style * const s, const struct css_node * v) -{ - css_page_break_inside z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_page_break_inside_parse(v->data, v->data_length); - if (z != CSS_PAGE_BREAK_INSIDE_UNKNOWN) - s->page_break_inside = z; -} - -#define PARSE_POS(side, z) \ -void parse_ ## side(struct css_style * const s, \ - const struct css_node * const v) \ -{ \ - parse_pos(s, v, z); \ -} - -PARSE_POS(top, TOP) -PARSE_POS(right, RIGHT) -PARSE_POS(bottom, BOTTOM) -PARSE_POS(left, LEFT) - -void parse_pos(struct css_style * const s, const struct css_node * v, unsigned int i) -{ - if (v->next != 0) - return; - - switch (v->type) { - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->pos[i].pos = CSS_POS_INHERIT; - else if (v->data_length == 4 && - strncasecmp(v->data, "auto", 4) == 0) - s->pos[i].pos = CSS_POS_AUTO; - break; - case CSS_NODE_DIMENSION: - case CSS_NODE_NUMBER: - if (parse_length(&s->pos[i].value.length, v, false) == 0) - s->pos[i].pos = CSS_POS_LENGTH; - break; - case CSS_NODE_PERCENTAGE: - s->pos[i].pos = CSS_POS_PERCENT; - s->pos[i].value.percent = atof(v->data); - break; - default: - break; - } -} - -void parse_position(struct css_style * const s, const struct css_node * v) -{ - css_position z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_position_parse(v->data, v->data_length); - if (z != CSS_POSITION_UNKNOWN) - s->position = z; -} - -void parse_table_layout(struct css_style * const s, const struct css_node * v) -{ - css_table_layout z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_table_layout_parse(v->data, v->data_length); - if (z != CSS_TABLE_LAYOUT_UNKNOWN) - s->table_layout = z; -} - -void parse_text_align(struct css_style * const s, const struct css_node * const v) -{ - css_text_align z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_text_align_parse(v->data, v->data_length); - if (z != CSS_TEXT_ALIGN_UNKNOWN) - s->text_align = z; -} - -void parse_text_indent(struct css_style * const s, const struct css_node * const v) -{ - if (v->type == CSS_NODE_IDENT) { - return; - } else if (v->type == CSS_NODE_PERCENTAGE) { - s->text_indent.size = CSS_TEXT_INDENT_PERCENT; - s->text_indent.value.percent = atof(v->data); - } else if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && - parse_length(&s->text_indent.value.length, v, false) == 0) { - s->text_indent.size = CSS_TEXT_INDENT_LENGTH; - } -} - -void parse_text_decoration(struct css_style * const s, const struct css_node * const v) -{ - struct css_node *temp; - css_text_decoration z; - if (v->type != CSS_NODE_IDENT) - return; - z = css_text_decoration_parse(v->data, v->data_length); - if (z == CSS_TEXT_DECORATION_INHERIT || z == CSS_TEXT_DECORATION_NONE) { - if (v->next != 0) - return; - s->text_decoration = z; - } - if (z != CSS_TEXT_DECORATION_UNKNOWN) - s->text_decoration |= z; - for (temp = v->next; temp; temp = temp->next) { - z = css_text_decoration_parse(temp->data, temp->data_length); - if (z != CSS_TEXT_DECORATION_UNKNOWN) - s->text_decoration |= z; - } -} - -void parse_text_transform(struct css_style * const s, const struct css_node * const v) -{ - css_text_transform z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_text_transform_parse(v->data, v->data_length); - if (z != CSS_TEXT_TRANSFORM_UNKNOWN) - s->text_transform = z; -} - -void parse_unicode_bidi(struct css_style * const s, const struct css_node * const v) -{ - css_unicode_bidi z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_unicode_bidi_parse(v->data, v->data_length); - if (z != CSS_UNICODE_BIDI_UNKNOWN) - s->unicode_bidi = z; -} - -void parse_vertical_align(struct css_style * const s, const struct css_node * v) -{ - if (v->next != 0) - return; - switch (v->type) { - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->vertical_align.type = CSS_VERTICAL_ALIGN_INHERIT; - else if (v->data_length == 8 && - strncasecmp(v->data, "baseline", 8) == 0) - s->vertical_align.type = CSS_VERTICAL_ALIGN_BASELINE; - else if (v->data_length == 3 && - strncasecmp(v->data, "sub", 3) == 0) - s->vertical_align.type = CSS_VERTICAL_ALIGN_SUB; - else if (v->data_length == 5 && - strncasecmp(v->data, "super", 5) == 0) - s->vertical_align.type = CSS_VERTICAL_ALIGN_SUPER; - else if (v->data_length == 3 && - strncasecmp(v->data, "top", 3) == 0) - s->vertical_align.type = CSS_VERTICAL_ALIGN_TOP; - else if (v->data_length == 8 && - strncasecmp(v->data, "text-top", 8) == 0) - s->vertical_align.type = CSS_VERTICAL_ALIGN_TEXT_TOP; - else if (v->data_length == 6 && - strncasecmp(v->data, "middle", 6) == 0) - s->vertical_align.type = CSS_VERTICAL_ALIGN_MIDDLE; - else if (v->data_length == 6 && - strncasecmp(v->data, "bottom", 6) == 0) - s->vertical_align.type = CSS_VERTICAL_ALIGN_BOTTOM; - else if (v->data_length == 11 && - strncasecmp(v->data, "text-bottom", 11) == 0) - s->vertical_align.type = CSS_VERTICAL_ALIGN_TEXT_BOTTOM; - break; - case CSS_NODE_DIMENSION: - case CSS_NODE_NUMBER: - if (parse_length(&s->vertical_align.value.length, v, false) == 0) - s->vertical_align.type = CSS_VERTICAL_ALIGN_LENGTH; - break; - case CSS_NODE_PERCENTAGE: - s->vertical_align.value.percent = atof(v->data); - s->vertical_align.type = CSS_VERTICAL_ALIGN_PERCENT; - break; - default: - break; - } -} - -void parse_visibility(struct css_style * const s, const struct css_node * const v) -{ - css_visibility z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_visibility_parse(v->data, v->data_length); - if (z != CSS_VISIBILITY_UNKNOWN) - s->visibility = z; -} - -void parse_widows(struct css_style * const s, const struct css_node * const v) -{ - if (v->next != 0) - return; - switch (v->type) { - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->widows.widows = CSS_WIDOWS_INHERIT; - break; - case CSS_NODE_NUMBER: - s->widows.value = atoi(v->data); - s->widows.widows = CSS_WIDOWS_INTEGER; - break; - default: - break; - } -} - -void parse_width(struct css_style * const s, const struct css_node * const v) -{ - if (v->type == CSS_NODE_IDENT && v->data_length == 4 && - strncasecmp(v->data, "auto", 4) == 0) - s->width.width = CSS_WIDTH_AUTO; - else if (v->type == CSS_NODE_PERCENTAGE) { - s->width.width = CSS_WIDTH_PERCENT; - s->width.value.percent = atof(v->data); - } else if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && - parse_length(&s->width.value.length, v, true) == 0) - s->width.width = CSS_WIDTH_LENGTH; -} - -void parse_white_space(struct css_style * const s, const struct css_node * const v) -{ - css_white_space z; - if (v->type != CSS_NODE_IDENT || v->next != 0) - return; - z = css_white_space_parse(v->data, v->data_length); - if (z != CSS_WHITE_SPACE_UNKNOWN) - s->white_space = z; -} - -void parse_word_spacing(struct css_style * const s, const struct css_node * v) -{ - if (v->next != 0) - return; - - switch (v->type) { - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->word_spacing.word_spacing = CSS_WORD_SPACING_INHERIT; - else if (v->data_length == 6 && - strncasecmp(v->data, "normal", 6) == 0) - s->word_spacing.word_spacing = CSS_WORD_SPACING_NORMAL; - break; - case CSS_NODE_DIMENSION: - case CSS_NODE_NUMBER: - if (parse_length(&s->word_spacing.length, v, false) == 0) - s->word_spacing.word_spacing = CSS_WORD_SPACING_LENGTH; - break; - default: - break; - } -} - -void parse_z_index(struct css_style * const s, const struct css_node * const v) -{ - if (v->next != 0) - return; - switch (v->type) { - case CSS_NODE_IDENT: - if (v->data_length == 7 && - strncasecmp(v->data, "inherit", 7) == 0) - s->z_index.z_index = CSS_Z_INDEX_INHERIT; - else if (v->data_length == 4 && - strncasecmp(v->data, "auto", 4) == 0) - s->z_index.z_index = CSS_Z_INDEX_AUTO; - break; - case CSS_NODE_NUMBER: - s->z_index.value = atoi(v->data); - s->z_index.z_index = CSS_Z_INDEX_INTEGER; - break; - default: - break; - } -} - -css_text_decoration css_text_decoration_parse(const char * const s, - int length) -{ - if (length == 7 && strncasecmp(s, "inherit", 7) == 0) - return CSS_TEXT_DECORATION_INHERIT; - if (length == 4 && strncasecmp(s, "none", 4) == 0) - return CSS_TEXT_DECORATION_NONE; - if (length == 5 && strncasecmp(s, "blink", 5) == 0) - return CSS_TEXT_DECORATION_BLINK; - if (length == 12 && strncasecmp(s, "line-through", 12) == 0) - return CSS_TEXT_DECORATION_LINE_THROUGH; - if (length == 8 && strncasecmp(s, "overline", 8) == 0) - return CSS_TEXT_DECORATION_OVERLINE; - if (length == 9 && strncasecmp(s, "underline", 9) == 0) - return CSS_TEXT_DECORATION_UNDERLINE; - return CSS_TEXT_DECORATION_UNKNOWN; -} - -/** \} */ diff --git a/css/scanner.l b/css/scanner.l deleted file mode 100644 index 91574954e..000000000 --- a/css/scanner.l +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This file is part of NetSurf, http://netsurf-browser.org/ - * Licensed under the GNU General Public License, - * http://www.opensource.org/licenses/gpl-license - * Copyright 2004 James Bursa - */ - -/** \file - * CSS tokeniser using re2c. - * - * see CSS2 Specification, chapter 4 - * http://www.w3.org/TR/REC-CSS2/syndata.html, - * and errata - * http://www.w3.org/Style/css2-updates/REC-CSS2-19980512-errata - */ - -#include -#define CSS_INTERNALS -#include "css/css.h" -#include "css/parser.h" - -#define YYCTYPE unsigned char -#define YYCURSOR (*buffer) -#define YYLIMIT end -#define YYMARKER marker -#define YYFILL(n) { return 0; } - - -/** - * Identify a CSS source token. - * - * \param buffer source to tokenise, updated to new position - * \param end end of source - * \param token_text updated to start of recognized token - * \return token number - */ - -int css_tokenise(unsigned char **buffer, unsigned char *end, - unsigned char **token_text) -{ - unsigned char *marker; - -start: - *token_text = YYCURSOR; - -/*!re2c -nonascii = [\200-\377]; -unicode = "\\" [0-9a-f]+ ("\r\n" | [ \n\r\t\f])?; -escape = unicode | "\\" [^\n\r\f0-9a-f]; -nmchar = [-a-zA-Z0-9_] | nonascii | escape; -nmstart = [a-zA-Z_] | nonascii | escape; -ident = [-]? nmstart nmchar*; -name = nmchar+; -num = [+-]? ([0-9]+ | [0-9]* "." [0-9]+); -nl = "\n" | "\r\n" | "\r" | "\f"; -string1 = "\"" ([\t !#$%&(-~] | "\\" nl | "'" | nonascii | escape)* "\""; -string2 = "'" ([\t !#$%&(-~] | "\\" nl | "\""| nonascii | escape)* "'"; -string = string1 | string2; -s = [ \t\r\n\f]; -w = s*; -any = [\000-\377]; - -ident { return IDENT; } -"@" ident { return ATKEYWORD; } -string { return STRING; } -"#" name { return HASH; } - -num { return NUMBER; } -num "%" { return PERCENTAGE; } -num ident { return DIMENSION; } - -"url(" w string w ")" | "url(" w ([!#$%&*-~]|nonascii|escape)* w ")" - { return URI; } -"U+" [0-9A-F?]+ ("-" [0-9A-F]+ )? - { return UNICODE_RANGE; } - -"" { goto start; /* ignore CDC */ } - -";" { return SEMI; } -"{" { return LBRACE; } -"}" { return RBRACE; } -"(" { return LPAREN; } -")" { return RPAREN; } -"[" { return LBRAC; } -"]" { return RBRAC; } - -s+ { return S; } - -"/*" (any\[*])* "*"+ ((any\[/*]) (any\[*])* "*"+)* "/" - { goto start; /* ignore comments */ } - -ident "(" { return FUNCTION; } - -"~=" { return INCLUDES; } -"|=" { return DASHMATCH; } -"^=" { return PREFIX; } -"$=" { return SUFFIX; } -"*=" { return SUBSTR; } - -"=" { return EQUALS; } -":" { return COLON; } -"," { return COMMA; } -"+" { return PLUS; } -">" { return GT; } -"." { return DOT; } -"*" { return ASTERISK; } - -any { return DELIM; } -*/ - -} diff --git a/css/select.c b/css/select.c new file mode 100644 index 000000000..0f2f7327e --- /dev/null +++ b/css/select.c @@ -0,0 +1,1981 @@ +/* + * Copyright 2009 John-Mark Bell + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "content/content.h" +#include "content/urldb.h" +#include "css/internal.h" +#include "css/select.h" +#include "css/utils.h" +#include "desktop/options.h" +#include "utils/url.h" +#include "utils/utils.h" + +static css_error node_name(void *pw, void *node, + lwc_context *dict, lwc_string **name); +static css_error named_ancestor_node(void *pw, void *node, + lwc_string *name, void **ancestor); +static css_error named_parent_node(void *pw, void *node, + lwc_string *name, void **parent); +static css_error named_sibling_node(void *pw, void *node, + lwc_string *name, void **sibling); +static css_error parent_node(void *pw, void *node, void **parent); +static css_error sibling_node(void *pw, void *node, void **sibling); +static css_error node_has_name(void *pw, void *node, + lwc_string *name, bool *match); +static css_error node_has_class(void *pw, void *node, + lwc_string *name, bool *match); +static css_error node_has_id(void *pw, void *node, + lwc_string *name, bool *match); +static css_error node_has_attribute(void *pw, void *node, + lwc_string *name, bool *match); +static css_error node_has_attribute_equal(void *pw, void *node, + lwc_string *name, lwc_string *value, + bool *match); +static css_error node_has_attribute_dashmatch(void *pw, void *node, + lwc_string *name, lwc_string *value, + bool *match); +static css_error node_has_attribute_includes(void *pw, void *node, + lwc_string *name, lwc_string *value, + bool *match); +static css_error node_is_first_child(void *pw, void *node, bool *match); +static css_error node_is_link(void *pw, void *node, bool *match); +static css_error node_is_visited(void *pw, void *node, bool *match); +static css_error node_is_hover(void *pw, void *node, bool *match); +static css_error node_is_active(void *pw, void *node, bool *match); +static css_error node_is_focus(void *pw, void *node, bool *match); +static css_error node_is_lang(void *pw, void *node, + lwc_string *lang, bool *match); +static css_error node_presentational_hint(void *pw, void *node, + uint32_t property, css_hint *hint); +static css_error ua_default_for_property(void *pw, uint32_t property, + css_hint *hint); + +static int cmp_colour_name(const void *a, const void *b); +static bool parse_named_colour(const char *data, css_color *result); +static bool parse_dimension(const char *data, bool strict, + css_fixed *length, css_unit *unit); +static bool parse_number(const char *data, bool non_negative, bool real, + css_fixed *value, size_t *consumed); + +static bool isWhitespace(char c); +static bool isHex(char c); +static uint8_t charToHex(char c); + +/** + * Selection callback table for libcss + */ +static css_select_handler selection_handler = { + node_name, + named_ancestor_node, + named_parent_node, + named_sibling_node, + parent_node, + sibling_node, + node_has_name, + node_has_class, + node_has_id, + node_has_attribute, + node_has_attribute_equal, + node_has_attribute_dashmatch, + node_has_attribute_includes, + node_is_first_child, + node_is_link, + node_is_visited, + node_is_hover, + node_is_active, + node_is_focus, + node_is_lang, + node_presentational_hint, + ua_default_for_property, + nscss_compute_font_size +}; + +/** + * Create an inline style + * + * \param data Source data + * \param len Length of data in bytes + * \param charset Charset of data, or NULL if unknown + * \param url URL of document containing data + * \param allow_quirks True to permit CSS parsing quirks + * \param dict String internment context + * \param alloc Memory allocation function + * \param pw Private word for allocator + * \return Pointer to stylesheet, or NULL on failure. + */ +css_stylesheet *nscss_create_inline_style(const uint8_t *data, size_t len, + const char *charset, const char *url, bool allow_quirks, + lwc_context *dict, css_allocator_fn alloc, void *pw) +{ + css_stylesheet *sheet; + css_error error; + + error = css_stylesheet_create(CSS_LEVEL_DEFAULT, charset, url, NULL, + CSS_ORIGIN_AUTHOR, CSS_MEDIA_ALL, allow_quirks, true, + dict, alloc, pw, nscss_resolve_url, NULL, &sheet); + if (error != CSS_OK) + return NULL; + + error = css_stylesheet_append_data(sheet, data, len); + if (error != CSS_OK && error != CSS_NEEDDATA) { + css_stylesheet_destroy(sheet); + return NULL; + } + + error = css_stylesheet_data_done(sheet); + if (error != CSS_OK) { + css_stylesheet_destroy(sheet); + return NULL; + } + + return sheet; +} + +/** + * Get a style for an element + * + * \param html HTML document + * \param n Element to select for + * \param pseudo_element Pseudo element to select for, instead + * \param media Permitted media types + * \param inline_style Inline style associated with element, or NULL + * \param alloc Memory allocation function + * \param pw Private word for allocator + * \return Pointer to partial computed style, or NULL on failure + */ +css_computed_style *nscss_get_style(struct content *html, xmlNode *n, + uint32_t pseudo_element, uint64_t media, + const css_stylesheet *inline_style, + css_allocator_fn alloc, void *pw) +{ + css_computed_style *style; + css_error error; + + assert(html->type == CONTENT_HTML); + + error = css_computed_style_create(alloc, pw, &style); + if (error != CSS_OK) + return NULL; + + error = css_select_style(html->data.html.select_ctx, n, + pseudo_element, media, inline_style, style, + &selection_handler, html); + if (error != CSS_OK) { + css_computed_style_destroy(style); + return NULL; + } + + return style; +} + +/** + * Get an initial style + * + * \param html HTML document + * \param alloc Memory allocation function + * \param pw Private word for allocator + * \return Pointer to partial computed style, or NULL on failure + */ +css_computed_style *nscss_get_initial_style(struct content *html, + css_allocator_fn alloc, void *pw) +{ + css_computed_style *style; + css_error error; + + assert(html->type == CONTENT_HTML); + + error = css_computed_style_create(alloc, pw, &style); + if (error != CSS_OK) + return NULL; + + error = css_computed_style_initialise(style, &selection_handler, html); + if (error != CSS_OK) { + css_computed_style_destroy(style); + return NULL; + } + + return style; +} + +/** + * Get a blank style + * + * \param html HTML document + * \param parent Parent style to cascade inherited properties from + * \param alloc Memory allocation function + * \param pw Private word for allocator + * \return Pointer to blank style, or NULL on failure + */ +css_computed_style *nscss_get_blank_style(struct content *html, + const css_computed_style *parent, + css_allocator_fn alloc, void *pw) +{ + css_computed_style *partial; + css_error error; + + assert(html->type == CONTENT_HTML); + + partial = nscss_get_initial_style(html, alloc, pw); + if (partial == NULL) + return NULL; + + error = css_computed_style_compose(parent, partial, + nscss_compute_font_size, NULL, partial); + if (error != CSS_OK) { + css_computed_style_destroy(partial); + return NULL; + } + + return partial; +} + +/** + * Font size computation callback for libcss + * + * \param pw Computation context + * \param parent Parent font size (absolute) + * \param size Font size to compute + * \return CSS_OK on success + * + * \post \a size will be an absolute font size + */ +css_error nscss_compute_font_size(void *pw, const css_hint *parent, + css_hint *size) +{ + /** + * Table of font-size keyword scale factors + * + * These are multiplied by the configured default font size + * to produce an absolute size for the relevant keyword + */ + static const css_fixed factors[] = { + FLTTOFIX(0.5625), /* xx-small */ + FLTTOFIX(0.6250), /* x-small */ + FLTTOFIX(0.8125), /* small */ + FLTTOFIX(1.0000), /* medium */ + FLTTOFIX(1.1250), /* large */ + FLTTOFIX(1.5000), /* x-large */ + FLTTOFIX(2.0000) /* xx-large */ + }; + css_hint_length parent_size; + + /* Grab parent size, defaulting to medium if none */ + if (parent == NULL) { + parent_size.value = FDIVI( + FMULI(factors[CSS_FONT_SIZE_MEDIUM - 1], + option_font_size), 10); + parent_size.unit = CSS_UNIT_PT; + } else { + assert(parent->status == CSS_FONT_SIZE_DIMENSION); + assert(parent->data.length.unit != CSS_UNIT_EM); + assert(parent->data.length.unit != CSS_UNIT_EX); + assert(parent->data.length.unit != CSS_UNIT_PCT); + + parent_size = parent->data.length; + } + + assert(size->status != CSS_FONT_SIZE_INHERIT); + + if (size->status < CSS_FONT_SIZE_LARGER) { + /* Keyword -- simple */ + size->data.length.value = FDIVI( + FMULI(factors[size->status - 1], + option_font_size), 10); + size->data.length.unit = CSS_UNIT_PT; + } else if (size->status == CSS_FONT_SIZE_LARGER) { + /** \todo Step within table, if appropriate */ + size->data.length.value = + FMUL(parent_size.value, FLTTOFIX(1.2)); + size->data.length.unit = parent_size.unit; + } else if (size->status == CSS_FONT_SIZE_SMALLER) { + /** \todo Step within table, if appropriate */ + size->data.length.value = + FDIV(parent_size.value, FLTTOFIX(1.2)); + size->data.length.unit = parent_size.unit; + } else if (size->data.length.unit == CSS_UNIT_EM || + size->data.length.unit == CSS_UNIT_EX) { + size->data.length.value = + FMUL(size->data.length.value, parent_size.value); + + if (size->data.length.unit == CSS_UNIT_EX) { + /* 1ex = 0.6em in NetSurf */ + size->data.length.value = FMUL(size->data.length.value, + FLTTOFIX(0.6)); + } + + size->data.length.unit = parent_size.unit; + } else if (size->data.length.unit == CSS_UNIT_PCT) { + size->data.length.value = FDIV(FMUL(size->data.length.value, + parent_size.value), INTTOFIX(100)); + size->data.length.unit = parent_size.unit; + } + + size->status = CSS_FONT_SIZE_DIMENSION; + + return CSS_OK; +} + +/** + * Parser for colours specified in attribute values. + * + * \param data Data to parse (NUL-terminated) + * \param result Pointer to location to receive resulting css_color + * \return true on success, false on invalid input + */ +bool nscss_parse_colour(const char *data, css_color *result) +{ + size_t len = strlen(data); + uint8_t r, g, b; + + /* 2 */ + if (len == 0) + return false; + + /* 3 */ + if (len == SLEN("transparent") && strcasecmp(data, "transparent") == 0) + return false; + + /* 4 */ + if (parse_named_colour(data, result)) + return true; + + /** \todo Implement HTML5's utterly insane legacy colour parsing */ + + if (data[0] == '#') { + data++; + len--; + } + + if (len == 3 && isHex(data[0]) && isHex(data[1]) && isHex(data[2])) { + r = charToHex(data[0]); + g = charToHex(data[1]); + b = charToHex(data[2]); + + r |= (r << 4); + g |= (g << 4); + b |= (b << 4); + + *result = (r << 24) | (g << 16) | (b << 8); + + return true; + } else if (len == 6 && isHex(data[0]) && isHex(data[1]) && + isHex(data[2]) && isHex(data[3]) && isHex(data[4]) && + isHex(data[5])) { + r = (charToHex(data[0]) << 4) | charToHex(data[1]); + g = (charToHex(data[2]) << 4) | charToHex(data[3]); + b = (charToHex(data[4]) << 4) | charToHex(data[5]); + + *result = (r << 24) | (g << 16) | (b << 8); + + return true; + } + + return false; +} + +/****************************************************************************** + * Style selection callbacks * + ******************************************************************************/ + +/** + * Callback to retrieve a node's name. + * + * \param pw HTML document + * \param node DOM node + * \param dict Dictionary to intern result in + * \param name Pointer to location to receive node name + * \return CSS_OK on success, + * CSS_NOMEM on memory exhaustion. + */ +css_error node_name(void *pw, void *node, + lwc_context *dict, lwc_string **name) +{ + xmlNode *n = node; + lwc_error lerror; + + lerror = lwc_context_intern(dict, (const char *) n->name, + strlen((const char *) n->name), name); + switch (lerror) { + case lwc_error_oom: + return CSS_NOMEM; + case lwc_error_range: + assert(0); + default: + break; + } + + return CSS_OK; + +} + +/** + * Callback to find a named ancestor node. + * + * \param pw HTML document + * \param node DOM node + * \param name Node name to search for + * \param ancestor Pointer to location to receive ancestor + * \return CSS_OK. + * + * \post \a ancestor will contain the result, or NULL if there is no match + */ +css_error named_ancestor_node(void *pw, void *node, + lwc_string *name, void **ancestor) +{ + xmlNode *n = node; + size_t len = lwc_string_length(name); + const char *data = lwc_string_data(name); + + *ancestor = NULL; + + for (n = n->parent; n != NULL && n->type == XML_ELEMENT_NODE; + n = n->parent) { + bool match = strlen((const char *) n->name) == len && + strncasecmp((const char *) n->name, + data, len) == 0; + + if (match) { + *ancestor = (void *) n; + break; + } + } + + return CSS_OK; +} + +/** + * Callback to find a named parent node + * + * \param pw HTML document + * \param node DOM node + * \param name Node name to search for + * \param parent Pointer to location to receive parent + * \return CSS_OK. + * + * \post \a parent will contain the result, or NULL if there is no match + */ +css_error named_parent_node(void *pw, void *node, + lwc_string *name, void **parent) +{ + xmlNode *n = node; + size_t len = lwc_string_length(name); + const char *data = lwc_string_data(name); + + *parent = NULL; + + if (n->parent != NULL && n->parent->type == XML_ELEMENT_NODE && + strlen((const char *) n->parent->name) == len && + strncasecmp((const char *) n->parent->name, + data, len) == 0) + *parent = (void *) n->parent; + + return CSS_OK; +} + +/** + * Callback to find a named sibling node. + * + * \param pw HTML document + * \param node DOM node + * \param name Node name to search for + * \param sibling Pointer to location to receive sibling + * \return CSS_OK. + * + * \post \a sibling will contain the result, or NULL if there is no match + */ +css_error named_sibling_node(void *pw, void *node, + lwc_string *name, void **sibling) +{ + xmlNode *n = node; + size_t len = lwc_string_length(name); + const char *data = lwc_string_data(name); + + *sibling = NULL; + + while (n->prev != NULL && n->prev->type != XML_ELEMENT_NODE) + n = n->prev; + + if (n->prev != NULL && strlen((const char *) n->prev->name) == len && + strncasecmp((const char *) n->prev->name, + data, len) == 0) + *sibling = (void *) n->prev; + + return CSS_OK; +} + +/** + * Callback to retrieve the parent of a node. + * + * \param pw HTML document + * \param node DOM node + * \param parent Pointer to location to receive parent + * \return CSS_OK. + * + * \post \a parent will contain the result, or NULL if there is no match + */ +css_error parent_node(void *pw, void *node, void **parent) +{ + xmlNode *n = node; + + if (n->parent != NULL && n->parent->type == XML_ELEMENT_NODE) + *parent = (void *) n->parent; + else + *parent = NULL; + + return CSS_OK; +} + +/** + * Callback to retrieve the preceding sibling of a node. + * + * \param pw HTML document + * \param node DOM node + * \param sibling Pointer to location to receive sibling + * \return CSS_OK. + * + * \post \a sibling will contain the result, or NULL if there is no match + */ +css_error sibling_node(void *pw, void *node, void **sibling) +{ + xmlNode *n = node; + + while (n->prev != NULL && n->prev->type != XML_ELEMENT_NODE) + n = n->prev; + + *sibling = (void *) n->prev; + + return CSS_OK; +} + +/** + * Callback to determine if a node has the given name. + * + * \param pw HTML document + * \param node DOM node + * \param name Name to match + * \param match Pointer to location to receive result + * \return CSS_OK. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_has_name(void *pw, void *node, + lwc_string *name, bool *match) +{ + xmlNode *n = node; + size_t len = lwc_string_length(name); + const char *data = lwc_string_data(name); + + /* Element names are case insensitive in HTML */ + *match = strlen((const char *) n->name) == len && + strncasecmp((const char *) n->name, data, len) == 0; + + return CSS_OK; +} + +/** + * Callback to determine if a node has the given class. + * + * \param pw HTML document + * \param node DOM node + * \param name Name to match + * \param match Pointer to location to receive result + * \return CSS_OK. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_has_class(void *pw, void *node, + lwc_string *name, bool *match) +{ + struct content *html = pw; + xmlNode *n = node; + xmlAttr *class; + xmlChar *value = NULL; + const char *p; + const char *start; + const char *data; + size_t len; + int (*cmp)(const char *, const char *, size_t); + + /* Class names are case insensitive in quirks mode */ + if (html->data.html.quirks == BINDING_QUIRKS_MODE_FULL) + cmp = strncasecmp; + else + cmp = strncmp; + + *match = false; + + /* See if there is a class attribute on this node */ + class = xmlHasProp(n, (const xmlChar *) "class"); + if (class == NULL) + return CSS_OK; + + /* We have a class attribute -- extract its value */ + if (class->children != NULL && class->children->next == NULL && + class->children->children == NULL) { + /* Simple case -- no XML entities */ + start = (const char *) class->children->content; + } else { + /* Awkward case -- fall back to string copying */ + value = xmlGetProp(n, (const xmlChar *) "class"); + if (value == NULL) + return CSS_OK; + + start = (const char *) value; + } + + /* Extract expected class name data */ + data = lwc_string_data(name); + len = lwc_string_length(name); + + /* The class attribute is a space separated list of tokens. + * Search it for the one we're looking for. + */ + do { + /* Find next space or end of string */ + p = strchrnul(start, ' '); + + /* Does it match? */ + if ((size_t) (p - start) == len && cmp(start, data, len) == 0) { + *match = true; + break; + } + + /* Move to start of next token in string */ + start = p + 1; + } while (*p != '\0'); + + /* Clean up, if necessary */ + if (value != NULL) { + xmlFree(value); + } + + return CSS_OK; +} + +/** + * Callback to determine if a node has the given id. + * + * \param pw HTML document + * \param node DOM node + * \param name Name to match + * \param match Pointer to location to receive result + * \return CSS_OK. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_has_id(void *pw, void *node, + lwc_string *name, bool *match) +{ + xmlNode *n = node; + xmlAttr *id; + xmlChar *value = NULL; + const char *start; + const char *data; + size_t len; + + *match = false; + + /* See if there's an id attribute on this node */ + id = xmlHasProp(n, (const xmlChar *) "id"); + if (id == NULL) + return CSS_OK; + + /* We have an id attribute -- extract its value */ + if (id->children != NULL && id->children->next == NULL && + id->children->children == NULL) { + /* Simple case -- no XML entities */ + start = (const char *) id->children->content; + } else { + /* Awkward case -- fall back to string copying */ + value = xmlGetProp(n, (const xmlChar *) "id"); + if (value == NULL) + return CSS_OK; + + start = (const char *) value; + } + + /* Extract expected id data */ + len = lwc_string_length(name); + data = lwc_string_data(name); + + /* Compare */ + *match = strlen(start) == len && strncmp(start, data, len) == 0; + + /* Clean up if necessary */ + if (value != NULL) { + xmlFree(value); + } + + return CSS_OK; +} + +/** + * Callback to determine if a node has an attribute with the given name. + * + * \param pw HTML document + * \param node DOM node + * \param name Name to match + * \param match Pointer to location to receive result + * \return CSS_OK on success, + * CSS_NOMEM on memory exhaustion. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_has_attribute(void *pw, void *node, + lwc_string *name, bool *match) +{ + xmlNode *n = node; + xmlAttr *attr; + char *buf; + + buf = malloc(lwc_string_length(name) + 1); + if (buf == NULL) + return CSS_NOMEM; + + memcpy(buf, lwc_string_data(name), lwc_string_length(name)); + buf[lwc_string_length(name)] = '\0'; + + attr = xmlHasProp(n, (const xmlChar *) buf); + *match = attr != NULL; + + free(buf); + + return CSS_OK; + +} + +/** + * Callback to determine if a node has an attribute with given name and value. + * + * \param pw HTML document + * \param node DOM node + * \param name Name to match + * \param value Value to match + * \param match Pointer to location to receive result + * \return CSS_OK on success, + * CSS_NOMEM on memory exhaustion. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_has_attribute_equal(void *pw, void *node, + lwc_string *name, lwc_string *value, + bool *match) +{ + xmlNode *n = node; + xmlChar *attr; + char *buf; + + buf = malloc(lwc_string_length(name) + 1); + if (buf == NULL) + return CSS_NOMEM; + + memcpy(buf, lwc_string_data(name), lwc_string_length(name)); + buf[lwc_string_length(name)] = '\0'; + + *match = false; + + attr = xmlGetProp(n, (const xmlChar *) buf); + if (attr != NULL) { + *match = strlen((const char *) attr) == + lwc_string_length(value) && + strncmp((const char *) attr, + lwc_string_data(value), + lwc_string_length(value)) == 0; + xmlFree(attr); + } + + free(buf); + + return CSS_OK; +} + +/** + * Callback to determine if a node has an attribute with the given name whose + * value dashmatches that given. + * + * \param pw HTML document + * \param node DOM node + * \param name Name to match + * \param value Value to match + * \param match Pointer to location to receive result + * \return CSS_OK on success, + * CSS_NOMEM on memory exhaustion. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_has_attribute_dashmatch(void *pw, void *node, + lwc_string *name, lwc_string *value, + bool *match) +{ + xmlNode *n = node; + xmlChar *attr; + char *buf; + size_t vlen = lwc_string_length(value); + + buf = malloc(lwc_string_length(name) + 1); + if (buf == NULL) + return CSS_NOMEM; + + memcpy(buf, lwc_string_data(name), lwc_string_length(name)); + buf[lwc_string_length(name)] = '\0'; + + *match = false; + + attr = xmlGetProp(n, (const xmlChar *) buf); + if (attr != NULL) { + const char *p; + const char *start = (const char *) attr; + const char *end = start + strlen(start); + + for (p = start; p <= end; p++) { + if (*p == '-' || *p == '\0') { + if ((size_t) (p - start) == vlen && + strncasecmp(start, + lwc_string_data(value), + vlen) == 0) { + *match = true; + break; + } + + start = p + 1; + } + } + } + + free(buf); + + return CSS_OK; +} + +/** + * Callback to determine if a node has an attribute with the given name whose + * value includes that given. + * + * \param pw HTML document + * \param node DOM node + * \param name Name to match + * \param value Value to match + * \param match Pointer to location to receive result + * \return CSS_OK on success, + * CSS_NOMEM on memory exhaustion. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_has_attribute_includes(void *pw, void *node, + lwc_string *name, lwc_string *value, + bool *match) +{ + xmlNode *n = node; + xmlChar *attr; + char *buf; + size_t vlen = lwc_string_length(value); + + buf = malloc(lwc_string_length(name) + 1); + if (buf == NULL) + return CSS_NOMEM; + + memcpy(buf, lwc_string_data(name), lwc_string_length(name)); + buf[lwc_string_length(name)] = '\0'; + + *match = false; + + attr = xmlGetProp(n, (const xmlChar *) buf); + if (attr != NULL) { + const char *p; + const char *start = (const char *) attr; + const char *end = start + strlen(start); + + for (p = start; p <= end; p++) { + if (*p == ' ' || *p == '\0') { + if ((size_t) (p - start) == vlen && + strncasecmp(start, + lwc_string_data(value), + vlen) == 0) { + *match = true; + break; + } + + start = p + 1; + } + } + } + + free(buf); + + return CSS_OK; +} + +/** + * Callback to determine if a node is the first child of its parent. + * + * \param pw HTML document + * \param node DOM node + * \param match Pointer to location to receive result + * \return CSS_OK. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_is_first_child(void *pw, void *node, bool *match) +{ + xmlNode *n = node; + + *match = (n->parent != NULL && n->parent->children == n); + + return CSS_OK; +} + +/** + * Callback to determine if a node is a linking element. + * + * \param pw HTML document + * \param node DOM node + * \param match Pointer to location to receive result + * \return CSS_OK. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_is_link(void *pw, void *node, bool *match) +{ + xmlNode *n = node; + + *match = (strcasecmp((const char *) n->name, "a") == 0 && + xmlHasProp(n, (const xmlChar *) "href") != NULL); + + return CSS_OK; +} + +/** + * Callback to determine if a node is a linking element whose target has been + * visited. + * + * \param pw HTML document + * \param node DOM node + * \param match Pointer to location to receive result + * \return CSS_OK. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_is_visited(void *pw, void *node, bool *match) +{ + *match = false; + + /** \todo Implement visted check in a more performant way */ + +#ifdef SUPPORT_VISITED + struct content *html = pw; + xmlNode *n = node; + + if (strcasecmp((const char *) n->name, "a") == 0) { + char *url, *nurl; + url_func_result res; + xmlChar *href = xmlGetProp(n, (const xmlChar *) "href"); + + if (href == NULL) + return CSS_OK; + + /* Make href absolute */ + res = url_join((const char *) href, + html->data.html.base_url, &url); + + xmlFree(href); + + if (res == URL_FUNC_NOMEM) { + return CSS_NOMEM; + } else if (res == URL_FUNC_OK) { + /* Normalize it */ + res = url_normalize(url, &nurl); + + free(url); + + if (res == URL_FUNC_NOMEM) { + return CSS_NOMEM; + } else if (res == URL_FUNC_OK) { + const struct url_data *data; + + data = urldb_get_url_data(nurl); + + /* Visited if in the db and has + * non-zero visit count */ + if (data != NULL && data->visits > 0) + *match = true; + + free(nurl); + } + } + } +#endif + + return CSS_OK; +} + +/** + * Callback to determine if a node is currently being hovered over. + * + * \param pw HTML document + * \param node DOM node + * \param match Pointer to location to receive result + * \return CSS_OK. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_is_hover(void *pw, void *node, bool *match) +{ + /** \todo Support hovering */ + + *match = false; + + return CSS_OK; +} + +/** + * Callback to determine if a node is currently activated. + * + * \param pw HTML document + * \param node DOM node + * \param match Pointer to location to receive result + * \return CSS_OK. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_is_active(void *pw, void *node, bool *match) +{ + /** \todo Support active nodes */ + + *match = false; + + return CSS_OK; +} + +/** + * Callback to determine if a node has the input focus. + * + * \param pw HTML document + * \param node DOM node + * \param match Pointer to location to receive result + * \return CSS_OK. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_is_focus(void *pw, void *node, bool *match) +{ + /** \todo Support focussed nodes */ + + *match = false; + + return CSS_OK; +} + +/** + * Callback to determine if a node has the given language + * + * \param pw HTML document + * \param node DOM node + * \param lang Language specifier to match + * \param match Pointer to location to receive result + * \return CSS_OK. + * + * \post \a match will contain true if the node matches and false otherwise. + */ +css_error node_is_lang(void *pw, void *node, + lwc_string *lang, bool *match) +{ + /** \todo Support languages */ + + *match = false; + + return CSS_OK; +} + +/** + * Callback to retrieve presentational hints for a node + * + * \param pw HTML document + * \param node DOM node + * \param property CSS property to retrieve + * \param hint Pointer to hint object to populate + * \return CSS_OK on success, + * CSS_PROPERTY_NOT_SET if there is no hint for the requested property, + * CSS_NOMEM on memory exhaustion. + */ +css_error node_presentational_hint(void *pw, void *node, + uint32_t property, css_hint *hint) +{ + struct content *html = pw; + xmlNode *n = node; + + if (property == CSS_PROP_BACKGROUND_IMAGE) { + char *url; + url_func_result res; + xmlChar *bg = xmlGetProp(n, (const xmlChar *) "background"); + + if (bg == NULL) + return CSS_PROPERTY_NOT_SET; + + + res = url_join((const char *) bg, + html->data.html.base_url, &url); + + xmlFree(bg); + + if (res == URL_FUNC_NOMEM) { + return CSS_NOMEM; + } else if (res == URL_FUNC_OK) { + lwc_string *iurl; + lwc_error lerror; + + lerror = lwc_context_intern( + html->data.html.dict, url, + strlen(url), &iurl); + + free(url); + + if (lerror == lwc_error_oom) { + return CSS_NOMEM; + } else if (lerror == lwc_error_ok) { + hint->data.string = iurl; + hint->status = CSS_BACKGROUND_IMAGE_IMAGE; + return CSS_OK; + } + } + } else if (property == CSS_PROP_BACKGROUND_COLOR) { + xmlChar *bgcol = xmlGetProp(n, (const xmlChar *) "bgcolor"); + if (bgcol == NULL) + return CSS_PROPERTY_NOT_SET; + + if (nscss_parse_colour((const char *) bgcol, + &hint->data.color)) { + hint->status = CSS_BACKGROUND_COLOR_COLOR; + } else { + xmlFree(bgcol); + return CSS_PROPERTY_NOT_SET; + } + + xmlFree(bgcol); + + return CSS_OK; + } else if (property == CSS_PROP_COLOR) { + xmlChar *col; + css_error error; + bool is_link, is_visited; + + error = node_is_link(html, n, &is_link); + if (error != CSS_OK) + return error; + + if (is_link) { + xmlNode *body; + for (body = n; body != NULL && body->parent != NULL && + body->parent->parent != NULL; + body = body->parent) { + if (body->parent->parent->parent == NULL) + break; + } + + error = node_is_visited(html, n, &is_visited); + if (error != CSS_OK) + return error; + + if (is_visited) + col = xmlGetProp(body, + (const xmlChar *) "vlink"); + else + col = xmlGetProp(body, + (const xmlChar *) "link"); + } else if (strcmp((const char *) n->name, "body") == 0) { + col = xmlGetProp(n, (const xmlChar *) "text"); + } else { + col = xmlGetProp(n, (const xmlChar *) "color"); + } + + if (col == NULL) + return CSS_PROPERTY_NOT_SET; + + if (nscss_parse_colour((const char *) col, &hint->data.color)) { + hint->status = CSS_COLOR_COLOR; + } else { + xmlFree(col); + return CSS_PROPERTY_NOT_SET; + } + + xmlFree(col); + + return CSS_OK; + } else if (property == CSS_PROP_HEIGHT) { + xmlChar *height; + + if (strcmp((const char *) n->name, "iframe") == 0 || + strcmp((const char *) n->name, "td") == 0 || + strcmp((const char *) n->name, "th") == 0 || + strcmp((const char *) n->name, "tr") == 0 || + strcmp((const char *) n->name, "img") == 0 || + strcmp((const char *) n->name, "object") == 0 || + strcmp((const char *) n->name, "applet") == 0) + height = xmlGetProp(n, (const xmlChar *) "height"); + else if (strcmp((const char *) n->name, "textarea") == 0) + height = xmlGetProp(n, (const xmlChar *) "rows"); + else + height = NULL; + + if (height == NULL) + return CSS_PROPERTY_NOT_SET; + + if (parse_dimension((const char *) height, false, + &hint->data.length.value, + &hint->data.length.unit)) { + hint->status = CSS_HEIGHT_SET; + } else { + xmlFree(height); + return CSS_PROPERTY_NOT_SET; + } + + xmlFree(height); + + if (strcmp((const char *) n->name, "textarea") == 0) + hint->data.length.unit = CSS_UNIT_EM; + + return CSS_OK; + } else if (property == CSS_PROP_WIDTH) { + xmlChar *width; + + if (strcmp((const char *) n->name, "hr") == 0 || + strcmp((const char *) n->name, "iframe") == 0 || + strcmp((const char *) n->name, "img") == 0 || + strcmp((const char *) n->name, "object") == 0 || + strcmp((const char *) n->name, "table") == 0 || + strcmp((const char *) n->name, "td") == 0 || + strcmp((const char *) n->name, "th") == 0 || + strcmp((const char *) n->name, "applet") == 0) + width = xmlGetProp(n, (const xmlChar *) "width"); + else if (strcmp((const char *) n->name, "textarea") == 0) + width = xmlGetProp(n, (const xmlChar *) "cols"); + else if (strcmp((const char *) n->name, "input") == 0) { + width = xmlGetProp(n, (const xmlChar *) "size"); + } else + width = NULL; + + if (width == NULL) + return CSS_PROPERTY_NOT_SET; + + if (parse_dimension((const char *) width, false, + &hint->data.length.value, + &hint->data.length.unit)) { + hint->status = CSS_WIDTH_SET; + } else { + xmlFree(width); + return CSS_PROPERTY_NOT_SET; + } + + xmlFree(width); + + if (strcmp((const char *) n->name, "textarea") == 0) + hint->data.length.unit = CSS_UNIT_EX; + else if (strcmp((const char *) n->name, "input") == 0) { + xmlChar *type = xmlGetProp(n, (const xmlChar *) "type"); + + if (type == NULL || strcasecmp((const char *) type, + "text") == 0 || + strcasecmp((const char *) type, + "password") == 0) + hint->data.length.unit = CSS_UNIT_EX; + + if (type != NULL) + xmlFree(type); + } + + return CSS_OK; + } else if (property == CSS_PROP_BORDER_SPACING) { + xmlChar *cellspacing; + + if (strcmp((const char *) n->name, "table") != 0) + return CSS_PROPERTY_NOT_SET; + + cellspacing = xmlGetProp(n, (const xmlChar *) "cellspacing"); + if (cellspacing == NULL) + return CSS_PROPERTY_NOT_SET; + + if (parse_dimension((const char *) cellspacing, false, + &hint->data.position.h.value, + &hint->data.position.h.unit)) { + hint->data.position.v = hint->data.position.h; + hint->status = CSS_BORDER_SPACING_SET; + } else { + xmlFree(cellspacing); + return CSS_PROPERTY_NOT_SET; + } + + xmlFree(cellspacing); + + return CSS_OK; + } else if (property == CSS_PROP_BORDER_TOP_COLOR || + property == CSS_PROP_BORDER_RIGHT_COLOR || + property == CSS_PROP_BORDER_BOTTOM_COLOR || + property == CSS_PROP_BORDER_LEFT_COLOR) { + xmlChar *col; + + if (strcmp((const char *) n->name, "td") == 0 || + strcmp((const char *) n->name, "th") == 0) { + /* Find table */ + for (n = n->parent; n != NULL && + n->type == XML_ELEMENT_NODE; + n = n->parent) { + if (strcmp((const char *) n->name, "table") == + 0) + break; + } + + if (n == NULL) + return CSS_PROPERTY_NOT_SET; + } + + if (strcmp((const char *) n->name, "table") == 0) + col = xmlGetProp(n, (const xmlChar *) "bordercolor"); + else + col = NULL; + + if (col == NULL) + return CSS_PROPERTY_NOT_SET; + + if (nscss_parse_colour((const char *) col, &hint->data.color)) { + hint->status = CSS_BORDER_COLOR_COLOR; + } else { + xmlFree(col); + return CSS_PROPERTY_NOT_SET; + } + + xmlFree(col); + + return CSS_OK; + } else if (property == CSS_PROP_BORDER_TOP_STYLE || + property == CSS_PROP_BORDER_RIGHT_STYLE || + property == CSS_PROP_BORDER_BOTTOM_STYLE || + property == CSS_PROP_BORDER_LEFT_STYLE) { + if (strcmp((const char *) n->name, "td") == 0 || + strcmp((const char *) n->name, "th") == 0) { + /* Find table */ + for (n = n->parent; n != NULL && + n->type == XML_ELEMENT_NODE; + n = n->parent) { + if (strcmp((const char *) n->name, "table") == + 0) + break; + } + + if (n == NULL) + return CSS_PROPERTY_NOT_SET; + } + + if (strcmp((const char *) n->name, "table") == 0 && + xmlHasProp(n, + (const xmlChar *) "border") != NULL) { + hint->status = CSS_BORDER_STYLE_OUTSET; + return CSS_OK; + } + } else if (property == CSS_PROP_BORDER_TOP_WIDTH || + property == CSS_PROP_BORDER_RIGHT_WIDTH || + property == CSS_PROP_BORDER_BOTTOM_WIDTH || + property == CSS_PROP_BORDER_LEFT_WIDTH) { + xmlChar *width; + + if (strcmp((const char *) n->name, "td") == 0 || + strcmp((const char *) n->name, "th") == 0) { + /* Find table */ + for (n = n->parent; n != NULL && + n->type == XML_ELEMENT_NODE; + n = n->parent) { + if (strcmp((const char *) n->name, "table") == + 0) + break; + } + + if (n == NULL) + return CSS_PROPERTY_NOT_SET; + } + + if (strcmp((const char *) n->name, "table") == 0) + width = xmlGetProp(n, (const xmlChar *) "border"); + else + width = NULL; + + if (width == NULL) + return CSS_PROPERTY_NOT_SET; + + if (parse_dimension((const char *) width, false, + &hint->data.length.value, + &hint->data.length.unit)) { + hint->status = CSS_BORDER_WIDTH_WIDTH; + } else { + xmlFree(width); + return CSS_PROPERTY_NOT_SET; + } + + xmlFree(width); + + return CSS_OK; + } else if (property == CSS_PROP_MARGIN_TOP || + property == CSS_PROP_MARGIN_BOTTOM) { + xmlChar *vspace; + + if (strcmp((const char *) n->name, "img") == 0 || + strcmp((const char *) n->name, "applet") == 0) + vspace = xmlGetProp(n, (const xmlChar *) "vspace"); + else + vspace = NULL; + + if (vspace == NULL) + return CSS_PROPERTY_NOT_SET; + + if (parse_dimension((const char *) vspace, false, + &hint->data.length.value, + &hint->data.length.unit)) { + hint->status = CSS_MARGIN_SET; + } else { + xmlFree(vspace); + return CSS_PROPERTY_NOT_SET; + } + + xmlFree(vspace); + + return CSS_OK; + } else if (property == CSS_PROP_MARGIN_RIGHT || + property == CSS_PROP_MARGIN_LEFT) { + xmlChar *hspace; + + if (strcmp((const char *) n->name, "img") == 0 || + strcmp((const char *) n->name, "applet") == 0) + hspace = xmlGetProp(n, (const xmlChar *) "hspace"); + else + hspace = NULL; + + if (hspace == NULL) + return CSS_PROPERTY_NOT_SET; + + if (parse_dimension((const char *) hspace, false, + &hint->data.length.value, + &hint->data.length.unit)) { + hint->status = CSS_MARGIN_SET; + } else { + xmlFree(hspace); + return CSS_PROPERTY_NOT_SET; + } + + xmlFree(hspace); + + return CSS_OK; + } else if (property == CSS_PROP_PADDING_TOP || + property == CSS_PROP_PADDING_RIGHT || + property == CSS_PROP_PADDING_BOTTOM || + property == CSS_PROP_PADDING_LEFT) { + xmlChar *cellpadding = NULL; + + if (strcmp((const char *) n->name, "td") == 0 || + strcmp((const char *) n->name, "th") == 0) { + /* Find table */ + for (n = n->parent; n != NULL && + n->type == XML_ELEMENT_NODE; + n = n->parent) { + if (strcmp((const char *) n->name, "table") == + 0) + break; + } + + if (n != NULL) + cellpadding = xmlGetProp(n, + (const xmlChar *) "cellpadding"); + } + + if (cellpadding == NULL) + return CSS_PROPERTY_NOT_SET; + + if (parse_dimension((const char *) cellpadding, false, + &hint->data.length.value, + &hint->data.length.unit)) { + hint->status = CSS_PADDING_SET; + } else { + xmlFree(cellpadding); + return CSS_PROPERTY_NOT_SET; + } + + xmlFree(cellpadding); + + return CSS_OK; + } + + return CSS_PROPERTY_NOT_SET; +} + +/** + * Callback to retrieve the User-Agent defaults for a CSS property. + * + * \param pw HTML document + * \param property Property to retrieve defaults for + * \param hint Pointer to hint object to populate + * \return CSS_OK on success, + * CSS_INVALID if the property should not have a user-agent default. + */ +css_error ua_default_for_property(void *pw, uint32_t property, css_hint *hint) +{ + if (property == CSS_PROP_COLOR) { + hint->data.color = 0x00000000; + hint->status = CSS_COLOR_COLOR; + } else if (property == CSS_PROP_FONT_FAMILY) { + hint->data.strings = NULL; + switch (option_font_default) { + case PLOT_FONT_FAMILY_SANS_SERIF: + hint->status = CSS_FONT_FAMILY_SANS_SERIF; + break; + case PLOT_FONT_FAMILY_SERIF: + hint->status = CSS_FONT_FAMILY_SERIF; + break; + case PLOT_FONT_FAMILY_MONOSPACE: + hint->status = CSS_FONT_FAMILY_MONOSPACE; + break; + case PLOT_FONT_FAMILY_CURSIVE: + hint->status = CSS_FONT_FAMILY_CURSIVE; + break; + case PLOT_FONT_FAMILY_FANTASY: + hint->status = CSS_FONT_FAMILY_FANTASY; + break; + } + } else if (property == CSS_PROP_QUOTES) { + /** \todo Not exactly useful :) */ + hint->data.strings = NULL; + hint->status = CSS_QUOTES_NONE; + } else if (property == CSS_PROP_VOICE_FAMILY) { + /** \todo Fix this when we have voice-family done */ + hint->data.strings = NULL; + hint->status = 0; + } else { + return CSS_INVALID; + } + + return CSS_OK; +} + +/** + * Mapping of colour name to CSS color + */ +struct colour_map { + const char *name; + css_color color; +}; + +/** + * Name comparator for named colour matching + * + * \param a Name to match + * \param b Colour map entry to consider + * \return 0 on match, + * < 0 if a < b, + * > 0 if b > a. + */ +int cmp_colour_name(const void *a, const void *b) +{ + const char *aa = a; + const struct colour_map *bb = b; + + return strcasecmp(aa, bb->name); +} + +/** + * Parse a named colour + * + * \param name Name to parse + * \param result Pointer to location to receive css_color + * \return true on success, false on invalid input + */ +bool parse_named_colour(const char *name, css_color *result) +{ + static const struct colour_map named_colours[] = { + { "aliceblue", 0xf0f8ff00 }, + { "antiquewhite", 0xfaebd700 }, + { "aqua", 0x00ffff00 }, + { "aquamarine", 0x7fffd400 }, + { "azure", 0xf0ffff00 }, + { "beige", 0xf5f5dc00 }, + { "bisque", 0xffe4c400 }, + { "black", 0x00000000 }, + { "blanchedalmond", 0xffebcd00 }, + { "blue", 0x0000ff00 }, + { "blueviolet", 0x8a2be200 }, + { "brown", 0xa52a2a00 }, + { "burlywood", 0xdeb88700 }, + { "cadetblue", 0x5f9ea000 }, + { "chartreuse", 0x7fff0000 }, + { "chocolate", 0xd2691e00 }, + { "coral", 0xff7f5000 }, + { "cornflowerblue", 0x6495ed00 }, + { "cornsilk", 0xfff8dc00 }, + { "crimson", 0xdc143c00 }, + { "cyan", 0x00ffff00 }, + { "darkblue", 0x00008b00 }, + { "darkcyan", 0x008b8b00 }, + { "darkgoldenrod", 0xb8860b00 }, + { "darkgray", 0xa9a9a900 }, + { "darkgreen", 0x00640000 }, + { "darkgrey", 0xa9a9a900 }, + { "darkkhaki", 0xbdb76b00 }, + { "darkmagenta", 0x8b008b00 }, + { "darkolivegreen", 0x556b2f00 }, + { "darkorange", 0xff8c0000 }, + { "darkorchid", 0x9932cc00 }, + { "darkred", 0x8b000000 }, + { "darksalmon", 0xe9967a00 }, + { "darkseagreen", 0x8fbc8f00 }, + { "darkslateblue", 0x483d8b00 }, + { "darkslategray", 0x2f4f4f00 }, + { "darkslategrey", 0x2f4f4f00 }, + { "darkturquoise", 0x00ced100 }, + { "darkviolet", 0x9400d300 }, + { "deeppink", 0xff149300 }, + { "deepskyblue", 0x00bfff00 }, + { "dimgray", 0x69696900 }, + { "dimgrey", 0x69696900 }, + { "dodgerblue", 0x1e90ff00 }, + { "feldspar", 0xd1927500 }, + { "firebrick", 0xb2222200 }, + { "floralwhite", 0xfffaf000 }, + { "forestgreen", 0x228b2200 }, + { "fuchsia", 0xff00ff00 }, + { "gainsboro", 0xdcdcdc00 }, + { "ghostwhite", 0xf8f8ff00 }, + { "gold", 0xffd70000 }, + { "goldenrod", 0xdaa52000 }, + { "gray", 0x80808000 }, + { "green", 0x00800000 }, + { "greenyellow", 0xadff2f00 }, + { "grey", 0x80808000 }, + { "honeydew", 0xf0fff000 }, + { "hotpink", 0xff69b400 }, + { "indianred", 0xcd5c5c00 }, + { "indigo", 0x4b008200 }, + { "ivory", 0xfffff000 }, + { "khaki", 0xf0e68c00 }, + { "lavender", 0xe6e6fa00 }, + { "lavenderblush", 0xfff0f500 }, + { "lawngreen", 0x7cfc0000 }, + { "lemonchiffon", 0xfffacd00 }, + { "lightblue", 0xadd8e600 }, + { "lightcoral", 0xf0808000 }, + { "lightcyan", 0xe0ffff00 }, + { "lightgoldenrodyellow", 0xfafad200 }, + { "lightgray", 0xd3d3d300 }, + { "lightgreen", 0x90ee9000 }, + { "lightgrey", 0xd3d3d300 }, + { "lightpink", 0xffb6c100 }, + { "lightsalmon", 0xffa07a00 }, + { "lightseagreen", 0x20b2aa00 }, + { "lightskyblue", 0x87cefa00 }, + { "lightslateblue", 0x8470ff00 }, + { "lightslategray", 0x77889900 }, + { "lightslategrey", 0x77889900 }, + { "lightsteelblue", 0xb0c4de00 }, + { "lightyellow", 0xffffe000 }, + { "lime", 0x00ff0000 }, + { "limegreen", 0x32cd3200 }, + { "linen", 0xfaf0e600 }, + { "magenta", 0xff00ff00 }, + { "maroon", 0x80000000 }, + { "mediumaquamarine", 0x66cdaa00 }, + { "mediumblue", 0x0000cd00 }, + { "mediumorchid", 0xba55d300 }, + { "mediumpurple", 0x9370db00 }, + { "mediumseagreen", 0x3cb37100 }, + { "mediumslateblue", 0x7b68ee00 }, + { "mediumspringgreen", 0x00fa9a00 }, + { "mediumturquoise", 0x48d1cc00 }, + { "mediumvioletred", 0xc7158500 }, + { "midnightblue", 0x19197000 }, + { "mintcream", 0xf5fffa00 }, + { "mistyrose", 0xffe4e100 }, + { "moccasin", 0xffe4b500 }, + { "navajowhite", 0xffdead00 }, + { "navy", 0x00008000 }, + { "oldlace", 0xfdf5e600 }, + { "olive", 0x80800000 }, + { "olivedrab", 0x6b8e2300 }, + { "orange", 0xffa50000 }, + { "orangered", 0xff450000 }, + { "orchid", 0xda70d600 }, + { "palegoldenrod", 0xeee8aa00 }, + { "palegreen", 0x98fb9800 }, + { "paleturquoise", 0xafeeee00 }, + { "palevioletred", 0xdb709300 }, + { "papayawhip", 0xffefd500 }, + { "peachpuff", 0xffdab900 }, + { "peru", 0xcd853f00 }, + { "pink", 0xffc0cb00 }, + { "plum", 0xdda0dd00 }, + { "powderblue", 0xb0e0e600 }, + { "purple", 0x80008000 }, + { "red", 0xff000000 }, + { "rosybrown", 0xbc8f8f00 }, + { "royalblue", 0x4169e100 }, + { "saddlebrown", 0x8b451300 }, + { "salmon", 0xfa807200 }, + { "sandybrown", 0xf4a46000 }, + { "seagreen", 0x2e8b5700 }, + { "seashell", 0xfff5ee00 }, + { "sienna", 0xa0522d00 }, + { "silver", 0xc0c0c000 }, + { "skyblue", 0x87ceeb00 }, + { "slateblue", 0x6a5acd00 }, + { "slategray", 0x70809000 }, + { "slategrey", 0x70809000 }, + { "snow", 0xfffafa00 }, + { "springgreen", 0x00ff7f00 }, + { "steelblue", 0x4682b400 }, + { "tan", 0xd2b48c00 }, + { "teal", 0x00808000 }, + { "thistle", 0xd8bfd800 }, + { "tomato", 0xff634700 }, + { "turquoise", 0x40e0d000 }, + { "violet", 0xee82ee00 }, + { "violetred", 0xd0209000 }, + { "wheat", 0xf5deb300 }, + { "white", 0xffffff00 }, + { "whitesmoke", 0xf5f5f500 }, + { "yellow", 0xffff0000 }, + { "yellowgreen", 0x9acd3200 } + }; + const struct colour_map *entry; + + entry = bsearch(name, named_colours, + sizeof(named_colours) / sizeof(named_colours[0]), + sizeof(named_colours[0]), + cmp_colour_name); + + if (entry != NULL) + *result = entry->color; + + return entry != NULL; +} + +/** + * Parse a dimension string + * + * \param data Data to parse (NUL-terminated) + * \param strict Whether to enforce strict parsing rules + * \param length Pointer to location to receive dimension's length + * \param unit Pointer to location to receive dimension's unit + * \return true on success, false on invalid input + */ +bool parse_dimension(const char *data, bool strict, css_fixed *length, + css_unit *unit) +{ + size_t len; + size_t read; + css_fixed value; + + len = strlen(data); + + if (parse_number(data, false, true, &value, &read) == false) + return false; + + if (strict && value < INTTOFIX(1)) + return false; + + *length = value; + + if (len > read && data[read] == '%') + *unit = CSS_UNIT_PCT; + else + *unit = CSS_UNIT_PX; + + return true; +} + +/** + * Parse a number string + * + * \param data Data to parse (NUL-terminated) + * \param maybe_negative Negative numbers permitted + * \param real Floating point numbers permitted + * \param value Pointer to location to receive numeric value + * \param consumed Pointer to location to receive number of input + * bytes consumed + * \return true on success, false on invalid input + */ +bool parse_number(const char *data, bool maybe_negative, bool real, + css_fixed *value, size_t *consumed) +{ + size_t len; + const uint8_t *ptr; + int32_t intpart = 0; + int32_t fracpart = 0; + int32_t pwr = 1; + int sign = 1; + + *consumed = 0; + + len = strlen(data); + ptr = (const uint8_t *) data; + + if (len == 0) + return false; + + /* Skip leading whitespace */ + while (len > 0 && isWhitespace(ptr[0])) { + len--; + ptr++; + } + + if (len == 0) + return false; + + /* Extract sign, if any */ + if (ptr[0] == '+') { + len--; + ptr++; + } else if (ptr[0] == '-' && maybe_negative) { + sign = -1; + len--; + ptr++; + } + + if (len == 0) + return false; + + /* Must have a digit [0,9] */ + if ('0' > ptr[0] || ptr[0] > '9') + return false; + + /* Now extract intpart, assuming base 10 */ + while (len > 0) { + /* Stop on first non-digit */ + if (ptr[0] < '0' || '9' < ptr[0]) + break; + + /* Prevent overflow of 'intpart'; proper clamping below */ + if (intpart < (1 << 22)) { + intpart *= 10; + intpart += ptr[0] - '0'; + } + ptr++; + len--; + } + + /* And fracpart, again, assuming base 10 */ + if (real && len > 1 && ptr[0] == '.' && + ('0' <= ptr[1] && ptr[1] <= '9')) { + ptr++; + len--; + + while (len > 0) { + if (ptr[0] < '0' || '9' < ptr[0]) + break; + + if (pwr < 1000000) { + pwr *= 10; + fracpart *= 10; + fracpart += ptr[0] - '0'; + } + ptr++; + len--; + } + + fracpart = ((1 << 10) * fracpart + pwr/2) / pwr; + if (fracpart >= (1 << 10)) { + intpart++; + fracpart &= (1 << 10) - 1; + } + } + + if (sign > 0) { + /* If the result is larger than we can represent, + * then clamp to the maximum value we can store. */ + if (intpart >= (1 << 21)) { + intpart = (1 << 21) - 1; + fracpart = (1 << 10) - 1; + } + } else { + /* If the negated result is smaller than we can represent + * then clamp to the minimum value we can store. */ + if (intpart >= (1 << 21)) { + intpart = -(1 << 21); + fracpart = 0; + } else { + intpart = -intpart; + if (fracpart) { + fracpart = (1 << 10) - fracpart; + intpart--; + } + } + } + + *value = (intpart << 10) | fracpart; + + *consumed = ptr - (const uint8_t *) data; + + return true; +} + +/****************************************************************************** + * Utility functions * + ******************************************************************************/ + +/** + * Determine if a given character is whitespace + * + * \param c Character to consider + * \return true if character is whitespace, false otherwise + */ +bool isWhitespace(char c) +{ + return c == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\n'; +} + +/** + * Determine if a given character is a valid hex digit + * + * \param c Character to consider + * \return true if character is a valid hex digit, false otherwise + */ +bool isHex(char c) +{ + return ('0' <= c && c <= '9') || + ('A' <= (c & ~0x20) && (c & ~0x20) <= 'F'); +} + +/** + * Convert a character representing a hex digit to the corresponding hex value + * + * \param c Character to convert + * \return Hex value represented by character + * + * \note This function assumes an ASCII-compatible character set + */ +uint8_t charToHex(char c) +{ + /* 0-9 */ + c -= '0'; + + /* A-F */ + if (c > 9) + c -= 'A' - '9' - 1; + + /* a-f */ + if (c > 15) + c -= 'a' - 'A'; + + return c; +} + diff --git a/css/select.h b/css/select.h new file mode 100644 index 000000000..7b87b2783 --- /dev/null +++ b/css/select.h @@ -0,0 +1,51 @@ +/* + * Copyright 2009 John-Mark Bell + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETSURF_CSS_SELECT_H_ +#define NETSURF_CSS_SELECT_H_ + +#include + +#include + +#include "css/css.h" + +struct content; + +css_stylesheet *nscss_create_inline_style(const uint8_t *data, size_t len, + const char *charset, const char *url, bool allow_quirks, + lwc_context *dict, css_allocator_fn alloc, void *pw); + +css_computed_style *nscss_get_style(struct content *html, xmlNode *n, + uint32_t pseudo_element, uint64_t media, + const css_stylesheet *inline_style, + css_allocator_fn alloc, void *pw); + +css_computed_style *nscss_get_initial_style(struct content *html, + css_allocator_fn, void *pw); + +css_computed_style *nscss_get_blank_style(struct content *html, + const css_computed_style *parent, + css_allocator_fn alloc, void *pw); + +css_error nscss_compute_font_size(void *pw, const css_hint *parent, + css_hint *size); + +bool nscss_parse_colour(const char *data, css_color *result); + +#endif diff --git a/css/testcss.c b/css/testcss.c deleted file mode 100644 index d21efa619..000000000 --- a/css/testcss.c +++ /dev/null @@ -1,181 +0,0 @@ -#include -#include -#include -#include -#include - -#include "utils/config.h" -#include "content/content.h" -#include "css/css.h" -#include "desktop/options.h" -#include "utils/messages.h" -#include "utils/talloc.h" -#include "utils/utils.h" - -#define ITERATIONS (1) - -bool verbose_log = 0; -int option_font_size = 10; -int option_font_min_size = 10; - -void die(const char * const error) -{ -} - -static bool css_process_data(struct content *c, const char *data, - unsigned int size) -{ - char *source_data; - union content_msg_data msg_data; - unsigned int extra_space; - - assert(c); - - if ((c->source_size + size) > c->source_allocated) { - extra_space = (c->source_size + size) / 4; - if (extra_space < 65536) - extra_space = 65536; - source_data = talloc_realloc(c, c->source_data, char, - c->source_size + size + extra_space); - if (!source_data) { - c->status = CONTENT_STATUS_ERROR; - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; - } - c->source_data = source_data; - c->source_allocated = c->source_size + size + extra_space; - } - memcpy(c->source_data + c->source_size, data, size); - c->source_size += size; - - return true; -} - -void content_broadcast(struct content *c, content_msg msg, - union content_msg_data data) -{ -} - -void content_remove_user(struct content *c, - void (*callback)(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data), - intptr_t p1, intptr_t p2) -{ -} - -void content_add_error(struct content *c, const char *token, - unsigned int line) -{ -} - -void fetch_abort(struct fetch *f) -{ -} - -void fetch_poll(void) -{ -} - -struct content * fetchcache(const char *url, - void (*callback)(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data), - intptr_t p1, intptr_t p2, - int width, int height, - bool no_error_pages, - char *post_urlenc, - struct form_successful_control *post_multipart, - bool verifiable, - bool download) -{ - return NULL; -} - -void fetchcache_go(struct content *content, const char *referer, - void (*callback)(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data), - intptr_t p1, intptr_t p2, - int width, int height, - char *post_urlenc, - struct form_successful_control *post_multipart, - bool verifiable, const char *parent_url) -{ -} - -void gui_multitask(void) -{ -} - -int main(int argc, char **argv) -{ -/* const char data[] = "h1 { blah: foo; display: block; }" - "h1.c1 h2#id1 + h3, h4 h5.c2#id2 { size: 100mm; color: red }" - "p { background-color: #123; clear: left; color: #ff0000; display: block;" - "float: left; font-size: 150%; height: blah; line-height: 100;" - "text-align: left right; width: 90%;}"; -*/ - struct content *c; - FILE *fp; -#define CHUNK_SIZE (4096) - char data[CHUNK_SIZE]; - size_t len, origlen; - - if (argc != 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; - } - - printf("sizeof(struct css_style): %zu\n", sizeof(struct css_style)); - - for (int i = 0; i < ITERATIONS; i++) { - c = talloc_zero(0, struct content); - if (c == NULL) { - fprintf(stderr, "No memory for content\n"); - return 1; - } - - c->url = talloc_strdup(c, "http://www.example.com/"); - if (c->url == NULL) { - fprintf(stderr, "No memory for url\n"); - talloc_free(c); - return 1; - } - - c->type = CONTENT_CSS; - - fp = fopen(argv[1], "rb"); - if (fp == NULL) { - fprintf(stderr, "Failed opening %s\n", argv[1]); - talloc_free(c); - return 1; - } - - fseek(fp, 0, SEEK_END); - origlen = len = ftell(fp); - fseek(fp, 0, SEEK_SET); - - while (len >= CHUNK_SIZE) { - fread(data, 1, CHUNK_SIZE, fp); - - css_process_data(c, data, CHUNK_SIZE); - - len -= CHUNK_SIZE; - } - - if (len > 0) { - fread(data, 1, len, fp); - - css_process_data(c, data, len); - - len = 0; - } - - fclose(fp); - - css_convert(c, 100, 100); - - talloc_free(c); - } - - return 0; -} diff --git a/css/utils.c b/css/utils.c new file mode 100644 index 000000000..5ed569474 --- /dev/null +++ b/css/utils.c @@ -0,0 +1,124 @@ +/* + * Copyright 2004 James Bursa + * Copyright 2009 John-Mark Bell + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "css/utils.h" + +#include "desktop/options.h" +#include "utils/log.h" + +/** Screen DPI in fixed point units: defaults to 90, which RISC OS uses */ +css_fixed nscss_screen_dpi = INTTOFIX(90); + +/** + * Convert an absolute CSS length to points. + * + * \param length Length to convert + * \param unit Corresponding unit + * \return length in points + */ +css_fixed nscss_len2pt(css_fixed length, css_unit unit) +{ + /* Length must not be relative */ + assert(unit != CSS_UNIT_EM && unit != CSS_UNIT_EX); + + switch (unit) { + /* We assume the screen and any other output has the same dpi */ + /* 1in = DPIpx => 1px = (72/DPI)pt */ + case CSS_UNIT_PX: return FDIV(FMULI(length, 72), nscss_screen_dpi); + /* 1in = 72pt */ + case CSS_UNIT_IN: return FMULI(length, 72); + /* 1in = 2.54cm => 1cm = (72/2.54)pt */ + case CSS_UNIT_CM: return FMUL(length, + FDIV(INTTOFIX(72), FLTTOFIX(2.54))); + /* 1in = 25.4mm => 1mm = (72/25.4)pt */ + case CSS_UNIT_MM: return FMUL(length, + FDIV(INTTOFIX(72), FLTTOFIX(25.4))); + case CSS_UNIT_PT: return length; + /* 1pc = 12pt */ + case CSS_UNIT_PC: return FMULI(length, 12); + default: break; + } + + return 0; +} + + +/** + * Convert a CSS length to pixels. + * + * \param length Length to convert + * \param unit Corresponding unit + * \param style Computed style applying to length. May be NULL if unit is + * neither em nor ex + * \return length in pixels + */ +css_fixed nscss_len2px(css_fixed length, css_unit unit, + const css_computed_style *style) +{ + /* We assume the screen and any other output has the same dpi */ + const css_fixed lendpi = FMUL(length, nscss_screen_dpi); + + assert(style != NULL || (unit != CSS_UNIT_EM && unit != CSS_UNIT_EX)); + + switch (unit) { + case CSS_UNIT_EM: + case CSS_UNIT_EX: + { + css_fixed font_size = 0; + css_unit font_unit = CSS_UNIT_PT; + + css_computed_font_size(style, &font_size, &font_unit); + + /* Convert to points */ + font_size = nscss_len2pt(font_size, font_unit); + + /* Clamp to configured minimum */ + if (font_size < FDIVI(INTTOFIX(option_font_min_size), 10)) { + font_size = FDIVI(INTTOFIX(option_font_min_size), 10); + } + + /* Expand relative length */ + length = FMUL(length, + nscss_len2px(font_size, CSS_UNIT_PT, style)); + + /* Scale ex units: we use a fixed ratio of 1ex = 0.6em */ + if (unit == CSS_UNIT_EX) + length = FMUL(length, FLTTOFIX(0.6)); + + return length; + } + case CSS_UNIT_PX: return length; + /* 1in = DPIpx */ + case CSS_UNIT_IN: return lendpi; + /* 1in = 2.54cm => 1cm = (DPI/2.54)px */ + case CSS_UNIT_CM: return FDIV(lendpi, FLTTOFIX(2.54)); + /* 1in = 25.4mm => 1mm = (DPI/25.4)px */ + case CSS_UNIT_MM: return FDIV(lendpi, FLTTOFIX(25.4)); + /* 1in = 72pt => 1pt = (DPI/72)px */ + case CSS_UNIT_PT: return FDIV(lendpi, INTTOFIX(72)); + /* 1pc = 12pt => 1in = 6pc => 1pc = (DPI/6)px */ + case CSS_UNIT_PC: return FDIV(lendpi, INTTOFIX(6)); + default: break; + } + + return 0; +} + diff --git a/css/utils.h b/css/utils.h new file mode 100644 index 000000000..71da8a41a --- /dev/null +++ b/css/utils.h @@ -0,0 +1,44 @@ +/* + * Copyright 2009 John-Mark Bell + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NETSURF_CSS_UTILS_H_ +#define NETSURF_CSS_UTILS_H_ + +#include "css/css.h" +#include "desktop/plot_style.h" + +/* DPI of the screen, in fixed point units */ +extern css_fixed nscss_screen_dpi; + +/** + * Convert a CSS color to a NetSurf colour primitive + * + * \param color The CSS color to convert + * \return Corresponding NetSurf colour primitive + */ +#define nscss_color_to_ns(color) \ + (((color) & 0xff000000) >> 24) | \ + (((color) & 0xff0000) >> 8) | \ + (((color) & 0xff00) << 8) | \ + (((color) & 0xff) << 24) + +css_fixed nscss_len2pt(css_fixed length, css_unit unit); +css_fixed nscss_len2px(css_fixed length, css_unit unit, + const css_computed_style *style); + +#endif -- cgit v1.2.3