kolibrios/contrib/network/netsurf/libcss/test/select-auto.c

1581 lines
33 KiB
C
Raw Normal View History

#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(&params, 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;
}