1
0
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:
Kalle Olavi Niemitalo 2007-12-22 03:18:06 +00:00 committed by Miciah Dashiel Butler Masters
parent ae88223664
commit 60144e9ff6
8 changed files with 208 additions and 18 deletions

View File

@ -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();

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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. */

View File

@ -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,

View File

@ -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;
}

View File

@ -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. */