1 /* CSS style applier */
12 #include "document/css/apply.h"
13 #include "document/css/css.h"
14 #include "document/css/parser.h"
15 #include "document/css/property.h"
16 #include "document/css/scanner.h"
17 #include "document/css/stylesheet.h"
18 #include "document/html/parser/parse.h"
19 #include "document/options.h"
20 #include "util/align.h"
21 #include "util/color.h"
22 #include "util/lists.h"
23 #include "util/error.h"
24 #include "util/memory.h"
25 #include "util/string.h"
27 /* XXX: Some strange dependency makes it necessary to this include last. */
28 #include "document/html/internal.h"
30 /* #define DEBUG_CSS */
33 /* TODO: A way to disable CSS completely, PLUS a way to stop various property
34 * groups from taking effect. (Ie. way to turn out effect of 'display: none'
35 * or aligning or colors but keeping all the others.) --pasky */
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
.fg
= 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
.bg
= 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 INTERNAL("Bad prop->value.display %d", prop
->value
.display
);
86 css_apply_font_attribute(struct html_context
*html_context
,
87 struct html_element
*element
, struct css_property
*prop
)
89 assert(prop
->value_type
== CSS_VT_FONT_ATTRIBUTE
);
90 element
->attr
.style
.attr
|= prop
->value
.font_attribute
.add
;
91 element
->attr
.style
.attr
&= ~prop
->value
.font_attribute
.rem
;
94 /* FIXME: Because the current CSS doesn't provide reasonable defaults for each
95 * HTML element this applier will cause bad rendering of <pre> tags. */
97 css_apply_text_align(struct html_context
*html_context
,
98 struct html_element
*element
, struct css_property
*prop
)
100 assert(prop
->value_type
== CSS_VT_TEXT_ALIGN
);
101 element
->parattr
.align
= prop
->value
.text_align
;
104 /* XXX: Sort like the css_property_type */
105 static css_applier_T css_appliers
[CSS_PT_LAST
] = {
106 /* CSS_PT_NONE */ NULL
,
107 /* CSS_PT_BACKGROUND */ css_apply_background_color
,
108 /* CSS_PT_BACKGROUND_COLOR */ css_apply_background_color
,
109 /* CSS_PT_COLOR */ css_apply_color
,
110 /* CSS_PT_DISPLAY */ css_apply_display
,
111 /* CSS_PT_FONT_STYLE */ css_apply_font_attribute
,
112 /* CSS_PT_FONT_WEIGHT */ css_apply_font_attribute
,
113 /* CSS_PT_TEXT_ALIGN */ css_apply_text_align
,
114 /* CSS_PT_TEXT_DECORATION */ css_apply_font_attribute
,
115 /* CSS_PT_WHITE_SPACE */ css_apply_font_attribute
,
118 /* This looks for a match in list of selectors. */
120 examine_element(struct html_context
*html_context
, struct css_selector
*base
,
121 enum css_selector_type seltype
, enum css_selector_relation rel
,
122 struct list_head
*selectors
, struct html_element
*element
,
123 struct list_head
*html_stack
)
125 struct css_selector
*selector
;
129 DBG("examine_element(%p, %s, %d, %d, %p, %.*s);", html_context
, base
->name
, seltype
, rel
, selectors
, element
->namelen
, element
->name
);
130 #define dbginfo(sel, type_, base) \
131 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, !list_empty(sel->leaves), !list_empty(sel->properties))
133 #define dbginfo(sel, type, base)
136 #define process_found_selector(sel, type, base) \
138 dbginfo(sel, type, base); \
139 merge_css_selectors(base, sel); \
140 /* Ancestor matches? */ \
141 if ((struct list_head *) element->next != html_stack) { \
142 struct html_element *ancestor; \
143 /* This is less effective than doing reverse iterations,
144 * first over sel->leaves and then over the HTML stack,
145 * which shines in the most common case where there are
146 * no CSR_ANCESTOR selector leaves. However we would
147 * have to duplicate the whole examine_element(), so if
148 * profiles won't show it really costs... */ \
149 for (ancestor = element->next; \
150 (struct list_head *) ancestor != html_stack;\
151 ancestor = ancestor->next) \
152 examine_element(html_context, base, \
153 CST_ELEMENT, CSR_ANCESTOR, \
154 &sel->leaves, ancestor, \
156 examine_element(html_context, base, \
157 CST_ELEMENT, CSR_PARENT, \
158 &sel->leaves, element->next, \
161 /* More specific matches? */ \
162 examine_element(html_context, base, type + 1, \
164 &sel->leaves, element, html_stack); \
167 if (seltype
<= CST_ELEMENT
&& element
->namelen
) {
168 selector
= find_css_selector(selectors
, CST_ELEMENT
, rel
,
170 process_found_selector(selector
, CST_ELEMENT
, base
);
172 selector
= find_css_selector(selectors
, CST_ELEMENT
, rel
,
173 element
->name
, element
->namelen
);
174 process_found_selector(selector
, CST_ELEMENT
, base
);
177 if (!element
->options
)
180 /* TODO: More pseudo-classess. --pasky */
181 if (element
->pseudo_class
& ELEMENT_LINK
) {
182 selector
= find_css_selector(selectors
, CST_PSEUDO
, rel
, "link", -1);
183 process_found_selector(selector
, CST_PSEUDO
, base
);
185 if (element
->pseudo_class
& ELEMENT_VISITED
) {
186 selector
= find_css_selector(selectors
, CST_PSEUDO
, rel
, "visited", -1);
187 process_found_selector(selector
, CST_PSEUDO
, base
);
190 code
= get_attr_val(element
->options
, "class", html_context
->options
);
191 if (code
&& seltype
<= CST_CLASS
) {
192 selector
= find_css_selector(selectors
, CST_CLASS
, rel
, code
, -1);
193 process_found_selector(selector
, CST_CLASS
, base
);
195 if (code
) mem_free(code
);
197 code
= get_attr_val(element
->options
, "id", html_context
->options
);
198 if (code
&& seltype
<= CST_ID
) {
199 selector
= find_css_selector(selectors
, CST_ID
, rel
, code
, -1);
200 process_found_selector(selector
, CST_ID
, base
);
202 if (code
) mem_free(code
);
204 #undef process_found_selector
208 struct css_selector
*
209 get_css_selector_for_element(struct html_context
*html_context
,
210 struct html_element
*element
,
211 struct css_stylesheet
*css
,
212 struct list_head
*html_stack
)
215 struct css_selector
*selector
;
217 assert(element
&& element
->options
&& css
);
219 selector
= init_css_selector(NULL
, CST_ELEMENT
, NULL
, 0);
224 DBG("Applying to element %.*s...", element
->namelen
, element
->name
);
227 examine_element(html_context
, selector
, CST_ELEMENT
, CSR_ROOT
,
228 &css
->selectors
, element
, html_stack
);
231 DBG("Element %.*s applied.", element
->namelen
, element
->name
);
234 code
= get_attr_val(element
->options
, "style", html_context
->options
);
236 struct css_selector
*stylesel
;
237 struct scanner scanner
;
239 stylesel
= init_css_selector(NULL
, CST_ELEMENT
, NULL
, 0);
242 init_scanner(&scanner
, &css_scanner_info
, code
, NULL
);
243 css_parse_properties(&stylesel
->properties
, &scanner
);
244 merge_css_selectors(selector
, stylesel
);
245 done_css_selector(stylesel
);
254 apply_css_selector_style(struct html_context
*html_context
,
255 struct html_element
*element
,
256 struct css_selector
*selector
)
258 struct css_property
*property
;
260 foreach (property
, selector
->properties
) {
261 assert(property
->type
< CSS_PT_LAST
);
262 /* We don't assert general prop->value_type here because I
263 * don't want hinder properties' ability to potentially make
264 * use of multiple value types. */
265 assert(css_appliers
[property
->type
]);
266 css_appliers
[property
->type
](html_context
, element
, property
);
271 css_apply(struct html_context
*html_context
, struct html_element
*element
,
272 struct css_stylesheet
*css
, struct list_head
*html_stack
)
274 struct css_selector
*selector
;
276 selector
= get_css_selector_for_element(html_context
, element
, css
,
278 if (!selector
) return;
280 apply_css_selector_style(html_context
, element
, selector
);
282 done_css_selector(selector
);