1 /** CSS module management
12 #include "cache/cache.h"
13 #include "config/home.h"
14 #include "config/options.h"
15 #include "document/css/css.h"
16 #include "document/css/parser.h"
17 #include "document/css/stylesheet.h"
18 #include "encoding/encoding.h"
19 #include "intl/gettext/libintl.h"
20 #include "main/module.h"
21 #include "network/connection.h"
22 #include "protocol/uri.h"
23 #include "session/session.h"
24 #include "util/error.h"
25 #include "util/memory.h"
26 #include "viewer/text/draw.h"
29 union option_info css_options_info
[] = {
30 INIT_OPT_TREE("document", N_("Cascading Style Sheets"),
32 N_("Options concerning how to use CSS for styling "
35 INIT_OPT_BOOL("document.css", N_("Enable CSS"),
37 N_("Enable adding of CSS style info to documents.")),
39 INIT_OPT_BOOL("document.css", N_("Ignore \"display: none\""),
40 "ignore_display_none", 0, 1,
41 N_("When enabled, elements are rendered, even when their "
42 "display property has the value \"none\". Because ELinks's "
43 "CSS support is still very incomplete, this setting can "
44 "improve the way that some documents are rendered.")),
46 INIT_OPT_BOOL("document.css", N_("Import external style sheets"),
48 N_("When enabled any external style sheets that are imported "
49 "from either CSS itself using the @import keyword or from the "
50 "HTML using <link> tags in the document header will also be "
53 INIT_OPT_STRING("document.css", N_("Default style sheet"),
55 N_("The path to the file containing the default user defined "
56 "Cascading Style Sheet. It can be used to control the basic "
57 "layout of HTML documents. The path is assumed to be relative "
58 "to ELinks' home directory.\n"
60 "Leave as \"\" to use built-in document styling.")),
62 INIT_OPT_STRING("document.css", N_("Media types"),
64 N_("CSS media types that ELinks claims to support, separated "
65 "with commas. The \"all\" type is implied. Currently, only "
66 "ASCII characters work reliably here. See CSS2 section 7: "
67 "http://www.w3.org/TR/1998/REC-CSS2-19980512/media.html")),
73 /** Check whether ELinks claims to support a specific CSS media type.
76 * Null-terminated value of the document.css.media option.
78 * A name parsed from a CSS file or from an HTML media attribute.
79 * Need not be null-terminated.
81 * Length of @a token, in bytes.
83 * Both strings should be in the ASCII charset.
85 * @return nonzero if the media type is supported, 0 if not. */
87 supports_css_media_type(const unsigned char *optstr
,
88 const unsigned char *token
, size_t token_length
)
90 /* Split @optstr into comma-delimited strings, strip leading
91 * and trailing spaces from each, and compare them to the
93 while (*optstr
!= '\0') {
94 const unsigned char *beg
, *end
;
96 while (*optstr
== ' ')
100 optstr
+= strcspn(optstr
, ",");
102 while (end
> beg
&& end
[-1] == ' ')
105 /* W3C REC-html401-19991224 section 6.13:
106 * "3. A case-sensitive match is then made with
107 * the set of media types defined above."
108 * W3C REC-html401-19991224 section 14.2.3:
109 * "media = media-descriptors [CI]"
110 * where CI stands for case-insensitive.
111 * This mismatch has been reported to the
112 * www-html-editor@w3.org mailing list on 2000-11-16.
114 * W3C REC-CSS2-19980512 section 7.3:
115 * "Media type names are case-insensitive." */
116 if (!strlcasecmp(token
, token_length
, beg
, end
- beg
))
119 while (*optstr
== ',')
123 /* An explicit "all" is probably rarer than e.g. "tty". */
124 if (!strlcasecmp(token
, token_length
, "all", 3))
131 import_css(struct css_stylesheet
*css
, struct uri
*uri
)
133 /* Do we have it in the cache? (TODO: CSS cache) */
134 struct cache_entry
*cached
;
135 struct fragment
*fragment
;
137 if (!uri
|| css
->import_level
>= MAX_REDIRECTS
)
140 cached
= get_redirected_cache_entry(uri
);
143 fragment
= get_cache_fragment(cached
);
145 unsigned char *end
= fragment
->data
+ fragment
->length
;
148 css_parse_stylesheet(css
, uri
, fragment
->data
, end
);
155 import_css_file(struct css_stylesheet
*css
, struct uri
*base_uri
,
156 const unsigned char *url
, int urllen
)
158 struct string string
, filename
;
161 || css
->import_level
>= MAX_REDIRECTS
162 || !init_string(&filename
))
165 if (*url
!= '/' && elinks_home
) {
166 add_to_string(&filename
, elinks_home
);
169 add_bytes_to_string(&filename
, url
, urllen
);
171 if (is_in_state(read_encoded_file(&filename
, &string
), S_OK
)) {
172 unsigned char *end
= string
.source
+ string
.length
;
175 css_parse_stylesheet(css
, base_uri
, string
.source
, end
);
176 done_string(&string
);
180 done_string(&filename
);
183 struct css_stylesheet default_stylesheet
= INIT_CSS_STYLESHEET(default_stylesheet
, import_css_file
);
186 import_default_css(void)
188 unsigned char *url
= get_opt_str("document.css.stylesheet", NULL
);
190 if (!css_selector_set_empty(&default_stylesheet
.selectors
))
191 done_css_stylesheet(&default_stylesheet
);
195 import_css_file(&default_stylesheet
, NULL
, url
, strlen(url
));
199 change_hook_css(struct session
*ses
, struct option
*current
, struct option
*changed
)
203 if (!strcmp(changed
->name
, "stylesheet")) {
204 /** @todo TODO: We need to update all entries in
205 * format cache. --jonas */
206 import_default_css();
209 if (!strcmp(changed
->name
, "media"))
212 /* Instead of using the value of the @ses parameter, iterate
213 * through the @sessions list. The parameter may be NULL and
214 * anyway we don't support session-specific options yet. */
215 foreach (ses
, sessions
) draw_formatted(ses
, 1 + reload_css
);
221 init_css(struct module
*module
)
223 static const struct change_hook_info css_change_hooks
[] = {
224 { "document.css", change_hook_css
},
228 register_change_hooks(css_change_hooks
);
229 import_default_css();
233 done_css(struct module
*module
)
235 done_css_stylesheet(&default_stylesheet
);
239 struct module css_module
= struct_module(
240 /* name: */ N_("Cascading Style Sheets"),
241 /* options: */ css_options_info
,
243 /* submodules: */ NULL
,
245 /* init: */ init_css
,