diff options
96 files changed, 7673 insertions, 10758 deletions
diff --git a/!NetSurf/Resources/Quirks,f79 b/!NetSurf/Resources/Quirks,f79 new file mode 100644 index 000000000..2f0f9f369 --- /dev/null +++ b/!NetSurf/Resources/Quirks,f79 @@ -0,0 +1,14 @@ +/* Quirks mode stylesheet for NetSurf */ + +/** + * Netscape/IE 4 compatibility. + * + * See https://developer.mozilla.org/en/Fixing_Table_Inheritance_in_Quirks_Mode + */ +table { + /* Reset font properties (except family) */ + font-size: medium; + font-style: normal; + font-variant: normal; + font-weight: normal; +} @@ -254,9 +254,10 @@ CFLAGS += -DNETSURF_HOMEPAGE=\"$(NETSURF_HOMEPAGE)\" ifeq ($(TARGET),riscos) ifeq ($(HOST),riscos) LDFLAGS += -Xlinker -symbols=$(OBJROOT)/sym -lxml2 -lz -lm -lcurl -lcares - LDFLAGS += -lssl -lcrypto -lhubbub -lparserutils + LDFLAGS += -lssl -lcrypto -lhubbub -lcss -lparserutils -lwapcaplet else - LDFLAGS += $(shell $(PKG_CONFIG) --libs libxml-2.0 libcurl libhubbub openssl) + LDFLAGS += $(shell $(PKG_CONFIG) --libs libxml-2.0 libcurl openssl) + LDFLAGS += $(shell $(PKG_CONFIG) --libs libhubbub libcss) endif $(eval $(call feature_enabled,NSSVG,-DWITH_NS_SVG,-lsvgtiny,SVG rendering)) @@ -289,9 +290,7 @@ ifeq ($(TARGET),riscos) CFLAGS += -I$(GCCSDK_INSTALL_ENV)/include \ -I$(GCCSDK_INSTALL_ENV)/include/libxml2 \ - -I$(GCCSDK_INSTALL_ENV)/include/libmng \ - -I$(GCCSDK_INSTALL_ENV)/include/hubbub0 \ - -I$(GCCSDK_INSTALL_ENV)/include/parserutils0 + -I$(GCCSDK_INSTALL_ENV)/include/libmng ifeq ($(HOST),riscos) CFLAGS += -I<OSLib$$Dir> -mthrowback endif @@ -322,7 +321,7 @@ ifeq ($(TARGET),beos) LDFLAGS += -L/boot/common/lib # some people do *not* have libm... LDFLAGS += -lxml2 -lcurl -liconv - LDFLAGS += -lssl -lcrypto -lhubbub -lparserutils + LDFLAGS += -lssl -lcrypto -lhubbub -lcss -lparserutils -lwapcaplet CFLAGS += -I. -O $(WARNFLAGS) -Dnsbeos \ -D_BSD_SOURCE -D_POSIX_C_SOURCE \ @@ -345,8 +344,9 @@ ifeq ($(TARGET),beos) CFLAGS += -I/boot/home/config/include \ -I/boot/home/config/include/libxml2 \ -I/boot/home/config/include/libmng \ - -I/boot/home/config/include/hubbub0 \ - -I/boot/home/config/include/parserutils0 + -I/boot/home/config/include/hubbub \ + -I/boot/home/config/include/libcss \ + -I/boot/home/config/include/parserutils ifneq ($(wildcard /boot/develop/lib/*/libzeta.so),) LDFLAGS += -lzeta endif @@ -355,8 +355,9 @@ ifeq ($(TARGET),beos) CFLAGS += -I/boot/common/include \ -I/boot/common/include/libxml2 \ -I/boot/common/include/libmng \ - -I/boot/common/include/hubbub0 \ - -I/boot/common/include/parserutils0 + -I/boot/common/include/hubbub \ + -I/boot/common/include/libcss \ + -I/boot/common/include/parserutils NETLDFLAGS := -lnetwork else ifneq ($(wildcard /boot/develop/lib/*/libbind.so),) @@ -377,16 +378,13 @@ ifeq ($(TARGET),beos) ifeq ($(HOST),beos) CFLAGS += -I$(PREFIX)/include LDFLAGS += -L$(PREFIX)/lib - $(eval $(call feature_enabled,HUBBUB,-DWITH_HUBBUB,-lhubbub-debug -lparserutils-debug,Hubbub HTML parser)) $(eval $(call feature_enabled,BMP,-DWITH_BMP,-lnsbmp,NetSurf BMP decoder)) $(eval $(call feature_enabled,GIF,-DWITH_GIF,-lnsgif,NetSurf GIF decoder)) $(eval $(call feature_enabled,PNG,-DWITH_PNG,-lpng,PNG support)) else - NETSURF_FEATURE_HUBBUB_CFLAGS := -DWITH_HUBBUB NETSURF_FEATURE_BMP_CFLAGS := -DWITH_BMP NETSURF_FEATURE_GIF_CFLAGS := -DWITH_GIF NETSURF_FEATURE_PNG_CFLAGS := -DWITH_PNG - $(eval $(call pkg_config_find_and_add,HUBBUB,libhubbub,Hubbub HTML parser)) $(eval $(call pkg_config_find_and_add,BMP,libnsbmp,NetSurf BMP decoder)) $(eval $(call pkg_config_find_and_add,GIF,libnsgif,NetSurf GIF decoder)) $(eval $(call pkg_config_find_and_add,PNG,libpng,PNG support)) @@ -399,6 +397,7 @@ endif ifeq ($(TARGET),gtk) LDFLAGS += $(shell $(PKG_CONFIG) --libs libxml-2.0 libcurl libhubbub openssl) + LDFLAGS += $(shell $(PKG_CONFIG) --libs libcss) # define additional CFLAGS and LDFLAGS requirements for pkg-configed libs here NETSURF_FEATURE_RSVG_CFLAGS := -DWITH_RSVG @@ -460,7 +459,7 @@ ifeq ($(TARGET),amiga) CFLAGS += -D__USE_INLINE__ -std=c99 -I . -Dnsamiga LDFLAGS += -lxml2 -lcurl -lpthread -lregex -lauto - LDFLAGS += -lssl -lcrypto -lhubbub -lparserutils + LDFLAGS += -lssl -lcrypto -lhubbub -lcss -lparserutils -lwapcaplet ifeq ($(NETSURF_AMIGA_USE_CAIRO),YES) CFLAGS += -DNS_AMIGA_CAIRO -I SDK:local/common/include/cairo @@ -503,10 +502,12 @@ ifeq ($(TARGET),framebuffer) -D_POSIX_C_SOURCE=200112L \ $(shell $(PKG_CONFIG) --cflags libnsfb) \ $(shell $(PKG_CONFIG) --cflags libhubbub libcurl openssl) \ + $(shell $(PKG_CONFIG) --cflags libcss) \ $(shell xml2-config --cflags) LDFLAGS += -Wl,--whole-archive $(shell $(PKG_CONFIG) --libs libnsfb) -Wl,--no-whole-archive LDFLAGS += $(shell $(PKG_CONFIG) --libs libxml-2.0 libcurl libhubbub openssl) + LDFLAGS += $(shell $(PKG_CONFIG) --libs libcss) endif @@ -634,7 +635,7 @@ DEPFILES := # 3 = obj filename, no prefix define dependency_generate_c DEPFILES += $(2) -$$(DEPROOT)/$(2): $$(DEPROOT)/created $(1) css/css_enum.h css/parser.h Makefile.config +$$(DEPROOT)/$(2): $$(DEPROOT)/created $(1) Makefile.config endef diff --git a/Makefile.sources b/Makefile.sources index e3404739d..2ffe1e7f4 100644 --- a/Makefile.sources +++ b/Makefile.sources @@ -7,10 +7,10 @@ S_CONTENT := content.c fetch.c fetchcache.c urldb.c \ fetchers/fetch_curl.c fetchers/fetch_data.c -S_CSS := css.c css_enum.c parser.c ruleset.c scanner.c +S_CSS := css.c dump.c internal.c select.c utils.c S_RENDER := box.c box_construct.c box_normalise.c directory.c \ font.c form.c html.c html_redraw.c hubbub_binding.c imagemap.c \ - layout.c list.c loosen.c table.c textplain.c + layout.c list.c table.c textplain.c S_UTILS := base64.c filename.c hashtable.c locale.c messages.c talloc.c \ url.c utf8.c utils.c useragent.c S_DESKTOP := knockout.c options.c print.c tree.c version.c textarea.c \ @@ -102,38 +102,14 @@ endif S_FRAMEBUFFER := $(addprefix framebuffer/,$(S_FRAMEBUFFER)) -# Some extra rules for building the scanner etc. -css/css_enum.c css/css_enum.h: css/css_enums css/makeenum - $(VQ)echo "MAKEENUM: css" - $(Q)$(PERL) css/makeenum css/css_enum < css/css_enums +# Some extra rules for building the transliteration table. ifeq ($(HOST),riscos) -css/parser.c css/parser.h: css/parser.y - $(VQ)echo " LEMON: css/parser.y" - $(Q)dir css - $(Q)-lemon parser.y - $(Q)dir ^ - $(Q)touch css/parser.c css/parser.h -css/scanner.c: css/scanner.l - $(VQ)echo " RE2C: css/scanner.l" - $(Q)dir css - $(Q)re2c -s scanner.l > scanner.c - $(Q)dir ^ utils/translit.c: transtab $(VQ)echo "TRANSTAB: utils/translit.c" $(Q)dir utils $(Q)$(PERL) tt2code < transtab > translit.c $(Q)dir ^ else -css/parser.c css/parser.h: css/parser.y - $(VQ)echo " LEMON: css/parser.y" - $(Q)# If lemon really fails hard, we'll never know, sorry - $(Q)cd css; lemon parser.y || true - $(Q)# Unfortunately if the output hasn't changed, lemon doesn't - $(Q)# alter the file and thus make gets confused. - $(Q)touch css/parser.c css/parser.h -css/scanner.c: css/scanner.l - $(VQ)echo " RE2C: css/scanner.l" - $(Q)cd css; re2c -s scanner.l > scanner.c utils/translit.c: transtab $(VQ)echo "TRANSTAB: utils/translit.c" $(Q)cd utils; $(PERL) tt2code < transtab > translit.c @@ -141,9 +117,7 @@ endif clean-intermediates: $(VQ)echo " CLEAN: intermediates" - $(Q)$(RM) css/css_enum.c css/css_enum.h css/parser.c \ - css/parser.out css/parser.h - $(Q)$(RM) css/scanner.c utils/translit.c + $(Q)$(RM) utils/translit.c CLEANS += clean-intermediates diff --git a/amiga/context_menu.c b/amiga/context_menu.c index 7f60d4963..24dd90f2b 100755 --- a/amiga/context_menu.c +++ b/amiga/context_menu.c @@ -92,7 +92,7 @@ void ami_context_menu_show(struct gui_window_2 *gwin,int x,int y) while(curbox = box_at_point(curbox,x,y,&box_x,&box_y,&cc)) { - if (curbox->style && curbox->style->visibility == CSS_VISIBILITY_HIDDEN) continue; + if (curbox->style && css_computed_visibility(curbox->style) == CSS_VISIBILITY_HIDDEN) continue; if(curbox->href) { diff --git a/amiga/font.c b/amiga/font.c index cd7d834e2..5c694c199 100644 --- a/amiga/font.c +++ b/amiga/font.c @@ -237,7 +237,7 @@ struct OutlineFont *ami_open_outline_font(const plot_font_style_t *fstyle) { struct OutlineFont *ofont; char *fontname; - WORD ysize; + ULONG ysize; int tstyle = 0; if ((fstyle->flags & FONTF_ITALIC) || (fstyle->flags & FONTF_OBLIQUE)) @@ -268,14 +268,12 @@ struct OutlineFont *ami_open_outline_font(const plot_font_style_t *fstyle) break; } - ysize = fstyle->size; - - if(ysize < (option_font_min_size / 10) * FONT_SIZE_SCALE) - ysize = (option_font_min_size / 10) * FONT_SIZE_SCALE; + /* Scale to 16.16 fixed point */ + ysize = fstyle->size * ((1 << 16) / FONT_SIZE_SCALE); if(ESetInfo(&ofont->olf_EEngine, OT_DeviceDPI,(72<<16) | 72, - OT_PointHeight,(ysize<<16)/FONT_SIZE_SCALE, + OT_PointHeight,ysize, TAG_END) == OTERR_Success) { return ofont; diff --git a/amiga/gui.c b/amiga/gui.c index f36f4a5df..62e98afdf 100755 --- a/amiga/gui.c +++ b/amiga/gui.c @@ -27,6 +27,7 @@ #include "amiga/schedule.h" #include <proto/timer.h> #include "content/urldb.h" +#include "css/utils.h" #include <libraries/keymap.h> #include "desktop/history_core.h" #include <proto/locale.h> @@ -105,6 +106,7 @@ #include <reaction/reaction_macros.h> char *default_stylesheet_url; +char *quirks_stylesheet_url; char *adblock_stylesheet_url; struct MsgPort *appport; @@ -324,6 +326,7 @@ void gui_init(int argc, char** argv) messages_load(lang); default_stylesheet_url = "file:///PROGDIR:Resources/amiga.css"; + quirks_stylesheet_url = "file:///PROGDIR:Resources/quirks.css"; adblock_stylesheet_url = "file:///PROGDIR:Resources/adblock.css"; if(hubbub_initialise("PROGDIR:Resources/Aliases",myrealloc,NULL) != HUBBUB_OK) @@ -331,7 +334,7 @@ void gui_init(int argc, char** argv) die(messages_get("NoMemory")); } - css_screen_dpi = 72; + nscss_screen_dpi = INTTOFIX(72); css_scrollbar_fg_colour = 0x00aaaaaa; css_scrollbar_bg_colour = 0x00833c3c; css_scrollbar_arrow_colour = 0x00d6d6d6; @@ -1303,7 +1306,7 @@ void ami_handle_appmsg(void) box = content->data.html.layout; while ((box = box_at_point(box, x, y, &box_x, &box_y, &content))) { - if (box->style && box->style->visibility == CSS_VISIBILITY_HIDDEN) continue; + if (box->style && css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN) continue; if (box->gadget) { diff --git a/amiga/resources/quirks.css b/amiga/resources/quirks.css new file mode 120000 index 000000000..d9fb80334 --- /dev/null +++ b/amiga/resources/quirks.css @@ -0,0 +1 @@ +../../!NetSurf/Resources/Quirks,f79
\ No newline at end of file diff --git a/amiga/save_complete.c b/amiga/save_complete.c index 26b55adf5..2f5ebd584 100755 --- a/amiga/save_complete.c +++ b/amiga/save_complete.c @@ -120,17 +120,20 @@ bool save_complete_html(struct content *c, const char *path, bool index) return true; /* save stylesheets, ignoring the base and adblocking sheets */ - for (i = STYLESHEET_STYLE; i != c->data.html.stylesheet_count; i++) { + for (i = STYLESHEET_START; i != c->data.html.stylesheet_count; i++) { struct content *css = c->data.html.stylesheet_content[i]; char *source; int source_len; + bool is_style; if (!css) continue; if (save_complete_list_check(css)) continue; - if (i != STYLESHEET_STYLE) { + is_style = (strcmp(css->url, c->data.html.base_url) == 0); + + if (is_style == false) { if (!save_complete_list_add(css)) { warn_user("NoMemory", 0); return false; @@ -140,7 +143,7 @@ bool save_complete_html(struct content *c, const char *path, bool index) if (!save_imported_sheets(css, path)) return false; - if (i == STYLESHEET_STYLE) + if (is_style) continue; /* don't save <style> elements */ snprintf(spath, sizeof spath, "%s/%x", path, @@ -317,7 +320,7 @@ bool save_imported_sheets(struct content *c, const char *path) BPTR fh = 0; for (j = 0; j != c->data.css.import_count; j++) { - struct content *css = c->data.css.import_content[j]; + struct content *css = c->data.css.imports[j]; if (!css) continue; diff --git a/beos/beos_font.cpp b/beos/beos_font.cpp index 5549c51e6..f640859ca 100644 --- a/beos/beos_font.cpp +++ b/beos/beos_font.cpp @@ -366,10 +366,6 @@ void nsbeos_style_to_font(BFont &font, const plot_font_style_t *style) //fprintf(stderr, "nsbeos_style_to_font: value %f unit %d\n", style->font_size.value.length.value, style->font_size.value.length.unit); size = fstyle->size / FONT_SIZE_SCALE; - //XXX: pango stuff ? - if (size < abs(option_font_min_size / 10)) - size = option_font_min_size / 10; - //fprintf(stderr, "nsbeos_style_to_font: %f %d\n", size, style->font_size.value.length.unit); font.SetSize(size); diff --git a/beos/beos_gui.cpp b/beos/beos_gui.cpp index 866c73023..0835a934c 100644 --- a/beos/beos_gui.cpp +++ b/beos/beos_gui.cpp @@ -96,6 +96,7 @@ bool gui_in_multitask = false; bool replicated = false; /**< if we are running as a replicant */ char *default_stylesheet_url; +char *quirks_stylesheet_url; char *adblock_stylesheet_url; char *options_file_location; char *glade_file_location; @@ -266,7 +267,7 @@ static char *generate_default_css() return NULL; const char *params[] = { 0 }; - if (!content_set_type(c, CONTENT_CSS, "text/css", params)) + if (!content_set_type(c, CONTENT_CSS, "text/css", params, NULL)) return NULL; if (!content_process_data(c, text.String(), text.Length())) @@ -567,6 +568,14 @@ void gui_init(int argc, char** argv) LOG(("Using '%s' as Default CSS URL", default_stylesheet_url)); #ifdef USE_RESOURCES + quirks_stylesheet_url = strdup("rsrc:/quirks.css,text/css"); +#else + find_resource(buf, "quirks.css", "./beos/res/quirks.css"); + default_stylesheet_url = path_to_url(buf); +#endif + + +#ifdef USE_RESOURCES adblock_stylesheet_url = strdup("rsrc:/adblock.css,text/css"); #else find_resource(buf, "adblock.css", "./beos/res/adblock.css"); @@ -740,6 +749,7 @@ void gui_quit(void) //options_save_tree(hotlist,option_hotlist_file,messages_get("TreeHotlist")); free(default_stylesheet_url); + free(quirks_stylesheet_url); free(adblock_stylesheet_url); free(option_cookie_file); free(option_cookie_jar); diff --git a/beos/res/quirks.css b/beos/res/quirks.css new file mode 120000 index 000000000..d9fb80334 --- /dev/null +++ b/beos/res/quirks.css @@ -0,0 +1 @@ +../../!NetSurf/Resources/Quirks,f79
\ No newline at end of file diff --git a/content/content.c b/content/content.c index bda652fad..2b4499a29 100644 --- a/content/content.c +++ b/content/content.c @@ -247,7 +247,8 @@ const char * const content_status_name[] = { /** An entry in handler_map. */ struct handler_entry { - bool (*create)(struct content *c, const char *params[]); + bool (*create)(struct content *c, struct content *parent, + const char *params[]); bool (*process_data)(struct content *c, char *data, unsigned int size); bool (*convert)(struct content *c, int width, int height); void (*reformat)(struct content *c, int width, int height); @@ -280,7 +281,8 @@ static const struct handler_entry handler_map[] = { {textplain_create, textplain_process_data, textplain_convert, textplain_reformat, textplain_destroy, 0, textplain_redraw, 0, 0, 0, true}, - {0, 0, css_convert, 0, css_destroy, 0, 0, 0, 0, 0, false}, + {nscss_create, nscss_process_data, nscss_convert, 0, nscss_destroy, + 0, 0, 0, 0, 0, true}, #ifdef WITH_JPEG {0, 0, nsjpeg_convert, 0, nsjpeg_destroy, 0, nsjpeg_redraw, nsjpeg_redraw_tiled, 0, 0, false}, @@ -565,7 +567,8 @@ bool content_can_reformat(struct content *c) */ bool content_set_type(struct content *c, content_type type, - const char *mime_type, const char *params[]) + const char *mime_type, const char *params[], + struct content *parent) { union content_msg_data msg_data; struct content *clone; @@ -637,7 +640,7 @@ bool content_set_type(struct content *c, content_type type, } if (handler_map[type].create) { - if (!handler_map[type].create(c, params)) { + if (!handler_map[type].create(c, parent, params)) { c->type = CONTENT_UNKNOWN; c->status = CONTENT_STATUS_ERROR; return false; diff --git a/content/content.h b/content/content.h index 298e669ca..1db6122ee 100644 --- a/content/content.h +++ b/content/content.h @@ -283,7 +283,8 @@ struct content * content_get(const char *url); struct content * content_get_ready(const char *url); bool content_can_reformat(struct content *c); bool content_set_type(struct content *c, content_type type, - const char *mime_type, const char *params[]); + const char *mime_type, const char *params[], + struct content *parent); void content_set_status(struct content *c, const char *status_message, ...); bool content_process_data(struct content *c, const char *data, unsigned int size); diff --git a/content/fetchcache.c b/content/fetchcache.c index cb5cc9132..71e39e4fa 100644 --- a/content/fetchcache.c +++ b/content/fetchcache.c @@ -409,6 +409,7 @@ void fetchcache_callback(fetch_msg msg, void *p, const void *data, content_type type; char *mime_type; char **params; + struct content *parent; unsigned int i; union content_msg_data msg_data; @@ -426,9 +427,11 @@ void fetchcache_callback(fetch_msg msg, void *p, const void *data, return; } type = content_lookup(mime_type); + parent = fetch_get_parent(c->fetch); res = content_set_type(c, c->download ? CONTENT_OTHER : type, - mime_type, (const char **) params); + mime_type, (const char **) params, + parent); free(mime_type); for (i = 0; params[i]; i++) free(params[i]); @@ -734,7 +737,7 @@ void fetchcache_error_page(struct content *c, const char *error) if ((length = snprintf(error_page, sizeof(error_page), messages_get("ErrorPage"), error)) < 0) length = 0; - if (!content_set_type(c, CONTENT_HTML, "text/html", params)) + if (!content_set_type(c, CONTENT_HTML, "text/html", params, NULL)) return; if (!content_process_data(c, error_page, length)) return; 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 $@ $< - @@ -1,7 +1,5 @@ /* - * Copyright 2004 James Bursa <bursa@users.sourceforge.net> - * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk> - * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org> + * Copyright 2009 John-Mark Bell <jmb@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -18,3313 +16,337 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \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 <em>type selector</em> of the last <em>simple - * selector</em> in the selector. The <em>universal selector</em> is hashed to - * chain 0. - * - * A <em>simple selector</em> is a struct css_selector with type - * CSS_SELECTOR_ELEMENT. The data field is the <em>type selector</em>, or 0 for - * the <em>universal selector</em>. Any <em>attribute selectors</em>, <em>ID - * selectors</em>, or <em>pseudo-classes</em> form a linked list of - * css_selector hanging from detail. - * - * A <em>selector</em> 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 <assert.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <strings.h> -#include <math.h> -#include <limits.h> -#define CSS_INTERNALS -#include "utils/config.h" + +#include <libwapcaplet/libwapcaplet.h> + #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. - * - * \param style The style to duplicate - * \return The duplicate style, or NULL if out of memory. - */ - -struct css_style *css_duplicate_style(const struct css_style * const style) -{ - 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; -} - - -/** - * Free a CSS style. - * - * \param style The style to free - */ - -void css_free_style(struct css_style *style) -{ - assert(style); - - free(style); -} - - -/** - * 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); - - if (style->background_image.type == CSS_BACKGROUND_IMAGE_URI) - free(style->background_image.uri); - - if (style->list_style_image.type == CSS_LIST_STYLE_IMAGE_URI) - free(style->list_style_image.uri); - - if (style->content.type == CSS_CONTENT_INTERPRET) - css_deep_free_content(style->content.content); - - if (style->counter_reset.type == CSS_COUNTER_RESET_INTERPRET) - css_deep_free_counter_control(style->counter_reset.data); - - if (style->counter_increment.type == CSS_COUNTER_INCREMENT_INTERPRET) - css_deep_free_counter_control(style->counter_increment.data); - - free(style); -} - - -/** - * Free all auto-generated content data. - * - * \param content the auto-generated content data to free - */ - -void css_deep_free_content(struct css_content *content) -{ - struct css_content *next; - - 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; - } - free(content); - content = next; - } -} - /** - * Free all counter control data. + * Allocation callback for libcss * - * \param counter the counter control data to free + * \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 */ - -void css_deep_free_counter_control(struct css_counter_control *control) -{ - struct css_counter_control *next; - - while (control) { - next = control->next; - free(control->name); - free(control); - control = next; - } -} - - -/** - * 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; - - return node; -} - - -/** - * Free a struct css_node recursively. - * - * \param node css_node to free - * - * Used by the parser. - */ - -void css_free_node(struct css_node *node) +static void *myrealloc(void *ptr, size_t size, void *pw) { - if (!node) - return; - if (node->value) - css_free_node(node->value); - if (node->next) - css_free_node(node->next); - free(node); + return realloc(ptr, size); } - /** - * Create a new struct css_selector. + * Initialise a CSS content * - * \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 + * \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 */ - -struct css_selector * css_new_selector(css_selector_type type, - const char *data, unsigned int data_length) +bool nscss_create(struct content *c, struct content *parent, + const char *params[]) { - 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; -} + css_origin origin = CSS_ORIGIN_AUTHOR; + css_media_type media = CSS_MEDIA_ALL; + lwc_context *dict = NULL; + bool quirks = true; + css_error error; + /** \todo extract charset from params */ + /** \todo what happens about the allocator? */ -/** - * Free a struct css_selector recursively. - * - * \param node css_selector to free - */ + if (parent != NULL) { + assert(parent->type == CONTENT_HTML || + parent->type == CONTENT_CSS); -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. - */ + if (parent->type == CONTENT_HTML) { + assert(parent->data.html.dict != NULL); -void css_atimport(struct content *c, struct css_node *node) -{ - const char *s; - char *t, *url, *url1; - bool string = false, screen = true; - unsigned int i; - struct content **import_content; - url_func_result res; + 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; - LOG(("@import rule")); + /** \todo media types */ - 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; + quirks = (parent->data.html.quirks != + BINDING_QUIRKS_MODE_NONE); - /* 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; - } - break; - default: - return; - } + dict = parent->data.html.dict; + } else { + assert(parent->data.css.sheet != NULL); + assert(parent->data.css.dict != NULL); - /* 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; - } - 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; - } - node = node->next; - if (node == 0 || node->type != CSS_NODE_COMMA) { - free(url); - return; - } - } - 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]) { - 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); - } - 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; - - 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; - - case CONTENT_MSG_STATUS: - break; - - case CONTENT_MSG_NEWPTR: - c->data.css.import_content[i] = css; - break; - - default: - assert(0); - } -} - - -/** - * Prepare a working stylesheet with pre-sorted lists of selectors from an - * array of stylesheets. - * - * \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. - */ - -struct css_working_stylesheet *css_make_working_stylesheet( - struct content **stylesheet_content, - unsigned int stylesheet_count) -{ - 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; + error = css_stylesheet_get_origin( + parent->data.css.sheet, &origin); + if (error != CSS_OK) + return false; - /* make a complete list of stylesheets involved by walking @imports */ - css_working_list_imports(stylesheet_content, stylesheet_count, - &css, &css_count); + error = css_stylesheet_quirks_allowed( + parent->data.css.sheet, &quirks); + if (error != CSS_OK) + return false; - rule_scratch = talloc_array(working_stylesheet, struct css_selector *, - css_count); - if (!rule_scratch) { - free(css); - talloc_free(working_stylesheet); - return 0; - } + /** \todo media types */ - /* 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; + dict = parent->data.css.dict; } } - 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 - */ + if (dict == NULL) { + lwc_error lerror = lwc_create_context(myrealloc, NULL, &dict); -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)) + if (lerror != lwc_error_ok) return false; - css2 = realloc(*css, sizeof *css * (*css_count + 1)); - if (!css2) - return false; - *css = css2; - (*css)[*css_count] = import[i]; - (*css_count)++; } - 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]) + 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; - - /* mergesort by specificity (increasing) */ - for (sheet = 0; sheet != css_count; sheet++) { - rule[sheet] = 0; - rule[sheet] = css[sheet]->data.css.css->rule[chain]; } - 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 + * Process CSS source data * - * The style is updated with any rules that match the element. + * \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_get_style(struct css_working_stylesheet *working_stylesheet, - xmlNode *element, struct css_style *style, - struct css_importance *author) +bool nscss_process_data(struct content *c, char *data, unsigned int size) { - 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)); + css_error error; - /* 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); - } - - /* 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); - } + error = css_stylesheet_append_data(c->data.css.sheet, + (const uint8_t *) data, size); - /* 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); - } + return (error == CSS_OK || error == CSS_NEEDDATA); } - /** - * 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. + * Convert a CSS content ready for use * - * \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 + * \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 */ - -bool css_match_detail(const struct css_selector *detail, - xmlNode *element) +bool nscss_convert(struct content *c, int w, int h) { - 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; + css_error error; - 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; + error = css_stylesheet_data_done(c->data.css.sheet); - 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; + /* 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; } - 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"); + /* Give up if there are no more imports */ + if (error == CSS_INVALID) { + error = CSS_OK; 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; + /* 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; } + memcpy(temp_url, lwc_string_data(uri), lwc_string_length(uri)); + temp_url[lwc_string_length(uri)] = '\0'; - 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; + /* 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; } - 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; - } + 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; } - 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; + /* Fetch content */ + c->active++; + 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; } - 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]; + error = css_stylesheet_register_import( + c->data.css.sheet, sheet); + if (error != CSS_OK) { + c->status = CONTENT_STATUS_ERROR; + return false; } - } - - - /* 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]; + error = CSS_IMPORTS_PENDING; } - /* 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; + c->status = CONTENT_STATUS_DONE; - if (apply->margin[i].margin != CSS_MARGIN_NOT_SET) - author->margin[i] = true; + /* Filthy hack to stop this content being reused + * when whatever is using it has finished with it. */ + c->fresh = false; - if (apply->padding[i].padding != CSS_PADDING_NOT_SET) - author->padding[i] = true; - } - } + return error == CSS_OK; } - /** - * Merge styles. + * Clean up a CSS content * - * \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. + * \param c Content to clean up */ - -void css_merge(struct css_style * const style, - const struct css_style * const apply, - const unsigned long specificity, - struct css_importance * const author) +void nscss_destroy(struct content *c) { - unsigned int i; + uint32_t 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]; + 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; } - /* 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; + free(c->data.css.imports); - /* 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]; + if (c->data.css.sheet != NULL) { + css_stylesheet_destroy(c->data.css.sheet); + c->data.css.sheet = NULL; } -} - - -/** - * 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; + if (c->data.css.dict != NULL) { + lwc_context_unref(c->data.css.dict); + c->data.css.dict = NULL; } - return 0; } /** - * Return the most 'eyecatching' border. + * Fetchcache handler for imported stylesheets * - * \return the most eyecatching border, favoured towards test2 + * \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 */ - -struct css_border *css_eyecatching_border(struct css_border *test1, - struct css_style *style1, struct css_border *test2, - struct css_style *style2) +void nscss_import(content_msg msg, struct content *c, + intptr_t p1, intptr_t p2, union content_msg_data data) { - float width1, width2; - int impact = 0; - - assert(test1); - assert(style1); - assert(test2); - assert(style2); + struct content *parent = (struct content *) p1; + uint32_t i = (uint32_t) p2; - /* 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; + 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; + } - /* 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; + 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); } - 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; } - @@ -1,6 +1,5 @@ /* - * Copyright 2004 James Bursa <bursa@users.sourceforge.net> - * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk> + * Copyright 2009 John-Mark Bell <jmb@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -17,700 +16,34 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \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 <stdint.h> -#include <stdbool.h> -#include <libxml/HTMLparser.h> -#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 <libcss/libcss.h> - 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 <jmb@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> + +#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/render/loosen.h b/css/dump.h index a5b3822bd..25d283e9e 100644 --- a/render/loosen.h +++ b/css/dump.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Adam Blokus <adamblokus@gmail.com> + * Copyright 2009 John-Mark Bell <jmb@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -16,20 +16,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** -\file -General idea - a set of routines working themselves recursively through -the box tree and trying to change the layout of the document as little -as possible to acquire the desired width ( - to make it fit in a printed -page ), where possible - also taking the dividing height into consideration, -to prevent objects being cut by ends of pages. -*/ +#ifndef NETSURF_CSS_DUMP_H_ +#define NETSURF_CSS_DUMP_H_ -#ifndef NETSURF_RENDER_LOOSEN_H -#define NETSURF_RENDER_LOOSEN_H -#include <stdbool.h> +#include "css/css.h" -bool loosen_document_layout(struct content *content, struct box *layout, - int width, int height); +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 <jmb@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> + +#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 <jmb@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#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 <bursa@users.sourceforge.net> -# - -$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 <strings.h>\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 <bursa@users.sourceforge.net> - */ - -/** \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 <assert.h> -#include <string.h> -#include <strings.h> -#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 <bursa@users.sourceforge.net> - * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk> - * - * This file is part of NetSurf, http://www.netsurf-browser.org/ - * - * NetSurf is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * NetSurf is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/** \file - * 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 <assert.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <strings.h> -#include <math.h> -#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 <bursa@users.sourceforge.net> - */ - -/** \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 <stdbool.h> -#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 CDO */ } -"-->" { 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 <jmb@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <string.h> +#include <strings.h> + +#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 <jmb@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef NETSURF_CSS_SELECT_H_ +#define NETSURF_CSS_SELECT_H_ + +#include <stdint.h> + +#include <libxml/tree.h> + +#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 <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> - -#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 <filename>\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 <james@netsurf-browser.org> + * Copyright 2009 John-Mark Bell <jmb@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#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 <jmb@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#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 diff --git a/desktop/browser.c b/desktop/browser.c index b188316ea..d19281e9d 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -1421,10 +1421,12 @@ void browser_window_mouse_action_html(struct browser_window *bw, while ((next_box = box_at_point(box, x, y, &box_x, &box_y, &content)) != NULL) { + enum css_overflow overflow = CSS_OVERFLOW_VISIBLE; + box = next_box; - if (box->style && - box->style->visibility == CSS_VISIBILITY_HIDDEN) + if (box->style && css_computed_visibility(box->style) == + CSS_VISIBILITY_HIDDEN) continue; if (box->object) @@ -1458,14 +1460,16 @@ void browser_window_mouse_action_html(struct browser_window *bw, if (box->title) title = box->title; - if (box->style && box->style->cursor != CSS_CURSOR_UNKNOWN) - pointer = get_pointer_shape(bw, box, false); + pointer = get_pointer_shape(bw, box, false); + + if (box->style) + overflow = css_computed_overflow(box->style); if (box->style && box->type != BOX_BR && box->type != BOX_INLINE && box->type != BOX_TEXT && - (box->style->overflow == CSS_OVERFLOW_SCROLL || - box->style->overflow == CSS_OVERFLOW_AUTO) && + (overflow == CSS_OVERFLOW_SCROLL || + overflow == CSS_OVERFLOW_AUTO) && ((box_vscrollbar_present(box) && box_x + box->scroll_x + box->padding[LEFT] + box->width < x) || @@ -2464,7 +2468,9 @@ gui_pointer_shape get_pointer_shape(struct browser_window *bw, struct box *box, bool imagemap) { gui_pointer_shape pointer; - struct css_style *style; + css_computed_style *style; + enum css_cursor cursor; + lwc_string **cursor_uris; assert(bw); @@ -2481,84 +2487,83 @@ gui_pointer_shape get_pointer_shape(struct browser_window *bw, struct box *box, else style = box->style; - assert(style); - switch (style->cursor) { - case CSS_CURSOR_AUTO: - if (box->href || (box->gadget && - (box->gadget->type == GADGET_IMAGE || - box->gadget->type == GADGET_SUBMIT)) || - imagemap) { - /* link */ - pointer = GUI_POINTER_POINT; - } else if (box->gadget && - (box->gadget->type == GADGET_TEXTBOX || - box->gadget->type == GADGET_PASSWORD || - box->gadget->type == GADGET_TEXTAREA)) { - /* text input */ - pointer = GUI_POINTER_CARET; - } else { - /* anything else */ - if (loading) - /* loading new content */ - pointer = GUI_POINTER_PROGRESS; - else - pointer = GUI_POINTER_DEFAULT; - } - break; - case CSS_CURSOR_CROSSHAIR: - pointer = GUI_POINTER_CROSS; - break; - case CSS_CURSOR_POINTER: + if (style == NULL) + return GUI_POINTER_DEFAULT; + + cursor = css_computed_cursor(style, &cursor_uris); + + switch (cursor) { + case CSS_CURSOR_AUTO: + if (box->href || (box->gadget && + (box->gadget->type == GADGET_IMAGE || + box->gadget->type == GADGET_SUBMIT)) || + imagemap) { + /* link */ pointer = GUI_POINTER_POINT; - break; - case CSS_CURSOR_MOVE: - pointer = GUI_POINTER_MOVE; - break; - case CSS_CURSOR_E_RESIZE: - pointer = GUI_POINTER_RIGHT; - break; - case CSS_CURSOR_W_RESIZE: - pointer = GUI_POINTER_LEFT; - break; - case CSS_CURSOR_N_RESIZE: - pointer = GUI_POINTER_UP; - break; - case CSS_CURSOR_S_RESIZE: - pointer = GUI_POINTER_DOWN; - break; - case CSS_CURSOR_NE_RESIZE: - pointer = GUI_POINTER_RU; - break; - case CSS_CURSOR_SW_RESIZE: - pointer = GUI_POINTER_LD; - break; - case CSS_CURSOR_SE_RESIZE: - pointer = GUI_POINTER_RD; - break; - case CSS_CURSOR_NW_RESIZE: - pointer = GUI_POINTER_LU; - break; - case CSS_CURSOR_TEXT: + } else if (box->gadget && + (box->gadget->type == GADGET_TEXTBOX || + box->gadget->type == GADGET_PASSWORD || + box->gadget->type == GADGET_TEXTAREA)) { + /* text input */ pointer = GUI_POINTER_CARET; - break; - case CSS_CURSOR_WAIT: - pointer = GUI_POINTER_WAIT; - break; - case CSS_CURSOR_PROGRESS: - pointer = GUI_POINTER_PROGRESS; - break; - case CSS_CURSOR_NO_DROP: - pointer = GUI_POINTER_NO_DROP; - break; - case CSS_CURSOR_NOT_ALLOWED: - pointer = GUI_POINTER_NOT_ALLOWED; - break; - case CSS_CURSOR_HELP: - pointer = GUI_POINTER_HELP; - break; - default: - pointer = GUI_POINTER_DEFAULT; - break; + } else { + /* anything else */ + if (loading) { + /* loading new content */ + pointer = GUI_POINTER_PROGRESS; + } else { + pointer = GUI_POINTER_DEFAULT; + } + } + break; + case CSS_CURSOR_CROSSHAIR: + pointer = GUI_POINTER_CROSS; + break; + case CSS_CURSOR_POINTER: + pointer = GUI_POINTER_POINT; + break; + case CSS_CURSOR_MOVE: + pointer = GUI_POINTER_MOVE; + break; + case CSS_CURSOR_E_RESIZE: + pointer = GUI_POINTER_RIGHT; + break; + case CSS_CURSOR_W_RESIZE: + pointer = GUI_POINTER_LEFT; + break; + case CSS_CURSOR_N_RESIZE: + pointer = GUI_POINTER_UP; + break; + case CSS_CURSOR_S_RESIZE: + pointer = GUI_POINTER_DOWN; + break; + case CSS_CURSOR_NE_RESIZE: + pointer = GUI_POINTER_RU; + break; + case CSS_CURSOR_SW_RESIZE: + pointer = GUI_POINTER_LD; + break; + case CSS_CURSOR_SE_RESIZE: + pointer = GUI_POINTER_RD; + break; + case CSS_CURSOR_NW_RESIZE: + pointer = GUI_POINTER_LU; + break; + case CSS_CURSOR_TEXT: + pointer = GUI_POINTER_CARET; + break; + case CSS_CURSOR_WAIT: + pointer = GUI_POINTER_WAIT; + break; + case CSS_CURSOR_PROGRESS: + pointer = GUI_POINTER_PROGRESS; + break; + case CSS_CURSOR_HELP: + pointer = GUI_POINTER_HELP; + break; + default: + pointer = GUI_POINTER_DEFAULT; + break; } return pointer; diff --git a/desktop/plot_style.c b/desktop/plot_style.c index 54dbd40ce..94e52ca00 100644 --- a/desktop/plot_style.c +++ b/desktop/plot_style.c @@ -140,7 +140,7 @@ plot_style_t *plot_style_stroke_history = &plot_style_stroke_history_static; /* Generic font style */ static const plot_font_style_t plot_style_font_static = { .family = PLOT_FONT_FAMILY_SANS_SERIF, - .size = 10, + .size = 10 * FONT_SIZE_SCALE, .weight = 400, .flags = FONTF_NONE, .background = 0xffffff, diff --git a/desktop/plot_style.h b/desktop/plot_style.h index 088f0d275..a3b3f0103 100644 --- a/desktop/plot_style.h +++ b/desktop/plot_style.h @@ -23,6 +23,8 @@ #ifndef _NETSURF_DESKTOP_PLOT_STYLE_H_ #define _NETSURF_DESKTOP_PLOT_STYLE_H_ +#include <stdint.h> + /* html widget colours */ #define WIDGET_BASEC 0xd9d9d9 #define WIDGET_BLOBC 0x000000 @@ -63,6 +65,15 @@ ((((c0 & 0xff) + (c1 & 0xff)) >> 1) << 0) /** + * Colour type: XBGR + */ +typedef uint32_t colour; +/** + * Magical transparent value + */ +#define NS_TRANSPARENT 0x01000000 + +/** * Type of plot operation */ typedef enum { diff --git a/desktop/print.c b/desktop/print.c index 1fdd5170d..4f86bbc22 100644 --- a/desktop/print.c +++ b/desktop/print.c @@ -26,10 +26,10 @@ #include <string.h> #include "content/content.h" +#include "css/utils.h" #include "desktop/options.h" #include "desktop/print.h" #include "desktop/printer.h" -#include "render/loosen.h" #include "render/box.h" #include "utils/log.h" #include "utils/talloc.h" @@ -197,8 +197,7 @@ struct content *print_init(struct content *content, } /** - * The content is resized to fit page width. In case it is to wide, it is - * loosened. + * The content is resized to fit page width. * * \param content The content to be printed * \param settings The settings for printing to use @@ -213,24 +212,18 @@ bool print_apply_settings(struct content *content, /*Apply settings - adjust page size etc*/ page_content_width = (settings->page_width - - settings->margins[MARGINLEFT] - - settings->margins[MARGINRIGHT]) / settings->scale; + FIXTOFLT(FSUB(settings->margins[MARGINLEFT], + settings->margins[MARGINRIGHT]))) / settings->scale; page_content_height = (settings->page_height - - settings->margins[MARGINTOP] - - settings->margins[MARGINBOTTOM]) / settings->scale; + FIXTOFLT(FSUB(settings->margins[MARGINTOP], + settings->margins[MARGINBOTTOM]))) / settings->scale; content_reformat(content, page_content_width, 0); LOG(("New layout applied.New height = %d ; New width = %d ", content->height, content->width)); - /*check if loosening is necessary and requested*/ - if (option_enable_loosening && content->width > page_content_width) - return loosen_document_layout(content, - content->data.html.layout, - page_content_width, page_content_height); - return true; } @@ -275,9 +268,8 @@ struct print_settings *print_make_settings(print_configuration configuration, const char *filename, const struct font_functions *font_func) { struct print_settings *settings; - struct css_length length; - - length.unit = CSS_UNIT_MM; + css_fixed length = 0; + css_unit unit = CSS_UNIT_MM; switch (configuration){ case PRINT_DEFAULT: @@ -292,14 +284,18 @@ struct print_settings *print_make_settings(print_configuration configuration, settings->scale = DEFAULT_EXPORT_SCALE; - length.value = DEFAULT_MARGIN_LEFT_MM; - settings->margins[MARGINLEFT] = css_len2px(&length, 0); - length.value = DEFAULT_MARGIN_RIGHT_MM; - settings->margins[MARGINRIGHT] = css_len2px(&length, 0); - length.value = DEFAULT_MARGIN_TOP_MM; - settings->margins[MARGINTOP] = css_len2px(&length, 0); - length.value = DEFAULT_MARGIN_BOTTOM_MM; - settings->margins[MARGINBOTTOM] = css_len2px(&length, 0); + length = INTTOFIX(DEFAULT_MARGIN_LEFT_MM); + settings->margins[MARGINLEFT] = + nscss_len2px(length, unit, NULL); + length = INTTOFIX(DEFAULT_MARGIN_RIGHT_MM); + settings->margins[MARGINRIGHT] = + nscss_len2px(length, unit, NULL); + length = INTTOFIX(DEFAULT_MARGIN_TOP_MM); + settings->margins[MARGINTOP] = + nscss_len2px(length, unit, NULL); + length = INTTOFIX(DEFAULT_MARGIN_BOTTOM_MM); + settings->margins[MARGINBOTTOM] = + nscss_len2px(length, unit, NULL); break; /* use settings from the Export options tab */ case PRINT_OPTIONS: @@ -314,14 +310,18 @@ struct print_settings *print_make_settings(print_configuration configuration, settings->scale = (float)option_export_scale / 100; - length.value = option_margin_left; - settings->margins[MARGINLEFT] = css_len2px(&length, 0); - length.value = option_margin_right; - settings->margins[MARGINRIGHT] = css_len2px(&length, 0); - length.value = option_margin_top; - settings->margins[MARGINTOP] = css_len2px(&length, 0); - length.value = option_margin_bottom; - settings->margins[MARGINBOTTOM] = css_len2px(&length, 0); + length = INTTOFIX(option_margin_left); + settings->margins[MARGINLEFT] = + nscss_len2px(length, unit, NULL); + length = INTTOFIX(option_margin_right); + settings->margins[MARGINRIGHT] = + nscss_len2px(length, unit, NULL); + length = INTTOFIX(option_margin_top); + settings->margins[MARGINTOP] = + nscss_len2px(length, unit, NULL); + length = INTTOFIX(option_margin_bottom); + settings->margins[MARGINBOTTOM] = + nscss_len2px(length, unit, NULL); break; default: return NULL; diff --git a/desktop/print.h b/desktop/print.h index bc07e377e..fece526be 100644 --- a/desktop/print.h +++ b/desktop/print.h @@ -34,6 +34,8 @@ #include <stdbool.h> +#include "css/css.h" + struct content; struct printer; @@ -48,7 +50,7 @@ typedef enum { PRINT_DEFAULT, PRINT_OPTIONS } print_configuration; struct print_settings{ /*Standard parameters*/ float page_width, page_height; - int margins[4]; + css_fixed margins[4]; float scale; diff --git a/desktop/save_pdf/font_haru.c b/desktop/save_pdf/font_haru.c index 0bf86c340..c90753121 100644 --- a/desktop/save_pdf/font_haru.c +++ b/desktop/save_pdf/font_haru.c @@ -37,6 +37,7 @@ #include <hpdf.h> #include "css/css.h" +#include "css/utils.h" #include "desktop/options.h" #include "desktop/save_pdf/font_haru.h" @@ -216,8 +217,7 @@ bool haru_nsfont_position_in_string(const plot_font_style_t *fstyle, /** * Find where to split a string to make it fit a width. * - * \param fstyle css_style for this text, with style->font_size.size == - * CSS_FONT_SIZE_LENGTH + * \param fstyle style for this text * \param string string to measure (no UTF-8 currently) * \param length length of string * \param x width available @@ -268,7 +268,7 @@ bool haru_nsfont_split(const plot_font_style_t *fstyle, /** * Apply font style to a Haru HPDF_Page * - * \param style plot style for this page + * \param fstyle plot style for this page * \param doc document owning the page * \param page the page to apply the style to * \param font if this is non NULL it is updated to the font based diff --git a/desktop/save_pdf/pdf_plotters.c b/desktop/save_pdf/pdf_plotters.c index a29db2415..8dbe43971 100644 --- a/desktop/save_pdf/pdf_plotters.c +++ b/desktop/save_pdf/pdf_plotters.c @@ -146,7 +146,7 @@ bool pdf_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *psty { DashPattern_e dash; #ifdef PDF_DEBUG - LOG(("%d %d %d %d %f %X", x0, y0, x1, y1, page_height - y0, style->fill_colour)); + LOG(("%d %d %d %d %f %X", x0, y0, x1, y1, page_height - y0, pstyle->fill_colour)); #endif if (pstyle->fill_type != PLOT_OP_TYPE_NONE) { @@ -353,7 +353,7 @@ bool pdf_plot_disc(int x, int y, int radius, const plot_style_t *style) bool pdf_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style) { #ifdef PDF_DEBUG - LOG(("%d %d %d %d %d %X", x, y, radius, angle1, angle2, c)); + LOG(("%d %d %d %d %d %X", x, y, radius, angle1, angle2, style->stroke_colour)); #endif /* FIXME: line width 1 is ok ? */ @@ -381,8 +381,8 @@ bool pdf_plot_bitmap_tile(int x, int y, int width, int height, HPDF_REAL max_width, max_height; #ifdef PDF_DEBUG - LOG(("%d %d %d %d %p 0x%x %p", x, y, width, height, - bitmap, bg, content)); + LOG(("%d %d %d %d %p 0x%x", x, y, width, height, + bitmap, bg)); #endif if (width == 0 || height == 0) return true; @@ -662,10 +662,12 @@ bool pdf_begin(struct print_settings *print_settings) settings = print_settings; - page_width = settings->page_width - settings->margins[MARGINLEFT] - - settings->margins[MARGINRIGHT]; + page_width = settings->page_width - + FIXTOFLT(FSUB(settings->margins[MARGINLEFT], + settings->margins[MARGINRIGHT])); - page_height = settings->page_height - settings->margins[MARGINTOP]; + page_height = settings->page_height - + FIXTOFLT(settings->margins[MARGINTOP]); #ifndef PDF_DEBUG @@ -708,7 +710,8 @@ bool pdf_next_page(void) HPDF_Page_SetWidth (pdf_page, settings->page_width); HPDF_Page_SetHeight(pdf_page, settings->page_height); - HPDF_Page_Concat(pdf_page, 1, 0, 0, 1, settings->margins[MARGINLEFT], 0); + HPDF_Page_Concat(pdf_page, 1, 0, 0, 1, + FIXTOFLT(settings->margins[MARGINLEFT]), 0); pdfw_gs_save(pdf_page); diff --git a/desktop/save_text.c b/desktop/save_text.c index 535fec461..15f41702e 100644 --- a/desktop/save_text.c +++ b/desktop/save_text.c @@ -141,9 +141,9 @@ void save_text_solve_whitespace(struct box *box, bool *first, (box->type != BOX_INLINE && (box->parent && box->parent->list_marker == box)) || (box->parent->style && - (box->parent->style->white_space == + (css_computed_white_space(box->parent->style) == CSS_WHITE_SPACE_PRE || - box->parent->style->white_space == + css_computed_white_space(box->parent->style) == CSS_WHITE_SPACE_PRE_WRAP) && box->type == BOX_INLINE_CONTAINER))) { if (*before == WHITESPACE_ONE_NEW_LINE) diff --git a/desktop/textarea.c b/desktop/textarea.c index 1b48d9d55..43ac85b0c 100644 --- a/desktop/textarea.c +++ b/desktop/textarea.c @@ -24,6 +24,7 @@ #include <stdint.h> #include <string.h> #include "css/css.h" +#include "css/utils.h" #include "desktop/textarea.h" #include "desktop/textinput.h" #include "desktop/plotters.h" @@ -135,14 +136,14 @@ static void textarea_normalise_text(struct text_area *ta, * \param width width of the text area * \param height width of the text area * \param flags text area flags - * \param style css style (font style properties are used only) + * \param style font style * \param redraw_start_callback will be called when textarea wants to redraw * \param redraw_end_callback will be called when textarea finisjes redrawing * \param data user specified data which will be passed to redraw callbacks * \return Opaque handle for textarea or 0 on error */ struct text_area *textarea_create(int x, int y, int width, int height, - unsigned int flags, const struct css_style *style, + unsigned int flags, const plot_font_style_t *style, textarea_start_redraw_callback redraw_start_callback, textarea_end_redraw_callback redraw_end_callback, void *data) { @@ -183,9 +184,10 @@ struct text_area *textarea_create(int x, int y, int width, int height, ret->text_len = 1; ret->text_utf8_len = 0; - font_plot_style_from_css(style, &ret->fstyle); - ret->line_height = css_len2px(&(style->line_height.value.length), - style); + ret->fstyle = *style; + + ret->line_height = FIXTOINT(FDIVI(FMUL( + FLTTOFIX(1.2 * style->size), nscss_screen_dpi), 72)); ret->caret_pos.line = ret->caret_pos.char_off = 0; ret->selection_start = -1; @@ -427,7 +429,7 @@ bool textarea_set_caret(struct text_area *ta, int caret) int index; int x, y; int x0, y0, x1, y1; - int height; + int height = 0; if (ta->flags & TEXTAREA_READONLY) @@ -440,7 +442,7 @@ bool textarea_set_caret(struct text_area *ta, int caret) if (caret != -1 && (unsigned)caret > c_len) caret = c_len; - height = ta->fstyle.size * css_screen_dpi / 72; + height = ta->fstyle.size * nscss_screen_dpi / 72; /* Delete the old caret */ if (ta->caret_pos.char_off != -1) { diff --git a/desktop/textarea.h b/desktop/textarea.h index 14e93f5e8..ca02927b4 100644 --- a/desktop/textarea.h +++ b/desktop/textarea.h @@ -26,8 +26,8 @@ #include <stdint.h> #include <stdbool.h> -#include "css/css.h" #include "desktop/browser.h" +#include "desktop/plot_style.h" /* Text area flags */ #define TEXTAREA_MULTILINE 0x01 /**< Text area is multiline */ @@ -39,7 +39,7 @@ typedef void(*textarea_start_redraw_callback)(void *data); typedef void(*textarea_end_redraw_callback)(void *data); struct text_area *textarea_create(int x, int y, int width, int height, - unsigned int flags, const struct css_style *style, + unsigned int flags, const plot_font_style_t *style, textarea_start_redraw_callback redraw_start_callback, textarea_end_redraw_callback redraw_end_callback, void *data); void textarea_set_position(struct text_area *ta, int x, int y); diff --git a/framebuffer/fbtk.c b/framebuffer/fbtk.c index c564a9117..d212fb4d5 100644 --- a/framebuffer/fbtk.c +++ b/framebuffer/fbtk.c @@ -41,7 +41,7 @@ static plot_font_style_t root_style = { .family = PLOT_FONT_FAMILY_SANS_SERIF, - .size = 11, + .size = 11 * FONT_SIZE_SCALE, .weight = 400, .flags = FONTF_NONE, }; diff --git a/framebuffer/gui.c b/framebuffer/gui.c index 65080db8a..fbed5bd86 100644 --- a/framebuffer/gui.c +++ b/framebuffer/gui.c @@ -56,6 +56,7 @@ #include "content/fetch.h" char *default_stylesheet_url; +char *quirks_stylesheet_url; char *adblock_stylesheet_url; char *options_file_location; @@ -367,6 +368,9 @@ void gui_init(int argc, char** argv) default_stylesheet_url = path_to_url(buf); LOG(("Using '%s' as Default CSS URL", default_stylesheet_url)); + fb_find_resource(buf, "quirks.css", "./framebuffer/res/quirks.css"); + quirks_stylesheet_url = path_to_url(buf); + nsfb = framebuffer_initialise(argc, argv); if (nsfb == NULL) die("Unable to initialise framebuffer"); diff --git a/framebuffer/res/quirks.css b/framebuffer/res/quirks.css new file mode 120000 index 000000000..d9fb80334 --- /dev/null +++ b/framebuffer/res/quirks.css @@ -0,0 +1 @@ +../../!NetSurf/Resources/Quirks,f79
\ No newline at end of file diff --git a/gtk/font_pango.c b/gtk/font_pango.c index c0d789344..e4944f0be 100644 --- a/gtk/font_pango.c +++ b/gtk/font_pango.c @@ -27,6 +27,7 @@ #include <stdio.h> #include <gtk/gtk.h> #include "css/css.h" +#include "css/utils.h" #include "gtk/font_pango.h" #include "gtk/gtk_plotters.h" #include "render/font.h" @@ -55,10 +56,6 @@ const struct font_functions nsfont = { nsfont_split }; - - - - /** * Measure the width of a string. * @@ -302,12 +299,7 @@ PangoFontDescription *nsfont_style_to_description( break; } - size = fstyle->size; - - if (size < (unsigned)abs(option_font_min_size / 10) * FONT_SIZE_SCALE) - size = (option_font_min_size / 10) * FONT_SIZE_SCALE; - - size = (size * PANGO_SCALE) / FONT_SIZE_SCALE; + size = (fstyle->size * PANGO_SCALE) / FONT_SIZE_SCALE; if (fstyle->flags & FONTF_ITALIC) style = PANGO_STYLE_ITALIC; diff --git a/gtk/gtk_gui.c b/gtk/gtk_gui.c index b8217b7f1..58bc18ed6 100644 --- a/gtk/gtk_gui.c +++ b/gtk/gtk_gui.c @@ -65,6 +65,7 @@ bool gui_in_multitask = false; char *default_stylesheet_url; +char *quirks_stylesheet_url; char *adblock_stylesheet_url; char *options_file_location; char *glade_file_location; @@ -307,6 +308,9 @@ void gui_init(int argc, char** argv) default_stylesheet_url = path_to_url(buf); LOG(("Using '%s' as Default CSS URL", default_stylesheet_url)); + find_resource(buf, "quirks.css", "./gtk/res/quirks.css"); + quirks_stylesheet_url = path_to_url(buf); + find_resource(buf, "adblock.css", "./gtk/res/adblock.css"); adblock_stylesheet_url = path_to_url(buf); LOG(("Using '%s' as AdBlock CSS URL", adblock_stylesheet_url)); @@ -417,6 +421,7 @@ void gui_quit(void) urldb_save_cookies(option_cookie_jar); urldb_save(option_url_file); free(default_stylesheet_url); + free(quirks_stylesheet_url); free(adblock_stylesheet_url); free(option_cookie_file); free(option_cookie_jar); diff --git a/gtk/gtk_print.c b/gtk/gtk_print.c index 8daa21df8..98b43cb89 100644 --- a/gtk/gtk_print.c +++ b/gtk/gtk_print.c @@ -449,7 +449,6 @@ static const struct plotter_table nsgtk_print_plotters = { .option_knockout = false, }; - static bool gtk_print_begin(struct print_settings* settings) { return true; @@ -504,8 +503,9 @@ void gtk_print_signal_begin_print (GtkPrintOperation *operation, settings->page_width, settings->page_height, height_to_print)); height_on_page = settings->page_height; - height_on_page = height_on_page - settings->margins[MARGINTOP] - - settings->margins[MARGINBOTTOM]; + height_on_page = height_on_page - + FIXTOFLT(FSUB(settings->margins[MARGINTOP], + settings->margins[MARGINBOTTOM])); height_to_print *= settings->scale; page_number = height_to_print / height_on_page; diff --git a/gtk/gtk_scaffolding.c b/gtk/gtk_scaffolding.c index d6c4f1e55..6905408fa 100644 --- a/gtk/gtk_scaffolding.c +++ b/gtk/gtk_scaffolding.c @@ -24,6 +24,7 @@ #include <gtk/gtk.h> #include <libxml/debugXML.h> #include "content/content.h" +#include "css/utils.h" #include "desktop/browser.h" #include "desktop/history_core.h" #include "desktop/gui.h" @@ -1241,9 +1242,9 @@ nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel) g->preferences_dialog = NULL; - css_screen_dpi = gdk_screen_get_resolution( - gtk_widget_get_screen(GTK_WIDGET(g->window))); - LOG(("Set CSS DPI to %f", css_screen_dpi)); + nscss_screen_dpi = FLTTOFIX(gdk_screen_get_resolution( + gtk_widget_get_screen(GTK_WIDGET(g->window)))); + LOG(("Set CSS DPI to %f", FIXTOFLT(nscss_screen_dpi))); /* set this window's size and position to what's in the options, or * or some sensible default if they're not set yet. diff --git a/gtk/res/quirks.css b/gtk/res/quirks.css new file mode 120000 index 000000000..d9fb80334 --- /dev/null +++ b/gtk/res/quirks.css @@ -0,0 +1 @@ +../../!NetSurf/Resources/Quirks,f79
\ No newline at end of file diff --git a/image/bmp.c b/image/bmp.c index b7ca94b9c..4eaf5daa6 100644 --- a/image/bmp.c +++ b/image/bmp.c @@ -49,7 +49,8 @@ bmp_bitmap_callback_vt bmp_bitmap_callbacks = { .bitmap_get_bpp = bitmap_get_bpp }; -bool nsbmp_create(struct content *c, const char *params[]) +bool nsbmp_create(struct content *c, struct content *parent, + const char *params[]) { union content_msg_data msg_data; diff --git a/image/bmp.h b/image/bmp.h index 905f9b40f..f7b974f03 100644 --- a/image/bmp.h +++ b/image/bmp.h @@ -40,7 +40,8 @@ struct content_bmp_data { extern bmp_bitmap_callback_vt bmp_bitmap_callbacks; /** Only to be used by ICO code. */ -bool nsbmp_create(struct content *c, const char *params[]); +bool nsbmp_create(struct content *c, struct content *parent, + const char *params[]); bool nsbmp_convert(struct content *c, int width, int height); void nsbmp_destroy(struct content *c); bool nsbmp_redraw(struct content *c, int x, int y, diff --git a/image/gif.c b/image/gif.c index e16275448..62d73f0b0 100644 --- a/image/gif.c +++ b/image/gif.c @@ -64,7 +64,8 @@ gif_bitmap_callback_vt gif_bitmap_callbacks = { }; -bool nsgif_create(struct content *c, const char *params[]) +bool nsgif_create(struct content *c, struct content *parent, + const char *params[]) { union content_msg_data msg_data; /* Initialise our data structure */ diff --git a/image/gif.h b/image/gif.h index 571818ea2..0e3ef6605 100644 --- a/image/gif.h +++ b/image/gif.h @@ -37,7 +37,8 @@ struct content_gif_data { int current_frame; /**< current frame to display [0...(max-1)] */ }; -bool nsgif_create(struct content *c, const char *params[]); +bool nsgif_create(struct content *c, struct content *parent, + const char *params[]); bool nsgif_convert(struct content *c, int width, int height); void nsgif_destroy(struct content *c); bool nsgif_redraw(struct content *c, int x, int y, diff --git a/image/ico.c b/image/ico.c index dc687a0e3..5416e612a 100644 --- a/image/ico.c +++ b/image/ico.c @@ -37,7 +37,8 @@ #include "utils/messages.h" #include "utils/utils.h" -bool nsico_create(struct content *c, const char *params[]) +bool nsico_create(struct content *c, struct content *parent, + const char *params[]) { union content_msg_data msg_data; c->data.ico.ico = calloc(sizeof(ico_collection), 1); diff --git a/image/ico.h b/image/ico.h index 435981470..cd7b0b432 100644 --- a/image/ico.h +++ b/image/ico.h @@ -35,7 +35,8 @@ struct content_ico_data { struct ico_collection *ico; /** ICO collection data */ }; -bool nsico_create(struct content *c, const char *params[]); +bool nsico_create(struct content *c, struct content *parent, + const char *params[]); bool nsico_convert(struct content *c, int width, int height); void nsico_destroy(struct content *c); bool nsico_redraw(struct content *c, int x, int y, diff --git a/image/mng.c b/image/mng.c index 4f19d7202..d21d9e879 100644 --- a/image/mng.c +++ b/image/mng.c @@ -69,7 +69,8 @@ static void nsmng_free(mng_ptr p, mng_size_t n); #endif -bool nsmng_create(struct content *c, const char *params[]) +bool nsmng_create(struct content *c, struct content *parent, + const char *params[]) { mng_retcode code; union content_msg_data msg_data; diff --git a/image/mng.h b/image/mng.h index 1a3c8852d..2ea85409c 100644 --- a/image/mng.h +++ b/image/mng.h @@ -40,7 +40,8 @@ struct content_mng_data { void *handle; }; -bool nsmng_create(struct content *c, const char *params[]); +bool nsmng_create(struct content *c, struct content *parent, + const char *params[]); bool nsmng_process_data(struct content *c, char *data, unsigned int size); bool nsmng_convert(struct content *c, int width, int height); void nsmng_destroy(struct content *c); diff --git a/image/png.c b/image/png.c index 34c517d90..176b89423 100644 --- a/image/png.c +++ b/image/png.c @@ -51,7 +51,8 @@ static void row_callback(png_structp png, png_bytep new_row, static void end_callback(png_structp png, png_infop info); -bool nspng_create(struct content *c, const char *params[]) +bool nspng_create(struct content *c, struct content *parent, + const char *params[]) { union content_msg_data msg_data; diff --git a/image/png.h b/image/png.h index 4c5b1bed3..ffabd73b9 100644 --- a/image/png.h +++ b/image/png.h @@ -24,7 +24,7 @@ #ifdef WITH_PNG -#include "css/css.h" +#include "desktop/plot_style.h" #include <stdbool.h> #include <png.h> @@ -42,7 +42,8 @@ struct content_png_data { size_t rowbytes; /**< Number of bytes per row */ }; -bool nspng_create(struct content *c, const char *params[]); +bool nspng_create(struct content *c, struct content *parent, + const char *params[]); bool nspng_process_data(struct content *c, char *data, unsigned int size); bool nspng_convert(struct content *c, int width, int height); void nspng_destroy(struct content *c); diff --git a/image/rsvg.c b/image/rsvg.c index b1d597052..86e1d5b66 100644 --- a/image/rsvg.c +++ b/image/rsvg.c @@ -49,7 +49,8 @@ static inline void rsvg_argb_to_abgr(uint32_t pixels[], int width, int height, size_t rowstride); -bool rsvg_create(struct content *c, const char *params[]) +bool rsvg_create(struct content *c, struct content *parent, + const char *params[]) { struct content_rsvg_data *d = &c->data.rsvg; union content_msg_data msg_data; diff --git a/image/rsvg.h b/image/rsvg.h index d627991cc..ac414d85c 100644 --- a/image/rsvg.h +++ b/image/rsvg.h @@ -41,7 +41,8 @@ struct content_rsvg_data { struct bitmap *bitmap; /**< Created NetSurf bitmap */ }; -bool rsvg_create(struct content *c, const char *params[]); +bool rsvg_create(struct content *c, struct content *parent, + const char *params[]); bool rsvg_process_data(struct content *c, char *data, unsigned int size); bool rsvg_convert(struct content *c, int width, int height); void rsvg_destroy(struct content *c); diff --git a/image/svg.c b/image/svg.c index b87a8a67d..4321a9fc3 100644 --- a/image/svg.c +++ b/image/svg.c @@ -40,7 +40,7 @@ * Create a CONTENT_SVG. */ -bool svg_create(struct content *c, const char *params[]) +bool svg_create(struct content *c, struct content *parent, const char *params[]) { union content_msg_data msg_data; diff --git a/image/svg.h b/image/svg.h index ec9c789c2..b191c4cd6 100644 --- a/image/svg.h +++ b/image/svg.h @@ -32,7 +32,8 @@ struct content_svg_data { struct svgtiny_diagram *diagram; }; -bool svg_create(struct content *c, const char *params[]); +bool svg_create(struct content *c, struct content *parent, + const char *params[]); bool svg_convert(struct content *c, int width, int height); void svg_destroy(struct content *c); bool svg_redraw(struct content *c, int x, int y, diff --git a/render/box.c b/render/box.c index 7dde0b759..b1ebfcbad 100644 --- a/render/box.c +++ b/render/box.c @@ -28,6 +28,7 @@ #include <string.h> #include "content/content.h" #include "css/css.h" +#include "css/dump.h" #include "desktop/options.h" #include "render/box.h" #include "render/form.h" @@ -58,7 +59,7 @@ static struct box_duplicate_llist *box_duplicate_last = NULL; * \return allocated and initialised box, or 0 on memory exhaustion */ -struct box * box_create(struct css_style *style, +struct box * box_create(css_computed_style *style, char *href, const char *target, char *title, char *id, void *context) { @@ -78,10 +79,11 @@ struct box * box_create(struct css_style *style, box->descendant_x0 = box->descendant_y0 = 0; box->descendant_x1 = box->descendant_y1 = 0; for (i = 0; i != 4; i++) - box->margin[i] = box->padding[i] = box->border[i] = 0; + box->margin[i] = box->padding[i] = box->border[i].width = 0; box->scroll_x = box->scroll_y = 0; box->min_width = 0; box->max_width = UNKNOWN_MAX_WIDTH; + box->byte_offset = 0; box->text = NULL; box->length = 0; box->space = 0; @@ -444,34 +446,34 @@ siblings: bool box_contains_point(struct box *box, int x, int y, bool *physically) { - if (box->x <= x + box->border[LEFT] && + if (box->x <= x + box->border[LEFT].width && x < box->x + box->padding[LEFT] + box->width + - box->border[RIGHT] + box->padding[RIGHT] && - box->y <= y + box->border[TOP] && + box->border[RIGHT].width + box->padding[RIGHT] && + box->y <= y + box->border[TOP].width && y < box->y + box->padding[TOP] + box->height + - box->border[BOTTOM] + box->padding[BOTTOM]) { + box->border[BOTTOM].width + box->padding[BOTTOM]) { *physically = true; return true; } if (box->list_marker && box->list_marker->x <= x + - box->list_marker->border[LEFT] && + box->list_marker->border[LEFT].width && x < box->list_marker->x + box->list_marker->padding[LEFT] + box->list_marker->width + - box->list_marker->border[RIGHT] + + box->list_marker->border[RIGHT].width + box->list_marker->padding[RIGHT] && box->list_marker->y <= y + - box->list_marker->border[TOP] && + box->list_marker->border[TOP].width && y < box->list_marker->y + box->list_marker->padding[TOP] + box->list_marker->height + - box->list_marker->border[BOTTOM] + + box->list_marker->border[BOTTOM].width + box->list_marker->padding[BOTTOM]) { *physically = true; return true; } - if ((box->style && box->style->overflow == CSS_OVERFLOW_VISIBLE) || - !box->style) { + if ((box->style && css_computed_overflow(box->style) == + CSS_OVERFLOW_VISIBLE) || !box->style) { if (box->x + box->descendant_x0 <= x && x < box->x + box->descendant_x1 && box->y + box->descendant_y0 <= y && @@ -502,8 +504,8 @@ struct box *box_object_at_point(struct content *c, int x, int y) assert(c->type == CONTENT_HTML); while ((box = box_at_point(box, x, y, &box_x, &box_y, &content))) { - if (box->style && - box->style->visibility == CSS_VISIBILITY_HIDDEN) + if (box->style && css_computed_visibility(box->style) == + CSS_VISIBILITY_HIDDEN) continue; if (box->object) @@ -532,8 +534,8 @@ struct box *box_href_at_point(struct content *c, int x, int y) assert(c->type == CONTENT_HTML); while ((box = box_at_point(box, x, y, &box_x, &box_y, &content))) { - if (box->style && - box->style->visibility == CSS_VISIBILITY_HIDDEN) + if (box->style && css_computed_visibility(box->style) == + CSS_VISIBILITY_HIDDEN) continue; if (box->href) @@ -580,7 +582,8 @@ bool box_visible(struct box *box) struct box *fallback; /* visibility: hidden */ - if (box->style && box->style->visibility == CSS_VISIBILITY_HIDDEN) + if (box->style && css_computed_visibility(box->style) == + CSS_VISIBILITY_HIDDEN) return false; /* check if a fallback */ @@ -651,7 +654,7 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth) if (box->gadget) fprintf(stream, "(gadget) "); if (box->style) - css_dump_style(stream, box->style); + nscss_dump_computed_style(stream, box->style); if (box->href) fprintf(stream, " -> '%s'", box->href); if (box->target) diff --git a/render/box.h b/render/box.h index 9a90c21a4..3134ea1a2 100644 --- a/render/box.h +++ b/render/box.h @@ -91,10 +91,10 @@ #include <stdio.h> #include <libxml/HTMLparser.h> +#include "css/css.h" struct box; struct column; -struct css_style; struct object_params; struct object_param; @@ -106,7 +106,7 @@ typedef enum { BOX_TABLE_ROW_GROUP, BOX_FLOAT_LEFT, BOX_FLOAT_RIGHT, BOX_INLINE_BLOCK, BOX_BR, BOX_TEXT, - BOX_INLINE_END + BOX_INLINE_END, BOX_NONE } box_type; struct rect { @@ -114,6 +114,18 @@ struct rect { int x1, y1; }; +/* Sides of a box */ +enum box_side { TOP, RIGHT, BOTTOM, LEFT }; + +/** + * Container for box border details + */ +struct box_border { + enum css_border_style style; /**< border-style */ + enum css_border_color color; /**< border-color type */ + css_color c; /**< border-color value */ + int width; /**< border-width (pixels) */ +}; /** Node in box tree. All dimensions are in pixels. */ struct box { @@ -121,7 +133,7 @@ struct box { box_type type; /** Style for this box. 0 for INLINE_CONTAINER and FLOAT_*. */ - struct css_style * style; + css_computed_style *style; /** Coordinate of left padding edge relative to parent box, or relative * to ancestor that contains this box in float_children for FLOAT_. */ @@ -153,7 +165,7 @@ struct box { int margin[4]; /**< Margin: TOP, RIGHT, BOTTOM, LEFT. */ int padding[4]; /**< Padding: TOP, RIGHT, BOTTOM, LEFT. */ - int border[4]; /**< Border width: TOP, RIGHT, BOTTOM, LEFT. */ + struct box_border border[4]; /**< Border: TOP, RIGHT, BOTTOM, LEFT. */ int scroll_x; /**< Horizontal scroll of descendants. */ int scroll_y; /**< Vertical scroll of descendants. */ @@ -282,7 +294,7 @@ extern const char *TARGET_BLANK; #define UNKNOWN_MAX_WIDTH INT_MAX -struct box * box_create(struct css_style *style, +struct box * box_create(css_computed_style *style, char *href, const char *target, char *title, char *id, void *context); void box_add_child(struct box *parent, struct box *child); diff --git a/render/box_construct.c b/render/box_construct.c index b8aa5e600..564a443c3 100644 --- a/render/box_construct.c +++ b/render/box_construct.c @@ -37,6 +37,8 @@ #include "utils/config.h" #include "content/content.h" #include "css/css.h" +#include "css/utils.h" +#include "css/select.h" #include "desktop/browser.h" #include "desktop/options.h" #include "render/box.h" @@ -82,59 +84,30 @@ static const content_type image_types[] = { #endif CONTENT_UNKNOWN }; -#define MAX_SPAN (100) - - /* the strings are not important, since we just compare the pointers */ const char *TARGET_SELF = "_self"; const char *TARGET_PARENT = "_parent"; const char *TARGET_TOP = "_top"; const char *TARGET_BLANK = "_blank"; -/* keeps track of markup presentation */ -struct markup_track { - enum { - ALIGN_NONE, - ALIGN_LEFT, - ALIGN_CENTER, - ALIGN_RIGHT - } align; - bool cell_border; - colour border_color; - - bool cell_padding; - long padding_width; - - bool table; -}; - static bool convert_xml_to_box(xmlNode *n, struct content *content, - struct css_style *parent_style, + const css_computed_style *parent_style, struct box *parent, struct box **inline_container, - char *href, const char *target, char *title, - struct markup_track markup_track, - struct css_importance *author); + char *href, const char *target, char *title); bool box_construct_element(xmlNode *n, struct content *content, - struct css_style *parent_style, + const css_computed_style *parent_style, struct box *parent, struct box **inline_container, - char *href, const char *target, char *title, - struct markup_track markup_track, - struct css_importance *author); + char *href, const char *target, char *title); bool box_construct_text(xmlNode *n, struct content *content, - struct css_style *parent_style, + const css_computed_style *parent_style, struct box *parent, struct box **inline_container, char *href, const char *target, char *title); -static struct css_style * box_get_style(struct content *c, - struct css_style *parent_style, - xmlNode *n, struct markup_track *markup_track, - struct css_importance *author); -static void box_solve_display(struct css_style *style, bool root); +static css_computed_style * box_get_style(struct content *c, + const css_computed_style *parent_style, xmlNode *n); static void box_text_transform(char *s, unsigned int len, - css_text_transform tt); + enum css_text_transform tt); #define BOX_SPECIAL_PARAMS xmlNode *n, struct content *content, \ - struct box *box, bool *convert_children, \ - struct markup_track markup_track, \ - struct css_importance *author + struct box *box, bool *convert_children static bool box_a(BOX_SPECIAL_PARAMS); static bool box_body(BOX_SPECIAL_PARAMS); static bool box_br(BOX_SPECIAL_PARAMS); @@ -157,8 +130,10 @@ static bool box_get_attribute(xmlNode *n, const char *attribute, void *context, char **value); static struct frame_dimension *box_parse_multi_lengths(const char *s, unsigned int *count); -static void parse_inline_colour(char *text, colour *variable); - +static bool fetch_object_interned_url(struct content *c, lwc_string *url, + struct box *box, const content_type *permitted_types, + int available_width, int available_height, + bool background); /* element_table must be sorted by name */ struct element_entry { @@ -195,12 +170,7 @@ static const struct element_entry element_table[] = { bool xml_to_box(xmlNode *n, struct content *c) { struct box root; - struct box *inline_container = 0; - struct css_importance author; - struct markup_track markup_track; - markup_track.cell_border = false; - markup_track.cell_padding = false; - markup_track.align = ALIGN_NONE; + struct box *inline_container = NULL; assert(c->type == CONTENT_HTML); @@ -214,20 +184,12 @@ bool xml_to_box(xmlNode *n, struct content *c) root.float_children = NULL; root.next_float = NULL; - c->data.html.style = talloc_memdup(c, &css_base_style, - sizeof css_base_style); - if (!c->data.html.style) - return false; - c->data.html.style->font_size.value.length.value = - option_font_size * 0.1; - /* and get the default font family from the options */ - c->data.html.style->font_family = option_font_default; - c->data.html.object_count = 0; c->data.html.object = 0; - if (!convert_xml_to_box(n, c, c->data.html.style, &root, - &inline_container, 0, 0, 0, markup_track, &author)) + /* The root box's style */ + if (!convert_xml_to_box(n, c, NULL, &root, + &inline_container, 0, 0, 0)) return false; if (!box_normalise_block(&root, c)) @@ -241,7 +203,7 @@ bool xml_to_box(xmlNode *n, struct content *c) /* mapping from CSS display to box type - * this table must be in sync with css/css_enums */ + * this table must be in sync with libcss' css_display enum */ static const box_type box_map[] = { 0, /*CSS_DISPLAY_INHERIT,*/ BOX_INLINE, /*CSS_DISPLAY_INLINE,*/ @@ -255,10 +217,11 @@ static const box_type box_map[] = { BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_HEADER_GROUP,*/ BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_FOOTER_GROUP,*/ BOX_TABLE_ROW, /*CSS_DISPLAY_TABLE_ROW,*/ - BOX_INLINE, /*CSS_DISPLAY_TABLE_COLUMN_GROUP,*/ - BOX_INLINE, /*CSS_DISPLAY_TABLE_COLUMN,*/ + BOX_NONE, /*CSS_DISPLAY_TABLE_COLUMN_GROUP,*/ + BOX_NONE, /*CSS_DISPLAY_TABLE_COLUMN,*/ BOX_TABLE_CELL, /*CSS_DISPLAY_TABLE_CELL,*/ - BOX_INLINE /*CSS_DISPLAY_TABLE_CAPTION,*/ + BOX_INLINE, /*CSS_DISPLAY_TABLE_CAPTION,*/ + BOX_NONE /*CSS_DISPLAY_NONE*/ }; @@ -267,31 +230,25 @@ static const box_type box_map[] = { * * \param n fragment of xml tree * \param content content of type CONTENT_HTML that is being processed - * \param parent_style style at this point in xml tree + * \param parent_style style at this point in xml tree, or NULL for root box * \param parent parent in box tree * \param inline_container current inline container box, or 0, updated to * new current inline container on exit * \param href current link URL, or 0 if not in a link * \param target current link target, or 0 if none * \param title current title, or 0 if none - * \param markup_track track presentation markup that affects descendents - * \param author denotes whether current style has author level - * importance for certain properties * \return true on success, false on memory exhaustion */ bool convert_xml_to_box(xmlNode *n, struct content *content, - struct css_style *parent_style, + const css_computed_style *parent_style, struct box *parent, struct box **inline_container, - char *href, const char *target, char *title, - struct markup_track markup_track, - struct css_importance *author) + char *href, const char *target, char *title) { switch (n->type) { case XML_ELEMENT_NODE: return box_construct_element(n, content, parent_style, parent, - inline_container, - href, target, title, markup_track, author); + inline_container, href, target, title); case XML_TEXT_NODE: return box_construct_text(n, content, parent_style, parent, inline_container, href, target, title); @@ -307,25 +264,20 @@ bool convert_xml_to_box(xmlNode *n, struct content *content, * * \param n XML node of type XML_ELEMENT_NODE * \param content content of type CONTENT_HTML that is being processed - * \param parent_style style at this point in xml tree + * \param parent_style style at this point in xml tree, or NULL for root node * \param parent parent in box tree * \param inline_container current inline container box, or 0, updated to * new current inline container on exit * \param href current link URL, or 0 if not in a link * \param target current link target, or 0 if none * \param title current title, or 0 if none - * \param markup_track track presentation markup that affects descendents - * \param author denotes whether current style has author level - * importance for certain properties * \return true on success, false on memory exhaustion */ bool box_construct_element(xmlNode *n, struct content *content, - struct css_style *parent_style, + const css_computed_style *parent_style, struct box *parent, struct box **inline_container, - char *href, const char *target, char *title, - struct markup_track markup_track, - struct css_importance *author) + char *href, const char *target, char *title) { bool convert_children = true; char *id = 0; @@ -333,14 +285,14 @@ bool box_construct_element(xmlNode *n, struct content *content, struct box *box = 0; struct box *inline_container_c; struct box *inline_end; - struct css_style *style = 0; + css_computed_style *style = 0; struct element_entry *element; xmlChar *title0; xmlNode *c; + lwc_string *bgimage_uri; assert(n); assert(n->type == XML_ELEMENT_NODE); - assert(parent_style); assert(parent); assert(inline_container); @@ -352,18 +304,23 @@ bool box_construct_element(xmlNode *n, struct content *content, */ parent->strip_leading_newline = 0; - style = box_get_style(content, parent_style, n, &markup_track, author); + style = box_get_style(content, parent_style, n); if (!style) return false; /* extract title attribute, if present */ if ((title0 = xmlGetProp(n, (const xmlChar *) "title"))) { char *title1 = squash_whitespace((char *) title0); + xmlFree(title0); + if (!title1) return false; + title = talloc_strdup(content, title1); + free(title1); + if (!title) return false; } @@ -376,8 +333,25 @@ bool box_construct_element(xmlNode *n, struct content *content, box = box_create(style, href, target, title, id, content); if (!box) return false; - /* set box type from style */ - box->type = box_map[style->display]; + /* set box type from computed display */ + if ((css_computed_position(style) == CSS_POSITION_ABSOLUTE || + css_computed_position(style) == CSS_POSITION_FIXED) && + (css_computed_display_static(style) == + CSS_DISPLAY_INLINE || + css_computed_display_static(style) == + CSS_DISPLAY_INLINE_BLOCK || + css_computed_display_static(style) == + CSS_DISPLAY_INLINE_TABLE)) { + /* Special case for absolute positioning: make absolute inlines + * into inline block so that the boxes are constructed in an + * inline container as if they were not absolutely positioned. + * Layout expects and handles this. */ + box->type = box_map[CSS_DISPLAY_INLINE_BLOCK]; + } else { + /* Normal mapping */ + box->type = box_map[css_computed_display(style, + n->parent == NULL)]; + } /* special elements */ element = bsearch((const char *) n->name, element_table, @@ -385,16 +359,17 @@ bool box_construct_element(xmlNode *n, struct content *content, (int (*)(const void *, const void *)) strcmp); if (element) { /* a special convert function exists for this element */ - if (!element->convert(n, content, box, &convert_children, - markup_track, author)) + if (!element->convert(n, content, box, &convert_children)) return false; + href = box->href; target = box->target; } - if (style->display == CSS_DISPLAY_NONE) { + if (box->type == BOX_NONE || css_computed_display(box->style, + n->parent == NULL) == CSS_DISPLAY_NONE) { /* Free style and invalidate box's style pointer */ - talloc_free(style); + css_computed_style_destroy(style); box->style = NULL; /* If this box has an associated gadget, invalidate the @@ -416,60 +391,71 @@ bool box_construct_element(xmlNode *n, struct content *content, (box->type == BOX_INLINE || box->type == BOX_BR || box->type == BOX_INLINE_BLOCK || - style->float_ == CSS_FLOAT_LEFT || - style->float_ == CSS_FLOAT_RIGHT)) { + css_computed_float(style) == CSS_FLOAT_LEFT || + css_computed_float(style) == CSS_FLOAT_RIGHT)) { /* this is the first inline in a block: make a container */ *inline_container = box_create(0, 0, 0, 0, 0, content); if (!*inline_container) return false; + (*inline_container)->type = BOX_INLINE_CONTAINER; + box_add_child(parent, *inline_container); } if (box->type == BOX_INLINE || box->type == BOX_BR) { /* inline box: add to tree and recurse */ box_add_child(*inline_container, box); + if (convert_children && n->children) { for (c = n->children; c; c = c->next) if (!convert_xml_to_box(c, content, style, parent, inline_container, - href, target, title, - markup_track, author)) + href, target, title)) return false; + inline_end = box_create(style, href, target, title, id, content); if (!inline_end) return false; + inline_end->type = BOX_INLINE_END; + if (*inline_container) box_add_child(*inline_container, inline_end); else box_add_child(box->parent, inline_end); + box->inline_end = inline_end; inline_end->inline_end = box; } } else if (box->type == BOX_INLINE_BLOCK) { /* inline block box: add to tree and recurse */ box_add_child(*inline_container, box); + inline_container_c = 0; + for (c = n->children; convert_children && c; c = c->next) if (!convert_xml_to_box(c, content, style, box, &inline_container_c, - href, target, title, markup_track, - author)) + href, target, title)) return false; } else { /* list item: compute marker, then treat as non-inline box */ - if (style->display == CSS_DISPLAY_LIST_ITEM) { + if (css_computed_display(style, n->parent == NULL) == + CSS_DISPLAY_LIST_ITEM) { + lwc_string *image_uri; struct box *marker; + marker = box_create(style, 0, 0, title, 0, content); if (!marker) return false; + marker->type = BOX_BLOCK; + /** \todo marker content (list-style-type) */ - switch (style->list_style_type) { + switch (css_computed_list_style_type(style)) { case CSS_LIST_STYLE_TYPE_DISC: - default: /* 2022 BULLET */ marker->text = (char *) "\342\200\242"; marker->length = 3; @@ -489,6 +475,7 @@ bool box_construct_element(xmlNode *n, struct content *content, case CSS_LIST_STYLE_TYPE_LOWER_ROMAN: case CSS_LIST_STYLE_TYPE_UPPER_ALPHA: case CSS_LIST_STYLE_TYPE_UPPER_ROMAN: + default: if (parent->last) { struct box *last = parent->last; @@ -515,9 +502,11 @@ bool box_construct_element(xmlNode *n, struct content *content, list_marker->rows + 1; } } + marker->text = talloc_array(content, char, 20); if (!marker->text) return false; + snprintf(marker->text, 20, "%u.", marker->rows); marker->length = strlen(marker->text); break; @@ -526,43 +515,50 @@ bool box_construct_element(xmlNode *n, struct content *content, marker->length = 0; break; } - if (style->list_style_image.type == - CSS_LIST_STYLE_IMAGE_URI) { - if (!html_fetch_object(content, - style->list_style_image.uri, + + if (css_computed_list_style_image(style, &image_uri) == + CSS_LIST_STYLE_IMAGE_URI && + image_uri != NULL) { + if (!fetch_object_interned_url(content, + image_uri, marker, 0, content->available_width, 1000, false)) return false; } + box->list_marker = marker; marker->parent = box; } /* float: insert a float box between the parent and * current node. Note: new parent will be the float */ - if (style->float_ == CSS_FLOAT_LEFT || - style->float_ == CSS_FLOAT_RIGHT) { + if (css_computed_float(style) == CSS_FLOAT_LEFT || + css_computed_float(style) == CSS_FLOAT_RIGHT) { parent = box_create(0, href, target, title, 0, content); if (!parent) return false; - if (style->float_ == CSS_FLOAT_LEFT) + + if (css_computed_float(style) == CSS_FLOAT_LEFT) parent->type = BOX_FLOAT_LEFT; else parent->type = BOX_FLOAT_RIGHT; + box_add_child(*inline_container, parent); } /* non-inline box: add to tree and recurse */ box_add_child(parent, box); + inline_container_c = 0; + for (c = n->children; convert_children && c; c = c->next) if (!convert_xml_to_box(c, content, style, box, &inline_container_c, - href, target, title, markup_track, - author)) + href, target, title)) return false; - if (style->float_ == CSS_FLOAT_NONE) + + if (css_computed_float(style) == CSS_FLOAT_NONE) /* new inline container unless this is a float */ *inline_container = 0; } @@ -571,23 +567,23 @@ bool box_construct_element(xmlNode *n, struct content *content, if ((s = (char *) xmlGetProp(n, (const xmlChar *) "colspan"))) { if (isdigit(s[0])) { box->columns = strtol(s, NULL, 10); - if ((MAX_SPAN < box->columns) || (box->columns < 1)) - box->columns = 1; } xmlFree(s); } + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "rowspan"))) { if (isdigit(s[0])) { box->rows = strtol(s, NULL, 10); - if ((MAX_SPAN < box->rows) || (box->rows < 1)) - box->rows = 1; } xmlFree(s); } /* fetch any background image for this box */ - if (style->background_image.type == CSS_BACKGROUND_IMAGE_URI) { - if (!html_fetch_object(content, style->background_image.uri, + if (css_computed_background_image(style, &bgimage_uri) == + CSS_BACKGROUND_IMAGE_IMAGE && + bgimage_uri != NULL) { + if (!fetch_object_interned_url(content, + bgimage_uri, box, image_types, content->available_width, 1000, true)) return false; @@ -613,7 +609,7 @@ bool box_construct_element(xmlNode *n, struct content *content, */ bool box_construct_text(xmlNode *n, struct content *content, - struct css_style *parent_style, + const css_computed_style *parent_style, struct box *parent, struct box **inline_container, char *href, const char *target, char *title) { @@ -625,8 +621,9 @@ bool box_construct_text(xmlNode *n, struct content *content, assert(parent); assert(inline_container); - if (parent_style->white_space == CSS_WHITE_SPACE_NORMAL || - parent_style->white_space == CSS_WHITE_SPACE_NOWRAP) { + if (css_computed_white_space(parent_style) == CSS_WHITE_SPACE_NORMAL || + css_computed_white_space(parent_style) == + CSS_WHITE_SPACE_NOWRAP) { char *text = squash_whitespace((char *) n->content); if (!text) return false; @@ -643,10 +640,14 @@ bool box_construct_text(xmlNode *n, struct content *content, parent = parent->parent; box_dump(stderr, parent, 0); } + assert((*inline_container)->last != 0); + (*inline_container)->last->space = 1; } + free(text); + return true; } @@ -657,34 +658,48 @@ bool box_construct_text(xmlNode *n, struct content *content, free(text); return false; } + (*inline_container)->type = BOX_INLINE_CONTAINER; + box_add_child(parent, *inline_container); } - box = box_create(parent_style, href, target, title, 0, content); + /** \todo Dropping const here is not clever */ + box = box_create((css_computed_style *) parent_style, + href, target, title, 0, content); if (!box) { free(text); return false; } + box->type = BOX_TEXT; + box->text = talloc_strdup(content, text); free(text); if (!box->text) return false; + box->length = strlen(box->text); + /* strip ending space char off */ if (box->length > 1 && box->text[box->length - 1] == ' ') { box->space = 1; box->length--; } - if (parent_style->text_transform != CSS_TEXT_TRANSFORM_NONE) + + if (css_computed_text_transform(parent_style) != + CSS_TEXT_TRANSFORM_NONE) box_text_transform(box->text, box->length, - parent_style->text_transform); - if (parent_style->white_space == CSS_WHITE_SPACE_NOWRAP) { + css_computed_text_transform(parent_style)); + + if (css_computed_white_space(parent_style) == + CSS_WHITE_SPACE_NOWRAP) { unsigned int i; + for (i = 0; i != box->length && box->text[i] != ' '; ++i) ; /* no body */ + if (i != box->length) { /* there is a space in text block and we * want all spaces to be converted to NBSP @@ -699,9 +714,12 @@ bool box_construct_text(xmlNode *n, struct content *content, } box_add_child(*inline_container, box); + if (box->text[0] == ' ') { box->length--; + memmove(box->text, &box->text[1], box->length); + if (box->prev != NULL) box->prev->space = 1; } @@ -710,17 +728,22 @@ bool box_construct_text(xmlNode *n, struct content *content, /* white-space: pre */ char *text = cnv_space2nbsp((char *) n->content); char *current; + enum css_white_space white_space = + css_computed_white_space(parent_style); + /* note: pre-wrap/pre-line are unimplemented */ - assert(parent_style->white_space == CSS_WHITE_SPACE_PRE || - parent_style->white_space == - CSS_WHITE_SPACE_PRE_LINE || - parent_style->white_space == - CSS_WHITE_SPACE_PRE_WRAP); + assert(white_space == CSS_WHITE_SPACE_PRE || + white_space == CSS_WHITE_SPACE_PRE_LINE || + white_space == CSS_WHITE_SPACE_PRE_WRAP); + if (!text) return false; - if (parent_style->text_transform != CSS_TEXT_TRANSFORM_NONE) + + if (css_computed_text_transform(parent_style) != + CSS_TEXT_TRANSFORM_NONE) box_text_transform(text, strlen(text), - parent_style->text_transform); + css_computed_text_transform(parent_style)); + current = text; /* swallow a single leading new line */ @@ -739,7 +762,9 @@ bool box_construct_text(xmlNode *n, struct content *content, do { size_t len = strcspn(current, "\r\n"); char old = current[len]; + current[len] = 0; + if (!*inline_container) { *inline_container = box_create(0, 0, 0, 0, 0, content); @@ -747,26 +772,37 @@ bool box_construct_text(xmlNode *n, struct content *content, free(text); return false; } + (*inline_container)->type = BOX_INLINE_CONTAINER; + box_add_child(parent, *inline_container); } - box = box_create(parent_style, href, target, title, 0, - content); + + /** \todo Dropping const isn't clever */ + box = box_create((css_computed_style *) parent_style, + href, target, title, 0, content); if (!box) { free(text); return false; } + box->type = BOX_TEXT; + box->text = talloc_strdup(content, current); if (!box->text) { free(text); return false; } + box->length = strlen(box->text); + box_add_child(*inline_container, box); + current[len] = old; + current += len; + if (current[0] == '\r' && current[1] == '\n') { current += 2; *inline_container = 0; @@ -775,6 +811,7 @@ bool box_construct_text(xmlNode *n, struct content *content, *inline_container = 0; } } while (*current); + free(text); } @@ -782,524 +819,69 @@ bool box_construct_text(xmlNode *n, struct content *content, } +static void *myrealloc(void *ptr, size_t len, void *pw) +{ + return talloc_realloc_size(pw, ptr, len); +} + /** * Get the style for an element. * * \param c content of type CONTENT_HTML that is being processed - * \param parent_style style at this point in xml tree + * \param parent_style style at this point in xml tree, or NULL for root * \param n node in xml tree - * \param markup_track track presentation markup that affects descendents - * \param author denotes whether current style has author level - * importance for certain properties - * \return the new style, or 0 on memory exhaustion - * - * The style is collected from three sources: - * 1. any styles for this element in the document stylesheet(s) - * 2. the 'style' attribute - * 3. non-CSS HTML attributes (subject to importance of CSS style properties) + * \return the new style, or NULL on memory exhaustion */ - -struct css_style * box_get_style(struct content *c, - struct css_style *parent_style, - xmlNode *n, struct markup_track *markup_track, - struct css_importance *author) +css_computed_style *box_get_style(struct content *c, + const css_computed_style *parent_style, + xmlNode *n) { char *s; - struct css_style *style; - struct css_style *style_new; - char *url; - url_func_result res; - colour border_color = 0x888888; /* mid-grey default for tables */ - - /* if not in a table, switch off cellpadding and cell borders - * and record that we're not in a table */ - if (strcmp((const char *) n->name, "thead") != 0 && - strcmp((const char *) n->name, "tbody") != 0 && - strcmp((const char *) n->name, "tfoot") != 0 && - strcmp((const char *) n->name, "tr") != 0 && - strcmp((const char *) n->name, "td") != 0 && - strcmp((const char *) n->name, "th") != 0 && - strcmp((const char *) n->name, "col") != 0 && - strcmp((const char *) n->name, "colgroup") != 0) { - markup_track->cell_border = false; - markup_track->cell_padding = false; - markup_track->table = false; - } - - style = talloc_memdup(c, parent_style, sizeof *style); - if (!style) - return 0; - - style_new = talloc_memdup(c, &css_blank_style, sizeof *style_new); - if (!style_new) - return 0; - css_get_style(c->data.html.working_stylesheet, n, style_new, author); - css_cascade(style, style_new, NULL); + css_stylesheet *inline_style = NULL; + css_computed_style *partial; + css_computed_style *style; - /* style_new isn't needed past this point */ - talloc_free(style_new); - - /* Handle style attribute. (style attribute values have high enough - * specificity to override existing style data.) */ + /* Firstly, construct inline stylesheet, if any */ if ((s = (char *) xmlGetProp(n, (const xmlChar *) "style"))) { - struct css_style *astyle; - astyle = css_duplicate_style(&css_empty_style); - if (!astyle) { - xmlFree(s); - return 0; - } - css_parse_property_list(c, astyle, s); - css_cascade(style, astyle, author); - css_free_style(astyle); - xmlFree(s); - } - - /* Apply presentational HTML attributes to style - * (Only apply if style property does not have "author" level - * importance or higher.) - */ - - /* This property only applies to the body element, if you believe - * the spec. Many browsers seem to allow it on other elements too, - * so let's be generic ;) */ - if (!author->background_image && (s = (char *) xmlGetProp(n, - (const xmlChar *) "background"))) { - res = url_join(s, c->data.html.base_url, &url); - xmlFree(s); - if (res == URL_FUNC_NOMEM) { - return 0; - } else if (res == URL_FUNC_OK) { - /* if url is equivalent to the parent's url, - * we've got infinite inclusion: ignore */ - if (strcmp(url, c->data.html.base_url) == 0) - free(url); - else { - style->background_image.type = - CSS_BACKGROUND_IMAGE_URI; - style->background_image.uri = talloc_strdup( - c, url); - free(url); - if (!style->background_image.uri) - return 0; - } - } - } - - if (!author->background_color && (s = (char *) xmlGetProp(n, - (const xmlChar *) "bgcolor"))) { - parse_inline_colour(s, &style->background_color); - xmlFree(s); - } - - if (!author->color && (s = (char *) xmlGetProp(n, - (const xmlChar *) "color"))) { - parse_inline_colour(s, &style->color); - xmlFree(s); - } - - if (!author->height && (s = (char *) xmlGetProp(n, - (const xmlChar *) "height")) && - ((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))) { - float value = isdigit(s[0]) ? atof(s) : -1; - if (value <= 0 || strlen(s) == 0) { - /* ignore negative values and height="" */ - } else if (strrchr(s, '%')) { - style->height.height = CSS_HEIGHT_PERCENT; - style->height.value.percent = value; - } else { - style->height.height = CSS_HEIGHT_LENGTH; - style->height.value.length.unit = CSS_UNIT_PX; - style->height.value.length.value = value; - } - xmlFree(s); - } - - if (!author->width && strcmp((const char *) n->name, "input") == 0) { - int size = -1; - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "size"))) { - size = isdigit(s[0]) ? atoi(s): -1; - if (0 < size) { - char *type = (char *) xmlGetProp(n, - (const xmlChar *) "type"); - style->width.width = CSS_WIDTH_LENGTH; - if (!type || strcasecmp(type, "text") == 0 || - strcasecmp(type, "password") == 0) - /* in characters for text, password */ - style->width.value.length.unit = - CSS_UNIT_EX; - else if (strcasecmp(type, "file") != 0) - /* in pixels otherwise; ignore width - * on file, because we do them - * differently to most browsers */ - style->width.value.length.unit = - CSS_UNIT_PX; - style->width.value.length.value = size; - if (type) - xmlFree(type); - } - xmlFree(s); - } - /* If valid maxlength value is provided, the size attribute is - * unset and maxlength is small, use it to reduce input width - * to sensible size */ - if ((s = (char *) xmlGetProp(n, (const xmlChar *) - "maxlength"))) { - int maxlength = isdigit(s[0]) ? atoi(s): -1; - if (0 < maxlength && size == -1 && maxlength < 10) { - char *type; - /* Bump up really small widths */ - maxlength = maxlength < 5 ? maxlength + 1 : - maxlength; - type = (char *) xmlGetProp(n, - (const xmlChar *) "type"); - style->width.width = CSS_WIDTH_LENGTH; - if (!type || strcasecmp(type, "text") == 0 || - strcasecmp(type, "password") == 0) - /* in characters for text, password */ - style->width.value.length.unit = - CSS_UNIT_EX; - style->width.value.length.value = maxlength; - if (type) - xmlFree(type); - } - xmlFree(s); - } - } + inline_style = nscss_create_inline_style( + (uint8_t *) s, strlen(s), + c->data.html.encoding, c->url, false, + c->data.html.dict, myrealloc, c); - if (!author->color && strcmp((const char *) n->name, "body") == 0) { - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "text"))) { - parse_inline_colour(s, &style->color); - xmlFree(s); - } - } - - if (!author->width && (s = (char *) xmlGetProp(n, - (const xmlChar *) "width")) && - ((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))) { - float value = isdigit(s[0]) ? atof(s) : -1; - if (value < 0 || strlen(s) == 0) { - /* ignore negative values and width="" */ - } else if (strrchr(s, '%')) { - style->width.width = CSS_WIDTH_PERCENT; - style->width.value.percent = value; - } else { - style->width.width = CSS_WIDTH_LENGTH; - style->width.value.length.unit = CSS_UNIT_PX; - style->width.value.length.value = value; - } xmlFree(s); - } - - if (strcmp((const char *) n->name, "textarea") == 0) { - if (!author->height && (s = (char *) xmlGetProp(n, - (const xmlChar *) "rows"))) { - int value = isdigit(s[0]) ? atoi(s): -1; - if (0 < value) { - style->height.height = CSS_HEIGHT_LENGTH; - style->height.value.length.unit = CSS_UNIT_EM; - style->height.value.length.value = value; - } - xmlFree(s); - } - if (!author->width && (s = (char *) xmlGetProp(n, - (const xmlChar *) "cols"))) { - int value = isdigit(s[0]) ? atoi(s): -1; - if (0 < value) { - style->width.width = CSS_WIDTH_LENGTH; - style->width.value.length.unit = CSS_UNIT_EX; - style->width.value.length.value = value; - } - xmlFree(s); - } - } - - if (strcmp((const char *) n->name, "table") == 0) { - if (!author->border_spacing && (s = (char *) xmlGetProp(n, - (const xmlChar *) "cellspacing"))) { - /* percentage cellspacing not implemented */ - if (!strrchr(s, '%')) { - int value = isdigit(s[0]) ? atoi(s): -1; - if (0 <= value) { - style->border_spacing.border_spacing = - CSS_BORDER_SPACING_LENGTH; - style->border_spacing.horz.unit = - style->border_spacing.vert.unit = - CSS_UNIT_PX; - style->border_spacing.horz.value = - style->border_spacing.vert.value = - value; - } - } - xmlFree(s); - } - - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "bordercolor"))) { - parse_inline_colour(s, &border_color); - xmlFree(s); - } - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "border"))) { - int border_width = atoi(s); - /* precentage border width not implemented */ - if (!strrchr(s, '%') && 0 < border_width) { - unsigned int i; - for (i = 0; i != 4; i++) { - if (!author->border_color[i]) - style->border[i].color = - border_color; - if (!author->border_width[i]) { - style->border[i].width.width = - CSS_BORDER_WIDTH_LENGTH; - style->border[i].width.value. - value = border_width; - style->border[i].width.value. - unit = CSS_UNIT_PX; - } - if (!author->border_style[i]) - style->border[i].style = - CSS_BORDER_STYLE_OUTSET; - } - } - xmlFree(s); - } - } - if (strcmp((const char *) n->name, "td") == 0 || - strcmp((const char *) n->name, "th") == 0) { - /* set any cellborders stipulated by associated table */ - if (markup_track->cell_border) { - unsigned int i; - for (i = 0; i != 4; i++) { - if (!author->border_color[i]) - style->border[i].color = markup_track-> - border_color; - if (!author->border_width[i]) { - style->border[i].width.width = - CSS_BORDER_WIDTH_LENGTH; - style->border[i].width.value.value = 1; - style->border[i].width.value.unit = - CSS_UNIT_PX; - } - if (!author->border_style[i]) - style->border[i].style = - CSS_BORDER_STYLE_INSET; - } - } - /* set any cellpadding stipulated by associated table */ - if (markup_track->cell_padding) { - unsigned int i; - for (i = 0; i != 4; i++) { - if (!author->padding[i]) { - style->padding[i].padding = - CSS_PADDING_LENGTH; - style->padding[i].value.length.value = - markup_track->padding_width; - style->padding[i].value.length.unit = - CSS_UNIT_PX; - } - } - } + if (inline_style == NULL) + return NULL; } - if ((strcmp((const char *) n->name, "img") == 0) || - (strcmp((const char *) n->name, "image") == 0) || - (strcmp((const char *) n->name, "applet") == 0)) { - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "hspace"))) { - /* percentage hspace not implemented */ - if (!strrchr(s, '%')) { - int value = isdigit(s[0]) ? atoi(s): -1; - if (0 <= value && !author->margin[LEFT]) { - style->margin[LEFT].margin = - CSS_MARGIN_LENGTH; - style->margin[LEFT].value.length.value = - value; - style->margin[LEFT].value.length.unit = - CSS_UNIT_PX; - } - if (0 <= value && !author->margin[RIGHT]) { - style->margin[RIGHT].margin = - CSS_MARGIN_LENGTH; - style->margin[RIGHT].value.length. - value = value; - style->margin[RIGHT].value.length.unit = - CSS_UNIT_PX; - } - } - xmlFree(s); - } - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "vspace"))) { - /* percentage vspace not implemented */ - if (!strrchr(s, '%')) { - int value = isdigit(s[0]) ? atoi(s): -1; - if (0 <= value && !author->margin[TOP]) { - style->margin[TOP].margin = - CSS_MARGIN_LENGTH; - style->margin[TOP].value.length.value = - value; - style->margin[TOP].value.length.unit = - CSS_UNIT_PX; - } - if (0 <= value && !author->margin[BOTTOM]) { - style->margin[BOTTOM].margin = - CSS_MARGIN_LENGTH; - style->margin[BOTTOM].value.length. - value = value; - style->margin[BOTTOM].value.length. - unit = CSS_UNIT_PX; - } - } - xmlFree(s); - } - } + /* Select partial style for element */ + partial = nscss_get_style(c, n, CSS_PSEUDO_ELEMENT_NONE, + CSS_MEDIA_SCREEN, inline_style, myrealloc, c); - /* Handle markup-originating alignment of block level elements. - * Adjust left and right margins. text-align property is handled in - * the default CSS file. - */ - if (markup_track->align != ALIGN_NONE && - (style->display == CSS_DISPLAY_BLOCK || - style->display == CSS_DISPLAY_TABLE) && - (strcmp((const char *) n->name, "blockquote") != 0)) { - if (!author->margin[LEFT]) { - if (markup_track->align == ALIGN_LEFT) { - /* left */ - style->margin[LEFT].margin = CSS_MARGIN_LENGTH; - style->margin[LEFT].value.length.value = 0; - style->margin[LEFT].value.length.unit = - CSS_UNIT_PX; - } else - /* center or right */ - style->margin[LEFT].margin = CSS_MARGIN_AUTO; - } - - if (!author->margin[RIGHT]) { - if (markup_track->align == ALIGN_RIGHT) { - /* right */ - style->margin[RIGHT].margin = CSS_MARGIN_LENGTH; - style->margin[RIGHT].value.length.value= 0; - style->margin[RIGHT].value.length.unit = - CSS_UNIT_PX; - } else - /* left or center */ - style->margin[RIGHT].margin = CSS_MARGIN_AUTO; - } - if (author->margin[LEFT] || author->margin[RIGHT]) { - /* author stylesheet sets a margin so stop markup - * alignment model propagation */ - markup_track->align = ALIGN_NONE; - } - } - /* Centered tables are a special case. The align attribute only - * affects the current element (table) and overrides any existing - * HTML alignment rule. Tables aligned to left or right are floated - * by the default CSS file. */ - if (!author->margin[LEFT] && !author->margin[RIGHT] && - strcmp((const char *) n->name, "table") == 0) { - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "align"))) { - if (strcasecmp(s, "center") == 0) { - style->margin[LEFT].margin = CSS_MARGIN_AUTO; - style->margin[RIGHT].margin = CSS_MARGIN_AUTO; - } - xmlFree(s); - } - } + /* No longer need inline style */ + if (inline_style != NULL) + css_stylesheet_destroy(inline_style); - box_solve_display(style, !n->parent); + /* Failed selecting partial style -- bail out */ + if (partial == NULL) + return NULL; - /* Update markup_track with attributes which affect children of - * current box. */ + /* If there's a parent style, compose with partial to obtain + * complete computed style for element */ + if (parent_style != NULL) { + css_error error; - /* Handle html block level element alignment model. - * Note that only margins of block level children are considered, - * text-align for the current block can be handled in the default - * CSS file. - */ - if (strcmp((const char *) n->name, "center") == 0) - markup_track->align = ALIGN_CENTER; - else if (strcmp((const char *) n->name, "div") == 0 || - strcmp((const char *) n->name, "col") == 0 || - strcmp((const char *) n->name, "colgroup") == 0 || - strcmp((const char *) n->name, "tbody") == 0 || - strcmp((const char *) n->name, "td") == 0 || - strcmp((const char *) n->name, "tfoot") == 0 || - strcmp((const char *) n->name, "th") == 0 || - strcmp((const char *) n->name, "thead") == 0 || - strcmp((const char *) n->name, "tr") == 0) { - - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "align"))) { - if (strcasecmp(s, "center") == 0) - markup_track->align = ALIGN_CENTER; - else if (strcasecmp(s, "right") == 0) - markup_track->align = ALIGN_RIGHT; - else if (strcasecmp(s, "left") == 0) - markup_track->align = ALIGN_LEFT; - xmlFree(s); - /* Need to remember if we're in a table, so that any - * alignment rules set on the table's elements won't - * get overridden by the default alignment of a cell - * with no align attribute. At this point, we're in a - * table if the element isn't a div */ - if (strcmp((const char *) n->name, "div") != 0) - markup_track->table = true; + error = css_computed_style_compose(parent_style, partial, + nscss_compute_font_size, NULL, partial); + if (error != CSS_OK) { + css_computed_style_destroy(partial); + return NULL; } - } - /* Table cells without an align value have a default implied - * alignment. */ - if (strcmp((const char *) n->name, "td") == 0 && !markup_track->table) { - if (!(s = (char *) xmlGetProp(n, (const xmlChar *) "align"))) - markup_track->align = ALIGN_LEFT; - else - xmlFree(s); - } - if (strcmp((const char *) n->name, "th") == 0 && !markup_track->table) { - if (!(s = (char *) xmlGetProp(n, (const xmlChar *) "align"))) - markup_track->align = ALIGN_CENTER; - else - xmlFree(s); - } - /* Some of TABLE's attributes apply to the table cells contained - * within the table. Those details are stored so they may be applied - * to the cells when we get to them. */ - if (strcmp((const char *) n->name, "table") == 0) { - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "cellpadding"))) { - char *endp; - long value = strtol(s, &endp, 10); - /* precentage padding width not implemented */ - if (*endp == 0 && 0 <= value && value < 1000) { - markup_track->padding_width = value; - markup_track->cell_padding = true; - } - xmlFree(s); - } - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "border"))) { - int border_width = atoi(s); - markup_track->border_color = border_color; - /* percentage border width not implemented */ - if (!strrchr(s, '%') && 0 < border_width) { - markup_track->cell_border = true; - } - xmlFree(s); - } + style = partial; + } else { + /* No parent style, so partial must be fully computed */ + style = partial; } return style; @@ -1307,52 +889,6 @@ struct css_style * box_get_style(struct content *c, /** - * Calculate 'display' based on 'display', 'position', and 'float', as given - * by CSS 2.1 9.7. - * - * \param style style to update - * \param root this is the root element - */ - -void box_solve_display(struct css_style *style, bool root) -{ - if (style->display == CSS_DISPLAY_NONE) /* 1. */ - return; - else if (style->position == CSS_POSITION_ABSOLUTE || - style->position == CSS_POSITION_FIXED) /* 2. */ - style->float_ = CSS_FLOAT_NONE; - else if (style->float_ != CSS_FLOAT_NONE) /* 3. */ - ; - else if (root) /* 4. */ - ; - else /* 5. */ - return; - - /* Special case for absolute positioning: make absolute inlines into - * inline block so that the boxes are constructed in an inline container - * as if they were not absolutely positioned. Layout expects and - * handles this. */ - if ((style->position == CSS_POSITION_ABSOLUTE || - style->position == CSS_POSITION_FIXED) && - (style->display == CSS_DISPLAY_INLINE || - style->display == CSS_DISPLAY_INLINE_BLOCK || - style->display == CSS_DISPLAY_INLINE_TABLE)) { - style->display = CSS_DISPLAY_INLINE_BLOCK; - return; - } - - /* map specified value to computed value using table given in 9.7 */ - if (style->display == CSS_DISPLAY_INLINE_TABLE) - style->display = CSS_DISPLAY_TABLE; - else if (style->display == CSS_DISPLAY_LIST_ITEM || - style->display == CSS_DISPLAY_TABLE) - ; /* same as specified */ - else - style->display = CSS_DISPLAY_BLOCK; -} - - -/** * Apply the CSS text-transform property to given text for its ASCII chars. * * \param s string to transform @@ -1360,8 +896,7 @@ void box_solve_display(struct css_style *style, bool root) * \param tt transform type */ -void box_text_transform(char *s, unsigned int len, - css_text_transform tt) +void box_text_transform(char *s, unsigned int len, enum css_text_transform tt) { unsigned int i; if (len == 0) @@ -1416,7 +951,15 @@ void box_text_transform(char *s, unsigned int len, bool box_body(BOX_SPECIAL_PARAMS) { - content->data.html.background_colour = box->style->background_color; + enum css_background_color type; + css_color color; + + type = css_computed_background_color(box->style, &color); + if (type == CSS_BACKGROUND_COLOR_TRANSPARENT) + content->data.html.background_colour = NS_TRANSPARENT; + else + content->data.html.background_colour = nscss_color_to_ns(color); + return true; } @@ -1507,7 +1050,8 @@ bool box_image(BOX_SPECIAL_PARAMS) char *s, *url; xmlChar *alt, *src; - if (box->style && box->style->display == CSS_DISPLAY_NONE) + if (box->style && css_computed_display(box->style, + n->parent == NULL) == CSS_DISPLAY_NONE) return true; /* handle alt text */ @@ -1558,7 +1102,8 @@ bool box_object(BOX_SPECIAL_PARAMS) xmlNode *c; struct box *inline_container = 0; - if (box->style && box->style->display == CSS_DISPLAY_NONE) + if (box->style && css_computed_display(box->style, + n->parent == NULL) == CSS_DISPLAY_NONE) return true; if (!box_get_attribute(n, "usemap", content, &box->usemap)) @@ -1685,8 +1230,7 @@ bool box_object(BOX_SPECIAL_PARAMS) /* convert children and place into fallback */ for (c = n->children; c; c = c->next) { if (!convert_xml_to_box(c, content, box->style, box, - &inline_container, 0, 0, 0, markup_track, - author)) + &inline_container, 0, 0, 0)) return false; } box->fallback = box->children; @@ -1849,7 +1393,7 @@ bool box_frameset(BOX_SPECIAL_PARAMS) if (convert_children) *convert_children = false; /* And ignore this spurious frameset */ - box->style->display = CSS_DISPLAY_NONE; + box->type = BOX_NONE; return true; } @@ -1860,7 +1404,7 @@ bool box_frameset(BOX_SPECIAL_PARAMS) ok = box_create_frameset(content->data.html.frameset, n, content); if (ok) - box->style->display = CSS_DISPLAY_NONE; + box->type = BOX_NONE; if (convert_children) *convert_children = false; @@ -1920,7 +1464,11 @@ bool box_create_frameset(struct content_html_frames *f, xmlNode *n, /* common extension: bordercolor="#RRGGBB|<named colour>" to control *all children */ if ((s = (char *) xmlGetProp(n, (const xmlChar *) "bordercolor"))) { - parse_inline_colour(s, &default_border_colour); + css_color color; + + if (nscss_parse_colour((const char *) s, &color)) + default_border_colour = nscss_color_to_ns(color); + xmlFree(s); } @@ -2030,7 +1578,13 @@ bool box_create_frameset(struct content_html_frames *f, xmlNode *n, } if ((s = (char *) xmlGetProp(c, (const xmlChar *) "bordercolor"))) { - parse_inline_colour(s, &frame->border_colour); + css_color color; + + if (nscss_parse_colour((const char *) s, + &color)) + frame->border_colour = + nscss_color_to_ns(color); + xmlFree(s); } @@ -2100,7 +1654,11 @@ bool box_iframe(BOX_SPECIAL_PARAMS) xmlFree(s); } if ((s = (char *) xmlGetProp(n, (const xmlChar *) "bordercolor"))) { - parse_inline_colour(s, &iframe->border_colour); + css_color color; + + if (nscss_parse_colour(s, &color)) + iframe->border_colour = nscss_color_to_ns(color); + xmlFree(s); } if ((s = (char *) xmlGetProp(n, @@ -2155,31 +1713,36 @@ bool box_input(BOX_SPECIAL_PARAMS) gadget->box = box; if (type && strcasecmp(type, "password") == 0) { - if (!box_input_text(n, content, box, 0, markup_track, author, - true)) + if (!box_input_text(n, content, box, 0, true)) goto no_memory; } else if (type && strcasecmp(type, "file") == 0) { box->type = BOX_INLINE_BLOCK; } else if (type && strcasecmp(type, "hidden") == 0) { /* no box for hidden inputs */ - box->style->display = CSS_DISPLAY_NONE; + box->type = BOX_NONE; } else if (type && (strcasecmp(type, "checkbox") == 0 || strcasecmp(type, "radio") == 0)) { } else if (type && (strcasecmp(type, "submit") == 0 || strcasecmp(type, "reset") == 0 || strcasecmp(type, "button") == 0)) { struct box *inline_container, *inline_box; - if (!box_button(n, content, box, 0, markup_track, author)) + + if (!box_button(n, content, box, 0)) goto no_memory; + inline_container = box_create(0, 0, 0, 0, 0, content); if (!inline_container) goto no_memory; + inline_container->type = BOX_INLINE_CONTAINER; + inline_box = box_create(box->style, 0, 0, box->title, 0, content); if (!inline_box) goto no_memory; + inline_box->type = BOX_TEXT; + if (box->gadget->value != NULL) inline_box->text = talloc_strdup(content, box->gadget->value); @@ -2191,15 +1754,20 @@ bool box_input(BOX_SPECIAL_PARAMS) messages_get("Form_Reset")); else inline_box->text = talloc_strdup(content, "Button"); + if (!inline_box->text) goto no_memory; + inline_box->length = strlen(inline_box->text); + box_add_child(inline_container, inline_box); + box_add_child(box, inline_container); } else if (type && strcasecmp(type, "image") == 0) { gadget->type = GADGET_IMAGE; - if (box->style && box->style->display != CSS_DISPLAY_NONE) { + if (box->style && css_computed_display(box->style, + n->parent == NULL) != CSS_DISPLAY_NONE) { if ((s = (char *) xmlGetProp(n, (const xmlChar*) "src"))) { res = url_join(s, @@ -2227,8 +1795,7 @@ bool box_input(BOX_SPECIAL_PARAMS) } } else { /* the default type is "text" */ - if (!box_input_text(n, content, box, 0, markup_track, author, - false)) + if (!box_input_text(n, content, box, 0, false)) goto no_memory; } @@ -2582,7 +2149,8 @@ bool box_embed(BOX_SPECIAL_PARAMS) xmlChar *src; xmlAttr *a; - if (box->style && box->style->display == CSS_DISPLAY_NONE) + if (box->style && css_computed_display(box->style, + n->parent == NULL) == CSS_DISPLAY_NONE) return true; params = talloc(content, struct object_params); @@ -2790,18 +2358,39 @@ struct frame_dimension *box_parse_multi_lengths(const char *s, return length; } - /** - * Parse an inline colour string + * Fetch an object from an interned URL + * + * \param c Current content + * \param url URL to fetch + * \param box Box containing object + * \param permitted_types Array of permitted types terminated by + * CONTENT_UNKNOWN, or NULL for all types + * \param available_width Estimate of width of object + * \param available_height Estimate of height of object + * \param background This object forms the box background + * \return true on success, false on memory exhaustion */ -static void parse_inline_colour(char *s, colour *variable) { - colour new_colour = CSS_COLOR_NONE; - if (s[0] == '#') { - if (strlen(s) == 7) - new_colour = hex_colour(s + 1, 6); - } else { - new_colour = named_colour(s); - } - if (new_colour != CSS_COLOR_NONE) - *variable = new_colour; +bool fetch_object_interned_url(struct content *c, lwc_string *url, + struct box *box, const content_type *permitted_types, + int available_width, int available_height, + bool background) +{ + char *url_buf; + bool ret = true; + + url_buf = malloc(lwc_string_length(url) + 1); + if (url_buf == NULL) + return false; + + memcpy(url_buf, lwc_string_data(url), lwc_string_length(url)); + url_buf[lwc_string_length(url)] = '\0'; + + ret = html_fetch_object(c, url_buf, box, permitted_types, + available_width, available_height, background); + + free(url_buf); + + return ret; } + diff --git a/render/box_normalise.c b/render/box_normalise.c index 865433e26..fc563e743 100644 --- a/render/box_normalise.c +++ b/render/box_normalise.c @@ -26,6 +26,7 @@ #include <assert.h> #include <stdbool.h> #include "css/css.h" +#include "css/select.h" #include "render/box.h" #include "render/table.h" #include "desktop/gui.h" @@ -35,29 +36,34 @@ #include "utils/talloc.h" +/** + * Row spanning information for a cell + */ struct span_info { + /** Number of rows this cell spans */ unsigned int row_span; + /** The cell in this column spans all rows until the end of the table */ bool auto_row; - bool auto_column; }; +/** + * Column record for a table + */ struct columns { + /** Current column index */ unsigned int current_column; - bool extra; - /* Number of columns in main part of table 1..max columns */ + /** Number of columns in main part of table 1..max columns */ unsigned int num_columns; - /* Information about columns in main table, - array 0 to num_columns - 1 */ + /** Information about columns in main table, array [0, num_columns) */ struct span_info *spans; - /* Number of columns that have cells after a colspan 0 */ - unsigned int extra_columns; - /* Number of rows in table */ + /** Number of rows in table */ unsigned int num_rows; }; static bool box_normalise_table(struct box *table, struct content *c); -static void box_normalise_table_spans(struct box *table); +static bool box_normalise_table_spans(struct box *table, + struct span_info *spans, struct content *c); static bool box_normalise_table_row_group(struct box *row_group, struct columns *col_info, struct content *c); @@ -69,6 +75,18 @@ static bool calculate_table_row(struct columns *col_info, unsigned int *start_column); static bool box_normalise_inline_container(struct box *cont, struct content *c); +/** + * Allocator + * + * \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 + */ +static void *myrealloc(void *ptr, size_t len, void *pw) +{ + return talloc_realloc_size(pw, ptr, len); +} /** * Ensure the box tree is correctly nested by adding and removing nodes. @@ -96,30 +114,34 @@ bool box_normalise_block(struct box *block, struct content *c) struct box *child; struct box *next_child; struct box *table; - struct css_style *style; + css_computed_style *style; + + assert(block != NULL); - assert(block != 0); LOG(("block %p, block->type %u", block, block->type)); + assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK || block->type == BOX_TABLE_CELL); + gui_multitask(); - for (child = block->children; child != 0; child = next_child) { + for (child = block->children; child != NULL; child = next_child) { LOG(("child %p, child->type = %d", child, child->type)); + next_child = child->next; /* child may be destroyed */ + switch (child->type) { case BOX_BLOCK: /* ok */ - if (!box_normalise_block(child, c)) + if (box_normalise_block(child, c) == false) return false; break; case BOX_INLINE_CONTAINER: - if (!box_normalise_inline_container(child, - c)) + if (box_normalise_inline_container(child, c) == false) return false; break; case BOX_TABLE: - if (!box_normalise_table(child, c)) + if (box_normalise_table(child, c) == false) return false; break; case BOX_INLINE: @@ -137,37 +159,48 @@ bool box_normalise_block(struct box *block, struct content *c) case BOX_TABLE_ROW: case BOX_TABLE_CELL: /* insert implied table */ - style = talloc_memdup(c, block->style, sizeof *style); - if (!style) + assert(block->style != NULL); + + style = nscss_get_blank_style(c, block->style, + myrealloc, c); + if (style == NULL) return false; - css_cascade(style, &css_blank_style, NULL); + table = box_create(style, block->href, block->target, - 0, 0, c); - if (!table) { - talloc_free(style); + NULL, NULL, c); + if (table == NULL) { + css_computed_style_destroy(style); return false; } table->type = BOX_TABLE; - if (child->prev == 0) + + if (child->prev == NULL) block->children = table; else child->prev->next = table; + table->prev = child->prev; - while (child != 0 && ( + + while (child != NULL && ( child->type == BOX_TABLE_ROW_GROUP || child->type == BOX_TABLE_ROW || child->type == BOX_TABLE_CELL)) { box_add_child(table, child); + next_child = child->next; - child->next = 0; + child->next = NULL; child = next_child; } - table->last->next = 0; + + table->last->next = NULL; table->next = next_child = child; - if (table->next) + if (table->next != NULL) table->next->prev = table; + else + block->last = table; table->parent = block; - if (!box_normalise_table(table, c)) + + if (box_normalise_table(table, c) == false) return false; break; default: @@ -184,30 +217,32 @@ bool box_normalise_table(struct box *table, struct content * c) struct box *child; struct box *next_child; struct box *row_group; - struct css_style *style; + css_computed_style *style; struct columns col_info; - assert(table != 0); + assert(table != NULL); assert(table->type == BOX_TABLE); + LOG(("table %p", table)); + col_info.num_columns = 1; col_info.current_column = 0; col_info.spans = malloc(2 * sizeof *col_info.spans); - if (!col_info.spans) + if (col_info.spans == NULL) return false; + col_info.spans[0].row_span = col_info.spans[1].row_span = 0; - col_info.spans[0].auto_row = col_info.spans[0].auto_column = - col_info.spans[1].auto_row = col_info.spans[1].auto_column = false; - col_info.num_rows = col_info.extra_columns = 0; - col_info.extra = false; + col_info.spans[0].auto_row = false; + col_info.spans[1].auto_row = false; + col_info.num_rows = 0; - for (child = table->children; child != 0; child = next_child) { + for (child = table->children; child != NULL; child = next_child) { next_child = child->next; switch (child->type) { case BOX_TABLE_ROW_GROUP: /* ok */ - if (!box_normalise_table_row_group(child, - &col_info, c)) { + if (box_normalise_table_row_group(child, + &col_info, c) == false) { free(col_info.spans); return false; } @@ -219,46 +254,56 @@ bool box_normalise_table(struct box *table, struct content * c) case BOX_TABLE_CELL: /* insert implied table row group */ assert(table->style != NULL); - style = talloc_memdup(c, table->style, sizeof *style); - if (!style) { + + style = nscss_get_blank_style(c, table->style, + myrealloc, c); + if (style == NULL) { free(col_info.spans); return false; } - css_cascade(style, &css_blank_style, NULL); + row_group = box_create(style, table->href, - table->target, 0, 0, c); - if (!row_group) { + table->target, NULL, NULL, c); + if (row_group == NULL) { + css_computed_style_destroy(style); free(col_info.spans); - talloc_free(style); return false; } + row_group->type = BOX_TABLE_ROW_GROUP; - if (child->prev == 0) + + if (child->prev == NULL) table->children = row_group; else child->prev->next = row_group; + row_group->prev = child->prev; - while (child != 0 && ( + + while (child != NULL && ( child->type == BOX_BLOCK || child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || child->type == BOX_TABLE_ROW || child->type == BOX_TABLE_CELL)) { box_add_child(row_group, child); + next_child = child->next; - child->next = 0; + child->next = NULL; child = next_child; } + assert(row_group->last != NULL); - row_group->last->next = 0; + + row_group->last->next = NULL; row_group->next = next_child = child; - if (row_group->next) + if (row_group->next != NULL) row_group->next->prev = row_group; else table->last = row_group; row_group->parent = table; - if (!box_normalise_table_row_group(row_group, - &col_info, c)) { + + if (box_normalise_table_row_group(row_group, + &col_info, c) == false) { free(col_info.spans); return false; } @@ -282,38 +327,43 @@ bool box_normalise_table(struct box *table, struct content * c) table->columns = col_info.num_columns; table->rows = col_info.num_rows; - free(col_info.spans); - if (table->children == 0) { + if (table->children == NULL) { struct box *row; LOG(("table->children == 0, creating implied row")); assert(table->style != NULL); - style = talloc_memdup(c, table->style, sizeof *style); - if (!style) { + + style = nscss_get_blank_style(c, table->style, myrealloc, c); + if (style == NULL) { + free(col_info.spans); return false; } - css_cascade(style, &css_blank_style, NULL); + row_group = box_create(style, table->href, - table->target, 0, 0, c); - if (!row_group) { - talloc_free(style); + table->target, NULL, NULL, c); + if (row_group == NULL) { + css_computed_style_destroy(style); + free(col_info.spans); return false; } row_group->type = BOX_TABLE_ROW_GROUP; - style = talloc_memdup(c, row_group->style, sizeof *style); - if (!style) { + style = nscss_get_blank_style(c, row_group->style, + myrealloc, c); + if (style == NULL) { box_free(row_group); + free(col_info.spans); return false; } - css_cascade(style, &css_blank_style, NULL); + row = box_create(style, row_group->href, - row_group->target, 0, 0, c); - if (!row) { - talloc_free(style); + row_group->target, NULL, NULL, c); + if (row == NULL) { + css_computed_style_destroy(style); box_free(row_group); + free(col_info.spans); return false; } row->type = BOX_TABLE_ROW; @@ -327,11 +377,15 @@ bool box_normalise_table(struct box *table, struct content * c) table->rows = 1; } - box_normalise_table_spans(table); - if (!table_calculate_column_types(table)) + if (box_normalise_table_spans(table, col_info.spans, c) == false) { + free(col_info.spans); + return false; + } + + free(col_info.spans); + + if (table_calculate_column_types(table) == false) return false; - if (table->style->border_collapse == CSS_BORDER_COLLAPSE_COLLAPSE) - table_collapse_borders(table); LOG(("table %p done", table)); @@ -339,62 +393,141 @@ bool box_normalise_table(struct box *table, struct content * c) } -void box_normalise_table_spans(struct box *table) +/** + * Normalise table cell column/row counts for colspan/rowspan = 0. + * Additionally, generate empty cells. + * + * \param table Table to process + * \param spans Array of length table->columns for use in empty cell detection + * \param c Content containing table + * \return True on success, false on memory exhaustion. + */ + +bool box_normalise_table_spans(struct box *table, struct span_info *spans, + struct content *c) { struct box *table_row_group; struct box *table_row; struct box *table_cell; - unsigned int last_column; - unsigned int max_extra = 0; - bool extra; - bool force = false; unsigned int rows_left = table->rows; + unsigned int col; + + /* Clear span data */ + memset(spans, 0, table->columns * sizeof(struct span_info)); - /* Scan table filling in table the width and height of table cells for - cells with colspan = 0 or rowspan = 0. Ignore the colspan and - rowspan of any cells that that follow an colspan = 0 */ + /* Scan table, filling in width and height of table cells with + * colspan = 0 and rowspan = 0. Also generate empty cells */ for (table_row_group = table->children; table_row_group != NULL; - table_row_group = table_row_group->next) { - for (table_row = table_row_group->children; NULL != table_row; + table_row_group = table_row_group->next) { + for (table_row = table_row_group->children; table_row != NULL; table_row = table_row->next){ - last_column = 0; - extra = false; - for (table_cell = table_row->children; NULL != table_cell; + for (table_cell = table_row->children; + table_cell != NULL; table_cell = table_cell->next) { - /* We hae reached the end of the row, and have passed - a cell with colspan = 0 so ignore col and row spans */ - if (force || extra || (table_cell->start_column + 1 <= - last_column)) { - extra = true; + /* colspan = 0 -> colspan = 1 */ + if (table_cell->columns == 0) table_cell->columns = 1; - table_cell->rows = 1; - if (table_cell->start_column <= max_extra) { - max_extra = table_cell->start_column + 1; + + /* rowspan = 0 -> rowspan = rows_left */ + if (table_cell->rows == 0) + table_cell->rows = rows_left; + + /* Record span information */ + for (col = table_cell->start_column; + col < table_cell->start_column + + table_cell->columns; col++) { + spans[col].row_span = table_cell->rows; + } + } + + /* Reduce span count of each column */ + for (col = 0; col < table->columns; col++) { + if (spans[col].row_span == 0) { + unsigned int start = col; + css_computed_style *style; + struct box *cell, *prev; + + /* If it's already zero, then we need + * to generate an empty cell for the + * gap in the row that spans as many + * columns as remain blank. + */ + assert(table_row->style != NULL); + + /* Find width of gap */ + while (col < table->columns && + spans[col].row_span == + 0) { + col++; } - table_cell->start_column += table->columns; - } else { - /* Fill out the number of columns or the number of rows - if necessary */ - if (0 == table_cell->columns) { - table_cell->columns = table->columns - - table_cell->start_column; - if ((0 == table_cell->start_column) && - (0 == table_cell->rows)) { - force = true; - } + + style = nscss_get_blank_style(c, + table_row->style, + myrealloc, c); + if (style == NULL) + return false; + + cell = box_create(style, + table_row->href, + table_row->target, + NULL, NULL, c); + if (cell == NULL) { + css_computed_style_destroy( + style); + return false; } - assert(0 != table_cell->columns); - if (0 == table_cell->rows) { - table_cell->rows = rows_left; + cell->type = BOX_TABLE_CELL; + + cell->rows = 1; + cell->columns = col - start; + cell->start_column = start; + + /* Find place to insert cell */ + for (prev = table_row->children; + prev != NULL; + prev = prev->next) { + if (prev->start_column + + prev->columns == + start) + break; + if (prev->next == NULL) + break; } - assert(0 != table_cell->rows); - last_column = table_cell->start_column + 1; + + /* Insert it */ + if (prev == NULL) { + if (table_row->children != NULL) + table_row->children-> + prev = cell; + else + table_row->last = cell; + + cell->next = + table_row->children; + table_row->children = cell; + } else { + if (prev->next != NULL) + prev->next->prev = cell; + else + table_row->last = cell; + + cell->next = prev->next; + prev->next = cell; + cell->prev = prev; + } + cell->parent = table_row; + } else { + spans[col].row_span--; } } + + assert(rows_left > 0); + rows_left--; } } - table->columns += max_extra; + + return true; } @@ -405,19 +538,21 @@ bool box_normalise_table_row_group(struct box *row_group, struct box *child; struct box *next_child; struct box *row; - struct css_style *style; + css_computed_style *style; assert(row_group != 0); assert(row_group->type == BOX_TABLE_ROW_GROUP); + LOG(("row_group %p", row_group)); - for (child = row_group->children; child != 0; child = next_child) { + for (child = row_group->children; child != NULL; child = next_child) { next_child = child->next; + switch (child->type) { case BOX_TABLE_ROW: /* ok */ - if (!box_normalise_table_row(child, col_info, - c)) + if (box_normalise_table_row(child, col_info, + c) == false) return false; break; case BOX_BLOCK: @@ -427,44 +562,52 @@ bool box_normalise_table_row_group(struct box *row_group, case BOX_TABLE_CELL: /* insert implied table row */ assert(row_group->style != NULL); - style = talloc_memdup(c, row_group->style, - sizeof *style); - if (!style) + + style = nscss_get_blank_style(c, row_group->style, + myrealloc, c); + if (style == NULL) return false; - css_cascade(style, &css_blank_style, NULL); + row = box_create(style, row_group->href, - row_group->target, 0, 0, c); - if (!row) { - talloc_free(style); + row_group->target, NULL, NULL, c); + if (row == NULL) { + css_computed_style_destroy(style); return false; } row->type = BOX_TABLE_ROW; - if (child->prev == 0) + + if (child->prev == NULL) row_group->children = row; else child->prev->next = row; + row->prev = child->prev; - while (child != 0 && ( + + while (child != NULL && ( child->type == BOX_BLOCK || child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || child->type == BOX_TABLE_ROW_GROUP || child->type == BOX_TABLE_CELL)) { box_add_child(row, child); + next_child = child->next; - child->next = 0; + child->next = NULL; child = next_child; } + assert(row->last != NULL); - row->last->next = 0; + + row->last->next = NULL; row->next = next_child = child; - if (row->next) + if (row->next != NULL) row->next->prev = row; else row_group->last = row; row->parent = row_group; - if (!box_normalise_table_row(row, col_info, - c)) + + if (box_normalise_table_row(row, col_info, + c) == false) return false; break; case BOX_INLINE: @@ -483,24 +626,30 @@ bool box_normalise_table_row_group(struct box *row_group, } } - if (row_group->children == 0) { + if (row_group->children == NULL) { LOG(("row_group->children == 0, inserting implied row")); + assert(row_group->style != NULL); - style = talloc_memdup(c, row_group->style, sizeof *style); - if (!style) { + + style = nscss_get_blank_style(c, row_group->style, + myrealloc, c); + if (style == NULL) { return false; } - css_cascade(style, &css_blank_style, NULL); + row = box_create(style, row_group->href, - row_group->target, 0, 0, c); - if (!row) { - talloc_free(style); + row_group->target, NULL, NULL, c); + if (row == NULL) { + css_computed_style_destroy(style); return false; } row->type = BOX_TABLE_ROW; row->parent = row_group; row_group->children = row_group->last = row; + + /* Keep table's row count in sync */ + col_info->num_rows++; } LOG(("row_group %p done", row_group)); @@ -516,19 +665,20 @@ bool box_normalise_table_row(struct box *row, struct box *child; struct box *next_child; struct box *cell = NULL; - struct css_style *style; + css_computed_style *style; unsigned int i; - assert(row != 0); + assert(row != NULL); assert(row->type == BOX_TABLE_ROW); LOG(("row %p", row)); - for (child = row->children; child != 0; child = next_child) { + for (child = row->children; child != NULL; child = next_child) { next_child = child->next; + switch (child->type) { case BOX_TABLE_CELL: /* ok */ - if (!box_normalise_block(child, c)) + if (box_normalise_block(child, c) == false) return false; cell = child; break; @@ -539,46 +689,51 @@ bool box_normalise_table_row(struct box *row, case BOX_TABLE_ROW: /* insert implied table cell */ assert(row->style != NULL); - style = talloc_memdup(c, row->style, sizeof *style); - if (!style) + + style = nscss_get_blank_style(c, row->style, + myrealloc, c); + if (style == NULL) return false; - css_cascade(style, &css_blank_style, NULL); - if (child->style && (child->style->position == - CSS_POSITION_ABSOLUTE || - child->style->position == - CSS_POSITION_FIXED)) { - style->position = child->style->position; - } - cell = box_create(style, row->href, row->target, 0, 0, - c); - if (!cell) { - talloc_free(style); + + cell = box_create(style, row->href, row->target, + NULL, NULL, c); + if (cell == NULL) { + css_computed_style_destroy(style); return false; } cell->type = BOX_TABLE_CELL; - if (child->prev == 0) + + if (child->prev == NULL) row->children = cell; else child->prev->next = cell; + cell->prev = child->prev; - while (child != 0 && ( + + while (child != NULL && ( child->type == BOX_BLOCK || child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || child->type == BOX_TABLE_ROW_GROUP || child->type == BOX_TABLE_ROW)) { box_add_child(cell, child); + next_child = child->next; - child->next = 0; + child->next = NULL; child = next_child; } + assert(cell->last != NULL); - cell->last->next = 0; + + cell->last->next = NULL; cell->next = next_child = child; - if (cell->next) + if (cell->next != NULL) cell->next->prev = cell; + else + row->last = cell; cell->parent = row; - if (!box_normalise_block(cell, c)) + + if (box_normalise_block(cell, c) == false) return false; break; case BOX_INLINE: @@ -596,42 +751,27 @@ bool box_normalise_table_row(struct box *row, assert(0); } - if (!calculate_table_row(col_info, cell->columns, cell->rows, - &cell->start_column)) + if (calculate_table_row(col_info, cell->columns, cell->rows, + &cell->start_column) == false) return false; } + + /* Update row spanning details for all columns */ for (i = 0; i < col_info->num_columns; i++) { - if ((col_info->spans[i].row_span != 0) && (!col_info->spans[i].auto_row)) { + if (col_info->spans[i].row_span != 0 && + col_info->spans[i].auto_row == false) { + /* This cell spans rows, and is not an auto row. + * Reduce number of rows left to span */ col_info->spans[i].row_span--; - if ((col_info->spans[i].auto_column) && (0 == col_info->spans[i].row_span)) { - col_info->spans[i].auto_column = false; - } } } + + /* Reset current column for next row */ col_info->current_column = 0; - col_info->extra = false; - - /* Removing empty rows causes ill effects for HTML such as: - * - * <tr><td colspan="2">1</td></tr><tr></tr><tr><td>2</td></tr> - * - * as it breaks the colspan value. Additionally, both MSIE and FF - * render in the same manner as NetSurf does with the empty row - * culling commented out. - */ -// if (row->children == 0) { -// LOG(("row->children == 0, removing")); -// if (row->prev == 0) -// row->parent->children = row->next; -// else -// row->prev->next = row->next; -// if (row->next != 0) -// row->next->prev = row->prev; -// box_free(row); -// } else { - col_info->num_rows++; -// } + + /* Increment row counter */ + col_info->num_rows++; LOG(("row %p done", row)); @@ -640,6 +780,13 @@ bool box_normalise_table_row(struct box *row, /** + * Compute the column index at which the current cell begins. + * Additionally, update the column record to reflect row spanning. + * + * \param col_info Column record + * \param col_span Number of columns that current cell spans + * \param row_span Number of rows that current cell spans + * \param start_column Pointer to location to receive column index * \return true on success, false on memory exhaustion */ @@ -647,70 +794,55 @@ bool calculate_table_row(struct columns *col_info, unsigned int col_span, unsigned int row_span, unsigned int *start_column) { - unsigned int cell_start_col; + unsigned int cell_start_col = col_info->current_column; unsigned int cell_end_col; unsigned int i; struct span_info *spans; - if (!col_info->extra) { - /* skip columns with cells spanning from above */ - while ((col_info->spans[col_info->current_column].row_span != 0) && - (!col_info->spans[col_info->current_column].auto_column)) { - col_info->current_column++; - } - if (col_info->spans[col_info->current_column].auto_column) { - col_info->extra = true; - col_info->current_column = 0; - } - } + /* Skip columns with cells spanning from above */ + while (col_info->spans[cell_start_col].row_span != 0) + cell_start_col++; + + /* Update current column with calculated start */ + col_info->current_column = cell_start_col; + + /* If this cell has a colspan of 0, then assume 1. + * No other browser supports colspan=0, anyway. */ + if (col_span == 0) + col_span = 1; + + cell_end_col = cell_start_col + col_span; + + if (col_info->num_columns < cell_end_col) { + /* It appears that this row has more columns than + * the maximum recorded for the table so far. + * Allocate more span records. */ + spans = realloc(col_info->spans, + sizeof *spans * (cell_end_col + 1)); + if (spans == NULL) + return false; - cell_start_col = col_info->current_column; - - /* If the current table cell follows a cell with colspan=0, - ignore both colspan and rowspan just assume it is a standard - size cell */ - if (col_info->extra) { - col_info->current_column++; - col_info->extra_columns = col_info->current_column; - } else { - /* If span to end of table, assume spaning single column - at the moment */ - cell_end_col = cell_start_col + ((0 == col_span) ? 1 : col_span); - - if (col_info->num_columns < cell_end_col) { - spans = realloc(col_info->spans, - sizeof *spans * (cell_end_col + 1)); - if (!spans) - return false; - col_info->spans = spans; - col_info->num_columns = cell_end_col; - - /* Mark new final column as sentinal */ - col_info->spans[cell_end_col].row_span = 0; - col_info->spans[cell_end_col].auto_row = - col_info->spans[cell_end_col].auto_column = - false; - } + col_info->spans = spans; + col_info->num_columns = cell_end_col; - if (0 == col_span) { - col_info->spans[cell_start_col].auto_column = true; - col_info->spans[cell_start_col].row_span = row_span; - col_info->spans[cell_start_col].auto_row = (0 == row_span); - } else { - for (i = cell_start_col; i < cell_end_col; i++) { - col_info->spans[i].row_span = (0 == row_span) ? - 1 : row_span; - col_info->spans[i].auto_row = (0 == row_span); - col_info->spans[i].auto_column = false; - } - } - if (0 == col_span) { - col_info->spans[cell_end_col].auto_column = true; - } - col_info->current_column = cell_end_col; + /* Mark new final column as sentinel */ + col_info->spans[cell_end_col].row_span = 0; + col_info->spans[cell_end_col].auto_row = false; + } + + /* This cell may span multiple columns. If it also wants to span + * multiple rows, temporarily assume it spans 1 row only. This will + * be fixed up in box_normalise_table_spans() */ + for (i = cell_start_col; i < cell_end_col; i++) { + col_info->spans[i].row_span = (row_span == 0) ? 1 : row_span; + col_info->spans[i].auto_row = (row_span == 0); } + /* Update current column with calculated end. */ + col_info->current_column = cell_end_col; + *start_column = cell_start_col; + return true; } @@ -720,11 +852,11 @@ bool box_normalise_inline_container(struct box *cont, struct content * c) struct box *child; struct box *next_child; - assert(cont != 0); + assert(cont != NULL); assert(cont->type == BOX_INLINE_CONTAINER); LOG(("cont %p", cont)); - for (child = cont->children; child != 0; child = next_child) { + for (child = cont->children; child != NULL; child = next_child) { next_child = child->next; switch (child->type) { case BOX_INLINE: @@ -735,37 +867,40 @@ bool box_normalise_inline_container(struct box *cont, struct content * c) break; case BOX_INLINE_BLOCK: /* ok */ - if (!box_normalise_block(child, c)) + if (box_normalise_block(child, c) == false) return false; break; case BOX_FLOAT_LEFT: case BOX_FLOAT_RIGHT: /* ok */ - assert(child->children != 0); + assert(child->children != NULL); + switch (child->children->type) { case BOX_BLOCK: - if (!box_normalise_block( - child->children, - c)) + if (box_normalise_block(child->children, + c) == false) return false; break; case BOX_TABLE: - if (!box_normalise_table( - child->children, - c)) + if (box_normalise_table(child->children, + c) == false) return false; break; default: assert(0); } - if (child->children == 0) { + + if (child->children == NULL) { /* the child has destroyed itself: remove float */ - if (child->prev == 0) + if (child->prev == NULL) child->parent->children = child->next; else child->prev->next = child->next; - if (child->next != 0) + if (child->next != NULL) child->next->prev = child->prev; + else + child->parent->last = child->prev; + box_free(child); } break; diff --git a/render/directory.c b/render/directory.c index 2c4a70dbd..82f24efa2 100644 --- a/render/directory.c +++ b/render/directory.c @@ -40,8 +40,9 @@ static const char header[] = "<html>\n<head>\n<title>\n"; static const char footer[] = "</pre>\n</body>\n</html>\n"; -bool directory_create(struct content *c, const char *params[]) { - if (!html_create(c, params)) +bool directory_create(struct content *c, struct content *parent, + const char *params[]) { + if (!html_create(c, parent, params)) /* html_create() must have broadcast MSG_ERROR already, so we * don't need to. */ return false; diff --git a/render/directory.h b/render/directory.h index 259f51b88..a54d516fb 100644 --- a/render/directory.h +++ b/render/directory.h @@ -29,7 +29,8 @@ #include "content/content_type.h" -bool directory_create(struct content *c, const char *params[]); +bool directory_create(struct content *c, struct content *parent, + const char *params[]); bool directory_convert(struct content *c, int width, int height); void directory_destroy(struct content *c); diff --git a/render/font.c b/render/font.c index 11135d959..4577ff373 100644 --- a/render/font.c +++ b/render/font.c @@ -17,13 +17,15 @@ */ #include "css/css.h" +#include "css/utils.h" +#include "desktop/options.h" #include "render/font.h" static plot_font_generic_family_t plot_font_generic_family( - css_font_family css); -static int plot_font_weight(css_font_weight css); -static plot_font_flags_t plot_font_flags(css_font_style style, - css_font_variant variant); + enum css_font_family css); +static int plot_font_weight(enum css_font_weight css); +static plot_font_flags_t plot_font_flags(enum css_font_style style, + enum css_font_variant variant); /** * Populate a font style using data from a computed CSS style @@ -31,15 +33,31 @@ static plot_font_flags_t plot_font_flags(css_font_style style, * \param css Computed style to consider * \param fstyle Font style to populate */ -void font_plot_style_from_css(const struct css_style *css, +void font_plot_style_from_css(const css_computed_style *css, plot_font_style_t *fstyle) { - fstyle->family = plot_font_generic_family(css->font_family); - fstyle->size = - css_len2pt(&css->font_size.value.length, css) * FONT_SIZE_SCALE; - fstyle->weight = plot_font_weight(css->font_weight); - fstyle->flags = plot_font_flags(css->font_style, css->font_variant); - fstyle->foreground = css->color; + lwc_string **families; + css_fixed length = 0; + css_unit unit = CSS_UNIT_PX; + css_color col; + + fstyle->family = plot_font_generic_family( + css_computed_font_family(css, &families)); + + css_computed_font_size(css, &length, &unit); + fstyle->size = FIXTOINT(FMULI(nscss_len2pt(length, unit), + FONT_SIZE_SCALE)); + + /* Clamp font size to configured minimum */ + if (fstyle->size < (option_font_min_size * FONT_SIZE_SCALE) / 10) + fstyle->size = (option_font_min_size * FONT_SIZE_SCALE) / 10; + + fstyle->weight = plot_font_weight(css_computed_font_weight(css)); + fstyle->flags = plot_font_flags(css_computed_font_style(css), + css_computed_font_variant(css)); + + css_computed_color(css, &col); + fstyle->foreground = nscss_color_to_ns(col); fstyle->background = 0; } @@ -54,7 +72,7 @@ void font_plot_style_from_css(const struct css_style *css, * \return Plot font family */ plot_font_generic_family_t plot_font_generic_family( - css_font_family css) + enum css_font_family css) { plot_font_generic_family_t plot; @@ -86,7 +104,7 @@ plot_font_generic_family_t plot_font_generic_family( * \param css CSS font weight * \return Plot weight */ -int plot_font_weight(css_font_weight css) +int plot_font_weight(enum css_font_weight css) { int weight; @@ -133,8 +151,8 @@ int plot_font_weight(css_font_weight css) * \param variant CSS font variant * \return Computed plot flags */ -plot_font_flags_t plot_font_flags(css_font_style style, - css_font_variant variant) +plot_font_flags_t plot_font_flags(enum css_font_style style, + enum css_font_variant variant) { plot_font_flags_t flags = FONTF_NONE; diff --git a/render/font.h b/render/font.h index 9a80af329..3012abce0 100644 --- a/render/font.h +++ b/render/font.h @@ -53,7 +53,7 @@ struct font_functions extern const struct font_functions nsfont; -void font_plot_style_from_css(const struct css_style *css, +void font_plot_style_from_css(const css_computed_style *css, plot_font_style_t *fstyle); #endif diff --git a/render/html.c b/render/html.c index 1905e8bc3..bad7ff2e2 100644 --- a/render/html.c +++ b/render/html.c @@ -61,7 +61,8 @@ static void html_convert_css_callback(content_msg msg, struct content *css, static bool html_meta_refresh(struct content *c, xmlNode *head); static bool html_head(struct content *c, xmlNode *head); static bool html_find_stylesheets(struct content *c, xmlNode *html); -static bool html_process_style_element(struct content *c, xmlNode *style); +static bool html_process_style_element(struct content *c, unsigned int index, + xmlNode *style); static void html_object_callback(content_msg msg, struct content *object, intptr_t p1, intptr_t p2, union content_msg_data data); static void html_object_done(struct box *box, struct content *object, @@ -92,6 +93,18 @@ static const char empty_document[] = "</body>" "</html>"; +/** + * Allocator + * + * \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 + */ +static void *myrealloc(void *ptr, size_t len, void *pw) +{ + return realloc(ptr, len); +} /** * Create a CONTENT_HTML. @@ -100,15 +113,19 @@ static const char empty_document[] = * created. */ -bool html_create(struct content *c, const char *params[]) +bool html_create(struct content *c, struct content *parent, + const char *params[]) { unsigned int i; struct content_html_data *html = &c->data.html; union content_msg_data msg_data; binding_error error; + lwc_context *dict; + lwc_error lerror; html->parser_binding = NULL; html->document = 0; + html->quirks = BINDING_QUIRKS_MODE_NONE; html->encoding = 0; html->base_url = c->url; html->base_target = NULL; @@ -116,8 +133,7 @@ bool html_create(struct content *c, const char *params[]) html->background_colour = NS_TRANSPARENT; html->stylesheet_count = 0; html->stylesheet_content = 0; - html->style = 0; - html->working_stylesheet = 0; + html->select_ctx = NULL; html->object_count = 0; html->object = 0; html->forms = 0; @@ -130,6 +146,14 @@ bool html_create(struct content *c, const char *params[]) html->box = 0; html->font_func = &nsfont; + lerror = lwc_create_context(myrealloc, c, &dict); + if (lerror != lwc_error_ok) { + error = BINDING_NOMEM; + goto error; + } + + html->dict = lwc_context_ref(dict); + for (i = 0; params[i]; i += 2) { if (strcasecmp(params[i], "charset") == 0) { html->encoding = talloc_strdup(c, params[i + 1]); @@ -343,7 +367,8 @@ bool html_convert(struct content *c, int width, int height) } c->data.html.document = - binding_get_document(c->data.html.parser_binding); + binding_get_document(c->data.html.parser_binding, + &c->data.html.quirks); /*xmlDebugDumpDocument(stderr, c->data.html.document);*/ if (!c->data.html.document) { @@ -467,7 +492,7 @@ bool html_convert(struct content *c, int width, int height) option_min_reflow_period : time_taken * 1.25)); LOG(("Scheduling relayout no sooner than %dcs", c->reformat_time - wallclock())); - /*box_dump(c->data.html.layout->children, 0);*/ + /*box_dump(stderr, c->data.html.layout->children, 0);*/ /* Destroy the parser binding */ binding_destroy_tree(c->data.html.parser_binding); @@ -778,16 +803,17 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) union content_msg_data msg_data; url_func_result res; struct content **stylesheet_content; + css_error error; /* stylesheet 0 is the base style sheet, - * stylesheet 1 is the adblocking stylesheet, - * stylesheet 2 is any <style> elements */ + * stylesheet 1 is the quirks mode style sheet, + * stylesheet 2 is the adblocking stylesheet */ c->data.html.stylesheet_content = talloc_array(c, struct content *, STYLESHEET_START); if (!c->data.html.stylesheet_content) goto no_memory; - c->data.html.stylesheet_content[STYLESHEET_ADBLOCK] = 0; - c->data.html.stylesheet_content[STYLESHEET_STYLE] = 0; + c->data.html.stylesheet_content[STYLESHEET_QUIRKS] = NULL; + c->data.html.stylesheet_content[STYLESHEET_ADBLOCK] = NULL; c->data.html.stylesheet_count = STYLESHEET_START; c->active = 0; @@ -805,6 +831,22 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) STYLESHEET_BASE, c->width, c->height, 0, 0, false, c); + if (c->data.html.quirks == BINDING_QUIRKS_MODE_FULL) { + c->data.html.stylesheet_content[STYLESHEET_QUIRKS] = + fetchcache(quirks_stylesheet_url, + html_convert_css_callback, (intptr_t) c, + STYLESHEET_QUIRKS, c->width, c->height, + true, 0, 0, false, false); + if (c->data.html.stylesheet_content[STYLESHEET_QUIRKS] == NULL) + goto no_memory; + c->active++; + fetchcache_go(c->data.html. + stylesheet_content[STYLESHEET_QUIRKS], + c->url, html_convert_css_callback, + (intptr_t) c, STYLESHEET_QUIRKS, c->width, + c->height, 0, 0, false, c); + } + if (option_block_ads) { c->data.html.stylesheet_content[STYLESHEET_ADBLOCK] = fetchcache(adblock_stylesheet_url, @@ -931,31 +973,14 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) i++; } else if (strcmp((const char *) node->name, "style") == 0) { - if (!html_process_style_element(c, node)) + if (!html_process_style_element(c, i, node)) return false; + i++; } } c->data.html.stylesheet_count = i; - if (c->data.html.stylesheet_content[STYLESHEET_STYLE] != 0) { - if (css_convert(c->data.html. - stylesheet_content[STYLESHEET_STYLE], c->width, - c->height)) { - if (!content_add_user(c->data.html. - stylesheet_content[STYLESHEET_STYLE], - html_convert_css_callback, - (intptr_t) c, STYLESHEET_STYLE)) { - /* no memory */ - c->data.html.stylesheet_content[STYLESHEET_STYLE] = 0; - goto no_memory; - } - } else { - /* conversion failed */ - c->data.html.stylesheet_content[STYLESHEET_STYLE] = 0; - } - } - /* complete the fetches */ while (c->active != 0) { if (c->active != last_active) { @@ -974,29 +999,23 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) return false; } - assert(c->data.html.stylesheet_content[STYLESHEET_BASE]); - css_set_origin(c->data.html.stylesheet_content[STYLESHEET_BASE], - CSS_ORIGIN_UA); - - /* any of our other stylesheet pointers could be NULL at this point if - * the CSS file(s) failed to load/fetch */ - if (c->data.html.stylesheet_content[STYLESHEET_ADBLOCK]) - css_set_origin(c->data.html.stylesheet_content[ - STYLESHEET_ADBLOCK], CSS_ORIGIN_UA); - if (c->data.html.stylesheet_content[STYLESHEET_STYLE]) - css_set_origin(c->data.html.stylesheet_content[ - STYLESHEET_STYLE], CSS_ORIGIN_AUTHOR); - for (i = STYLESHEET_START; i != c->data.html.stylesheet_count; i++) - if (c->data.html.stylesheet_content[i]) - css_set_origin(c->data.html.stylesheet_content[i], - CSS_ORIGIN_AUTHOR); - - c->data.html.working_stylesheet = css_make_working_stylesheet( - c->data.html.stylesheet_content, - c->data.html.stylesheet_count); - if (!c->data.html.working_stylesheet) + /* Create selection context */ + error = css_select_ctx_create(myrealloc, c, &c->data.html.select_ctx); + if (error != CSS_OK) goto no_memory; + /* Add sheets to it */ + for (i = STYLESHEET_BASE; i != c->data.html.stylesheet_count; i++) { + if (c->data.html.stylesheet_content[i]) { + error = css_select_ctx_append_sheet( + c->data.html.select_ctx, + c->data.html.stylesheet_content[i]-> + data.css.sheet); + if (error != CSS_OK) + goto no_memory; + } + } + return true; no_memory: @@ -1010,15 +1029,19 @@ no_memory: * Process an inline stylesheet in the document. * * \param c content structure + * \param index Index of stylesheet in stylesheet_content array * \param style xml node of style element * \return true on success, false if an error occurred */ -bool html_process_style_element(struct content *c, xmlNode *style) +bool html_process_style_element(struct content *c, unsigned int index, + xmlNode *style) { xmlNode *child; char *type, *media, *data; union content_msg_data msg_data; + struct content **stylesheet_content; + const char *params[] = { 0 }; /* type='text/css', or not present (invalid but common) */ if ((type = (char *) xmlGetProp(style, (const xmlChar *) "type"))) { @@ -1039,20 +1062,24 @@ bool html_process_style_element(struct content *c, xmlNode *style) xmlFree(media); } + /* Extend array */ + stylesheet_content = talloc_realloc(c, c->data.html.stylesheet_content, + struct content *, index + 1); + if (stylesheet_content == NULL) + goto no_memory; + + c->data.html.stylesheet_content = stylesheet_content; + /* create stylesheet */ - if (c->data.html.stylesheet_content[STYLESHEET_STYLE] == 0) { - const char *params[] = { 0 }; - c->data.html.stylesheet_content[STYLESHEET_STYLE] = - content_create(c->data.html.base_url); - if (!c->data.html.stylesheet_content[STYLESHEET_STYLE]) - goto no_memory; - if (!content_set_type(c->data.html. - stylesheet_content[STYLESHEET_STYLE], - CONTENT_CSS, "text/css", params)) - /** \todo not necessarily caused by - * memory exhaustion */ - goto no_memory; - } + c->data.html.stylesheet_content[index] = + content_create(c->data.html.base_url); + if (c->data.html.stylesheet_content[index] == NULL) + goto no_memory; + if (!content_set_type(c->data.html.stylesheet_content[index], + CONTENT_CSS, "text/css", params, c)) + /** \todo not necessarily caused by + * memory exhaustion */ + goto no_memory; /* can't just use xmlNodeGetContent(style), because that won't * give the content of comments which may be used to 'hide' @@ -1060,7 +1087,7 @@ bool html_process_style_element(struct content *c, xmlNode *style) for (child = style->children; child != 0; child = child->next) { data = (char *) xmlNodeGetContent(child); if (!content_process_data(c->data.html. - stylesheet_content[STYLESHEET_STYLE], + stylesheet_content[index], data, strlen(data))) { xmlFree(data); /** \todo not necessarily caused by @@ -1070,6 +1097,21 @@ bool html_process_style_element(struct content *c, xmlNode *style) xmlFree(data); } + /* Convert the content */ + if (nscss_convert(c->data.html.stylesheet_content[index], c->width, + c->height)) { + if (!content_add_user(c->data.html.stylesheet_content[index], + html_convert_css_callback, + (intptr_t) c, index)) { + /* no memory */ + c->data.html.stylesheet_content[index] = NULL; + goto no_memory; + } + } else { + /* conversion failed */ + c->data.html.stylesheet_content[index] = NULL; + } + return true; no_memory: @@ -1181,7 +1223,7 @@ void html_convert_css_callback(content_msg msg, struct content *css, * \return true on success, false on memory exhaustion */ -bool html_fetch_object(struct content *c, char *url, struct box *box, +bool html_fetch_object(struct content *c, const char *url, struct box *box, const content_type *permitted_types, int available_width, int available_height, bool background) @@ -1693,10 +1735,10 @@ void html_reformat(struct content *c, int width, int height) /* width and height are at least margin box of document */ c->width = layout->x + layout->padding[LEFT] + layout->width + - layout->padding[RIGHT] + layout->border[RIGHT] + + layout->padding[RIGHT] + layout->border[RIGHT].width + layout->margin[RIGHT]; c->height = layout->y + layout->padding[TOP] + layout->height + - layout->padding[BOTTOM] + layout->border[BOTTOM] + + layout->padding[BOTTOM] + layout->border[BOTTOM].width + layout->margin[BOTTOM]; /* if boxes overflow right or bottom edge, expand to contain it */ @@ -1757,6 +1799,12 @@ void html_destroy(struct content *c) c->data.html.iframe = NULL; } + /* Destroy selection context */ + if (c->data.html.select_ctx) { + css_select_ctx_destroy(c->data.html.select_ctx); + c->data.html.select_ctx = NULL; + } + /* Free stylesheets */ if (c->data.html.stylesheet_count) { for (i = 0; i != c->data.html.stylesheet_count; i++) { @@ -1768,11 +1816,6 @@ void html_destroy(struct content *c) } } - talloc_free(c->data.html.working_stylesheet); - - /*if (c->data.html.style) - css_free_style(c->data.html.style);*/ - /* Free objects */ for (i = 0; i != c->data.html.object_count; i++) { LOG(("object %i %p", i, c->data.html.object[i].content)); @@ -1784,6 +1827,8 @@ void html_destroy(struct content *c) c->data.html.object[i].content); } } + + lwc_context_unref(c->data.html.dict); } void html_destroy_frameset(struct content_html_frames *frameset) { diff --git a/render/html.h b/render/html.h index e6c322e14..b41406659 100644 --- a/render/html.h +++ b/render/html.h @@ -42,12 +42,13 @@ struct plotters; /* entries in stylesheet_content */ #define STYLESHEET_BASE 0 /* base style sheet */ -#define STYLESHEET_ADBLOCK 1 /* adblocking stylesheet */ -#define STYLESHEET_STYLE 2 /* <style> elements (not cached) */ +#define STYLESHEET_QUIRKS 1 /* quirks mode stylesheet */ +#define STYLESHEET_ADBLOCK 2 /* adblocking stylesheet */ #define STYLESHEET_START 3 /* start of document stylesheets */ extern char *default_stylesheet_url; extern char *adblock_stylesheet_url; +extern char *quirks_stylesheet_url; struct frame_dimension { float value; @@ -117,6 +118,9 @@ struct content_html_iframe { struct content_html_data { void *parser_binding; xmlDoc *document; + binding_quirks_mode quirks; /**< Quirkyness of document */ + + lwc_context *dict; /**< Internment context for this document */ char *encoding; /**< Encoding of source, 0 if unknown. */ binding_encoding_source encoding_source; @@ -133,9 +137,8 @@ struct content_html_data { unsigned int stylesheet_count; /** Stylesheets. Each may be 0. */ struct content **stylesheet_content; - struct css_style *style; /**< Base style. */ - /** Working stylesheet. */ - struct css_working_stylesheet *working_stylesheet; + /**< Style selection context */ + css_select_ctx *select_ctx; /** Number of entries in object. */ unsigned int object_count; @@ -168,12 +171,13 @@ struct content_html_data { extern bool html_redraw_debug; -bool html_create(struct content *c, const char *params[]); +bool html_create(struct content *c, struct content *parent, + const char *params[]); bool html_process_data(struct content *c, char *data, unsigned int size); bool html_convert(struct content *c, int width, int height); void html_reformat(struct content *c, int width, int height); void html_destroy(struct content *c); -bool html_fetch_object(struct content *c, char *url, struct box *box, +bool html_fetch_object(struct content *c, const char *url, struct box *box, const content_type *permitted_types, int available_width, int available_height, bool background); diff --git a/render/html_redraw.c b/render/html_redraw.c index 309b81332..735c241a3 100644 --- a/render/html_redraw.c +++ b/render/html_redraw.c @@ -32,6 +32,7 @@ #include "utils/config.h" #include "content/content.h" #include "css/css.h" +#include "css/utils.h" #include "desktop/gui.h" #include "desktop/plotters.h" #include "desktop/knockout.h" @@ -67,7 +68,7 @@ static bool html_redraw_borders(struct box *box, int x_parent, int y_parent, bool html_redraw_inline_borders(struct box *box, int x0, int y0, int x1, int y1, float scale, bool first, bool last); static bool html_redraw_border_plot(int i, int *p, colour c, - css_border_style style, int thickness); + enum css_border_style style, int thickness); static bool html_redraw_checkbox(int x, int y, int width, int height, bool selected); static bool html_redraw_radio(int x, int y, int width, int height, @@ -193,6 +194,7 @@ bool html_redraw_box(struct box *box, int x0, y0, x1, y1; int x_scrolled, y_scrolled; struct box *bg_box = NULL; + css_color bgcol = 0; if (html_redraw_printing && box->printed) return true; @@ -208,10 +210,10 @@ bool html_redraw_box(struct box *box, padding_width = padding_left + box->width + box->padding[RIGHT]; padding_height = padding_top + box->height + box->padding[BOTTOM]; - border_left = box->border[LEFT]; - border_top = box->border[TOP]; - border_right = box->border[RIGHT]; - border_bottom = box->border[BOTTOM]; + border_left = box->border[LEFT].width; + border_top = box->border[TOP].width; + border_right = box->border[RIGHT].width; + border_bottom = box->border[BOTTOM].width; } else { x = (x_parent + box->x) * scale; y = (y_parent + box->y) * scale; @@ -227,14 +229,15 @@ bool html_redraw_box(struct box *box, box->padding[RIGHT]) * scale; padding_height = (box->padding[TOP] + box->height + box->padding[BOTTOM]) * scale; - border_left = box->border[LEFT] * scale; - border_top = box->border[TOP] * scale; - border_right = box->border[RIGHT] * scale; - border_bottom = box->border[BOTTOM] * scale; + border_left = box->border[LEFT].width * scale; + border_top = box->border[TOP].width * scale; + border_right = box->border[RIGHT].width * scale; + border_bottom = box->border[BOTTOM].width * scale; } /* calculate rectangle covering this box and descendants */ - if (box->style && box->style->overflow != CSS_OVERFLOW_VISIBLE) { + if (box->style && css_computed_overflow(box->style) != + CSS_OVERFLOW_VISIBLE) { x0 = x - border_left; y0 = y - border_top; x1 = x + padding_width + border_right; @@ -296,7 +299,8 @@ bool html_redraw_box(struct box *box, } /* if visibility is hidden render children only */ - if (box->style && box->style->visibility == CSS_VISIBILITY_HIDDEN) { + if (box->style && css_computed_visibility(box->style) == + CSS_VISIBILITY_HIDDEN) { if ((plot.group_start) && (!plot.group_start("hidden box"))) return false; if (!html_redraw_box_children(box, x_parent, y_parent, @@ -346,20 +350,21 @@ bool html_redraw_box(struct box *box, */ if (!box->parent) { /* Root box */ - if (box->style && - (box->style->background_color != - NS_TRANSPARENT || + if (box->style && (css_computed_background_color(box->style, + &bgcol) != CSS_BACKGROUND_COLOR_TRANSPARENT || box->background)) { /* With its own background */ bg_box = box; } else if (!box->style || - (box->style->background_color == - NS_TRANSPARENT && + (css_computed_background_color(box->style, + &bgcol) == CSS_BACKGROUND_COLOR_TRANSPARENT && !box->background)) { /* Without its own background */ if (box->children && box->children->style && - (box->children->style-> - background_color != NS_TRANSPARENT || + (css_computed_background_color( + box->children->style, + &bgcol) != + CSS_BACKGROUND_COLOR_TRANSPARENT || box->children->background)) { /* But body has one, so use that */ bg_box = box->children; @@ -367,14 +372,14 @@ bool html_redraw_box(struct box *box, } } else if (box->parent && !box->parent->parent) { /* Body box */ - if (box->style && - (box->style->background_color != - NS_TRANSPARENT || + if (box->style && (css_computed_background_color(box->style, + &bgcol) != CSS_BACKGROUND_COLOR_TRANSPARENT || box->background)) { /* With a background */ if (box->parent->style && - (box->parent->style->background_color != - NS_TRANSPARENT || + (css_computed_background_color( + box->parent->style, &bgcol) != + CSS_BACKGROUND_COLOR_TRANSPARENT || box->parent->background)) { /* Root has own background; process normally */ bg_box = box; @@ -395,8 +400,9 @@ bool html_redraw_box(struct box *box, bg_box->type != BOX_TEXT && bg_box->type != BOX_INLINE_END && (bg_box->type != BOX_INLINE || bg_box->object) && - ((bg_box->style->background_color != NS_TRANSPARENT) || - (bg_box->background))) { + (css_computed_background_color(bg_box->style, + &bgcol) != CSS_BACKGROUND_COLOR_TRANSPARENT || + bg_box->background)) { /* find intersection of clip box and border edge */ int px0, py0, px1, py1; px0 = x - border_left < x0 ? x0 : x - border_left; @@ -451,7 +457,8 @@ bool html_redraw_box(struct box *box, /* backgrounds and borders for non-replaced inlines */ if (box->style && box->type == BOX_INLINE && box->inline_end && - (box->style->background_color != NS_TRANSPARENT || + (css_computed_background_color(box->style, &bgcol) != + CSS_BACKGROUND_COLOR_TRANSPARENT || box->background || border_top || border_right || border_bottom || border_left)) { /* inline backgrounds and borders span other boxes and may @@ -482,15 +489,15 @@ bool html_redraw_box(struct box *box, ib_y = y_parent + ib->y; ib_p_width = ib->padding[LEFT] + ib->width + ib->padding[RIGHT]; - ib_b_left = ib->border[LEFT]; - ib_b_right = ib->border[RIGHT]; + ib_b_left = ib->border[LEFT].width; + ib_b_right = ib->border[RIGHT].width; } else { ib_x = (x_parent + ib->x) * scale; ib_y = (y_parent + ib->y) * scale; ib_p_width = (ib->padding[LEFT] + ib->width + ib->padding[RIGHT]) * scale; - ib_b_left = ib->border[LEFT] * scale; - ib_b_right = ib->border[RIGHT] * scale; + ib_b_left = ib->border[LEFT].width * scale; + ib_b_right = ib->border[RIGHT].width * scale; } if (ib->inline_new_line && ib != box) { @@ -587,7 +594,8 @@ bool html_redraw_box(struct box *box, } /* clip to the padding edge for boxes with overflow hidden or scroll */ - if (box->style && box->style->overflow != CSS_OVERFLOW_VISIBLE) { + if (box->style && css_computed_overflow(box->style) != + CSS_OVERFLOW_VISIBLE) { x0 = x; y0 = y; x1 = x + padding_width; @@ -607,7 +615,7 @@ bool html_redraw_box(struct box *box, /* text decoration */ if (box->type != BOX_TEXT && box->style && - box->style->text_decoration != + css_computed_text_decoration(box->style) != CSS_TEXT_DECORATION_NONE) if (!html_redraw_text_decoration(box, x_parent, y_parent, scale, current_background_color)) @@ -665,8 +673,10 @@ bool html_redraw_box(struct box *box, /* scrollbars */ if (box->style && box->type != BOX_BR && box->type != BOX_TABLE && box->type != BOX_INLINE && - (box->style->overflow == CSS_OVERFLOW_SCROLL || - box->style->overflow == CSS_OVERFLOW_AUTO)) + (css_computed_overflow(box->style) == + CSS_OVERFLOW_SCROLL || + css_computed_overflow(box->style) == + CSS_OVERFLOW_AUTO)) if (!html_redraw_scrollbars(box, scale, x, y, padding_width, padding_height, current_background_color)) @@ -984,10 +994,10 @@ bool html_redraw_caret(struct caret *c, colour current_background_color, bool html_redraw_borders(struct box *box, int x_parent, int y_parent, int p_width, int p_height, float scale) { - int top = box->border[TOP]; - int right = box->border[RIGHT]; - int bottom = box->border[BOTTOM]; - int left = box->border[LEFT]; + int top = box->border[TOP].width; + int right = box->border[RIGHT].width; + int bottom = box->border[BOTTOM].width; + int left = box->border[LEFT].width; int x, y; unsigned int i; int p[20]; @@ -1017,12 +1027,19 @@ bool html_redraw_borders(struct box *box, int x_parent, int y_parent, p[18] = x - left; p[19] = y - top; for (i = 0; i != 4; i++) { - if (box->border[i] == 0) + colour col = 0; + + if (box->border[i].width == 0) continue; - if (!html_redraw_border_plot(i, p, - box->style->border[i].color, - box->style->border[i].style, - box->border[i] * scale)) + + if (box->border[i].color == CSS_BORDER_COLOR_TRANSPARENT) { + col = NS_TRANSPARENT; + } else { + col = nscss_color_to_ns(box->border[i].c); + } + + if (!html_redraw_border_plot(i, p, col, box->border[i].style, + box->border[i].width * scale)) return false; } @@ -1047,10 +1064,11 @@ bool html_redraw_borders(struct box *box, int x_parent, int y_parent, bool html_redraw_inline_borders(struct box *box, int x0, int y0, int x1, int y1, float scale, bool first, bool last) { - int top = box->border[TOP]; - int right = box->border[RIGHT]; - int bottom = box->border[BOTTOM]; - int left = box->border[LEFT]; + int top = box->border[TOP].width; + int right = box->border[RIGHT].width; + int bottom = box->border[BOTTOM].width; + int left = box->border[LEFT].width; + colour col; int p[20]; if (scale != 1.0) { @@ -1074,26 +1092,54 @@ bool html_redraw_inline_borders(struct box *box, int x0, int y0, int x1, int y1, assert(box->style); - if (box->border[LEFT] && first) - if (!html_redraw_border_plot(LEFT, p, - box->style->border[LEFT].color, - box->style->border[LEFT].style, left)) + if (box->border[LEFT].width && first) { + if (box->border[LEFT].color == CSS_BORDER_COLOR_TRANSPARENT) + col = NS_TRANSPARENT; + else + col = nscss_color_to_ns(box->border[LEFT].c); + + if (!html_redraw_border_plot(LEFT, p, col, + box->border[LEFT].style, + left)) return false; - if (box->border[TOP]) - if (!html_redraw_border_plot(TOP, p, - box->style->border[TOP].color, - box->style->border[TOP].style, top)) + } + + if (box->border[TOP].width) { + if (box->border[TOP].color == CSS_BORDER_COLOR_TRANSPARENT) + col = NS_TRANSPARENT; + else + col = nscss_color_to_ns(box->border[TOP].c); + + if (!html_redraw_border_plot(TOP, p, col, + box->border[TOP].style, + top)) return false; - if (box->border[BOTTOM]) - if (!html_redraw_border_plot(BOTTOM, p, - box->style->border[BOTTOM].color, - box->style->border[BOTTOM].style, bottom)) + } + + if (box->border[BOTTOM].width) { + if (box->border[BOTTOM].color == CSS_BORDER_COLOR_TRANSPARENT) + col = NS_TRANSPARENT; + else + col = nscss_color_to_ns(box->border[BOTTOM].c); + + if (!html_redraw_border_plot(BOTTOM, p, col, + box->border[BOTTOM].style, + bottom)) return false; - if (box->border[RIGHT] && last) - if (!html_redraw_border_plot(RIGHT, p, - box->style->border[RIGHT].color, - box->style->border[RIGHT].style, right)) + } + + if (box->border[RIGHT].width && last) { + if (box->border[RIGHT].color == CSS_BORDER_COLOR_TRANSPARENT) + col = NS_TRANSPARENT; + else + col = nscss_color_to_ns(box->border[RIGHT].c); + + if (!html_redraw_border_plot(RIGHT, p, col, + box->border[RIGHT].style, + right)) return false; + } + return true; } @@ -1128,7 +1174,7 @@ static plot_style_t plot_style_fillbdr_dlight = { */ bool html_redraw_border_plot(int i, int *p, colour c, - css_border_style style, int thickness) + enum css_border_style style, int thickness) { int z[8]; unsigned int light = i; @@ -1449,7 +1495,10 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, int px0 = clip_x0, py0 = clip_y0, px1 = clip_x1, py1 = clip_y1; int ox = x, oy = y; int width, height; + css_fixed hpos = 0, vpos = 0; + css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; struct box *parent; + css_color bgcol; plot_style_t pstyle_fill_bg = { .fill_type = PLOT_OP_TYPE_SOLID, .fill_colour = *background_colour, @@ -1479,7 +1528,7 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, box->padding[BOTTOM]; } /* handle background-repeat */ - switch (background->style->background_repeat) { + switch (css_computed_background_repeat(background->style)) { case CSS_BACKGROUND_REPEAT_REPEAT: repeat_x = repeat_y = true; /* optimisation: only plot the colour if @@ -1501,50 +1550,39 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, } /* handle background-position */ - switch (background->style->background_position.horz.pos) { - case CSS_BACKGROUND_POSITION_PERCENT: - x += (width - background->background->width) * - scale * - background->style->background_position. - horz.value.percent / 100; - break; - case CSS_BACKGROUND_POSITION_LENGTH: - x += (int) (css_len2px(&background->style-> - background_position.horz.value.length, - background->style) * scale); - break; - default: - break; + css_computed_background_position(background->style, + &hpos, &hunit, &vpos, &vunit); + if (hunit == CSS_UNIT_PCT) { + x += (width - background->background->width) * + scale * FIXTOFLT(hpos) / 100.; + } else { + x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit, + background->style)) * scale); } - switch (background->style->background_position.vert.pos) { - case CSS_BACKGROUND_POSITION_PERCENT: - y += (height - background->background->height) * - scale * - background->style->background_position. - vert.value.percent / 100; - break; - case CSS_BACKGROUND_POSITION_LENGTH: - y += (int) (css_len2px(&background->style-> - background_position.vert.value.length, - background->style) * scale); - break; - default: - break; + if (vunit == CSS_UNIT_PCT) { + y += (height - background->background->height) * + scale * FIXTOFLT(vpos) / 100.; + } else { + y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, + background->style)) * scale); } } /* special case for table rows as their background needs * to be clipped to all the cells */ if (box->type == BOX_TABLE_ROW) { + css_fixed h = 0, v = 0; + css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX; + for (parent = box->parent; ((parent) && (parent->type != BOX_TABLE)); parent = parent->parent); assert(parent && (parent->style)); - clip_to_children = - (parent->style->border_spacing.horz.value > 0) || - (parent->style->border_spacing.vert.value > 0); + css_computed_border_spacing(parent->style, &h, &hu, &v, &vu); + + clip_to_children = (h > 0) || (v > 0); if (clip_to_children) clip_box = box->children; @@ -1572,8 +1610,9 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, /* <td> attributes override <tr> */ if ((clip_x0 >= clip_x1) || (clip_y0 >= clip_y1) || - (clip_box->style->background_color != - NS_TRANSPARENT) || + (css_computed_background_color( + clip_box->style, &bgcol) != + CSS_BACKGROUND_COLOR_TRANSPARENT) || (clip_box->background && clip_box->background->bitmap && bitmap_get_opaque( @@ -1582,11 +1621,10 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, } /* plot the background colour */ - if (background->style->background_color != NS_TRANSPARENT) { - *background_colour = - background->style->background_color; - pstyle_fill_bg.fill_colour = - background->style->background_color; + if (css_computed_background_color(background->style, &bgcol) != + CSS_BACKGROUND_COLOR_TRANSPARENT) { + *background_colour = nscss_color_to_ns(bgcol); + pstyle_fill_bg.fill_colour = *background_colour; if (plot_colour) if (!plot.rectangle(clip_x0, clip_y0, clip_x1, clip_y1, @@ -1670,6 +1708,9 @@ bool html_redraw_inline_background(int x, int y, struct box *box, float scale, bool repeat_y = false; bool plot_colour = true; bool plot_content; + css_fixed hpos = 0, vpos = 0; + css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; + css_color bgcol; plot_style_t pstyle_fill_bg = { .fill_type = PLOT_OP_TYPE_SOLID, .fill_colour = *background_colour, @@ -1682,7 +1723,7 @@ bool html_redraw_inline_background(int x, int y, struct box *box, float scale, if (plot_content) { /* handle background-repeat */ - switch (box->style->background_repeat) { + switch (css_computed_background_repeat(box->style)) { case CSS_BACKGROUND_REPEAT_REPEAT: repeat_x = repeat_y = true; /* optimisation: only plot the colour if @@ -1704,57 +1745,35 @@ bool html_redraw_inline_background(int x, int y, struct box *box, float scale, } /* handle background-position */ - switch (box->style->background_position.horz.pos) { - case CSS_BACKGROUND_POSITION_PERCENT: - x += (px1 - px0 - - box->background->width * scale) * - box->style->background_position. - horz.value.percent / 100; - - if (!repeat_x && - ((box->style-> - background_position. - horz.value.percent < 2 && - !first) || - (box->style-> - background_position. - horz.value.percent > 98 && - !last))) { - plot_content = false; - } - break; - case CSS_BACKGROUND_POSITION_LENGTH: - x += (int) (css_len2px(&box->style-> - background_position.horz.value.length, - box->style) * scale); - break; - default: - break; + css_computed_background_position(box->style, + &hpos, &hunit, &vpos, &vunit); + if (hunit == CSS_UNIT_PCT) { + x += (px1 - px0 - box->background->width * scale) * + FIXTOFLT(hpos) / 100.; + + if (!repeat_x && ((hpos < 2 && !first) || + (hpos > 98 && !last))){ + plot_content = false; + } + } else { + x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit, + box->style)) * scale); } - switch (box->style->background_position.vert.pos) { - case CSS_BACKGROUND_POSITION_PERCENT: - y += (py1 - py0 - - box->background->height * scale) * - box->style->background_position. - vert.value.percent / 100; - break; - case CSS_BACKGROUND_POSITION_LENGTH: - y += (int) (css_len2px(&box->style-> - background_position.vert.value.length, - box->style) * scale); - break; - default: - break; + if (vunit == CSS_UNIT_PCT) { + y += (py1 - py0 - box->background->height * scale) * + FIXTOFLT(vpos) / 100.; + } else { + y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, + box->style)) * scale); } } /* plot the background colour */ - if (box->style->background_color != NS_TRANSPARENT) { - *background_colour = - box->style->background_color; - pstyle_fill_bg.fill_colour = - box->style->background_color; + if (css_computed_background_color(box->style, &bgcol) != + CSS_BACKGROUND_COLOR_TRANSPARENT) { + *background_colour = nscss_color_to_ns(bgcol); + pstyle_fill_bg.fill_colour = *background_colour; if (plot_colour) if (!plot.rectangle(clip_x0, clip_y0, @@ -1820,36 +1839,40 @@ bool html_redraw_text_decoration(struct box *box, int x_parent, int y_parent, float scale, colour background_colour) { - static const css_text_decoration decoration[] = { + static const enum css_text_decoration decoration[] = { CSS_TEXT_DECORATION_UNDERLINE, CSS_TEXT_DECORATION_OVERLINE, CSS_TEXT_DECORATION_LINE_THROUGH }; static const float line_ratio[] = { 0.9, 0.1, 0.5 }; - int colour; + colour fgcol; unsigned int i; + css_color col; + + css_computed_color(box->style, &col); + fgcol = nscss_color_to_ns(col); /* antialias colour for under/overline */ - if (html_redraw_printing) - colour = box->style->color; - else - colour = blend_colour(background_colour, box->style->color); + if (html_redraw_printing == false) + fgcol = blend_colour(background_colour, fgcol); if (box->type == BOX_INLINE) { if (!box->inline_end) return true; for (i = 0; i != NOF_ELEMENTS(decoration); i++) - if (box->style->text_decoration & decoration[i]) + if (css_computed_text_decoration(box->style) & + decoration[i]) if (!html_redraw_text_decoration_inline(box, x_parent, y_parent, scale, - colour, line_ratio[i])) + fgcol, line_ratio[i])) return false; } else { for (i = 0; i != NOF_ELEMENTS(decoration); i++) - if (box->style->text_decoration & decoration[i]) + if (css_computed_text_decoration(box->style) & + decoration[i]) if (!html_redraw_text_decoration_block(box, x_parent + box->x, y_parent + box->y, scale, - colour, line_ratio[i])) + fgcol, line_ratio[i])) return false; } @@ -2179,9 +2202,9 @@ bool html_redraw_scrollbars(struct box *box, float scale, bool box_vscrollbar_present(const struct box * const box) { - return box->descendant_y0 < -box->border[TOP] || + return box->descendant_y0 < -box->border[TOP].width || box->padding[TOP] + box->height + box->padding[BOTTOM] + - box->border[BOTTOM] < box->descendant_y1; + box->border[BOTTOM].width < box->descendant_y1; } @@ -2194,9 +2217,9 @@ bool box_vscrollbar_present(const struct box * const box) bool box_hscrollbar_present(const struct box * const box) { - return box->descendant_x0 < -box->border[LEFT] || + return box->descendant_x0 < -box->border[LEFT].width || box->padding[LEFT] + box->width + box->padding[RIGHT] + - box->border[RIGHT] < box->descendant_x1; + box->border[RIGHT].width < box->descendant_x1; } diff --git a/render/hubbub_binding.c b/render/hubbub_binding.c index 279dd9933..1d8af25ca 100644 --- a/render/hubbub_binding.c +++ b/render/hubbub_binding.c @@ -41,6 +41,8 @@ typedef struct hubbub_ctx { htmlDocPtr document; bool owns_doc; + binding_quirks_mode quirks; + const char *encoding; binding_encoding_source encoding_source; @@ -147,6 +149,7 @@ binding_error binding_create_tree(void *arena, const char *charset, void **ctx) : ENCODING_SOURCE_DETECTED; c->document = NULL; c->owns_doc = true; + c->quirks = BINDING_QUIRKS_MODE_NONE; c->forms = NULL; error = hubbub_parser_create(charset, true, myrealloc, arena, @@ -239,13 +242,15 @@ const char *binding_get_encoding(void *ctx, binding_encoding_source *source) return c->encoding != NULL ? c->encoding : "Windows-1252"; } -xmlDocPtr binding_get_document(void *ctx) +xmlDocPtr binding_get_document(void *ctx, binding_quirks_mode *quirks) { hubbub_ctx *c = (hubbub_ctx *) ctx; xmlDocPtr doc = c->document; c->owns_doc = false; + *quirks = c->quirks; + return doc; } @@ -744,6 +749,20 @@ hubbub_error add_attributes(void *ctx, void *node, hubbub_error set_quirks_mode(void *ctx, hubbub_quirks_mode mode) { + hubbub_ctx *c = (hubbub_ctx *) ctx; + + switch (mode) { + case HUBBUB_QUIRKS_MODE_NONE: + c->quirks = BINDING_QUIRKS_MODE_NONE; + break; + case HUBBUB_QUIRKS_MODE_LIMITED: + c->quirks = BINDING_QUIRKS_MODE_LIMITED; + break; + case HUBBUB_QUIRKS_MODE_FULL: + c->quirks = BINDING_QUIRKS_MODE_FULL; + break; + } + return HUBBUB_OK; } diff --git a/render/layout.c b/render/layout.c index 4d9adc468..411d6755e 100644 --- a/render/layout.c +++ b/render/layout.c @@ -40,6 +40,7 @@ #include <string.h> #include <math.h> #include "css/css.h" +#include "css/utils.h" #include "content/content.h" #include "desktop/gui.h" #include "desktop/options.h" @@ -47,6 +48,7 @@ #include "render/font.h" #include "render/form.h" #include "render/layout.h" +#include "render/table.h" #define NDEBUG #include "utils/log.h" #undef NDEBUG @@ -69,36 +71,36 @@ static bool layout_apply_minmax_height(struct box *box, struct box *container); static void layout_block_add_scrollbar(struct box *box, int which); static int layout_solve_width(int available_width, int width, int lm, int rm, int max_width, int min_width, - int margin[4], int padding[4], int border[4]); + int margin[4], int padding[4], struct box_border border[4]); static void layout_float_find_dimensions(int available_width, - struct css_style *style, struct box *box); + const css_computed_style *style, struct box *box); static void layout_find_dimensions(int available_width, int viewport_height, - struct box *box, struct css_style *style, + struct box *box, const css_computed_style *style, int *width, int *height, int *max_width, int *min_width, - int margin[4], int padding[4], int border[4]); + int margin[4], int padding[4], struct box_border border[4]); static void layout_tweak_form_dimensions(struct box *box, bool percentage, int available_width, bool setwidth, int *dimension); -static int layout_clear(struct box *fl, css_clear clear); +static int layout_clear(struct box *fl, enum css_clear clear); static void find_sides(struct box *fl, int y0, int y1, int *x0, int *x1, struct box **left, struct box **right); static void layout_minmax_inline_container(struct box *inline_container, const struct font_functions *font_func); -static int line_height(struct css_style *style); +static int line_height(const css_computed_style *style); static bool layout_line(struct box *first, int *width, int *y, int cx, int cy, struct box *cont, bool indent, bool has_text_children, struct content *content, struct box **next_box); static struct box *layout_minmax_line(struct box *first, int *min, int *max, const struct font_functions *font_func); -static int layout_text_indent(struct css_style *style, int width); +static int layout_text_indent(const css_computed_style *style, int width); static bool layout_float(struct box *b, int width, struct content *content); static void place_float_below(struct box *c, int width, int cx, int y, struct box *cont); static bool layout_table(struct box *box, int available_width, struct content *content); static void layout_move_children(struct box *box, int x, int y); -static void calculate_mbp_width(struct css_style *style, unsigned int side, - bool margin, bool border, bool padding, +static void calculate_mbp_width(const css_computed_style *style, + unsigned int side, bool margin, bool border, bool padding, int *fixed, float *frac); static void layout_lists(struct box *box, const struct font_functions *font_func); @@ -137,11 +139,11 @@ bool layout_document(struct content *content, int width, int height) layout_minmax_block(doc, font_func); layout_block_find_dimensions(width, height, 0, 0, doc); - doc->x = doc->margin[LEFT] + doc->border[LEFT]; - doc->y = doc->margin[TOP] + doc->border[TOP]; - width -= doc->margin[LEFT] + doc->border[LEFT] + doc->padding[LEFT] + - doc->padding[RIGHT] + doc->border[RIGHT] + - doc->margin[RIGHT]; + doc->x = doc->margin[LEFT] + doc->border[LEFT].width; + doc->y = doc->margin[TOP] + doc->border[TOP].width; + width -= doc->margin[LEFT] + doc->border[LEFT].width + + doc->padding[LEFT] + doc->padding[RIGHT] + + doc->border[RIGHT].width + doc->margin[RIGHT]; if (width < 0) width = 0; doc->width = width; @@ -150,18 +152,19 @@ bool layout_document(struct content *content, int width, int height) /* make <html> and <body> fill available height */ if (doc->y + doc->padding[TOP] + doc->height + doc->padding[BOTTOM] + - doc->border[BOTTOM] + doc->margin[BOTTOM] < + doc->border[BOTTOM].width + doc->margin[BOTTOM] < height) { doc->height = height - (doc->y + doc->padding[TOP] + - doc->padding[BOTTOM] + doc->border[BOTTOM] + + doc->padding[BOTTOM] + + doc->border[BOTTOM].width + doc->margin[BOTTOM]); if (doc->children) doc->children->height = doc->height - (doc->children->margin[TOP] + - doc->children->border[TOP] + + doc->children->border[TOP].width + doc->children->padding[TOP] + doc->children->padding[BOTTOM] + - doc->children->border[BOTTOM] + + doc->children->border[BOTTOM].width + doc->children->margin[BOTTOM]); } @@ -197,7 +200,8 @@ bool layout_block_context(struct box *block, int viewport_height, int y = 0; int lm, rm; struct box *margin_box; - struct css_length gadget_size; /* Checkbox / radio buttons */ + css_fixed gadget_size; + css_unit gadget_unit; /* Checkbox / radio buttons */ assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK || @@ -267,10 +271,11 @@ bool layout_block_context(struct box *block, int viewport_height, block->gadget->type == GADGET_CHECKBOX)) { /* form checkbox or radio button * if width or height is AUTO, set it to 1em */ - gadget_size.unit = CSS_UNIT_EM; - gadget_size.value = 1; + gadget_unit = CSS_UNIT_EM; + gadget_size = INTTOFIX(1); if (block->height == AUTO) - block->height = css_len2px(&gadget_size, block->style); + block->height = FIXTOINT(nscss_len2px(gadget_size, + gadget_unit, block->style)); } box = margin_box = block->children; @@ -317,8 +322,10 @@ bool layout_block_context(struct box *block, int viewport_height, */ if (box->style && - (box->style->position == CSS_POSITION_ABSOLUTE|| - box->style->position == CSS_POSITION_FIXED)) { + (css_computed_position(box->style) == + CSS_POSITION_ABSOLUTE || + css_computed_position(box->style) == + CSS_POSITION_FIXED)) { box->x = box->parent->padding[LEFT]; /* absolute positioned; this element will establish * its own block context when it gets laid out later, @@ -328,9 +335,10 @@ bool layout_block_context(struct box *block, int viewport_height, /* Clearance. */ y = 0; - if (box->style && box->style->clear != CSS_CLEAR_NONE) + if (box->style && css_computed_clear(box->style) != + CSS_CLEAR_NONE) y = layout_clear(block->float_children, - box->style->clear); + css_computed_clear(box->style)); /* Get top margin */ if (box->style) { @@ -351,7 +359,7 @@ bool layout_block_context(struct box *block, int viewport_height, if (box->type == BOX_BLOCK || box->object) { if (!box->object && box->style && - box->style->overflow != + css_computed_overflow(box->style) != CSS_OVERFLOW_VISIBLE) { /* box establishes new block formatting context * so available width may be diminished due to @@ -381,7 +389,13 @@ bool layout_block_context(struct box *block, int viewport_height, layout_block_add_scrollbar(box, BOTTOM); } } else if (box->type == BOX_TABLE) { - if (box->style->width.width == CSS_WIDTH_AUTO) { + enum css_width wtype; + css_fixed width = 0; + css_unit unit = CSS_UNIT_PX; + + wtype = css_computed_width(box->style, &width, &unit); + + if (wtype == CSS_WIDTH_AUTO) { /* max available width may be diminished due to * floats. */ int x0, x1, top; @@ -412,18 +426,18 @@ bool layout_block_context(struct box *block, int viewport_height, /* Position box: horizontal. */ box->x = box->parent->padding[LEFT] + box->margin[LEFT] + - box->border[LEFT]; + box->border[LEFT].width; cx += box->x; /* Position box: vertical. */ if (box->type != BOX_BLOCK || y || - box->border[TOP] || box->padding[TOP]) { + box->border[TOP].width || box->padding[TOP]) { margin_box->y += max_pos_margin - max_neg_margin; cy += max_pos_margin - max_neg_margin; max_pos_margin = max_neg_margin = 0; margin_box = 0; - box->y += box->border[TOP]; - cy += box->border[TOP]; + box->y += box->border[TOP].width; + cy += box->border[TOP].width; if (cy < y) { box->y += y - cy; cy = y; @@ -433,7 +447,8 @@ bool layout_block_context(struct box *block, int viewport_height, /* Unless the box has an overflow style of visible, the box * establishes a new block context. */ if (box->type == BOX_BLOCK && box->style && - box->style->overflow != CSS_OVERFLOW_VISIBLE) { + css_computed_overflow(box->style) != + CSS_OVERFLOW_VISIBLE) { cy += max_pos_margin - max_neg_margin; box->y += max_pos_margin - max_neg_margin; @@ -449,7 +464,7 @@ bool layout_block_context(struct box *block, int viewport_height, cx -= box->x; cy += box->height + box->padding[BOTTOM] + - box->border[BOTTOM]; + box->border[BOTTOM].width; max_pos_margin = max_neg_margin = 0; if (max_pos_margin < box->margin[BOTTOM]) max_pos_margin = box->margin[BOTTOM]; @@ -457,7 +472,7 @@ bool layout_block_context(struct box *block, int viewport_height, max_neg_margin = -box->margin[BOTTOM]; y = box->y + box->padding[TOP] + box->height + box->padding[BOTTOM] + - box->border[BOTTOM]; + box->border[BOTTOM].width; /* Skip children, because they are done in the new * block context */ goto advance_to_next_box; @@ -480,12 +495,19 @@ bool layout_block_context(struct box *block, int viewport_height, struct box *left, *right; y = cy; while (1) { + enum css_width wtype; + css_fixed width = 0; + css_unit unit = CSS_UNIT_PX; + + wtype = css_computed_width(box->style, + &width, &unit); + x0 = cx; x1 = cx + box->parent->width; find_sides(block->float_children, y, y + box->height, &x0, &x1, &left, &right); - if (box->style->width.width == CSS_WIDTH_AUTO) + if (wtype == CSS_WIDTH_AUTO) break; if (box->width <= x1 - x0) break; @@ -527,7 +549,8 @@ bool layout_block_context(struct box *block, int viewport_height, layout_block_add_scrollbar(box, BOTTOM); } - cy += box->height + box->padding[BOTTOM] + box->border[BOTTOM]; + cy += box->height + box->padding[BOTTOM] + + box->border[BOTTOM].width; max_pos_margin = max_neg_margin = 0; if (max_pos_margin < box->margin[BOTTOM]) max_pos_margin = box->margin[BOTTOM]; @@ -535,7 +558,8 @@ bool layout_block_context(struct box *block, int viewport_height, max_neg_margin = -box->margin[BOTTOM]; cx -= box->x; y = box->y + box->padding[TOP] + box->height + - box->padding[BOTTOM] + box->border[BOTTOM]; + box->padding[BOTTOM] + + box->border[BOTTOM].width; advance_to_next_box: if (!box->next) { /* No more siblings: @@ -556,7 +580,8 @@ bool layout_block_context(struct box *block, int viewport_height, /* Apply any min-height and max-height to * boxes in normal flow */ - if (box->style && box->style->position != + if (box->style && + css_computed_position(box->style) != CSS_POSITION_ABSOLUTE && layout_apply_minmax_height(box, NULL)) { @@ -567,7 +592,7 @@ bool layout_block_context(struct box *block, int viewport_height, } cy += box->padding[BOTTOM] + - box->border[BOTTOM]; + box->border[BOTTOM].width; if (max_pos_margin < box->margin[BOTTOM]) max_pos_margin = box->margin[BOTTOM]; else if (max_neg_margin < -box->margin[BOTTOM]) @@ -575,7 +600,7 @@ bool layout_block_context(struct box *block, int viewport_height, cx -= box->x; y = box->y + box->padding[TOP] + box->height + box->padding[BOTTOM] + - box->border[BOTTOM]; + box->border[BOTTOM].width; } while (box != block && !box->next); if (box == block) break; @@ -589,7 +614,7 @@ bool layout_block_context(struct box *block, int viewport_height, /* Increase height to contain any floats inside (CSS 2.1 10.6.7). */ for (box = block->float_children; box; box = box->next_float) { y = box->y + box->height + box->padding[BOTTOM] + - box->border[BOTTOM] + box->margin[BOTTOM]; + box->border[BOTTOM].width + box->margin[BOTTOM]; if (cy < y) cy = y; } @@ -600,7 +625,8 @@ bool layout_block_context(struct box *block, int viewport_height, layout_block_add_scrollbar(block, BOTTOM); } - if (block->style && block->style->position != CSS_POSITION_ABSOLUTE) { + if (block->style && css_computed_position(block->style) != + CSS_POSITION_ABSOLUTE) { /* Block is in normal flow */ layout_apply_minmax_height(block, NULL); } @@ -624,12 +650,9 @@ void layout_minmax_block(struct box *block, int min = 0, max = 0; int extra_fixed = 0; float extra_frac = 0; - struct css_length size; - struct css_length gadget_size; /* Checkbox / radio buttons */ - size.unit = CSS_UNIT_EM; - size.value = 10; - gadget_size.unit = CSS_UNIT_EM; - gadget_size.value = 1; + enum css_width wtype; + css_fixed width = 0; + css_unit wunit = CSS_UNIT_PX; assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK || @@ -639,22 +662,28 @@ void layout_minmax_block(struct box *block, if (block->max_width != UNKNOWN_MAX_WIDTH) return; + wtype = css_computed_width(block->style, &width, &wunit); + if (block->gadget && (block->gadget->type == GADGET_TEXTBOX || block->gadget->type == GADGET_PASSWORD || block->gadget->type == GADGET_FILE || block->gadget->type == GADGET_TEXTAREA) && - block->style && - block->style->width.width == CSS_WIDTH_AUTO) { - min = max = css_len2px(&size, block->style); + block->style && wtype == CSS_WIDTH_AUTO) { + css_fixed size = INTTOFIX(10); + css_unit unit = CSS_UNIT_EM; + + min = max = FIXTOINT(nscss_len2px(size, unit, block->style)); } if (block->gadget && (block->gadget->type == GADGET_RADIO || block->gadget->type == GADGET_CHECKBOX) && - block->style && - block->style->width.width == CSS_WIDTH_AUTO) { + block->style && wtype == CSS_WIDTH_AUTO) { + css_fixed size = INTTOFIX(1); + css_unit unit = CSS_UNIT_EM; + /* form checkbox or radio button * if width is AUTO, set it to 1em */ - min = max = css_len2px(&gadget_size, block->style); + min = max = FIXTOINT(nscss_len2px(size, unit, block->style)); } if (block->object) { @@ -686,9 +715,9 @@ void layout_minmax_block(struct box *block, assert(child->max_width != UNKNOWN_MAX_WIDTH); if (child->style && - (child->style->position == + (css_computed_position(child->style) == CSS_POSITION_ABSOLUTE || - child->style->position == + css_computed_position(child->style) == CSS_POSITION_FIXED)) { /* This child is positioned out of normal flow, * so it will have no affect on width */ @@ -708,18 +737,16 @@ void layout_minmax_block(struct box *block, } /* fixed width takes priority */ - if (block->type != BOX_TABLE_CELL && - block->style->width.width == CSS_WIDTH_LENGTH) { - min = max = css_len2px(&block->style->width.value.length, - block->style); + if (block->type != BOX_TABLE_CELL && wtype == CSS_WIDTH_SET && + wunit != CSS_UNIT_PCT) { + min = max = FIXTOINT(nscss_len2px(width, wunit, block->style)); } /* add margins, border, padding to min, max widths */ - if (block->gadget && (block->style->width.width == CSS_WIDTH_PERCENT || - (block->style->width.width == CSS_WIDTH_LENGTH && + if (block->gadget && wtype == CSS_WIDTH_SET && (block->gadget->type == GADGET_SUBMIT || block->gadget->type == GADGET_RESET || - block->gadget->type == GADGET_BUTTON)))) { + block->gadget->type == GADGET_BUTTON)) { /* some gadgets with specified width already include border and * padding, so just get margin */ calculate_mbp_width(block->style, LEFT, true, false, false, @@ -799,8 +826,8 @@ void layout_block_find_dimensions(int available_width, int viewport_height, int height; int *margin = box->margin; int *padding = box->padding; - int *border = box->border; - struct css_style *style = box->style; + struct box_border *border = box->border; + const css_computed_style *style = box->style; layout_find_dimensions(available_width, viewport_height, box, style, &width, &height, &max_width, &min_width, @@ -854,13 +881,13 @@ bool layout_apply_minmax_height(struct box *box, struct box *container) bool updated = false; /* Find containing block for percentage heights */ - if (box->style->position == CSS_POSITION_ABSOLUTE) { + if (css_computed_position(box->style) == CSS_POSITION_ABSOLUTE) { /* Box is absolutely positioned */ assert(container); containing_block = container; } else if (box->float_container && - (box->style->float_ == CSS_FLOAT_LEFT || - box->style->float_ == CSS_FLOAT_RIGHT)) { + (css_computed_float(box->style) == CSS_FLOAT_LEFT || + css_computed_float(box->style) == CSS_FLOAT_RIGHT)) { /* Box is a float */ assert(box->parent && box->parent->parent && box->parent->parent->parent); @@ -875,72 +902,73 @@ bool layout_apply_minmax_height(struct box *box, struct box *container) } if (box->style) { + enum css_height htype = CSS_HEIGHT_AUTO; + css_fixed length = 0; + css_unit unit = CSS_UNIT_PX; + + if (containing_block) { + htype = css_computed_height(containing_block->style, + &length, &unit); + } + /* max-height */ - switch (box->style->max_height.max_height) { - case CSS_MAX_HEIGHT_LENGTH: - h = css_len2px(&box->style->max_height.value.length, - box->style); - if (h < box->height) { - box->height = h; - updated = true; - } - break; - case CSS_MAX_HEIGHT_PERCENT: - if (containing_block && + if (css_computed_max_height(box->style, &length, &unit) == + CSS_MAX_HEIGHT_SET) { + if (unit == CSS_UNIT_PCT) { + if (containing_block && containing_block->height != AUTO && - (box->style->position == + (css_computed_position(box->style) == CSS_POSITION_ABSOLUTE || - (containing_block->style->height. - height == CSS_HEIGHT_LENGTH || - containing_block->style->height. - height == CSS_HEIGHT_PERCENT))) { - /* Box is absolutely positioned or its - * containing block has a valid specified - * height. (CSS 2.1 Section 10.5) */ - h = box->style->max_height.value.percent * + htype == CSS_HEIGHT_SET)) { + /* Box is absolutely positioned or its + * containing block has a valid + * specified height. (CSS 2.1 + * Section 10.5) */ + h = FIXTOFLT(length) * containing_block->height / 100; + if (h < box->height) { + box->height = h; + updated = true; + } + } + } else { + h = FIXTOINT(nscss_len2px(length, unit, + box->style)); if (h < box->height) { box->height = h; updated = true; } } - break; - default: - break; } /* min-height */ - switch (box->style->min_height.min_height) { - case CSS_MIN_HEIGHT_LENGTH: - h = css_len2px(&box->style->min_height.value.length, - box->style); - if (h > box->height) { - box->height = h; - updated = true; - } - break; - case CSS_MIN_HEIGHT_PERCENT: - if (containing_block && + if (css_computed_min_height(box->style, &length, &unit) == + CSS_MIN_HEIGHT_SET) { + if (unit == CSS_UNIT_PCT) { + if (containing_block && containing_block->height != AUTO && - (box->style->position == + (css_computed_position(box->style) == CSS_POSITION_ABSOLUTE || - (containing_block->style->height. - height == CSS_HEIGHT_LENGTH || - containing_block->style->height. - height == CSS_HEIGHT_PERCENT))) { - /* Box is absolutely positioned or its - * containing block has a valid specified - * height. (CSS 2.1 Section 10.5) */ - h = box->style->min_height.value.percent * + htype == CSS_HEIGHT_SET)) { + /* Box is absolutely positioned or its + * containing block has a valid + * specified height. (CSS 2.1 + * Section 10.5) */ + h = FIXTOFLT(length) * containing_block->height / 100; + if (h > box->height) { + box->height = h; + updated = true; + } + } + } else { + h = FIXTOINT(nscss_len2px(length, unit, + box->style)); if (h > box->height) { box->height = h; updated = true; } } - break; - default: - break; } } return updated; @@ -955,18 +983,24 @@ bool layout_apply_minmax_height(struct box *box, struct box *container) void layout_block_add_scrollbar(struct box *box, int which) { + enum css_overflow overflow; + assert(box->type == BOX_BLOCK && (which == RIGHT || which == BOTTOM)); - if (box->style && (box->style->overflow == CSS_OVERFLOW_SCROLL || - box->style->overflow == CSS_OVERFLOW_AUTO)) { + if (box->style == NULL) + return; + + overflow = css_computed_overflow(box->style); + + if (overflow == CSS_OVERFLOW_SCROLL || overflow == CSS_OVERFLOW_AUTO) { /* make space for scrollbars, unless height/width are AUTO */ if (which == BOTTOM && box->height != AUTO && - (box->style->overflow == CSS_OVERFLOW_SCROLL || + (overflow == CSS_OVERFLOW_SCROLL || box_hscrollbar_present(box))) { box->padding[BOTTOM] += SCROLLBAR_WIDTH; } if (which == RIGHT && box->width != AUTO && - (box->style->overflow == CSS_OVERFLOW_SCROLL || + (overflow == CSS_OVERFLOW_SCROLL || box_vscrollbar_present(box))) { box->width -= SCROLLBAR_WIDTH; box->padding[RIGHT] += SCROLLBAR_WIDTH; @@ -987,16 +1021,14 @@ void layout_block_add_scrollbar(struct box *box, int which) * \param min_width Box min-width ( <=0 means no min-width to apply) * \param margin[4] Current box margins. Updated with new box * left / right margins - * \param padding[4] Current box paddings. Updated with new box - * left / right paddings - * \param border[4] Current box border widths. Updated with new - * box left / right border widths + * \param padding[4] Current box paddings. + * \param border[4] Current box border widths. * \return New box width */ int layout_solve_width(int available_width, int width, int lm, int rm, int max_width, int min_width, - int margin[4], int padding[4], int border[4]) + int margin[4], int padding[4], struct box_border border[4]) { bool auto_width = false; @@ -1013,8 +1045,9 @@ int layout_solve_width(int available_width, int width, int lm, int rm, if (margin[RIGHT] == AUTO) margin[RIGHT] = rm; width = available_width - - (margin[LEFT] + border[LEFT] + padding[LEFT] + - padding[RIGHT] + border[RIGHT] + margin[RIGHT]); + (margin[LEFT] + border[LEFT].width + + padding[LEFT] + padding[RIGHT] + + border[RIGHT].width + margin[RIGHT]); width = width < 0 ? 0 : width; auto_width = true; } @@ -1032,8 +1065,8 @@ int layout_solve_width(int available_width, int width, int lm, int rm, if (!auto_width && margin[LEFT] == AUTO && margin[RIGHT] == AUTO) { /* make the margins equal, centering the element */ margin[LEFT] = margin[RIGHT] = (available_width - lm - rm - - (border[LEFT] + padding[LEFT] + width + - padding[RIGHT] + border[RIGHT])) / 2; + (border[LEFT].width + padding[LEFT] + width + + padding[RIGHT] + border[RIGHT].width)) / 2; if (margin[LEFT] < 0) { margin[RIGHT] += margin[LEFT]; @@ -1044,14 +1077,16 @@ int layout_solve_width(int available_width, int width, int lm, int rm, } else if (!auto_width && margin[LEFT] == AUTO) { margin[LEFT] = available_width - lm - - (border[LEFT] + padding[LEFT] + width + - padding[RIGHT] + border[RIGHT] + margin[RIGHT]); + (border[LEFT].width + padding[LEFT] + width + + padding[RIGHT] + border[RIGHT].width + + margin[RIGHT]); margin[LEFT] = margin[LEFT] < lm ? lm : margin[LEFT]; } else if (!auto_width) { /* margin-right auto or "over-constrained" */ margin[RIGHT] = available_width - rm - - (margin[LEFT] + border[LEFT] + padding[LEFT] + - width + padding[RIGHT] + border[RIGHT]); + (margin[LEFT] + border[LEFT].width + + padding[LEFT] + width + padding[RIGHT] + + border[RIGHT].width); } return width; @@ -1070,14 +1105,15 @@ int layout_solve_width(int available_width, int width, int lm, int rm, */ void layout_float_find_dimensions(int available_width, - struct css_style *style, struct box *box) + const css_computed_style *style, struct box *box) { int width, height, max_width, min_width; int *margin = box->margin; int *padding = box->padding; - int *border = box->border; - int scrollbar_width = (style->overflow == CSS_OVERFLOW_SCROLL || - style->overflow == CSS_OVERFLOW_AUTO) ? + struct box_border *border = box->border; + int scrollbar_width = + (css_computed_overflow(style) == CSS_OVERFLOW_SCROLL || + css_computed_overflow(style) == CSS_OVERFLOW_AUTO) ? SCROLLBAR_WIDTH : 0; layout_find_dimensions(available_width, -1, box, style, &width, &height, @@ -1107,44 +1143,49 @@ void layout_float_find_dimensions(int available_width, box->gadget->type == GADGET_PASSWORD || box->gadget->type == GADGET_FILE || box->gadget->type == GADGET_TEXTAREA)) { - struct css_length size; + css_fixed size = 0; + css_unit unit = CSS_UNIT_EM; + /* Give sensible dimensions to gadgets, with auto width/height, * that don't shrink to fit contained text. */ assert(box->style); - size.unit = CSS_UNIT_EM; if (box->gadget->type == GADGET_TEXTBOX || box->gadget->type == GADGET_PASSWORD || box->gadget->type == GADGET_FILE) { if (width == AUTO) { - size.value = 10; - width = css_len2px(&size, box->style); + size = INTTOFIX(10); + width = FIXTOINT(nscss_len2px(size, unit, + box->style)); } if (box->gadget->type == GADGET_FILE && height == AUTO) { - size.value = 1.5; - height = css_len2px(&size, box->style); + size = FLTTOFIX(1.5); + height = FIXTOINT(nscss_len2px(size, unit, + box->style)); } } if (box->gadget->type == GADGET_TEXTAREA) { if (width == AUTO) { - size.value = 10; - width = css_len2px(&size, box->style); + size = INTTOFIX(10); + width = FIXTOINT(nscss_len2px(size, unit, + box->style)); } else { width -= scrollbar_width; } if (height == AUTO) { - size.value = 4; - height = css_len2px(&size, box->style); + size = INTTOFIX(4); + height = FIXTOINT(nscss_len2px(size, unit, + box->style)); } } } else if (width == AUTO) { /* CSS 2.1 section 10.3.5 */ width = min(max(box->min_width, available_width), box->max_width); - width -= box->margin[LEFT] + box->border[LEFT] + + width -= box->margin[LEFT] + box->border[LEFT].width + box->padding[LEFT] + box->padding[RIGHT] + - box->border[RIGHT] + box->margin[RIGHT]; + box->border[RIGHT].width + box->margin[RIGHT]; if (max_width >= 0 && width > max_width) width = max_width; if (min_width > 0 && width < min_width) width = min_width; @@ -1183,33 +1224,37 @@ void layout_float_find_dimensions(int available_width, */ void layout_find_dimensions(int available_width, int viewport_height, - struct box *box, struct css_style *style, + struct box *box, const css_computed_style *style, int *width, int *height, int *max_width, int *min_width, - int margin[4], int padding[4], int border[4]) + int margin[4], int padding[4], struct box_border border[4]) { struct box *containing_block = NULL; unsigned int i; bool percentage; if (width) { - switch (style->width.width) { - case CSS_WIDTH_LENGTH: - *width = css_len2px(&style->width.value.length, style); - break; - case CSS_WIDTH_PERCENT: - *width = (style->width.value.percent * - available_width) / 100; - break; - case CSS_WIDTH_AUTO: - default: + enum css_width wtype; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + wtype = css_computed_width(style, &value, &unit); + + if (wtype == CSS_WIDTH_SET) { + if (unit == CSS_UNIT_PCT) { |