mirror of
https://github.com/rkd77/elinks.git
synced 2024-12-04 14:46:47 -05:00
Bug 722: Filter CSS according to media types.
This patch adds support for: - option document.css.media - CSS @import "foo.css" tty; - CSS @media tty { ... } - HTML <link rel="stylesheet" media="tty"> - HTML <style media="tty"> This patch is attachments 395 and 396 from bugzilla.elinks.cz, which are based on attachment 388 from bugzilla.elinks.cz. This new version of the patch fixes conflicts with recent 0.13.GIT changes, marks Doxygen commands with at-signs rather than backslashes, and adds a few comments.
This commit is contained in:
parent
ae88223664
commit
60144e9ff6
@ -49,10 +49,62 @@ struct option_info css_options_info[] = {
|
||||
"to ELinks' home directory.\n"
|
||||
"Leave as \"\" to use built-in document styling.")),
|
||||
|
||||
INIT_OPT_STRING("document.css", N_("Media types"),
|
||||
"media", 0, "tty",
|
||||
N_("CSS media types that ELinks claims to support, separated with\n"
|
||||
"commas. The \"all\" type is implied. Currently, only ASCII\n"
|
||||
"characters work reliably here. See CSS2 section 7:\n"
|
||||
"http://www.w3.org/TR/1998/REC-CSS2-19980512/media.html")),
|
||||
|
||||
NULL_OPTION_INFO,
|
||||
};
|
||||
|
||||
|
||||
/** Check whether ELinks claims to support a specific CSS media type.
|
||||
*
|
||||
* @param optstr
|
||||
* Null-terminated value of the document.css.media option.
|
||||
* @param token
|
||||
* A name parsed from a CSS file. Need not be null-terminated.
|
||||
* @param token_length
|
||||
* Length of @a token, in bytes.
|
||||
*
|
||||
* Both strings should be in the ASCII charset.
|
||||
*
|
||||
* @return nonzero if the media type is supported, 0 if not. */
|
||||
int
|
||||
supports_css_media_type(const unsigned char *optstr,
|
||||
const unsigned char *token, size_t token_length)
|
||||
{
|
||||
/* Split @optstr into comma-delimited strings, strip leading
|
||||
* and trailing spaces from each, and compare them to the
|
||||
* token. */
|
||||
while (*optstr != '\0') {
|
||||
const unsigned char *beg, *end;
|
||||
|
||||
while (*optstr == ' ')
|
||||
++optstr;
|
||||
|
||||
beg = optstr;
|
||||
optstr += strcspn(optstr, ",");
|
||||
end = optstr;
|
||||
while (end > beg && end[-1] == ' ')
|
||||
--end;
|
||||
|
||||
if (!strlcasecmp(token, token_length, beg, end - beg))
|
||||
return 1;
|
||||
|
||||
while (*optstr == ',')
|
||||
++optstr;
|
||||
}
|
||||
|
||||
/* An explicit "all" is probably rarer than e.g. "tty". */
|
||||
if (!strlcasecmp(token, token_length, "all", 3))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
import_css(struct css_stylesheet *css, struct uri *uri)
|
||||
{
|
||||
@ -124,7 +176,8 @@ import_default_css(void)
|
||||
static int
|
||||
change_hook_css(struct session *ses, struct option *current, struct option *changed)
|
||||
{
|
||||
if (!strcmp(changed->name, "stylesheet")) {
|
||||
if (!strcmp(changed->name, "stylesheet")
|
||||
|| !strcmp(changed->name, "media")) {
|
||||
/** @todo TODO: We need to update all entries in
|
||||
* format cache. --jonas */
|
||||
import_default_css();
|
||||
|
@ -23,4 +23,7 @@ extern struct module css_module;
|
||||
/** This function will try to import the given @a url from the cache. */
|
||||
void import_css(struct css_stylesheet *css, struct uri *uri);
|
||||
|
||||
int supports_css_media_type(const unsigned char *optstr,
|
||||
const unsigned char *token, size_t token_length);
|
||||
|
||||
#endif
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include "elinks.h"
|
||||
|
||||
#include "config/options.h"
|
||||
#include "document/css/css.h"
|
||||
#include "document/css/parser.h"
|
||||
#include "document/css/property.h"
|
||||
#include "document/css/scanner.h"
|
||||
@ -21,6 +23,9 @@
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
|
||||
static void css_parse_ruleset(struct css_stylesheet *css,
|
||||
struct scanner *scanner);
|
||||
|
||||
|
||||
void
|
||||
css_parse_properties(LIST_OF(struct css_property) *props,
|
||||
@ -109,16 +114,53 @@ skip_css_block(struct scanner *scanner)
|
||||
}
|
||||
}
|
||||
|
||||
/** Parse an atrule from @a scanner and update @a css accordingly.
|
||||
/* Parse a list of media types.
|
||||
*
|
||||
* Atrules grammar:
|
||||
* Media types grammar:
|
||||
*
|
||||
* @verbatim
|
||||
* media_types:
|
||||
* <empty>
|
||||
* | <ident>
|
||||
* | media_types ',' <ident>
|
||||
* @endverbatim
|
||||
*
|
||||
* This does not entirely match appendix D of CSS2: ELinks allows any
|
||||
* list of media types to be empty, whereas CSS2 allows that only in
|
||||
* @@import and not in @@media.
|
||||
*
|
||||
* @return nonzero if the directive containing this list should take
|
||||
* effect, zero if not.
|
||||
*/
|
||||
static int
|
||||
css_parse_media_types(struct scanner *scanner)
|
||||
{
|
||||
int matched = 0;
|
||||
int empty = 1;
|
||||
const unsigned char *const optstr = get_opt_str("document.css.media", NULL);
|
||||
struct scanner_token *token = get_scanner_token(scanner);
|
||||
|
||||
while (token && token->type == CSS_TOKEN_IDENT) {
|
||||
empty = 0;
|
||||
if (!matched) /* Skip string ops if already matched. */
|
||||
matched = supports_css_media_type(
|
||||
optstr, token->string, token->length);
|
||||
|
||||
token = get_next_scanner_token(scanner);
|
||||
if (!token || token->type != ',')
|
||||
break;
|
||||
|
||||
token = get_next_scanner_token(scanner);
|
||||
}
|
||||
|
||||
return matched || empty;
|
||||
}
|
||||
|
||||
/** Parse an atrule from @a scanner and update @a css accordingly.
|
||||
*
|
||||
* Atrules grammar:
|
||||
*
|
||||
* @verbatim
|
||||
* atrule:
|
||||
* '@charset' <string> ';'
|
||||
* | '@import' <string> media_types ';'
|
||||
@ -133,38 +175,81 @@ css_parse_atrule(struct css_stylesheet *css, struct scanner *scanner,
|
||||
struct uri *base_uri)
|
||||
{
|
||||
struct scanner_token *token = get_scanner_token(scanner);
|
||||
struct string import_uri;
|
||||
|
||||
/* Skip skip skip that code */
|
||||
switch (token->type) {
|
||||
case CSS_TOKEN_AT_IMPORT:
|
||||
token = get_next_scanner_token(scanner);
|
||||
if (!token) break;
|
||||
if (token->type != CSS_TOKEN_STRING
|
||||
&& token->type != CSS_TOKEN_URL)
|
||||
goto skip_rest_of_atrule;
|
||||
|
||||
if (token->type == CSS_TOKEN_STRING
|
||||
|| token->type == CSS_TOKEN_URL) {
|
||||
assert(css->import);
|
||||
css->import(css, base_uri, token->string, token->length);
|
||||
/* As of 2007-07, token->string points into the
|
||||
* original CSS text, so the pointer will remain
|
||||
* valid even if we parse more tokens. But this
|
||||
* may have to change when backslash escapes are
|
||||
* properly supported. So play it safe and make
|
||||
* a copy of the string. */
|
||||
if (!init_string(&import_uri))
|
||||
goto skip_rest_of_atrule;
|
||||
if (!add_bytes_to_string(&import_uri,
|
||||
token->string,
|
||||
token->length)) {
|
||||
done_string(&import_uri);
|
||||
goto skip_rest_of_atrule;
|
||||
}
|
||||
skip_css_tokens(scanner, ';');
|
||||
|
||||
skip_scanner_token(scanner);
|
||||
if (!css_parse_media_types(scanner)) {
|
||||
done_string(&import_uri);
|
||||
goto skip_rest_of_atrule;
|
||||
}
|
||||
|
||||
token = get_scanner_token(scanner);
|
||||
if (!token || token->type != ';') {
|
||||
done_string(&import_uri);
|
||||
goto skip_rest_of_atrule;
|
||||
}
|
||||
skip_scanner_token(scanner);
|
||||
|
||||
assert(css->import);
|
||||
css->import(css, base_uri,
|
||||
import_uri.source, import_uri.length);
|
||||
done_string(&import_uri);
|
||||
break;
|
||||
|
||||
case CSS_TOKEN_AT_CHARSET:
|
||||
skip_css_tokens(scanner, ';');
|
||||
break;
|
||||
|
||||
case CSS_TOKEN_AT_FONT_FACE:
|
||||
case CSS_TOKEN_AT_MEDIA:
|
||||
skip_scanner_token(scanner);
|
||||
if (!css_parse_media_types(scanner))
|
||||
goto skip_rest_of_atrule;
|
||||
token = get_scanner_token(scanner);
|
||||
if (!token || token->type != '{')
|
||||
goto skip_rest_of_atrule;
|
||||
token = get_next_scanner_token(scanner);
|
||||
while (token && token->type != '}') {
|
||||
css_parse_ruleset(css, scanner);
|
||||
token = get_scanner_token(scanner);
|
||||
}
|
||||
if (token && token->type == '}')
|
||||
skip_scanner_token(scanner);
|
||||
break;
|
||||
|
||||
case CSS_TOKEN_AT_FONT_FACE:
|
||||
case CSS_TOKEN_AT_PAGE:
|
||||
skip_css_block(scanner);
|
||||
break;
|
||||
|
||||
skip_rest_of_atrule:
|
||||
case CSS_TOKEN_AT_KEYWORD:
|
||||
/* TODO: Unkown @-rule so either skip til ';' or next block. */
|
||||
while (scanner_has_tokens(scanner)) {
|
||||
token = get_next_scanner_token(scanner);
|
||||
|
||||
if (!token) break;
|
||||
|
||||
token = get_scanner_token(scanner);
|
||||
while (token) {
|
||||
if (token->type == ';') {
|
||||
skip_scanner_token(scanner);
|
||||
break;
|
||||
@ -173,6 +258,8 @@ css_parse_atrule(struct css_stylesheet *css, struct scanner *scanner,
|
||||
skip_css_block(scanner);
|
||||
break;
|
||||
}
|
||||
|
||||
token = get_next_scanner_token(scanner);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -257,6 +257,7 @@ dump_css_selector_tree(struct css_selector_set *sels)
|
||||
#endif
|
||||
|
||||
|
||||
#if 0 /* used only by clone_css_stylesheet */
|
||||
struct css_stylesheet *
|
||||
init_css_stylesheet(css_stylesheet_importer_T importer, void *import_data)
|
||||
{
|
||||
@ -270,6 +271,7 @@ init_css_stylesheet(css_stylesheet_importer_T importer, void *import_data)
|
||||
init_css_selector_set(&css->selectors);
|
||||
return css;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
mirror_css_stylesheet(struct css_stylesheet *css1, struct css_stylesheet *css2)
|
||||
|
@ -103,7 +103,8 @@ typedef void (*css_stylesheet_importer_T)(struct css_stylesheet *, struct uri *,
|
||||
* stylesheet so it can contain stuff from both @<style> tags and @@import'ed
|
||||
* CSS documents. */
|
||||
struct css_stylesheet {
|
||||
/** The import callback function. */
|
||||
/** The import callback function. The caller must check the
|
||||
* media types first. */
|
||||
css_stylesheet_importer_T import;
|
||||
|
||||
/** The import callback's data. */
|
||||
|
@ -801,7 +801,8 @@ html_link(struct html_context *html_context, unsigned char *a,
|
||||
if (!link.href) goto free_and_return;
|
||||
|
||||
#ifdef CONFIG_CSS
|
||||
if (link.type == LT_STYLESHEET) {
|
||||
if (link.type == LT_STYLESHEET
|
||||
&& supports_html_media_attr(link.media)) {
|
||||
int len = strlen(link.href);
|
||||
|
||||
import_css_stylesheet(&html_context->css_styles,
|
||||
|
@ -12,7 +12,9 @@
|
||||
|
||||
#include "elinks.h"
|
||||
|
||||
#include "config/options.h"
|
||||
#include "document/css/apply.h"
|
||||
#include "document/css/css.h"
|
||||
#include "document/css/parser.h"
|
||||
#include "document/html/parser/forms.h"
|
||||
#include "document/html/parser/general.h"
|
||||
@ -846,8 +848,15 @@ start_element(struct element_info *ei,
|
||||
|
||||
#ifdef CONFIG_CSS
|
||||
if (ei->open == html_style && html_context->options->css_enable) {
|
||||
css_parse_stylesheet(&html_context->css_styles,
|
||||
html_context->base_href, html, eof);
|
||||
unsigned char *media
|
||||
= get_attr_val(attr, "media", html_context->doc_cp);
|
||||
int support = supports_html_media_attr(media);
|
||||
mem_free_if(media);
|
||||
|
||||
if (support)
|
||||
css_parse_stylesheet(&html_context->css_styles,
|
||||
html_context->base_href,
|
||||
html, eof);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1128,3 +1137,35 @@ xsp:
|
||||
add_crlf_to_string(head);
|
||||
goto se;
|
||||
}
|
||||
|
||||
/** Check whether ELinks claims to support any of the media types
|
||||
* listed in the media attribute of an HTML STYLE or LINK element. */
|
||||
int
|
||||
supports_html_media_attr(const unsigned char *media)
|
||||
{
|
||||
const unsigned char *const optstr = get_opt_str("document.css.media", NULL);
|
||||
const unsigned char *beg, *end;
|
||||
|
||||
/* According to HTML 4.01 section 14.2.3 (the STYLE element),
|
||||
* the default value of the media attribute is "screen".
|
||||
* Section 12.3 (the LINK element) also refers to that
|
||||
* attribute definition. */
|
||||
if (media == NULL || *media == '\0')
|
||||
media = "screen";
|
||||
|
||||
while (*media != '\0') {
|
||||
while (*media == ' ')
|
||||
++media;
|
||||
beg = media;
|
||||
media += strcspn(media, ",");
|
||||
end = media;
|
||||
if (*media == ',')
|
||||
++media;
|
||||
while (end > beg && end[-1] == ' ')
|
||||
--end;
|
||||
if (end > beg
|
||||
&& supports_css_media_type(optstr, beg, end - beg))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -63,6 +63,8 @@ unsigned char *skip_comment(unsigned char *, unsigned char *);
|
||||
|
||||
void scan_http_equiv(unsigned char *s, unsigned char *eof, struct string *head, struct string *title, struct document_options *options);
|
||||
|
||||
int supports_html_media_attr(const unsigned char *media);
|
||||
|
||||
|
||||
/* Lifecycle functions for the tags fastfind cache, if being in use. */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user