370da47fa0
git-svn-id: svn://kolibrios.org@3584 a494cfbc-eb01-0410-851d-a64ba20cac60
1581 lines
33 KiB
C
1581 lines
33 KiB
C
#include <ctype.h>
|
|
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <libcss/libcss.h>
|
|
#include <libcss/computed.h>
|
|
#include <libcss/select.h>
|
|
#include <libcss/stylesheet.h>
|
|
|
|
#include "utils/utils.h"
|
|
|
|
#include "dump_computed.h"
|
|
#include "testutils.h"
|
|
|
|
typedef struct attribute {
|
|
lwc_string *name;
|
|
lwc_string *value;
|
|
} attribute;
|
|
|
|
typedef struct node {
|
|
lwc_string *name;
|
|
|
|
uint32_t n_attrs;
|
|
attribute *attrs;
|
|
|
|
struct node *parent;
|
|
struct node *next;
|
|
struct node *prev;
|
|
struct node *children;
|
|
struct node *last_child;
|
|
} node;
|
|
|
|
typedef struct sheet_ctx {
|
|
css_stylesheet *sheet;
|
|
css_origin origin;
|
|
uint64_t media;
|
|
} sheet_ctx;
|
|
|
|
typedef struct line_ctx {
|
|
size_t explen;
|
|
size_t expused;
|
|
char *exp;
|
|
|
|
bool intree;
|
|
bool insheet;
|
|
bool inerrors;
|
|
bool inexp;
|
|
|
|
node *tree;
|
|
node *current;
|
|
uint32_t depth;
|
|
|
|
uint32_t n_sheets;
|
|
sheet_ctx *sheets;
|
|
|
|
uint64_t media;
|
|
uint32_t pseudo_element;
|
|
node *target;
|
|
|
|
lwc_string *attr_class;
|
|
lwc_string *attr_id;
|
|
} line_ctx;
|
|
|
|
static bool handle_line(const char *data, size_t datalen, void *pw);
|
|
static void css__parse_tree(line_ctx *ctx, const char *data, size_t len);
|
|
static void css__parse_tree_data(line_ctx *ctx, const char *data, size_t len);
|
|
static void css__parse_sheet(line_ctx *ctx, const char *data, size_t len);
|
|
static void css__parse_media_list(const char **data, size_t *len, uint64_t *media);
|
|
static void css__parse_pseudo_list(const char **data, size_t *len,
|
|
uint32_t *element);
|
|
static void css__parse_expected(line_ctx *ctx, const char *data, size_t len);
|
|
static void run_test(line_ctx *ctx, const char *exp, size_t explen);
|
|
static void destroy_tree(node *root);
|
|
|
|
static css_error node_name(void *pw, void *node,
|
|
css_qname *qname);
|
|
static css_error node_classes(void *pw, void *node,
|
|
lwc_string ***classes, uint32_t *n_classes);
|
|
static css_error node_id(void *pw, void *node,
|
|
lwc_string **id);
|
|
static css_error named_ancestor_node(void *pw, void *node,
|
|
const css_qname *qname,
|
|
void **ancestor);
|
|
static css_error named_parent_node(void *pw, void *node,
|
|
const css_qname *qname,
|
|
void **parent);
|
|
static css_error named_sibling_node(void *pw, void *node,
|
|
const css_qname *qname,
|
|
void **sibling);
|
|
static css_error named_generic_sibling_node(void *pw, void *node,
|
|
const css_qname *qname,
|
|
void **sibling);
|
|
static css_error parent_node(void *pw, void *node, void **parent);
|
|
static css_error sibling_node(void *pw, void *node, void **sibling);
|
|
static css_error node_has_name(void *pw, void *node,
|
|
const css_qname *qname,
|
|
bool *match);
|
|
static css_error node_has_class(void *pw, void *node,
|
|
lwc_string *name,
|
|
bool *match);
|
|
static css_error node_has_id(void *pw, void *node,
|
|
lwc_string *name,
|
|
bool *match);
|
|
static css_error node_has_attribute(void *pw, void *node,
|
|
const css_qname *qname,
|
|
bool *match);
|
|
static css_error node_has_attribute_equal(void *pw, void *node,
|
|
const css_qname *qname,
|
|
lwc_string *value,
|
|
bool *match);
|
|
static css_error node_has_attribute_dashmatch(void *pw, void *node,
|
|
const css_qname *qname,
|
|
lwc_string *value,
|
|
bool *match);
|
|
static css_error node_has_attribute_includes(void *pw, void *node,
|
|
const css_qname *qname,
|
|
lwc_string *value,
|
|
bool *match);
|
|
static css_error node_has_attribute_prefix(void *pw, void *node,
|
|
const css_qname *qname,
|
|
lwc_string *value,
|
|
bool *match);
|
|
static css_error node_has_attribute_suffix(void *pw, void *node,
|
|
const css_qname *qname,
|
|
lwc_string *value,
|
|
bool *match);
|
|
static css_error node_has_attribute_substring(void *pw, void *node,
|
|
const css_qname *qname,
|
|
lwc_string *value,
|
|
bool *match);
|
|
static css_error node_is_root(void *pw, void *node, bool *match);
|
|
static css_error node_count_siblings(void *pw, void *node,
|
|
bool same_name, bool after, int32_t *count);
|
|
static css_error node_is_empty(void *pw, void *node, bool *match);
|
|
static css_error node_is_link(void *pw, void *node, bool *match);
|
|
static css_error node_is_visited(void *pw, void *node, bool *match);
|
|
static css_error node_is_hover(void *pw, void *node, bool *match);
|
|
static css_error node_is_active(void *pw, void *node, bool *match);
|
|
static css_error node_is_focus(void *pw, void *node, bool *match);
|
|
static css_error node_is_enabled(void *pw, void *node, bool *match);
|
|
static css_error node_is_disabled(void *pw, void *node, bool *match);
|
|
static css_error node_is_checked(void *pw, void *node, bool *match);
|
|
static css_error node_is_target(void *pw, void *node, bool *match);
|
|
static css_error node_is_lang(void *pw, void *node,
|
|
lwc_string *lang, bool *match);
|
|
static css_error node_presentational_hint(void *pw, void *node,
|
|
uint32_t property, css_hint *hint);
|
|
static css_error ua_default_for_property(void *pw, uint32_t property,
|
|
css_hint *hint);
|
|
static css_error compute_font_size(void *pw, const css_hint *parent,
|
|
css_hint *size);
|
|
|
|
static css_select_handler select_handler = {
|
|
CSS_SELECT_HANDLER_VERSION_1,
|
|
|
|
node_name,
|
|
node_classes,
|
|
node_id,
|
|
named_ancestor_node,
|
|
named_parent_node,
|
|
named_sibling_node,
|
|
named_generic_sibling_node,
|
|
parent_node,
|
|
sibling_node,
|
|
node_has_name,
|
|
node_has_class,
|
|
node_has_id,
|
|
node_has_attribute,
|
|
node_has_attribute_equal,
|
|
node_has_attribute_dashmatch,
|
|
node_has_attribute_includes,
|
|
node_has_attribute_prefix,
|
|
node_has_attribute_suffix,
|
|
node_has_attribute_substring,
|
|
node_is_root,
|
|
node_count_siblings,
|
|
node_is_empty,
|
|
node_is_link,
|
|
node_is_visited,
|
|
node_is_hover,
|
|
node_is_active,
|
|
node_is_focus,
|
|
node_is_enabled,
|
|
node_is_disabled,
|
|
node_is_checked,
|
|
node_is_target,
|
|
node_is_lang,
|
|
node_presentational_hint,
|
|
ua_default_for_property,
|
|
compute_font_size
|
|
};
|
|
|
|
static void *myrealloc(void *data, size_t len, void *pw)
|
|
{
|
|
UNUSED(pw);
|
|
|
|
return realloc(data, len);
|
|
}
|
|
|
|
static css_error resolve_url(void *pw,
|
|
const char *base, lwc_string *rel, lwc_string **abs)
|
|
{
|
|
UNUSED(pw);
|
|
UNUSED(base);
|
|
|
|
/* About as useless as possible */
|
|
*abs = lwc_string_ref(rel);
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
static bool fail_because_lwc_leaked = false;
|
|
|
|
static void
|
|
printing_lwc_iterator(lwc_string *str, void *pw)
|
|
{
|
|
UNUSED(pw);
|
|
|
|
printf(" DICT: %*s\n", (int)(lwc_string_length(str)), lwc_string_data(str));
|
|
fail_because_lwc_leaked = true;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
line_ctx ctx;
|
|
|
|
if (argc != 2) {
|
|
printf("Usage: %s <filename>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
|
|
|
|
lwc_intern_string("class", SLEN("class"), &ctx.attr_class);
|
|
lwc_intern_string("id", SLEN("id"), &ctx.attr_id);
|
|
|
|
assert(css__parse_testfile(argv[1], handle_line, &ctx) == true);
|
|
|
|
/* and run final test */
|
|
if (ctx.tree != NULL)
|
|
run_test(&ctx, ctx.exp, ctx.expused);
|
|
|
|
free(ctx.exp);
|
|
|
|
lwc_string_unref(ctx.attr_class);
|
|
lwc_string_unref(ctx.attr_id);
|
|
|
|
lwc_iterate_strings(printing_lwc_iterator, NULL);
|
|
|
|
assert(fail_because_lwc_leaked == false);
|
|
|
|
printf("PASS\n");
|
|
return 0;
|
|
}
|
|
|
|
bool handle_line(const char *data, size_t datalen, void *pw)
|
|
{
|
|
line_ctx *ctx = (line_ctx *) pw;
|
|
css_error error;
|
|
|
|
if (data[0] == '#') {
|
|
if (ctx->intree) {
|
|
if (strncasecmp(data+1, "errors", 6) == 0) {
|
|
ctx->intree = false;
|
|
ctx->insheet = false;
|
|
ctx->inerrors = true;
|
|
ctx->inexp = false;
|
|
} else {
|
|
/* Assume start of stylesheet */
|
|
css__parse_sheet(ctx, data + 1, datalen - 1);
|
|
|
|
ctx->intree = false;
|
|
ctx->insheet = true;
|
|
ctx->inerrors = false;
|
|
ctx->inexp = false;
|
|
}
|
|
} else if (ctx->insheet) {
|
|
if (strncasecmp(data+1, "errors", 6) == 0) {
|
|
assert(css_stylesheet_data_done(
|
|
ctx->sheets[ctx->n_sheets - 1].sheet)
|
|
== CSS_OK);
|
|
|
|
ctx->intree = false;
|
|
ctx->insheet = false;
|
|
ctx->inerrors = true;
|
|
ctx->inexp = false;
|
|
} else if (strncasecmp(data+1, "ua", 2) == 0 ||
|
|
strncasecmp(data+1, "user", 4) == 0 ||
|
|
strncasecmp(data+1, "author", 6) == 0) {
|
|
assert(css_stylesheet_data_done(
|
|
ctx->sheets[ctx->n_sheets - 1].sheet)
|
|
== CSS_OK);
|
|
|
|
css__parse_sheet(ctx, data + 1, datalen - 1);
|
|
} else {
|
|
error = css_stylesheet_append_data(
|
|
ctx->sheets[ctx->n_sheets - 1].sheet,
|
|
(const uint8_t *) data,
|
|
datalen);
|
|
assert(error == CSS_OK ||
|
|
error == CSS_NEEDDATA);
|
|
}
|
|
} else if (ctx->inerrors) {
|
|
ctx->intree = false;
|
|
ctx->insheet = false;
|
|
ctx->inerrors = false;
|
|
ctx->inexp = true;
|
|
} else if (ctx->inexp) {
|
|
/* This marks end of testcase, so run it */
|
|
run_test(ctx, ctx->exp, ctx->expused);
|
|
|
|
ctx->expused = 0;
|
|
|
|
ctx->intree = false;
|
|
ctx->insheet = false;
|
|
ctx->inerrors = false;
|
|
ctx->inexp = false;
|
|
} else {
|
|
/* Start state */
|
|
if (strncasecmp(data+1, "tree", 4) == 0) {
|
|
css__parse_tree(ctx, data + 5, datalen - 5);
|
|
|
|
ctx->intree = true;
|
|
ctx->insheet = false;
|
|
ctx->inerrors = false;
|
|
ctx->inexp = false;
|
|
}
|
|
}
|
|
} else {
|
|
if (ctx->intree) {
|
|
/* Not interested in the '|' */
|
|
css__parse_tree_data(ctx, data + 1, datalen - 1);
|
|
} else if (ctx->insheet) {
|
|
error = css_stylesheet_append_data(
|
|
ctx->sheets[ctx->n_sheets - 1].sheet,
|
|
(const uint8_t *) data, datalen);
|
|
assert(error == CSS_OK || error == CSS_NEEDDATA);
|
|
} else if (ctx->inexp) {
|
|
css__parse_expected(ctx, data, datalen);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void css__parse_tree(line_ctx *ctx, const char *data, size_t len)
|
|
{
|
|
const char *p = data;
|
|
const char *end = data + len;
|
|
size_t left;
|
|
|
|
/* [ <media_list> <pseudo>? ] ? */
|
|
|
|
ctx->media = CSS_MEDIA_ALL;
|
|
ctx->pseudo_element = CSS_PSEUDO_ELEMENT_NONE;
|
|
|
|
/* Consume any leading whitespace */
|
|
while (p < end && isspace(*p))
|
|
p++;
|
|
|
|
if (p < end) {
|
|
left = end - p;
|
|
|
|
css__parse_media_list(&p, &left, &ctx->media);
|
|
|
|
end = p + left;
|
|
}
|
|
|
|
if (p < end) {
|
|
left = end - p;
|
|
|
|
css__parse_pseudo_list(&p, &left, &ctx->pseudo_element);
|
|
}
|
|
}
|
|
|
|
void css__parse_tree_data(line_ctx *ctx, const char *data, size_t len)
|
|
{
|
|
const char *p = data;
|
|
const char *end = data + len;
|
|
const char *name = NULL;
|
|
const char *value = NULL;
|
|
size_t namelen = 0;
|
|
size_t valuelen = 0;
|
|
uint32_t depth = 0;
|
|
bool target = false;
|
|
|
|
/* ' '{depth+1} [ <element> '*'? | <attr> ]
|
|
*
|
|
* <element> ::= [^=*[:space:]]+
|
|
* <attr> ::= [^=*[:space:]]+ '=' [^[:space:]]*
|
|
*/
|
|
|
|
while (p < end && isspace(*p)) {
|
|
depth++;
|
|
p++;
|
|
}
|
|
depth--;
|
|
|
|
/* Get element/attribute name */
|
|
name = p;
|
|
while (p < end && *p != '=' && *p != '*' && isspace(*p) == false) {
|
|
namelen++;
|
|
p++;
|
|
}
|
|
|
|
/* Skip whitespace */
|
|
while (p < end && isspace(*p))
|
|
p++;
|
|
|
|
if (p < end && *p == '=') {
|
|
/* Attribute value */
|
|
p++;
|
|
|
|
value = p;
|
|
|
|
while (p < end && isspace(*p) == false) {
|
|
valuelen++;
|
|
p++;
|
|
}
|
|
} else if (p < end && *p == '*') {
|
|
/* Element is target node */
|
|
target = true;
|
|
}
|
|
|
|
if (value == NULL) {
|
|
/* We have an element, so create it */
|
|
node *n = malloc(sizeof(node));
|
|
assert(n != NULL);
|
|
|
|
memset(n, 0, sizeof(node));
|
|
|
|
lwc_intern_string(name, namelen, &n->name);
|
|
|
|
/* Insert it into tree */
|
|
if (ctx->tree == NULL) {
|
|
ctx->tree = n;
|
|
} else {
|
|
assert(depth > 0);
|
|
assert(depth <= ctx->depth + 1);
|
|
|
|
/* Find node to insert into */
|
|
while (depth <= ctx->depth) {
|
|
ctx->depth--;
|
|
ctx->current = ctx->current->parent;
|
|
}
|
|
|
|
/* Insert into current node */
|
|
if (ctx->current->children == NULL) {
|
|
ctx->current->children = n;
|
|
ctx->current->last_child = n;
|
|
} else {
|
|
ctx->current->last_child->next = n;
|
|
n->prev = ctx->current->last_child;
|
|
|
|
ctx->current->last_child = n;
|
|
}
|
|
n->parent = ctx->current;
|
|
}
|
|
|
|
ctx->current = n;
|
|
ctx->depth = depth;
|
|
|
|
/* Mark the target, if it's us */
|
|
if (target)
|
|
ctx->target = n;
|
|
} else {
|
|
/* New attribute */
|
|
attribute *attr;
|
|
|
|
attribute *temp = realloc(ctx->current->attrs,
|
|
(ctx->current->n_attrs + 1) * sizeof(attribute));
|
|
assert(temp != NULL);
|
|
|
|
ctx->current->attrs = temp;
|
|
|
|
attr = &ctx->current->attrs[ctx->current->n_attrs];
|
|
|
|
lwc_intern_string(name, namelen, &attr->name);
|
|
lwc_intern_string(value, valuelen, &attr->value);
|
|
|
|
ctx->current->n_attrs++;
|
|
}
|
|
}
|
|
|
|
void css__parse_sheet(line_ctx *ctx, const char *data, size_t len)
|
|
{
|
|
css_stylesheet_params params;
|
|
const char *p;
|
|
const char *end = data + len;
|
|
css_origin origin = CSS_ORIGIN_AUTHOR;
|
|
uint64_t media = CSS_MEDIA_ALL;
|
|
css_stylesheet *sheet;
|
|
sheet_ctx *temp;
|
|
|
|
/* <origin> <media_list>? */
|
|
|
|
/* Find end of origin */
|
|
for (p = data; p < end; p++) {
|
|
if (isspace(*p))
|
|
break;
|
|
}
|
|
|
|
if (p - data == 6 && strncasecmp(data, "author", 6) == 0)
|
|
origin = CSS_ORIGIN_AUTHOR;
|
|
else if (p - data == 4 && strncasecmp(data, "user", 4) == 0)
|
|
origin = CSS_ORIGIN_USER;
|
|
else if (p - data == 2 && strncasecmp(data, "ua", 2) == 0)
|
|
origin = CSS_ORIGIN_UA;
|
|
else
|
|
assert(0 && "Unknown stylesheet origin");
|
|
|
|
/* Skip any whitespace */
|
|
while (p < end && isspace(*p))
|
|
p++;
|
|
|
|
if (p < end) {
|
|
size_t ignored = end - p;
|
|
|
|
css__parse_media_list(&p, &ignored, &media);
|
|
}
|
|
|
|
params.params_version = CSS_STYLESHEET_PARAMS_VERSION_1;
|
|
params.level = CSS_LEVEL_21;
|
|
params.charset = "UTF-8";
|
|
params.url = "foo";
|
|
params.title = "foo";
|
|
params.allow_quirks = false;
|
|
params.inline_style = false;
|
|
params.resolve = resolve_url;
|
|
params.resolve_pw = NULL;
|
|
params.import = NULL;
|
|
params.import_pw = NULL;
|
|
params.color = NULL;
|
|
params.color_pw = NULL;
|
|
params.font = NULL;
|
|
params.font_pw = NULL;
|
|
|
|
/** \todo How are we going to handle @import? */
|
|
assert(css_stylesheet_create(¶ms, myrealloc, NULL,
|
|
&sheet) == CSS_OK);
|
|
|
|
/* Extend array of sheets and append new sheet to it */
|
|
temp = realloc(ctx->sheets,
|
|
(ctx->n_sheets + 1) * sizeof(sheet_ctx));
|
|
assert(temp != NULL);
|
|
|
|
ctx->sheets = temp;
|
|
|
|
ctx->sheets[ctx->n_sheets].sheet = sheet;
|
|
ctx->sheets[ctx->n_sheets].origin = origin;
|
|
ctx->sheets[ctx->n_sheets].media = media;
|
|
|
|
ctx->n_sheets++;
|
|
}
|
|
|
|
void css__parse_media_list(const char **data, size_t *len, uint64_t *media)
|
|
{
|
|
const char *p = *data;
|
|
const char *end = p + *len;
|
|
uint64_t result = 0;
|
|
|
|
/* <medium> [ ',' <medium> ]* */
|
|
|
|
while (p < end) {
|
|
const char *start = p;
|
|
|
|
/* consume a medium */
|
|
while (isspace(*p) == false && *p != ',')
|
|
p++;
|
|
|
|
if (p - start == 10 &&
|
|
strncasecmp(start, "projection", 10) == 0)
|
|
result |= CSS_MEDIA_PROJECTION;
|
|
else if (p - start == 8 &&
|
|
strncasecmp(start, "handheld", 8) == 0)
|
|
result |= CSS_MEDIA_HANDHELD;
|
|
else if (p - start == 8 &&
|
|
strncasecmp(start, "embossed", 8) == 0)
|
|
result |= CSS_MEDIA_EMBOSSED;
|
|
else if (p - start == 7 &&
|
|
strncasecmp(start, "braille", 7) == 0)
|
|
result |= CSS_MEDIA_BRAILLE;
|
|
else if (p - start == 6 &&
|
|
strncasecmp(start, "speech", 6) == 0)
|
|
result |= CSS_MEDIA_SPEECH;
|
|
else if (p - start == 6 &&
|
|
strncasecmp(start, "screen", 6) == 0)
|
|
result |= CSS_MEDIA_SCREEN;
|
|
else if (p - start == 5 &&
|
|
strncasecmp(start, "print", 5) == 0)
|
|
result |= CSS_MEDIA_PRINT;
|
|
else if (p - start == 5 &&
|
|
strncasecmp(start, "aural", 5) == 0)
|
|
result |= CSS_MEDIA_AURAL;
|
|
else if (p - start == 3 &&
|
|
strncasecmp(start, "tty", 3) == 0)
|
|
result |= CSS_MEDIA_TTY;
|
|
else if (p - start == 3 &&
|
|
strncasecmp(start, "all", 3) == 0)
|
|
result |= CSS_MEDIA_ALL;
|
|
else if (p - start == 2 &&
|
|
strncasecmp(start, "tv", 2) == 0)
|
|
result |= CSS_MEDIA_TV;
|
|
else
|
|
assert(0 && "Unknown media type");
|
|
|
|
/* Consume whitespace */
|
|
while (p < end && isspace(*p))
|
|
p++;
|
|
|
|
/* Stop if we've reached the end */
|
|
if (p == end || *p != ',')
|
|
break;
|
|
|
|
/* Consume comma */
|
|
p++;
|
|
|
|
/* Consume whitespace */
|
|
while (p < end && isspace(*p))
|
|
p++;
|
|
}
|
|
|
|
*media = result;
|
|
|
|
*data = p;
|
|
*len = end - p;
|
|
}
|
|
|
|
void css__parse_pseudo_list(const char **data, size_t *len, uint32_t *element)
|
|
{
|
|
const char *p = *data;
|
|
const char *end = p + *len;
|
|
|
|
/* <pseudo> [ ',' <pseudo> ]* */
|
|
|
|
*element = CSS_PSEUDO_ELEMENT_NONE;
|
|
|
|
while (p < end) {
|
|
const char *start = p;
|
|
|
|
/* consume a pseudo */
|
|
while (isspace(*p) == false && *p != ',')
|
|
p++;
|
|
|
|
/* Pseudo elements */
|
|
if (p - start == 12 &&
|
|
strncasecmp(start, "first-letter", 12) == 0)
|
|
*element = CSS_PSEUDO_ELEMENT_FIRST_LETTER;
|
|
else if (p - start == 10 &&
|
|
strncasecmp(start, "first-line", 10) == 0)
|
|
*element = CSS_PSEUDO_ELEMENT_FIRST_LINE;
|
|
else if (p - start == 6 &&
|
|
strncasecmp(start, "before", 6) == 0)
|
|
*element = CSS_PSEUDO_ELEMENT_BEFORE;
|
|
else if (p - start == 5 &&
|
|
strncasecmp(start, "after", 5) == 0)
|
|
*element = CSS_PSEUDO_ELEMENT_AFTER;
|
|
else
|
|
assert(0 && "Unknown pseudo");
|
|
|
|
/* Consume whitespace */
|
|
while (p < end && isspace(*p))
|
|
p++;
|
|
|
|
/* Stop if we've reached the end */
|
|
if (p == end || *p != ',')
|
|
break;
|
|
|
|
/* Consume comma */
|
|
p++;
|
|
|
|
/* Consume whitespace */
|
|
while (p < end && isspace(*p))
|
|
p++;
|
|
}
|
|
|
|
*data = p;
|
|
*len = end - p;
|
|
}
|
|
|
|
void css__parse_expected(line_ctx *ctx, const char *data, size_t len)
|
|
{
|
|
while (ctx->expused + len >= ctx->explen) {
|
|
size_t required = ctx->explen == 0 ? 64 : ctx->explen * 2;
|
|
char *temp = realloc(ctx->exp, required);
|
|
if (temp == NULL) {
|
|
assert(0 && "No memory for expected output");
|
|
}
|
|
|
|
ctx->exp = temp;
|
|
ctx->explen = required;
|
|
}
|
|
|
|
memcpy(ctx->exp + ctx->expused, data, len);
|
|
|
|
ctx->expused += len;
|
|
}
|
|
|
|
void run_test(line_ctx *ctx, const char *exp, size_t explen)
|
|
{
|
|
css_select_ctx *select;
|
|
css_select_results *results;
|
|
uint32_t i;
|
|
char *buf;
|
|
size_t buflen;
|
|
static int testnum;
|
|
|
|
UNUSED(exp);
|
|
|
|
buf = malloc(8192);
|
|
if (buf == NULL) {
|
|
assert(0 && "No memory for result data");
|
|
}
|
|
buflen = 8192;
|
|
|
|
assert(css_select_ctx_create(myrealloc, NULL, &select) == CSS_OK);
|
|
|
|
for (i = 0; i < ctx->n_sheets; i++) {
|
|
assert(css_select_ctx_append_sheet(select,
|
|
ctx->sheets[i].sheet, ctx->sheets[i].origin,
|
|
ctx->sheets[i].media) == CSS_OK);
|
|
}
|
|
|
|
testnum++;
|
|
|
|
assert(css_select_style(select, ctx->target, ctx->media, NULL,
|
|
&select_handler, ctx, &results) == CSS_OK);
|
|
|
|
assert(results->styles[ctx->pseudo_element] != NULL);
|
|
|
|
dump_computed_style(results->styles[ctx->pseudo_element], buf, &buflen);
|
|
|
|
if (8192 - buflen != explen || memcmp(buf, exp, explen) != 0) {
|
|
printf("Expected (%u):\n%.*s\n",
|
|
(int) explen, (int) explen, exp);
|
|
printf("Result (%u):\n%.*s\n", (int) (8192 - buflen),
|
|
(int) (8192 - buflen), buf);
|
|
assert(0 && "Result doesn't match expected");
|
|
}
|
|
|
|
/* Clean up */
|
|
css_select_results_destroy(results);
|
|
css_select_ctx_destroy(select);
|
|
|
|
destroy_tree(ctx->tree);
|
|
|
|
for (i = 0; i < ctx->n_sheets; i++) {
|
|
css_stylesheet_destroy(ctx->sheets[i].sheet);
|
|
}
|
|
|
|
ctx->tree = NULL;
|
|
ctx->current = NULL;
|
|
ctx->depth = 0;
|
|
ctx->n_sheets = 0;
|
|
free(ctx->sheets);
|
|
ctx->sheets = NULL;
|
|
ctx->target = NULL;
|
|
|
|
free(buf);
|
|
|
|
printf("Test %d: PASS\n", testnum);
|
|
}
|
|
|
|
void destroy_tree(node *root)
|
|
{
|
|
node *n, *p;
|
|
uint32_t i;
|
|
|
|
for (n = root->children; n != NULL; n = p) {
|
|
p = n->next;
|
|
|
|
destroy_tree(n);
|
|
}
|
|
|
|
for (i = 0; i < root->n_attrs; ++i) {
|
|
lwc_string_unref(root->attrs[i].name);
|
|
lwc_string_unref(root->attrs[i].value);
|
|
}
|
|
|
|
free(root->attrs);
|
|
|
|
lwc_string_unref(root->name);
|
|
free(root);
|
|
}
|
|
|
|
|
|
css_error node_name(void *pw, void *n, css_qname *qname)
|
|
{
|
|
node *node = n;
|
|
|
|
UNUSED(pw);
|
|
|
|
qname->name = lwc_string_ref(node->name);
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_classes(void *pw, void *n,
|
|
lwc_string ***classes, uint32_t *n_classes)
|
|
{
|
|
node *node = n;
|
|
uint32_t i;
|
|
line_ctx *lc = pw;
|
|
|
|
for (i = 0; i < node->n_attrs; i++) {
|
|
bool amatch = false;
|
|
assert(lwc_string_caseless_isequal(
|
|
node->attrs[i].name, lc->attr_class, &amatch) ==
|
|
lwc_error_ok);
|
|
if (amatch == true)
|
|
break;
|
|
}
|
|
|
|
if (i != node->n_attrs) {
|
|
*classes = realloc(NULL, sizeof(lwc_string **));
|
|
if (*classes == NULL)
|
|
return CSS_NOMEM;
|
|
|
|
*(classes[0]) =
|
|
lwc_string_ref(node->attrs[i].value);
|
|
*n_classes = 1;
|
|
} else {
|
|
*classes = NULL;
|
|
*n_classes = 0;
|
|
}
|
|
|
|
return CSS_OK;
|
|
|
|
}
|
|
|
|
css_error node_id(void *pw, void *n,
|
|
lwc_string **id)
|
|
{
|
|
node *node = n;
|
|
uint32_t i;
|
|
line_ctx *lc = pw;
|
|
|
|
for (i = 0; i < node->n_attrs; i++) {
|
|
bool amatch = false;
|
|
assert(lwc_string_caseless_isequal(
|
|
node->attrs[i].name, lc->attr_id, &amatch) ==
|
|
lwc_error_ok);
|
|
if (amatch == true)
|
|
break;
|
|
}
|
|
|
|
if (i != node->n_attrs)
|
|
*id = lwc_string_ref(node->attrs[i].value);
|
|
else
|
|
*id = NULL;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error named_ancestor_node(void *pw, void *n,
|
|
const css_qname *qname,
|
|
void **ancestor)
|
|
{
|
|
node *node = n;
|
|
UNUSED(pw);
|
|
|
|
for (node = node->parent; node != NULL; node = node->parent) {
|
|
bool match = false;
|
|
assert(lwc_string_caseless_isequal(
|
|
qname->name, node->name,
|
|
&match) == lwc_error_ok);
|
|
if (match == true)
|
|
break;
|
|
}
|
|
|
|
*ancestor = (void *) node;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error named_parent_node(void *pw, void *n,
|
|
const css_qname *qname,
|
|
void **parent)
|
|
{
|
|
node *node = n;
|
|
UNUSED(pw);
|
|
|
|
*parent = NULL;
|
|
if (node->parent != NULL) {
|
|
bool match = false;
|
|
assert(lwc_string_caseless_isequal(
|
|
qname->name, node->parent->name, &match) ==
|
|
lwc_error_ok);
|
|
if (match == true)
|
|
*parent = (void *) node->parent;
|
|
}
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error named_sibling_node(void *pw, void *n,
|
|
const css_qname *qname,
|
|
void **sibling)
|
|
{
|
|
node *node = n;
|
|
UNUSED(pw);
|
|
|
|
*sibling = NULL;
|
|
if (node->prev != NULL) {
|
|
bool match = false;
|
|
assert(lwc_string_caseless_isequal(
|
|
qname->name, node->prev->name, &match) ==
|
|
lwc_error_ok);
|
|
if (match == true)
|
|
*sibling = (void *) node->prev;
|
|
}
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error named_generic_sibling_node(void *pw, void *n,
|
|
const css_qname *qname,
|
|
void **sibling)
|
|
{
|
|
node *node = n;
|
|
UNUSED(pw);
|
|
|
|
for (node = node->prev; node != NULL; node = node->prev) {
|
|
bool match = false;
|
|
assert(lwc_string_caseless_isequal(
|
|
qname->name, node->name,
|
|
&match) == lwc_error_ok);
|
|
if (match == true)
|
|
break;
|
|
}
|
|
|
|
*sibling = (void *) node;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error parent_node(void *pw, void *n, void **parent)
|
|
{
|
|
node *node = n;
|
|
|
|
UNUSED(pw);
|
|
|
|
*parent = (void *) node->parent;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error sibling_node(void *pw, void *n, void **sibling)
|
|
{
|
|
node *node = n;
|
|
|
|
UNUSED(pw);
|
|
|
|
*sibling = (void *) node->prev;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_has_name(void *pw, void *n,
|
|
const css_qname *qname,
|
|
bool *match)
|
|
{
|
|
node *node = n;
|
|
UNUSED(pw);
|
|
|
|
if (lwc_string_length(qname->name) == 1 &&
|
|
lwc_string_data(qname->name)[0] == '*')
|
|
*match = true;
|
|
else
|
|
assert(lwc_string_caseless_isequal(node->name,
|
|
qname->name, match) == lwc_error_ok);
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_has_class(void *pw, void *n,
|
|
lwc_string *name,
|
|
bool *match)
|
|
{
|
|
node *node = n;
|
|
uint32_t i;
|
|
line_ctx *ctx = pw;
|
|
|
|
for (i = 0; i < node->n_attrs; i++) {
|
|
bool amatch = false;
|
|
assert(lwc_string_caseless_isequal(
|
|
node->attrs[i].name, ctx->attr_class,
|
|
&amatch) == lwc_error_ok);
|
|
if (amatch == true)
|
|
break;
|
|
}
|
|
|
|
/* Classes are case-sensitive in HTML */
|
|
if (i != node->n_attrs && name == node->attrs[i].value)
|
|
*match = true;
|
|
else
|
|
*match = false;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_has_id(void *pw, void *n,
|
|
lwc_string *name,
|
|
bool *match)
|
|
{
|
|
node *node = n;
|
|
uint32_t i;
|
|
line_ctx *ctx = pw;
|
|
|
|
for (i = 0; i < node->n_attrs; i++) {
|
|
bool amatch = false;
|
|
assert(lwc_string_caseless_isequal(
|
|
node->attrs[i].name, ctx->attr_id, &amatch) ==
|
|
lwc_error_ok);
|
|
if (amatch == true)
|
|
break;
|
|
}
|
|
|
|
/* IDs are case-sensitive in HTML */
|
|
if (i != node->n_attrs && name == node->attrs[i].value)
|
|
*match = true;
|
|
else
|
|
*match = false;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_has_attribute(void *pw, void *n,
|
|
const css_qname *qname,
|
|
bool *match)
|
|
{
|
|
node *node = n;
|
|
uint32_t i;
|
|
UNUSED(pw);
|
|
|
|
*match = false;
|
|
for (i = 0; i < node->n_attrs; i++) {
|
|
assert(lwc_string_caseless_isequal(
|
|
node->attrs[i].name, qname->name, match) ==
|
|
lwc_error_ok);
|
|
if (*match == true)
|
|
break;
|
|
}
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_has_attribute_equal(void *pw, void *n,
|
|
const css_qname *qname,
|
|
lwc_string *value,
|
|
bool *match)
|
|
{
|
|
node *node = n;
|
|
uint32_t i;
|
|
UNUSED(pw);
|
|
|
|
*match = false;
|
|
|
|
for (i = 0; i < node->n_attrs; i++) {
|
|
assert(lwc_string_caseless_isequal(
|
|
node->attrs[i].name, qname->name, match) ==
|
|
lwc_error_ok);
|
|
if (*match == true)
|
|
break;
|
|
}
|
|
|
|
if (*match == true) {
|
|
assert(lwc_string_caseless_isequal(
|
|
node->attrs[i].name, value, match) ==
|
|
lwc_error_ok);
|
|
}
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_has_attribute_includes(void *pw, void *n,
|
|
const css_qname *qname,
|
|
lwc_string *value,
|
|
bool *match)
|
|
{
|
|
node *node = n;
|
|
uint32_t i;
|
|
size_t vlen = lwc_string_length(value);
|
|
UNUSED(pw);
|
|
|
|
*match = false;
|
|
|
|
for (i = 0; i < node->n_attrs; i++) {
|
|
assert(lwc_string_caseless_isequal(
|
|
node->attrs[i].name, qname->name, match) ==
|
|
lwc_error_ok);
|
|
if (*match == true)
|
|
break;
|
|
}
|
|
|
|
if (*match == true) {
|
|
const char *p;
|
|
const char *start = lwc_string_data(node->attrs[i].value);
|
|
const char *end = start +
|
|
lwc_string_length(node->attrs[i].value);
|
|
|
|
*match = false;
|
|
|
|
for (p = start; p < end; p++) {
|
|
if (*p == ' ') {
|
|
if ((size_t) (p - start) == vlen &&
|
|
strncasecmp(start,
|
|
lwc_string_data(value),
|
|
vlen) == 0) {
|
|
*match = true;
|
|
break;
|
|
}
|
|
|
|
start = p + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_has_attribute_dashmatch(void *pw, void *n,
|
|
const css_qname *qname,
|
|
lwc_string *value,
|
|
bool *match)
|
|
{
|
|
node *node = n;
|
|
uint32_t i;
|
|
size_t vlen = lwc_string_length(value);
|
|
UNUSED(pw);
|
|
|
|
*match = false;
|
|
|
|
for (i = 0; i < node->n_attrs; i++) {
|
|
assert(lwc_string_caseless_isequal(
|
|
node->attrs[i].name, qname->name, match) ==
|
|
lwc_error_ok);
|
|
if (*match == true)
|
|
break;
|
|
}
|
|
|
|
if (*match == true) {
|
|
const char *p;
|
|
const char *start = lwc_string_data(node->attrs[i].value);
|
|
const char *end = start +
|
|
lwc_string_length(node->attrs[i].value);
|
|
|
|
*match = false;
|
|
|
|
for (p = start; p < end; p++) {
|
|
if (*p == '-') {
|
|
if ((size_t) (p - start) == vlen &&
|
|
strncasecmp(start,
|
|
lwc_string_data(value),
|
|
vlen) == 0) {
|
|
*match = true;
|
|
break;
|
|
}
|
|
|
|
start = p + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_has_attribute_prefix(void *pw, void *n,
|
|
const css_qname *qname,
|
|
lwc_string *value,
|
|
bool *match)
|
|
{
|
|
node *node = n;
|
|
uint32_t i;
|
|
UNUSED(pw);
|
|
|
|
*match = false;
|
|
|
|
for (i = 0; i < node->n_attrs; i++) {
|
|
assert(lwc_string_caseless_isequal(
|
|
node->attrs[i].name, qname->name, match) ==
|
|
lwc_error_ok);
|
|
if (*match == true)
|
|
break;
|
|
}
|
|
|
|
if (*match == true) {
|
|
size_t len = lwc_string_length(node->attrs[i].value);
|
|
const char *data = lwc_string_data(node->attrs[i].value);
|
|
|
|
size_t vlen = lwc_string_length(value);
|
|
const char *vdata = lwc_string_data(value);
|
|
|
|
if (len < vlen)
|
|
*match = false;
|
|
else
|
|
*match = (strncasecmp(data, vdata, vlen) == 0);
|
|
}
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_has_attribute_suffix(void *pw, void *n,
|
|
const css_qname *qname,
|
|
lwc_string *value,
|
|
bool *match)
|
|
{
|
|
node *node = n;
|
|
uint32_t i;
|
|
UNUSED(pw);
|
|
|
|
*match = false;
|
|
|
|
for (i = 0; i < node->n_attrs; i++) {
|
|
assert(lwc_string_caseless_isequal(
|
|
node->attrs[i].name, qname->name, match) ==
|
|
lwc_error_ok);
|
|
if (*match == true)
|
|
break;
|
|
}
|
|
|
|
if (*match == true) {
|
|
size_t len = lwc_string_length(node->attrs[i].value);
|
|
const char *data = lwc_string_data(node->attrs[i].value);
|
|
|
|
size_t vlen = lwc_string_length(value);
|
|
const char *vdata = lwc_string_data(value);
|
|
|
|
size_t suffix_start = len - vlen;
|
|
|
|
if (len < vlen)
|
|
*match = false;
|
|
else {
|
|
*match = (strncasecmp(data + suffix_start,
|
|
vdata, vlen) == 0);
|
|
}
|
|
}
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_has_attribute_substring(void *pw, void *n,
|
|
const css_qname *qname,
|
|
lwc_string *value,
|
|
bool *match)
|
|
{
|
|
node *node = n;
|
|
uint32_t i;
|
|
UNUSED(pw);
|
|
|
|
*match = false;
|
|
|
|
for (i = 0; i < node->n_attrs; i++) {
|
|
assert(lwc_string_caseless_isequal(
|
|
node->attrs[i].name, qname->name, match) ==
|
|
lwc_error_ok);
|
|
if (*match == true)
|
|
break;
|
|
}
|
|
|
|
if (*match == true) {
|
|
size_t len = lwc_string_length(node->attrs[i].value);
|
|
const char *data = lwc_string_data(node->attrs[i].value);
|
|
|
|
size_t vlen = lwc_string_length(value);
|
|
const char *vdata = lwc_string_data(value);
|
|
|
|
const char *last_start = data + len - vlen;
|
|
|
|
if (len < vlen)
|
|
*match = false;
|
|
else {
|
|
while (data <= last_start) {
|
|
if (strncasecmp(data, vdata, vlen) == 0) {
|
|
*match = true;
|
|
break;
|
|
}
|
|
|
|
data++;
|
|
}
|
|
|
|
if (data > last_start)
|
|
*match = false;
|
|
}
|
|
}
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_is_root(void *pw, void *n, bool *match)
|
|
{
|
|
node *node = n;
|
|
UNUSED(pw);
|
|
|
|
*match = (node->parent == NULL);
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_count_siblings(void *pw, void *n,
|
|
bool same_name, bool after, int32_t *count)
|
|
{
|
|
int32_t cnt = 0;
|
|
bool match = false;
|
|
node *node = n;
|
|
lwc_string *name = node->name;
|
|
UNUSED(pw);
|
|
|
|
if (after) {
|
|
while (node->next != NULL) {
|
|
if (same_name) {
|
|
assert(lwc_string_caseless_isequal(
|
|
name, node->next->name, &match) ==
|
|
lwc_error_ok);
|
|
|
|
if (match)
|
|
cnt++;
|
|
} else {
|
|
cnt++;
|
|
}
|
|
|
|
node = node->next;
|
|
}
|
|
} else {
|
|
while (node->prev != NULL) {
|
|
if (same_name) {
|
|
assert(lwc_string_caseless_isequal(
|
|
name, node->prev->name, &match) ==
|
|
lwc_error_ok);
|
|
|
|
if (match)
|
|
cnt++;
|
|
} else {
|
|
cnt++;
|
|
}
|
|
|
|
node = node->prev;
|
|
}
|
|
}
|
|
|
|
*count = cnt;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_is_empty(void *pw, void *n, bool *match)
|
|
{
|
|
node *node = n;
|
|
UNUSED(pw);
|
|
|
|
*match = (node->children == NULL);
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_is_link(void *pw, void *n, bool *match)
|
|
{
|
|
node *node = n;
|
|
|
|
UNUSED(pw);
|
|
UNUSED(node);
|
|
|
|
*match = false;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_is_visited(void *pw, void *n, bool *match)
|
|
{
|
|
node *node = n;
|
|
|
|
UNUSED(pw);
|
|
UNUSED(node);
|
|
|
|
*match = false;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_is_hover(void *pw, void *n, bool *match)
|
|
{
|
|
node *node = n;
|
|
|
|
UNUSED(pw);
|
|
UNUSED(node);
|
|
|
|
*match = false;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_is_active(void *pw, void *n, bool *match)
|
|
{
|
|
node *node = n;
|
|
|
|
UNUSED(pw);
|
|
UNUSED(node);
|
|
|
|
*match = false;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_is_focus(void *pw, void *n, bool *match)
|
|
{
|
|
node *node = n;
|
|
|
|
UNUSED(pw);
|
|
UNUSED(node);
|
|
|
|
*match = false;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_is_enabled(void *pw, void *n, bool *match)
|
|
{
|
|
node *node = n;
|
|
|
|
UNUSED(pw);
|
|
UNUSED(node);
|
|
|
|
*match = false;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_is_disabled(void *pw, void *n, bool *match)
|
|
{
|
|
node *node = n;
|
|
|
|
UNUSED(pw);
|
|
UNUSED(node);
|
|
|
|
*match = false;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_is_checked(void *pw, void *n, bool *match)
|
|
{
|
|
node *node = n;
|
|
|
|
UNUSED(pw);
|
|
UNUSED(node);
|
|
|
|
*match = false;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_is_target(void *pw, void *n, bool *match)
|
|
{
|
|
node *node = n;
|
|
|
|
UNUSED(pw);
|
|
UNUSED(node);
|
|
|
|
*match = false;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_is_lang(void *pw, void *n,
|
|
lwc_string *lang,
|
|
bool *match)
|
|
{
|
|
node *node = n;
|
|
|
|
UNUSED(pw);
|
|
UNUSED(node);
|
|
UNUSED(lang);
|
|
|
|
*match = false;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error node_presentational_hint(void *pw, void *node,
|
|
uint32_t property, css_hint *hint)
|
|
{
|
|
UNUSED(pw);
|
|
UNUSED(node);
|
|
UNUSED(property);
|
|
UNUSED(hint);
|
|
|
|
return CSS_PROPERTY_NOT_SET;
|
|
}
|
|
|
|
css_error ua_default_for_property(void *pw, uint32_t property, css_hint *hint)
|
|
{
|
|
UNUSED(pw);
|
|
|
|
if (property == CSS_PROP_COLOR) {
|
|
hint->data.color = 0xff000000;
|
|
hint->status = CSS_COLOR_COLOR;
|
|
} else if (property == CSS_PROP_FONT_FAMILY) {
|
|
hint->data.strings = NULL;
|
|
hint->status = CSS_FONT_FAMILY_SANS_SERIF;
|
|
} else if (property == CSS_PROP_QUOTES) {
|
|
/* Not exactly useful :) */
|
|
hint->data.strings = NULL;
|
|
hint->status = CSS_QUOTES_NONE;
|
|
} else if (property == CSS_PROP_VOICE_FAMILY) {
|
|
/** \todo Fix this when we have voice-family done */
|
|
hint->data.strings = NULL;
|
|
hint->status = 0;
|
|
} else {
|
|
return CSS_INVALID;
|
|
}
|
|
|
|
return CSS_OK;
|
|
}
|
|
|
|
css_error compute_font_size(void *pw, const css_hint *parent, css_hint *size)
|
|
{
|
|
static css_hint_length sizes[] = {
|
|
{ FLTTOFIX(6.75), CSS_UNIT_PT },
|
|
{ FLTTOFIX(7.50), CSS_UNIT_PT },
|
|
{ FLTTOFIX(9.75), CSS_UNIT_PT },
|
|
{ FLTTOFIX(12.0), CSS_UNIT_PT },
|
|
{ FLTTOFIX(13.5), CSS_UNIT_PT },
|
|
{ FLTTOFIX(18.0), CSS_UNIT_PT },
|
|
{ FLTTOFIX(24.0), CSS_UNIT_PT }
|
|
};
|
|
const css_hint_length *parent_size;
|
|
|
|
UNUSED(pw);
|
|
|
|
/* Grab parent size, defaulting to medium if none */
|
|
if (parent == NULL) {
|
|
parent_size = &sizes[CSS_FONT_SIZE_MEDIUM - 1];
|
|
} else {
|
|
assert(parent->status == CSS_FONT_SIZE_DIMENSION);
|
|
assert(parent->data.length.unit != CSS_UNIT_EM);
|
|
assert(parent->data.length.unit != CSS_UNIT_EX);
|
|
parent_size = &parent->data.length;
|
|
}
|
|
|
|
assert(size->status != CSS_FONT_SIZE_INHERIT);
|
|
|
|
if (size->status < CSS_FONT_SIZE_LARGER) {
|
|
/* Keyword -- simple */
|
|
size->data.length = sizes[size->status - 1];
|
|
} else if (size->status == CSS_FONT_SIZE_LARGER) {
|
|
/** \todo Step within table, if appropriate */
|
|
size->data.length.value =
|
|
FMUL(parent_size->value, FLTTOFIX(1.2));
|
|
size->data.length.unit = parent_size->unit;
|
|
} else if (size->status == CSS_FONT_SIZE_SMALLER) {
|
|
/** \todo Step within table, if appropriate */
|
|
size->data.length.value =
|
|
FMUL(parent_size->value, FLTTOFIX(1.2));
|
|
size->data.length.unit = parent_size->unit;
|
|
} else if (size->data.length.unit == CSS_UNIT_EM ||
|
|
size->data.length.unit == CSS_UNIT_EX) {
|
|
size->data.length.value =
|
|
FMUL(size->data.length.value, parent_size->value);
|
|
|
|
if (size->data.length.unit == CSS_UNIT_EX) {
|
|
size->data.length.value = FMUL(size->data.length.value,
|
|
FLTTOFIX(0.6));
|
|
}
|
|
|
|
size->data.length.unit = parent_size->unit;
|
|
} else if (size->data.length.unit == CSS_UNIT_PCT) {
|
|
size->data.length.value = FDIV(FMUL(size->data.length.value,
|
|
parent_size->value), FLTTOFIX(100));
|
|
size->data.length.unit = parent_size->unit;
|
|
}
|
|
|
|
size->status = CSS_FONT_SIZE_DIMENSION;
|
|
|
|
return CSS_OK;
|
|
}
|
|
|