iconv: Bail out of the loop when an illegal sequence of bytes occurs.
[elinks/elinks-j605.git] / src / document / css / css.c
blobba6f22e2cb9a0c5c21832cc99e2cc046c57767a8
1 /** CSS module management
2 * @file */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include <stdlib.h>
10 #include "elinks.h"
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"),
31 "css", OPT_SORT,
32 N_("Options concerning how to use CSS for styling "
33 "documents.")),
35 INIT_OPT_BOOL("document.css", N_("Enable CSS"),
36 "enable", 0, 1,
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"),
47 "import", 0, 1,
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 "
51 "downloaded.")),
53 INIT_OPT_STRING("document.css", N_("Default style sheet"),
54 "stylesheet", 0, "",
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"
59 "\n"
60 "Leave as \"\" to use built-in document styling.")),
62 INIT_OPT_STRING("document.css", N_("Media types"),
63 "media", 0, "tty",
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")),
69 NULL_OPTION_INFO,
73 /** Check whether ELinks claims to support a specific CSS media type.
75 * @param optstr
76 * Null-terminated value of the document.css.media option.
77 * @param token
78 * A name parsed from a CSS file or from an HTML media attribute.
79 * Need not be null-terminated.
80 * @param token_length
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. */
86 int
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
92 * token. */
93 while (*optstr != '\0') {
94 const unsigned char *beg, *end;
96 while (*optstr == ' ')
97 ++optstr;
99 beg = optstr;
100 optstr += strcspn(optstr, ",");
101 end = optstr;
102 while (end > beg && end[-1] == ' ')
103 --end;
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))
117 return 1;
119 while (*optstr == ',')
120 ++optstr;
123 /* An explicit "all" is probably rarer than e.g. "tty". */
124 if (!strlcasecmp(token, token_length, "all", 3))
125 return 1;
127 return 0;
130 void
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)
138 return;
140 cached = get_redirected_cache_entry(uri);
141 if (!cached) return;
143 fragment = get_cache_fragment(cached);
144 if (fragment) {
145 unsigned char *end = fragment->data + fragment->length;
147 css->import_level++;
148 css_parse_stylesheet(css, uri, fragment->data, end);
149 css->import_level--;
154 static void
155 import_css_file(struct css_stylesheet *css, struct uri *base_uri,
156 const unsigned char *url, int urllen)
158 struct string string, filename;
160 if (!*url
161 || css->import_level >= MAX_REDIRECTS
162 || !init_string(&filename))
163 return;
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;
174 css->import_level++;
175 css_parse_stylesheet(css, base_uri, string.source, end);
176 done_string(&string);
177 css->import_level--;
180 done_string(&filename);
183 struct css_stylesheet default_stylesheet = INIT_CSS_STYLESHEET(default_stylesheet, import_css_file);
185 static void
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);
193 if (!*url) return;
195 import_css_file(&default_stylesheet, NULL, url, strlen(url));
198 static int
199 change_hook_css(struct session *ses, struct option *current, struct option *changed)
201 int reload_css = 0;
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"))
210 reload_css = 1;
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);
217 return 0;
220 static void
221 init_css(struct module *module)
223 static const struct change_hook_info css_change_hooks[] = {
224 { "document.css", change_hook_css },
225 { NULL, NULL },
228 register_change_hooks(css_change_hooks);
229 import_default_css();
232 void
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,
242 /* hooks: */ NULL,
243 /* submodules: */ NULL,
244 /* data: */ NULL,
245 /* init: */ init_css,
246 /* done: */ done_css