4 * @todo TODO: A way to disable CSS completely, PLUS a way to stop
5 * various property groups from taking effect. (Ie. way to turn out
6 * effect of 'display: none' or aligning or colors but keeping all the
18 #include "document/css/apply.h"
19 #include "document/css/css.h"
20 #include "document/css/parser.h"
21 #include "document/css/property.h"
22 #include "document/css/scanner.h"
23 #include "document/css/stylesheet.h"
24 #include "document/format.h"
25 #include "document/html/parser/parse.h"
26 #include "document/options.h"
27 #include "util/align.h"
28 #include "util/color.h"
29 #include "util/lists.h"
30 #include "util/error.h"
31 #include "util/memory.h"
32 #include "util/string.h"
34 /* XXX: Some strange dependency makes it necessary to this include last. */
35 #include "document/html/internal.h"
38 typedef void (*css_applier_T
)(struct html_context
*html_context
,
39 struct html_element
*element
,
40 struct css_property
*prop
);
43 css_apply_color(struct html_context
*html_context
, struct html_element
*element
,
44 struct css_property
*prop
)
46 assert(prop
->value_type
== CSS_VT_COLOR
);
48 if (use_document_fg_colors(html_context
->options
))
49 element
->attr
.style
.color
.foreground
= prop
->value
.color
;
53 css_apply_background_color(struct html_context
*html_context
,
54 struct html_element
*element
,
55 struct css_property
*prop
)
57 assert(prop
->value_type
== CSS_VT_COLOR
);
59 if (use_document_bg_colors(html_context
->options
))
60 element
->attr
.style
.color
.background
= prop
->value
.color
;
64 css_apply_display(struct html_context
*html_context
, struct html_element
*element
,
65 struct css_property
*prop
)
67 assert(prop
->value_type
== CSS_VT_DISPLAY
);
69 switch (prop
->value
.display
) {
71 element
->linebreak
= 0;
74 /* 1 or 2, that is the question. I went for 2 since it
75 * gives a more "blocky" feel and it's more common.
77 element
->linebreak
= 2;
80 if (!html_context
->options
->css_ignore_display_none
)
81 element
->invisible
= 2;
84 INTERNAL("Bad prop->value.display %d", prop
->value
.display
);
90 css_apply_font_attribute(struct html_context
*html_context
,
91 struct html_element
*element
, struct css_property
*prop
)
93 assert(prop
->value_type
== CSS_VT_FONT_ATTRIBUTE
);
94 element
->attr
.style
.attr
|= prop
->value
.font_attribute
.add
;
95 element
->attr
.style
.attr
&= ~prop
->value
.font_attribute
.rem
;
99 css_apply_list_style(struct html_context
*html_context
,
100 struct html_element
*element
, struct css_property
*prop
)
102 assert(prop
->value_type
== CSS_VT_LIST_STYLE
);
104 element
->parattr
.list_number
= (prop
->value
.list_style
> CSS_LIST_ORDINAL
);
105 switch (prop
->value
.list_style
) {
106 case CSS_LIST_NONE
: element
->parattr
.flags
= P_NO_BULLET
; break;
107 case CSS_LIST_DISC
: element
->parattr
.flags
= P_O
; break;
108 case CSS_LIST_CIRCLE
: element
->parattr
.flags
= P_O
; break;
109 case CSS_LIST_SQUARE
: element
->parattr
.flags
= P_PLUS
; break;
110 case CSS_LIST_DECIMAL
: element
->parattr
.flags
= P_NUMBER
; break;
111 case CSS_LIST_DECIMAL_LEADING_ZERO
: element
->parattr
.flags
= P_NUMBER
; break;
112 case CSS_LIST_LOWER_ROMAN
: element
->parattr
.flags
= P_roman
; break;
113 case CSS_LIST_UPPER_ROMAN
: element
->parattr
.flags
= P_ROMAN
; break;
114 case CSS_LIST_LOWER_ALPHA
: element
->parattr
.flags
= P_alpha
; break;
115 case CSS_LIST_UPPER_ALPHA
: element
->parattr
.flags
= P_ALPHA
; break;
116 case CSS_LIST_LOWER_GREEK
: element
->parattr
.flags
= P_roman
; break;
117 case CSS_LIST_LOWER_LATIN
: element
->parattr
.flags
= P_alpha
; break;
118 case CSS_LIST_UPPER_LATIN
: element
->parattr
.flags
= P_ALPHA
; break;
119 case CSS_LIST_HEBREW
: element
->parattr
.flags
= P_NUMBER
; break;
120 case CSS_LIST_ARMENIAN
: element
->parattr
.flags
= P_NUMBER
; break;
121 case CSS_LIST_GEORGIAN
: element
->parattr
.flags
= P_NUMBER
; break;
122 case CSS_LIST_CJK_IDEOGRAPHIC
: element
->parattr
.flags
= P_NUMBER
; break;
123 case CSS_LIST_HIRAGANA
: element
->parattr
.flags
= P_NUMBER
; break;
124 case CSS_LIST_KATAKANA
: element
->parattr
.flags
= P_NUMBER
; break;
125 case CSS_LIST_HIRAGANA_IROHA
: element
->parattr
.flags
= P_NUMBER
; break;
126 case CSS_LIST_KATAKANA_IROHA
: element
->parattr
.flags
= P_NUMBER
; break;
127 case CSS_LIST_ORDINAL
: assert(0); break;
131 /** @bug FIXME: Because the current CSS doesn't provide reasonable
132 * defaults for each HTML element this applier will cause bad
133 * rendering of @<pre> tags. */
135 css_apply_text_align(struct html_context
*html_context
,
136 struct html_element
*element
, struct css_property
*prop
)
138 assert(prop
->value_type
== CSS_VT_TEXT_ALIGN
);
139 element
->parattr
.align
= prop
->value
.text_align
;
142 /*! XXX: Sort like the css_property_type */
143 static const css_applier_T css_appliers
[CSS_PT_LAST
] = {
144 /* CSS_PT_NONE */ NULL
,
145 /* CSS_PT_BACKGROUND */ css_apply_background_color
,
146 /* CSS_PT_BACKGROUND_COLOR */ css_apply_background_color
,
147 /* CSS_PT_COLOR */ css_apply_color
,
148 /* CSS_PT_DISPLAY */ css_apply_display
,
149 /* CSS_PT_FONT_STYLE */ css_apply_font_attribute
,
150 /* CSS_PT_FONT_WEIGHT */ css_apply_font_attribute
,
151 /* CSS_PT_LIST_STYLE */ css_apply_list_style
,
152 /* CSS_PT_TEXT_ALIGN */ css_apply_text_align
,
153 /* CSS_PT_TEXT_DECORATION */ css_apply_font_attribute
,
154 /* CSS_PT_WHITE_SPACE */ css_apply_font_attribute
,
157 /** This looks for a match in list of selectors. */
159 examine_element(struct html_context
*html_context
, struct css_selector
*base
,
160 enum css_selector_type seltype
, enum css_selector_relation rel
,
161 struct css_selector_set
*selectors
,
162 struct html_element
*element
)
164 struct css_selector
*selector
;
167 /* Cannot use list_empty() inside the arglist of DBG() because
168 * GCC 4.1 "warning: operation on `errfile' may be undefined"
169 * breaks the build with -Werror. */
170 int dbg_has_leaves
, dbg_has_properties
;
172 DBG("examine_element(%p, %s, %d, %d, %p, %.*s);", html_context
, base
->name
, seltype
, rel
, selectors
, element
->namelen
, element
->name
);
173 #define dbginfo(sel, type_, base) \
174 dbg_has_leaves = !css_selector_set_empty(&sel->leaves), \
175 dbg_has_properties = !list_empty(sel->properties), \
176 DBG("Matched selector %s (rel %d type %d [m%d])! Children %p !!%d, props !!%d", sel->name, sel->relation, sel->type, sel->type == type_, &sel->leaves, dbg_has_leaves, dbg_has_properties)
178 #define dbginfo(sel, type, base)
181 #define process_found_selector(sel, type, base) \
183 dbginfo(sel, type, base); \
184 merge_css_selectors(base, sel); \
185 /* Ancestor matches? */ \
186 if (sel->leaves.may_contain_rel_ancestor_or_parent \
187 && (LIST_OF(struct html_element) *) element->next \
188 != &html_context->stack) { \
189 struct html_element *ancestor; \
190 /* This is less effective than doing reverse iterations,
191 * first over sel->leaves and then over the HTML stack,
192 * which shines in the most common case where there are
193 * no CSR_ANCESTOR selector leaves. However we would
194 * have to duplicate the whole examine_element(), so if
195 * profiles won't show it really costs... */ \
196 for (ancestor = element->next; \
197 (LIST_OF(struct html_element) *) ancestor \
198 != &html_context->stack;\
199 ancestor = ancestor->next) \
200 examine_element(html_context, base, \
201 CST_ELEMENT, CSR_ANCESTOR, \
202 &sel->leaves, ancestor); \
203 examine_element(html_context, base, \
204 CST_ELEMENT, CSR_PARENT, \
205 &sel->leaves, element->next); \
207 /* More specific matches? */ \
208 examine_element(html_context, base, type + 1, \
210 &sel->leaves, element); \
213 if (seltype
<= CST_ELEMENT
&& element
->namelen
) {
214 selector
= find_css_selector(selectors
, CST_ELEMENT
, rel
,
216 process_found_selector(selector
, CST_ELEMENT
, base
);
218 selector
= find_css_selector(selectors
, CST_ELEMENT
, rel
,
219 element
->name
, element
->namelen
);
220 process_found_selector(selector
, CST_ELEMENT
, base
);
223 if (!element
->options
)
226 /* TODO: More pseudo-classess. --pasky */
227 if (element
->pseudo_class
& ELEMENT_LINK
) {
228 selector
= find_css_selector(selectors
, CST_PSEUDO
, rel
, "link", -1);
229 process_found_selector(selector
, CST_PSEUDO
, base
);
231 if (element
->pseudo_class
& ELEMENT_VISITED
) {
232 selector
= find_css_selector(selectors
, CST_PSEUDO
, rel
, "visited", -1);
233 process_found_selector(selector
, CST_PSEUDO
, base
);
236 if (element
->attr
.class && seltype
<= CST_CLASS
) {
237 const unsigned char *class = element
->attr
.class;
240 const unsigned char *begin
;
242 while (*class == ' ') ++class;
243 if (*class == '\0') break;
245 while (*class != ' ' && *class != '\0') ++class;
247 selector
= find_css_selector(selectors
, CST_CLASS
, rel
,
248 begin
, class - begin
);
249 process_found_selector(selector
, CST_CLASS
, base
);
253 if (element
->attr
.id
&& seltype
<= CST_ID
) {
254 selector
= find_css_selector(selectors
, CST_ID
, rel
, element
->attr
.id
, -1);
255 process_found_selector(selector
, CST_ID
, base
);
258 #undef process_found_selector
262 struct css_selector
*
263 get_css_selector_for_element(struct html_context
*html_context
,
264 struct html_element
*element
,
265 struct css_stylesheet
*css
,
266 LIST_OF(struct html_element
) *html_stack
)
269 struct css_selector
*selector
;
271 assert(element
&& element
->options
&& css
);
273 selector
= init_css_selector(NULL
, CST_ELEMENT
, CSR_ROOT
, NULL
, 0);
278 DBG("Applying to element %.*s...", element
->namelen
, element
->name
);
281 examine_element(html_context
, selector
, CST_ELEMENT
, CSR_ROOT
,
282 &css
->selectors
, element
);
285 DBG("Element %.*s applied.", element
->namelen
, element
->name
);
288 code
= get_attr_val(element
->options
, "style", html_context
->doc_cp
);
290 struct css_selector
*stylesel
;
291 struct scanner scanner
;
293 stylesel
= init_css_selector(NULL
, CST_ELEMENT
, CSR_ROOT
, NULL
, 0);
296 init_scanner(&scanner
, &css_scanner_info
, code
, NULL
);
297 css_parse_properties(&stylesel
->properties
, &scanner
);
298 merge_css_selectors(selector
, stylesel
);
299 done_css_selector(stylesel
);
308 apply_css_selector_style(struct html_context
*html_context
,
309 struct html_element
*element
,
310 struct css_selector
*selector
)
312 struct css_property
*property
;
314 foreach (property
, selector
->properties
) {
315 assert(property
->type
< CSS_PT_LAST
);
316 /* We don't assert general prop->value_type here because I
317 * don't want hinder properties' ability to potentially make
318 * use of multiple value types. */
319 assert(css_appliers
[property
->type
]);
320 css_appliers
[property
->type
](html_context
, element
, property
);
325 css_apply(struct html_context
*html_context
, struct html_element
*element
,
326 struct css_stylesheet
*css
, LIST_OF(struct html_element
) *html_stack
)
328 struct css_selector
*selector
;
330 selector
= get_css_selector_for_element(html_context
, element
, css
,
332 if (!selector
) return;
334 apply_css_selector_style(html_context
, element
, selector
);
336 done_css_selector(selector
);