1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-06-27 01:25:34 +00:00
elinks/src/document/libdom/css.c
Witold Filipczyk cc718c30b5 [spidermonkey] document.querySelector
It passes test case.
2024-05-05 16:58:10 +02:00

2435 lines
57 KiB
C

/* CSS */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "elinks.h"
#include <stdio.h>
#include <dom/dom.h>
#include <dom/bindings/hubbub/parser.h>
#include <libcss/libcss.h>
#include "cache/cache.h"
#include "document/html/internal.h"
#include "document/libdom/css.h"
#include "document/libdom/mapa.h"
#include "document/libdom/corestrings.h"
#include "util/string.h"
#define UNUSED(a)
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_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_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 ua_default_for_property(void *pw, uint32_t property,
css_hint *hint);
static css_error set_libcss_node_data(void *pw, void *node,
void *libcss_node_data);
static css_error get_libcss_node_data(void *pw, void *node,
void **libcss_node_data);
static css_error named_ancestor_node(void *pw, void *node,
const css_qname *qname, void **ancestor);
static css_error node_is_visited(void *pw, void *node, bool *match);
static css_error node_presentational_hint(void *pw, void *node,
uint32_t *nhints, css_hint **hints);
static css_error node_presentational_hint(void *pw, void *node,
uint32_t *nhints, css_hint **hints)
{
//fprintf(stderr, "%s: node=%s\n", __FUNCTION__, node);
// UNUSED(pw);
// UNUSED(node);
*nhints = 0;
*hints = NULL;
return CSS_OK;
}
static css_error
resolve_url_empty(void *pw, const char *base, lwc_string *rel, lwc_string **abs)
{
return CSS_OK;
}
css_error
resolve_url(void *pw, const char *base, lwc_string *rel, lwc_string **abs)
{
lwc_error lerror;
char *url = straconcat(base, lwc_string_data(rel), NULL);
if (!url) {
*abs = NULL;
return CSS_NOMEM;
}
lerror = lwc_intern_string(url, strlen(url), abs);
if (lerror != lwc_error_ok) {
*abs = NULL;
mem_free(url);
return lerror == lwc_error_oom ? CSS_NOMEM : CSS_INVALID;
}
mem_free(url);
return CSS_OK;
}
/**
* Create an inline style
*
* \param data Source data
* \param len Length of data in bytes
* \param charset Charset of data, or NULL if unknown
* \param url Base URL of document containing data
* \param allow_quirks True to permit CSS parsing quirks
* \return Pointer to stylesheet, or NULL on failure.
*/
css_stylesheet *nscss_create_inline_style(const uint8_t *data, size_t len,
const char *charset, const char *url, bool allow_quirks)
{
css_stylesheet_params params;
css_stylesheet *sheet;
css_error error;
params.params_version = CSS_STYLESHEET_PARAMS_VERSION_1;
params.level = CSS_LEVEL_DEFAULT;
params.charset = charset;
params.url = url;
params.title = NULL;
params.allow_quirks = allow_quirks;
params.inline_style = true;
params.resolve = resolve_url;
params.resolve_pw = NULL;
params.import = NULL;
params.import_pw = NULL;
params.color = NULL;// ns_system_colour;
params.color_pw = NULL;
params.font = NULL;
params.font_pw = NULL;
error = css_stylesheet_create(&params, &sheet);
if (error != CSS_OK) {
fprintf(stderr, "Failed creating sheet: %d", error);
return NULL;
}
error = css_stylesheet_append_data(sheet, data, len);
if (error != CSS_OK && error != CSS_NEEDDATA) {
fprintf(stderr, "failed appending data: %d", error);
css_stylesheet_destroy(sheet);
return NULL;
}
error = css_stylesheet_data_done(sheet);
if (error != CSS_OK) {
fprintf(stderr, "failed completing parse: %d", error);
css_stylesheet_destroy(sheet);
return NULL;
}
return sheet;
}
/**
* Selection callback table for libcss
*/
static css_select_handler selection_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,
set_libcss_node_data,
get_libcss_node_data
};
/* Handler for libcss_node_data, stored as libdom node user data */
static void nscss_dom_user_data_handler(dom_node_operation operation,
dom_string *key, void *data, struct dom_node *src,
struct dom_node *dst)
{
css_error error;
if (dom_string_isequal(corestring_dom___ns_key_libcss_node_data,
key) == false || data == NULL) {
return;
}
switch (operation) {
case DOM_NODE_CLONED:
error = css_libcss_node_data_handler(&selection_handler,
CSS_NODE_CLONED,
NULL, src, dst, data);
if (error != CSS_OK)
fprintf(stderr,
"Failed to clone libcss_node_data.");
break;
case DOM_NODE_RENAMED:
error = css_libcss_node_data_handler(&selection_handler,
CSS_NODE_MODIFIED,
NULL, src, NULL, data);
if (error != CSS_OK)
fprintf(stderr,
"Failed to update libcss_node_data.");
break;
case DOM_NODE_IMPORTED:
case DOM_NODE_ADOPTED:
case DOM_NODE_DELETED:
error = css_libcss_node_data_handler(&selection_handler,
CSS_NODE_DELETED,
NULL, src, NULL, data);
if (error != CSS_OK)
fprintf(stderr,
"Failed to delete libcss_node_data.");
break;
default:
fprintf(stderr, "User data operation not handled.");
assert(0);
}
}
/**
* Get style selection results for an element
*
* \param ctx CSS selection context
* \param n Element to select for
* \param media Permitted media types
* \param inline_style Inline style associated with element, or NULL
* \return Pointer to selection results (containing computed styles),
* or NULL on failure
*/
css_select_results *nscss_get_style(nscss_select_ctx *ctx, dom_node *n,
const css_media *media,
const css_unit_ctx *unit_len_ctx,
const css_stylesheet *inline_style)
{
css_computed_style *composed;
css_select_results *styles;
int pseudo_element;
css_error error;
/* Select style for node */
error = css_select_style(ctx->ctx, n, unit_len_ctx, media, inline_style,
&selection_handler, ctx, &styles);
if (error != CSS_OK || styles == NULL) {
/* Failed selecting partial style -- bail out */
return NULL;
}
/* If there's a parent style, compose with partial to obtain
* complete computed style for element */
if (ctx->parent_style != NULL) {
/* Complete the computed style, by composing with the parent
* element's style */
error = css_computed_style_compose(ctx->parent_style,
styles->styles[CSS_PSEUDO_ELEMENT_NONE],
unit_len_ctx, &composed);
if (error != CSS_OK) {
css_select_results_destroy(styles);
return NULL;
}
/* Replace select_results style with composed style */
css_computed_style_destroy(
styles->styles[CSS_PSEUDO_ELEMENT_NONE]);
styles->styles[CSS_PSEUDO_ELEMENT_NONE] = composed;
}
for (pseudo_element = CSS_PSEUDO_ELEMENT_NONE + 1;
pseudo_element < CSS_PSEUDO_ELEMENT_COUNT;
pseudo_element++) {
if (pseudo_element == CSS_PSEUDO_ELEMENT_FIRST_LETTER ||
pseudo_element == CSS_PSEUDO_ELEMENT_FIRST_LINE)
/* TODO: Handle first-line and first-letter pseudo
* element computed style completion */
continue;
if (styles->styles[pseudo_element] == NULL)
/* There were no rules concerning this pseudo element */
continue;
/* Complete the pseudo element's computed style, by composing
* with the base element's style */
error = css_computed_style_compose(
styles->styles[CSS_PSEUDO_ELEMENT_NONE],
styles->styles[pseudo_element],
unit_len_ctx, &composed);
if (error != CSS_OK) {
/* TODO: perhaps this shouldn't be quite so
* catastrophic? */
css_select_results_destroy(styles);
return NULL;
}
/* Replace select_results style with composed style */
css_computed_style_destroy(styles->styles[pseudo_element]);
styles->styles[pseudo_element] = composed;
}
return styles;
}
/**
* Get a blank style
*
* \param ctx CSS selection context
* \param parent Parent style to cascade inherited properties from
* \return Pointer to blank style, or NULL on failure
*/
css_computed_style *nscss_get_blank_style(nscss_select_ctx *ctx,
const css_unit_ctx *unit_len_ctx,
const css_computed_style *parent)
{
css_computed_style *partial, *composed;
css_error error;
error = css_select_default_style(ctx->ctx,
&selection_handler, ctx, &partial);
if (error != CSS_OK) {
return NULL;
}
/* TODO: Do we really need to compose? Initial style shouldn't
* have any inherited properties. */
error = css_computed_style_compose(parent, partial,
unit_len_ctx, &composed);
css_computed_style_destroy(partial);
if (error != CSS_OK) {
css_computed_style_destroy(composed);
return NULL;
}
return composed;
}
/******************************************************************************
* Style selection callbacks *
******************************************************************************/
/**
* Callback to retrieve a node's name.
*
* \param pw HTML document
* \param node DOM node
* \param qname Pointer to location to receive node name
* \return CSS_OK on success,
* CSS_NOMEM on memory exhaustion.
*/
css_error node_name(void *pw, void *node, css_qname *qname)
{
dom_node *n = node;
dom_string *name;
dom_exception err;
err = dom_node_get_node_name(n, &name);
if (err != DOM_NO_ERR)
return CSS_NOMEM;
qname->ns = NULL;
err = dom_string_intern(name, &qname->name);
if (err != DOM_NO_ERR) {
dom_string_unref(name);
return CSS_NOMEM;
}
dom_string_unref(name);
return CSS_OK;
}
/**
* Callback to retrieve a node's classes.
*
* \param pw HTML document
* \param node DOM node
* \param classes Pointer to location to receive class name array
* \param n_classes Pointer to location to receive length of class name array
* \return CSS_OK on success,
* CSS_NOMEM on memory exhaustion.
*
* \note The returned array will be destroyed by libcss. Therefore, it must
* be allocated using the same allocator as used by libcss during style
* selection.
*/
css_error node_classes(void *pw, void *node,
lwc_string ***classes, uint32_t *n_classes)
{
dom_node *n = node;
dom_exception err;
*classes = NULL;
*n_classes = 0;
err = dom_element_get_classes(n, classes, n_classes);
if (err != DOM_NO_ERR)
return CSS_NOMEM;
return CSS_OK;
}
/**
* Callback to retrieve a node's ID.
*
* \param pw HTML document
* \param node DOM node
* \param id Pointer to location to receive id value
* \return CSS_OK on success,
* CSS_NOMEM on memory exhaustion.
*/
css_error node_id(void *pw, void *node, lwc_string **id)
{
dom_node *n = node;
dom_string *attr;
dom_exception err;
*id = NULL;
/** \todo Assumes an HTML DOM */
err = dom_html_element_get_id(n, &attr);
if (err != DOM_NO_ERR)
return CSS_NOMEM;
if (attr != NULL) {
err = dom_string_intern(attr, id);
if (err != DOM_NO_ERR) {
dom_string_unref(attr);
return CSS_NOMEM;
}
dom_string_unref(attr);
}
return CSS_OK;
}
/**
* Callback to find a named ancestor node.
*
* \param pw HTML document
* \param node DOM node
* \param qname Node name to search for
* \param ancestor Pointer to location to receive ancestor
* \return CSS_OK.
*
* \post \a ancestor will contain the result, or NULL if there is no match
*/
css_error named_ancestor_node(void *pw, void *node,
const css_qname *qname, void **ancestor)
{
dom_element_named_ancestor_node(node, qname->name,
(struct dom_element **)ancestor);
return CSS_OK;
}
/**
* Callback to find a named parent node
*
* \param pw HTML document
* \param node DOM node
* \param qname Node name to search for
* \param parent Pointer to location to receive parent
* \return CSS_OK.
*
* \post \a parent will contain the result, or NULL if there is no match
*/
css_error named_parent_node(void *pw, void *node,
const css_qname *qname, void **parent)
{
dom_element_named_parent_node(node, qname->name,
(struct dom_element **)parent);
return CSS_OK;
}
/**
* Callback to find a named sibling node.
*
* \param pw HTML document
* \param node DOM node
* \param qname Node name to search for
* \param sibling Pointer to location to receive sibling
* \return CSS_OK.
*
* \post \a sibling will contain the result, or NULL if there is no match
*/
css_error named_sibling_node(void *pw, void *node,
const css_qname *qname, void **sibling)
{
dom_node *n = node;
dom_node *prev;
dom_exception err;
*sibling = NULL;
/* Find sibling element */
err = dom_node_get_previous_sibling(n, &n);
if (err != DOM_NO_ERR)
return CSS_OK;
while (n != NULL) {
dom_node_type type;
err = dom_node_get_node_type(n, &type);
if (err != DOM_NO_ERR) {
dom_node_unref(n);
return CSS_OK;
}
if (type == DOM_ELEMENT_NODE)
break;
err = dom_node_get_previous_sibling(n, &prev);
if (err != DOM_NO_ERR) {
dom_node_unref(n);
return CSS_OK;
}
dom_node_unref(n);
n = prev;
}
if (n != NULL) {
dom_string *name;
err = dom_node_get_node_name(n, &name);
if (err != DOM_NO_ERR) {
dom_node_unref(n);
return CSS_OK;
}
dom_node_unref(n);
if (dom_string_caseless_lwc_isequal(name, qname->name)) {
*sibling = n;
}
dom_string_unref(name);
}
return CSS_OK;
}
/**
* Callback to find a named generic sibling node.
*
* \param pw HTML document
* \param node DOM node
* \param qname Node name to search for
* \param sibling Pointer to location to receive ancestor
* \return CSS_OK.
*
* \post \a sibling will contain the result, or NULL if there is no match
*/
css_error named_generic_sibling_node(void *pw, void *node,
const css_qname *qname, void **sibling)
{
dom_node *n = node;
dom_node *prev;
dom_exception err;
*sibling = NULL;
err = dom_node_get_previous_sibling(n, &n);
if (err != DOM_NO_ERR)
return CSS_OK;
while (n != NULL) {
dom_node_type type;
dom_string *name;
err = dom_node_get_node_type(n, &type);
if (err != DOM_NO_ERR) {
dom_node_unref(n);
return CSS_OK;
}
if (type == DOM_ELEMENT_NODE) {
err = dom_node_get_node_name(n, &name);
if (err != DOM_NO_ERR) {
dom_node_unref(n);
return CSS_OK;
}
if (dom_string_caseless_lwc_isequal(name,
qname->name)) {
dom_string_unref(name);
dom_node_unref(n);
*sibling = n;
break;
}
dom_string_unref(name);
}
err = dom_node_get_previous_sibling(n, &prev);
if (err != DOM_NO_ERR) {
dom_node_unref(n);
return CSS_OK;
}
dom_node_unref(n);
n = prev;
}
return CSS_OK;
}
/**
* Callback to retrieve the parent of a node.
*
* \param pw HTML document
* \param node DOM node
* \param parent Pointer to location to receive parent
* \return CSS_OK.
*
* \post \a parent will contain the result, or NULL if there is no match
*/
css_error parent_node(void *pw, void *node, void **parent)
{
dom_element_parent_node(node, (struct dom_element **)parent);
return CSS_OK;
}
/**
* Callback to retrieve the preceding sibling of a node.
*
* \param pw HTML document
* \param node DOM node
* \param sibling Pointer to location to receive sibling
* \return CSS_OK.
*
* \post \a sibling will contain the result, or NULL if there is no match
*/
css_error sibling_node(void *pw, void *node, void **sibling)
{
dom_node *n = node;
dom_node *prev;
dom_exception err;
*sibling = NULL;
/* Find sibling element */
err = dom_node_get_previous_sibling(n, &n);
if (err != DOM_NO_ERR)
return CSS_OK;
while (n != NULL) {
dom_node_type type;
err = dom_node_get_node_type(n, &type);
if (err != DOM_NO_ERR) {
dom_node_unref(n);
return CSS_OK;
}
if (type == DOM_ELEMENT_NODE)
break;
err = dom_node_get_previous_sibling(n, &prev);
if (err != DOM_NO_ERR) {
dom_node_unref(n);
return CSS_OK;
}
dom_node_unref(n);
n = prev;
}
if (n != NULL) {
/** \todo Sort out reference counting */
dom_node_unref(n);
*sibling = n;
}
return CSS_OK;
}
/**
* Callback to determine if a node has the given name.
*
* \param pw HTML document
* \param node DOM node
* \param qname Name to match
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_has_name(void *pw, void *node,
const css_qname *qname, bool *match)
{
nscss_select_ctx *ctx = pw;
dom_node *n = node;
if (lwc_string_isequal(qname->name, ctx->universal, match) ==
lwc_error_ok && *match == false) {
dom_string *name;
dom_exception err;
err = dom_node_get_node_name(n, &name);
if (err != DOM_NO_ERR)
return CSS_OK;
/* Element names are case insensitive in HTML */
*match = dom_string_caseless_lwc_isequal(name, qname->name);
dom_string_unref(name);
}
return CSS_OK;
}
/**
* Callback to determine if a node has the given class.
*
* \param pw HTML document
* \param node DOM node
* \param name Name to match
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_has_class(void *pw, void *node,
lwc_string *name, bool *match)
{
dom_node *n = node;
/** \todo: Ensure that libdom performs case-insensitive
* matching in quirks mode */
(void)dom_element_has_class(n, name, match);
return CSS_OK;
}
/**
* Callback to determine if a node has the given id.
*
* \param pw HTML document
* \param node DOM node
* \param name Name to match
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_has_id(void *pw, void *node,
lwc_string *name, bool *match)
{
dom_node *n = node;
dom_string *attr;
dom_exception err;
*match = false;
/** \todo Assumes an HTML DOM */
err = dom_html_element_get_id(n, &attr);
if (err != DOM_NO_ERR)
return CSS_OK;
if (attr != NULL) {
*match = dom_string_lwc_isequal(attr, name);
dom_string_unref(attr);
}
return CSS_OK;
}
/**
* Callback to determine if a node has an attribute with the given name.
*
* \param pw HTML document
* \param node DOM node
* \param qname Name to match
* \param match Pointer to location to receive result
* \return CSS_OK on success,
* CSS_NOMEM on memory exhaustion.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_has_attribute(void *pw, void *node,
const css_qname *qname, bool *match)
{
dom_node *n = node;
dom_string *name;
dom_exception err;
err = dom_string_create_interned(
(const uint8_t *) lwc_string_data(qname->name),
lwc_string_length(qname->name), &name);
if (err != DOM_NO_ERR)
return CSS_NOMEM;
err = dom_element_has_attribute(n, name, match);
if (err != DOM_NO_ERR) {
dom_string_unref(name);
return CSS_OK;
}
dom_string_unref(name);
return CSS_OK;
}
/**
* Callback to determine if a node has an attribute with given name and value.
*
* \param pw HTML document
* \param node DOM node
* \param qname Name to match
* \param value Value to match
* \param match Pointer to location to receive result
* \return CSS_OK on success,
* CSS_NOMEM on memory exhaustion.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_has_attribute_equal(void *pw, void *node,
const css_qname *qname, lwc_string *value,
bool *match)
{
dom_node *n = node;
dom_string *name;
dom_string *atr_val;
dom_exception err;
size_t vlen = lwc_string_length(value);
if (vlen == 0) {
*match = false;
return CSS_OK;
}
err = dom_string_create_interned(
(const uint8_t *) lwc_string_data(qname->name),
lwc_string_length(qname->name), &name);
if (err != DOM_NO_ERR)
return CSS_NOMEM;
err = dom_element_get_attribute(n, name, &atr_val);
if ((err != DOM_NO_ERR) || (atr_val == NULL)) {
dom_string_unref(name);
*match = false;
return CSS_OK;
}
dom_string_unref(name);
*match = dom_string_caseless_lwc_isequal(atr_val, value);
dom_string_unref(atr_val);
return CSS_OK;
}
/**
* Callback to determine if a node has an attribute with the given name whose
* value dashmatches that given.
*
* \param pw HTML document
* \param node DOM node
* \param qname Name to match
* \param value Value to match
* \param match Pointer to location to receive result
* \return CSS_OK on success,
* CSS_NOMEM on memory exhaustion.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_has_attribute_dashmatch(void *pw, void *node,
const css_qname *qname, lwc_string *value,
bool *match)
{
dom_node *n = node;
dom_string *name;
dom_string *atr_val;
dom_exception err;
size_t vlen = lwc_string_length(value);
if (vlen == 0) {
*match = false;
return CSS_OK;
}
err = dom_string_create_interned(
(const uint8_t *) lwc_string_data(qname->name),
lwc_string_length(qname->name), &name);
if (err != DOM_NO_ERR)
return CSS_NOMEM;
err = dom_element_get_attribute(n, name, &atr_val);
if ((err != DOM_NO_ERR) || (atr_val == NULL)) {
dom_string_unref(name);
*match = false;
return CSS_OK;
}
dom_string_unref(name);
/* check for exact match */
*match = dom_string_caseless_lwc_isequal(atr_val, value);
/* check for dashmatch */
if (*match == false) {
const char *vdata = lwc_string_data(value);
const char *data = (const char *) dom_string_data(atr_val);
size_t len = dom_string_byte_length(atr_val);
if (len > vlen && data[vlen] == '-' &&
strncasecmp(data, vdata, vlen) == 0) {
*match = true;
}
}
dom_string_unref(atr_val);
return CSS_OK;
}
/**
* Callback to determine if a node has an attribute with the given name whose
* value includes that given.
*
* \param pw HTML document
* \param node DOM node
* \param qname Name to match
* \param value Value to match
* \param match Pointer to location to receive result
* \return CSS_OK on success,
* CSS_NOMEM on memory exhaustion.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_has_attribute_includes(void *pw, void *node,
const css_qname *qname, lwc_string *value,
bool *match)
{
dom_node *n = node;
dom_string *name;
dom_string *atr_val;
dom_exception err;
size_t vlen = lwc_string_length(value);
const char *p;
const char *start;
const char *end;
*match = false;
if (vlen == 0) {
return CSS_OK;
}
err = dom_string_create_interned(
(const uint8_t *) lwc_string_data(qname->name),
lwc_string_length(qname->name), &name);
if (err != DOM_NO_ERR)
return CSS_NOMEM;
err = dom_element_get_attribute(n, name, &atr_val);
if ((err != DOM_NO_ERR) || (atr_val == NULL)) {
dom_string_unref(name);
*match = false;
return CSS_OK;
}
dom_string_unref(name);
/* check for match */
start = (const char *) dom_string_data(atr_val);
end = start + dom_string_byte_length(atr_val);
for (p = start; p <= end; p++) {
if (*p == ' ' || *p == '\0') {
if ((size_t) (p - start) == vlen &&
strncasecmp(start,
lwc_string_data(value),
vlen) == 0) {
*match = true;
break;
}
start = p + 1;
}
}
dom_string_unref(atr_val);
return CSS_OK;
}
/**
* Callback to determine if a node has an attribute with the given name whose
* value has the prefix given.
*
* \param pw HTML document
* \param node DOM node
* \param qname Name to match
* \param value Value to match
* \param match Pointer to location to receive result
* \return CSS_OK on success,
* CSS_NOMEM on memory exhaustion.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_has_attribute_prefix(void *pw, void *node,
const css_qname *qname, lwc_string *value,
bool *match)
{
dom_node *n = node;
dom_string *name;
dom_string *atr_val;
dom_exception err;
size_t vlen = lwc_string_length(value);
if (vlen == 0) {
*match = false;
return CSS_OK;
}
err = dom_string_create_interned(
(const uint8_t *) lwc_string_data(qname->name),
lwc_string_length(qname->name), &name);
if (err != DOM_NO_ERR)
return CSS_NOMEM;
err = dom_element_get_attribute(n, name, &atr_val);
if ((err != DOM_NO_ERR) || (atr_val == NULL)) {
dom_string_unref(name);
*match = false;
return CSS_OK;
}
dom_string_unref(name);
/* check for exact match */
*match = dom_string_caseless_lwc_isequal(atr_val, value);
/* check for prefix match */
if (*match == false) {
const char *data = (const char *) dom_string_data(atr_val);
size_t len = dom_string_byte_length(atr_val);
if ((len >= vlen) &&
(strncasecmp(data, lwc_string_data(value), vlen) == 0)) {
*match = true;
}
}
dom_string_unref(atr_val);
return CSS_OK;
}
/**
* Callback to determine if a node has an attribute with the given name whose
* value has the suffix given.
*
* \param pw HTML document
* \param node DOM node
* \param qname Name to match
* \param value Value to match
* \param match Pointer to location to receive result
* \return CSS_OK on success,
* CSS_NOMEM on memory exhaustion.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_has_attribute_suffix(void *pw, void *node,
const css_qname *qname, lwc_string *value,
bool *match)
{
dom_node *n = node;
dom_string *name;
dom_string *atr_val;
dom_exception err;
size_t vlen = lwc_string_length(value);
if (vlen == 0) {
*match = false;
return CSS_OK;
}
err = dom_string_create_interned(
(const uint8_t *) lwc_string_data(qname->name),
lwc_string_length(qname->name), &name);
if (err != DOM_NO_ERR)
return CSS_NOMEM;
err = dom_element_get_attribute(n, name, &atr_val);
if ((err != DOM_NO_ERR) || (atr_val == NULL)) {
dom_string_unref(name);
*match = false;
return CSS_OK;
}
dom_string_unref(name);
/* check for exact match */
*match = dom_string_caseless_lwc_isequal(atr_val, value);
/* check for prefix match */
if (*match == false) {
const char *data = (const char *) dom_string_data(atr_val);
size_t len = dom_string_byte_length(atr_val);
const char *start = (char *) data + len - vlen;
if ((len >= vlen) &&
(strncasecmp(start, lwc_string_data(value), vlen) == 0)) {
*match = true;
}
}
dom_string_unref(atr_val);
return CSS_OK;
}
/**
* Callback to determine if a node has an attribute with the given name whose
* value contains the substring given.
*
* \param pw HTML document
* \param node DOM node
* \param qname Name to match
* \param value Value to match
* \param match Pointer to location to receive result
* \return CSS_OK on success,
* CSS_NOMEM on memory exhaustion.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_has_attribute_substring(void *pw, void *node,
const css_qname *qname, lwc_string *value,
bool *match)
{
dom_node *n = node;
dom_string *name;
dom_string *atr_val;
dom_exception err;
size_t vlen = lwc_string_length(value);
if (vlen == 0) {
*match = false;
return CSS_OK;
}
err = dom_string_create_interned(
(const uint8_t *) lwc_string_data(qname->name),
lwc_string_length(qname->name), &name);
if (err != DOM_NO_ERR)
return CSS_NOMEM;
err = dom_element_get_attribute(n, name, &atr_val);
if ((err != DOM_NO_ERR) || (atr_val == NULL)) {
dom_string_unref(name);
*match = false;
return CSS_OK;
}
dom_string_unref(name);
/* check for exact match */
*match = dom_string_caseless_lwc_isequal(atr_val, value);
/* check for prefix match */
if (*match == false) {
const char *vdata = lwc_string_data(value);
const char *start = (const char *) dom_string_data(atr_val);
size_t len = dom_string_byte_length(atr_val);
const char *last_start = start + len - vlen;
if (len >= vlen) {
while (start <= last_start) {
if (strncasecmp(start, vdata,
vlen) == 0) {
*match = true;
break;
}
start++;
}
}
}
dom_string_unref(atr_val);
return CSS_OK;
}
/**
* Callback to determine if a node is the root node of the document.
*
* \param pw HTML document
* \param node DOM node
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_is_root(void *pw, void *node, bool *match)
{
dom_node *n = node;
dom_node *parent;
dom_node_type type;
dom_exception err;
err = dom_node_get_parent_node(n, &parent);
if (err != DOM_NO_ERR) {
return CSS_NOMEM;
}
if (parent != NULL) {
err = dom_node_get_node_type(parent, &type);
dom_node_unref(parent);
if (err != DOM_NO_ERR)
return CSS_NOMEM;
if (type != DOM_DOCUMENT_NODE) {
*match = false;
return CSS_OK;
}
}
*match = true;
return CSS_OK;
}
static int
node_count_siblings_check(dom_node *node,
bool check_name,
dom_string *name)
{
dom_node_type type;
int ret = 0;
dom_exception exc;
if (node == NULL)
return 0;
exc = dom_node_get_node_type(node, &type);
if ((exc != DOM_NO_ERR) || (type != DOM_ELEMENT_NODE)) {
return 0;
}
if (check_name) {
dom_string *node_name = NULL;
exc = dom_node_get_node_name(node, &node_name);
if ((exc == DOM_NO_ERR) && (node_name != NULL)) {
if (dom_string_caseless_isequal(name,
node_name)) {
ret = 1;
}
dom_string_unref(node_name);
}
} else {
ret = 1;
}
return ret;
}
/**
* Callback to count a node's siblings.
*
* \param pw HTML document
* \param n DOM node
* \param same_name Only count siblings with the same name, or all
* \param after Count anteceding instead of preceding siblings
* \param count Pointer to location to receive result
* \return CSS_OK.
*
* \post \a count will contain the number of siblings
*/
css_error node_count_siblings(void *pw, void *n, bool same_name,
bool after, int32_t *count)
{
int32_t cnt = 0;
dom_exception exc;
dom_string *node_name = NULL;
if (same_name) {
dom_node *node = n;
exc = dom_node_get_node_name(node, &node_name);
if ((exc != DOM_NO_ERR) || (node_name == NULL)) {
return CSS_NOMEM;
}
}
if (after) {
dom_node *node = dom_node_ref(n);
dom_node *next;
do {
exc = dom_node_get_next_sibling(node, &next);
if ((exc != DOM_NO_ERR))
break;
dom_node_unref(node);
node = next;
cnt += node_count_siblings_check(node, same_name, node_name);
} while (node != NULL);
} else {
dom_node *node = dom_node_ref(n);
dom_node *next;
do {
exc = dom_node_get_previous_sibling(node, &next);
if ((exc != DOM_NO_ERR))
break;
dom_node_unref(node);
node = next;
cnt += node_count_siblings_check(node, same_name, node_name);
} while (node != NULL);
}
if (node_name != NULL) {
dom_string_unref(node_name);
}
*count = cnt;
return CSS_OK;
}
/**
* Callback to determine if a node is empty.
*
* \param pw HTML document
* \param node DOM node
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match will contain true if the node is empty and false otherwise.
*/
css_error node_is_empty(void *pw, void *node, bool *match)
{
dom_node *n = node, *next;
dom_exception err;
*match = true;
err = dom_node_get_first_child(n, &n);
if (err != DOM_NO_ERR) {
return CSS_BADPARM;
}
while (n != NULL) {
dom_node_type ntype;
err = dom_node_get_node_type(n, &ntype);
if (err != DOM_NO_ERR) {
dom_node_unref(n);
return CSS_BADPARM;
}
if (ntype == DOM_ELEMENT_NODE ||
ntype == DOM_TEXT_NODE) {
*match = false;
dom_node_unref(n);
break;
}
err = dom_node_get_next_sibling(n, &next);
if (err != DOM_NO_ERR) {
dom_node_unref(n);
return CSS_BADPARM;
}
dom_node_unref(n);
n = next;
}
return CSS_OK;
}
/**
* Callback to determine if a node is a linking element.
*
* \param pw HTML document
* \param n DOM node
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_is_link(void *pw, void *n, bool *match)
{
dom_node *node = n;
dom_exception exc;
dom_string *node_name = NULL;
exc = dom_node_get_node_name(node, &node_name);
if ((exc != DOM_NO_ERR) || (node_name == NULL)) {
return CSS_NOMEM;
}
if (dom_string_caseless_lwc_isequal(node_name, corestring_lwc_a)) {
bool has_href;
exc = dom_element_has_attribute(node, corestring_dom_href,
&has_href);
if ((exc == DOM_NO_ERR) && (has_href)) {
*match = true;
} else {
*match = false;
}
} else {
*match = false;
}
dom_string_unref(node_name);
return CSS_OK;
}
/**
* Callback to determine if a node is a linking element whose target has been
* visited.
*
* \param pw HTML document
* \param node DOM node
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_is_visited(void *pw, void *node, bool *match)
{
#if 0
nscss_select_ctx *ctx = pw;
nsurl *url;
nserror error;
const struct url_data *data;
dom_exception exc;
dom_node *n = node;
dom_string *s = NULL;
*match = false;
exc = dom_node_get_node_name(n, &s);
if ((exc != DOM_NO_ERR) || (s == NULL)) {
return CSS_NOMEM;
}
if (!dom_string_caseless_lwc_isequal(s, corestring_lwc_a)) {
/* Can't be visited; not ancher element */
dom_string_unref(s);
return CSS_OK;
}
/* Finished with node name string */
dom_string_unref(s);
s = NULL;
exc = dom_element_get_attribute(n, corestring_dom_href, &s);
if ((exc != DOM_NO_ERR) || (s == NULL)) {
/* Can't be visited; not got a URL */
return CSS_OK;
}
/* Make href absolute */
/* TODO: this duplicates what we do for box->href
* should we put the absolute URL on the dom node? */
error = nsurl_join(ctx->base_url, dom_string_data(s), &url);
/* Finished with href string */
dom_string_unref(s);
if (error != NSERROR_OK) {
/* Couldn't make nsurl object */
return CSS_NOMEM;
}
data = urldb_get_url_data(url);
/* Visited if in the db and has
* non-zero visit count */
if (data != NULL && data->visits > 0)
*match = true;
nsurl_unref(url);
#endif
*match = false;
return CSS_OK;
}
/**
* Callback to determine if a node is currently being hovered over.
*
* \param pw HTML document
* \param node DOM node
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_is_hover(void *pw, void *node, bool *match)
{
/** \todo Support hovering */
*match = false;
return CSS_OK;
}
/**
* Callback to determine if a node is currently activated.
*
* \param pw HTML document
* \param node DOM node
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_is_active(void *pw, void *node, bool *match)
{
/** \todo Support active nodes */
*match = false;
return CSS_OK;
}
/**
* Callback to determine if a node has the input focus.
*
* \param pw HTML document
* \param node DOM node
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_is_focus(void *pw, void *node, bool *match)
{
/** \todo Support focussed nodes */
*match = false;
return CSS_OK;
}
/**
* Callback to determine if a node is enabled.
*
* \param pw HTML document
* \param node DOM node
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match with contain true if the node is enabled and false otherwise.
*/
css_error node_is_enabled(void *pw, void *node, bool *match)
{
/** \todo Support enabled nodes */
*match = false;
return CSS_OK;
}
/**
* Callback to determine if a node is disabled.
*
* \param pw HTML document
* \param node DOM node
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match with contain true if the node is disabled and false otherwise.
*/
css_error node_is_disabled(void *pw, void *node, bool *match)
{
/** \todo Support disabled nodes */
*match = false;
return CSS_OK;
}
/**
* Callback to determine if a node is checked.
*
* \param pw HTML document
* \param node DOM node
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match with contain true if the node is checked and false otherwise.
*/
css_error node_is_checked(void *pw, void *node, bool *match)
{
/** \todo Support checked nodes */
*match = false;
return CSS_OK;
}
/**
* Callback to determine if a node is the target of the document URL.
*
* \param pw HTML document
* \param node DOM node
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match with contain true if the node matches and false otherwise.
*/
css_error node_is_target(void *pw, void *node, bool *match)
{
/** \todo Support target */
*match = false;
return CSS_OK;
}
/**
* Callback to determine if a node has the given language
*
* \param pw HTML document
* \param node DOM node
* \param lang Language specifier to match
* \param match Pointer to location to receive result
* \return CSS_OK.
*
* \post \a match will contain true if the node matches and false otherwise.
*/
css_error node_is_lang(void *pw, void *node,
lwc_string *lang, bool *match)
{
/** \todo Support languages */
*match = false;
return CSS_OK;
}
/**
* Callback to retrieve the User-Agent defaults for a CSS property.
*
* \param pw HTML document
* \param property Property to retrieve defaults for
* \param hint Pointer to hint object to populate
* \return CSS_OK on success,
* CSS_INVALID if the property should not have a user-agent default.
*/
css_error ua_default_for_property(void *pw, uint32_t property, css_hint *hint)
{
UNUSED(pw);
if (property == CSS_PROP_COLOR) {
hint->data.color = 0x00bfbfbf;
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 set_libcss_node_data(void *pw, void *node, void *libcss_node_data)
{
dom_node *n = node;
dom_exception err;
void *old_node_data = NULL;
/* Set this node's node data */
err = dom_node_set_user_data(n,
corestring_dom___ns_key_libcss_node_data,
libcss_node_data, nscss_dom_user_data_handler,
(void *) &old_node_data);
if (err != DOM_NO_ERR) {
return CSS_NOMEM;
}
//assert(old_node_data == NULL);
return CSS_OK;
}
css_error get_libcss_node_data(void *pw, void *node, void **libcss_node_data)
{
dom_node *n = node;
dom_exception err;
/* Get this node's node data */
err = dom_node_get_user_data(n,
corestring_dom___ns_key_libcss_node_data,
libcss_node_data);
if (err != DOM_NO_ERR) {
return CSS_NOMEM;
}
return CSS_OK;
}
static void
apply_color(struct html_context *html_context, struct html_element *html_element, css_color color_shade)
{
if (use_document_fg_colors(html_context->options)) {
html_element->attr.style.color.foreground = color_shade & 0x00ffffff;
}
}
static void
apply_background_color(struct html_context *html_context, struct html_element *html_element, css_color color_shade)
{
if (use_document_bg_colors(html_context->options)) {
html_element->attr.style.color.background = color_shade & 0x00ffffff;
}
}
static void
apply_font_attribute(struct html_context *html_context,
struct html_element *element, bool underline, bool bold, bool strike)
{
int add = 0;
int rem = 0;
if (underline) {
add |= AT_UNDERLINE;
} else {
rem |= AT_UNDERLINE;
}
if (bold) {
add |= AT_BOLD;
} else {
rem |= AT_BOLD;
}
if (strike) {
add |= AT_STRIKE;
} else {
rem |= AT_STRIKE;
}
element->attr.style.attr |= add;
element->attr.style.attr &= ~rem;
}
static void
apply_list_style(struct html_context *html_context, struct html_element *element, uint8_t list_type)
{
element->parattr.list_number = 1;
switch (list_type) {
case CSS_LIST_STYLE_TYPE_DISC:
element->parattr.list_number = 0;
element->parattr.flags = P_DISC;
break;
case CSS_LIST_STYLE_TYPE_CIRCLE:
element->parattr.list_number = 0;
element->parattr.flags = P_O;
break;
case CSS_LIST_STYLE_TYPE_SQUARE:
element->parattr.list_number = 0;
element->parattr.flags = P_SQUARE;
break;
case CSS_LIST_STYLE_TYPE_DECIMAL:
element->parattr.flags = P_NUMBER;
break;
case CSS_LIST_STYLE_TYPE_DECIMAL_LEADING_ZERO:
element->parattr.flags = P_NUMBER;
break;
case CSS_LIST_STYLE_TYPE_LOWER_ALPHA:
element->parattr.flags = P_alpha;
break;
case CSS_LIST_STYLE_TYPE_LOWER_ROMAN:
element->parattr.flags = P_roman;
break;
case CSS_LIST_STYLE_TYPE_UPPER_ALPHA:
element->parattr.flags = P_ALPHA;
break;
case CSS_LIST_STYLE_TYPE_UPPER_ROMAN:
element->parattr.flags = P_ROMAN;
break;
case CSS_LIST_STYLE_TYPE_NONE:
element->parattr.flags = P_NO_BULLET;
break;
case CSS_LIST_STYLE_TYPE_LOWER_LATIN:
element->parattr.flags = P_alpha;
break;
case CSS_LIST_STYLE_TYPE_UPPER_LATIN:
element->parattr.flags = P_ALPHA;
break;
case CSS_LIST_STYLE_TYPE_ARMENIAN:
element->parattr.flags = P_NUMBER;
break;
case CSS_LIST_STYLE_TYPE_GEORGIAN:
element->parattr.flags = P_NUMBER;
break;
case CSS_LIST_STYLE_TYPE_LOWER_GREEK:
element->parattr.flags = P_NUMBER;
break;
default:
element->parattr.list_number = 0;
break;
}
}
static void
apply_display(struct html_context *html_context, struct html_element *element, uint8_t display)
{
switch (display) {
case CSS_DISPLAY_INLINE:
// element->linebreak = 0;
break;
case CSS_DISPLAY_BLOCK:
/* 1 or 2, that is the question. I went for 2 since it
* gives a more "blocky" feel and it's more common.
* YMMV. */
element->linebreak = 2;
break;
case CSS_DISPLAY_NONE:
if (!html_context->options->css_ignore_display_none)
element->invisible = 2;
break;
default:
//INTERNAL("Bad prop->value.display %d", prop->value.display);
break;
}
}
static void
apply_text_align(struct html_context *html_context, struct html_element *element, uint8_t text_align)
{
switch (text_align) {
case CSS_TEXT_ALIGN_LEFT:
element->parattr.align = ALIGN_LEFT;
break;
case CSS_TEXT_ALIGN_RIGHT:
element->parattr.align = ALIGN_RIGHT;
break;
case CSS_TEXT_ALIGN_CENTER:
element->parattr.align = ALIGN_CENTER;
break;
case CSS_TEXT_ALIGN_JUSTIFY:
element->parattr.align = ALIGN_JUSTIFY;
break;
default:
element->parattr.align = 0;
break;
}
}
static void
apply_font_style(struct html_context *html_context, struct html_element *element, uint8_t font_style)
{
int add = 0;
int rem = 0;
switch (font_style) {
case CSS_FONT_STYLE_NORMAL:
rem |= AT_ITALIC;
break;
case CSS_FONT_STYLE_ITALIC:
case CSS_FONT_STYLE_OBLIQUE:
add |= AT_ITALIC;
default:
break;
}
element->attr.style.attr |= add;
element->attr.style.attr &= ~rem;
}
static bool
is_bold(int val)
{
switch (val) {
case CSS_FONT_WEIGHT_100:
case CSS_FONT_WEIGHT_200:
case CSS_FONT_WEIGHT_300:
case CSS_FONT_WEIGHT_400:
case CSS_FONT_WEIGHT_NORMAL:
case CSS_FONT_WEIGHT_500:
case CSS_FONT_WEIGHT_600:
default:
return false;
case CSS_FONT_WEIGHT_700:
case CSS_FONT_WEIGHT_BOLD:
case CSS_FONT_WEIGHT_800:
case CSS_FONT_WEIGHT_900:
return true;
}
}
void
select_css(struct html_context *html_context, struct html_element *html_element)
{
css_error code;
uint8_t color_type;
css_color color_shade;
css_select_results *style;
css_stylesheet *inline_style = NULL;
dom_document *doc = NULL; /* document, loaded into libdom */
dom_node *root = NULL; /* root element of document */
dom_exception exc;
css_media media = {
.type = CSS_MEDIA_SCREEN,
};
css_unit_ctx unit_len_ctx = {0};
unit_len_ctx.viewport_width = 800; // TODO
unit_len_ctx.viewport_height = 600; // TODO
unit_len_ctx.device_dpi = F_90; //device_dpi;
/** \todo Change nsoption font sizes to px. */
/// f_size = FDIV(FMUL(F_96, FDIV(INTTOFIX(nsoption_int(font_size)), F_10)), F_72);
/// f_min = FDIV(FMUL(F_96, FDIV(INTTOFIX(nsoption_int(font_min_size)), F_10)), F_72);
unsigned int f_size = FDIV(FMUL(F_96, FDIV(INTTOFIX(50), F_10)), F_72); // TODO
unsigned int f_min = FDIV(FMUL(F_96, FDIV(INTTOFIX(50), F_10)), F_72); // TODO
unit_len_ctx.font_size_default = f_size;
unit_len_ctx.font_size_minimum = f_min;
int offset = html_element->name - html_context->document->text.source;
dom_node *el = (dom_node *)find_in_map(html_context->document->element_map, offset);
if (!el) {
return;
}
dom_string *s;
dom_exception err;
nscss_select_ctx ctx = {0};
/* Firstly, construct inline stylesheet, if any */
err = dom_element_get_attribute(el, corestring_dom_style, &s);
if (err != DOM_NO_ERR)
return;
if (s != NULL) {
inline_style = nscss_create_inline_style(
(const uint8_t *) dom_string_data(s),
dom_string_byte_length(s),
"utf-8",
"",
false);
dom_string_unref(s);
if (inline_style == NULL)
return;
}
/* Populate selection context */
ctx.ctx = html_context->select_ctx;
ctx.quirks = false; //(c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
// ctx.base_url = c->base_url;
// ctx.universal = c->universal;
/// ctx.root_style = root_style;
/// ctx.parent_style = parent_style;
/* Select style for element */
style = nscss_get_style(&ctx, el, &media, &unit_len_ctx, inline_style);
/* No longer need inline style */
if (inline_style != NULL) {
css_stylesheet_destroy(inline_style);
}
if (!style) {
return;
}
color_type = css_computed_color(
style->styles[CSS_PSEUDO_ELEMENT_NONE],
&color_shade);
if (color_type) {
apply_color(html_context, html_element, color_shade);
}
color_type = css_computed_background_color(
style->styles[CSS_PSEUDO_ELEMENT_NONE],
&color_shade);
if (color_shade) {
apply_background_color(html_context, html_element, color_shade);
}
bool underline = css_computed_text_decoration(style->styles[CSS_PSEUDO_ELEMENT_NONE]) & CSS_TEXT_DECORATION_UNDERLINE;
bool strike = css_computed_text_decoration(style->styles[CSS_PSEUDO_ELEMENT_NONE]) & CSS_TEXT_DECORATION_LINE_THROUGH;
bool bold = is_bold(css_computed_font_weight(style->styles[CSS_PSEUDO_ELEMENT_NONE]));
apply_font_attribute(html_context, html_element, underline, bold, strike);
uint8_t font_style = css_computed_font_style(style->styles[CSS_PSEUDO_ELEMENT_NONE]);
if (font_style) {
apply_font_style(html_context, html_element, font_style);
}
uint8_t list_type = css_computed_list_style_type(style->styles[CSS_PSEUDO_ELEMENT_NONE]);
if (list_type) {
apply_list_style(html_context, html_element, list_type);
}
doc = html_context->document->dom;
/* Get root element */
exc = dom_document_get_document_element(doc, &root);
if (exc != DOM_NO_ERR) {
fprintf(stderr, "Exception raised for get_document_element\n");
//dom_node_unref(doc);
} else if (root == NULL) {
fprintf(stderr, "Broken: root == NULL\n");
//dom_node_unref(doc);
}
bool is_root = (root == el);
if (root) {
dom_node_unref(root);
}
uint8_t display = css_computed_display(style->styles[CSS_PSEUDO_ELEMENT_NONE], is_root);
if (display) {
apply_display(html_context, html_element, display);
}
uint8_t text_align = css_computed_text_align(style->styles[CSS_PSEUDO_ELEMENT_NONE]);
if (text_align) {
apply_text_align(html_context, html_element, text_align);
}
code = css_select_results_destroy(style);
if (code != CSS_OK) {
fprintf(stderr, "css_computed_style_destroy code=%d\n", code);
}
}
static css_error
handle_import(void *pw, css_stylesheet *parent, lwc_string *url)
{
struct html_context *html_context = (struct html_context *)pw;
char *uristring = memacpy(lwc_string_data(url), lwc_string_length(url));
struct uri *uri;
if (!uristring) {
return CSS_NOMEM;
}
uri = get_uri(uristring, URI_BASE);
if (!uri) {
mem_free(uristring);
return CSS_NOMEM;
}
/* Request the imported stylesheet as part of the document ... */
html_context->special_f(html_context, SP_STYLESHEET, uri);
/* ... and then attempt to import from the cache. */
import_css2(html_context, uri);
done_uri(uri);
mem_free(uristring);
return CSS_OK;
}
static void
parse_css_common(struct html_context *html_context, const char *text, int length, struct uri *uri)
{
css_error code;
size_t size;
uint32_t count;
css_stylesheet_params params;
css_stylesheet *sheet;
static char url[4096];
if (uri) {
char *u = get_uri_string(uri, URI_BASE);
if (u) {
char *slash = strrchr(u, '/');
if (slash) {
slash[1] = '\0';
}
strncpy(url, u, 4095);
mem_free(u);
}
} else {
url[0] = '\0';
}
params.params_version = CSS_STYLESHEET_PARAMS_VERSION_1;
params.level = CSS_LEVEL_21;
params.charset = "UTF-8";
params.url = url;
params.title = NULL;
params.allow_quirks = false;
params.inline_style = false;
params.resolve = resolve_url;
params.resolve_pw = NULL;
params.import = handle_import;
params.import_pw = html_context;
params.color = NULL;
params.color_pw = NULL;
params.font = NULL;
params.font_pw = NULL;
/* create a stylesheet */
code = css_stylesheet_create(&params, &sheet);
if (code != CSS_OK) {
fprintf(stderr, "css_stylesheet_create code=%d\n", code);
return;
}
code = css_stylesheet_append_data(sheet, (const uint8_t *) text, length);
if (code != CSS_OK && code != CSS_NEEDDATA) {
fprintf(stderr, "css_stylesheet_append_data code=%d\n", code);
return;
}
code = css_stylesheet_data_done(sheet);
if (code != CSS_OK && code != CSS_IMPORTS_PENDING) {
fprintf(stderr, "css_stylesheet_data_done code=%d\n", code);
return;
}
code = css_stylesheet_size(sheet, &size);
code = css_select_ctx_append_sheet(html_context->select_ctx, sheet, CSS_ORIGIN_AUTHOR,
NULL);
if (code != CSS_OK) {
fprintf(stderr, "css_select_ctx_append_sheet code=%d\n", code);
return;
}
struct el_sheet *el_sheet = (struct el_sheet *)mem_alloc(sizeof(*el_sheet));
if (el_sheet) {
el_sheet->sheet = sheet;
add_to_list(html_context->sheets, el_sheet);
}
code = css_select_ctx_count_sheets(html_context->select_ctx, &count);
if (code != CSS_OK) {
fprintf(stderr, "css_select_ctx_count_sheets code=%d\n", code);
}
}
void
parse_css(struct html_context *html_context, char *name)
{
int offset = name - html_context->document->text.source;
dom_node *el = (dom_node *)find_in_map(html_context->document->element_map, offset);
dom_node *n, *next;
dom_exception err;
if (!el) {
return;
}
struct string buf;
if (!init_string(&buf)) {
return;
}
n = el;
err = dom_node_get_first_child(n, &n);
if (err != DOM_NO_ERR) {
goto end;
}
while (n != NULL) {
dom_node_type ntype;
err = dom_node_get_node_type(n, &ntype);
if (err != DOM_NO_ERR) {
dom_node_unref(n);
goto end;
}
if (ntype == DOM_TEXT_NODE) {
dom_string *str;
err = dom_node_get_text_content(n, &str);
if (err == DOM_NO_ERR && str != NULL) {
int length = dom_string_byte_length(str);
const char *string_text = dom_string_data(str);
if (!((length == 1) && (*string_text == '\n'))) {
add_bytes_to_string(&buf, string_text, length);
}
dom_string_unref(str);
}
}
err = dom_node_get_next_sibling(n, &next);
if (err != DOM_NO_ERR) {
dom_node_unref(n);
goto end;
}
dom_node_unref(n);
n = next;
}
parse_css_common(html_context, buf.source, buf.length, NULL);
end:
done_string(&buf);
}
void
import_css2(struct html_context *html_context, struct uri *uri)
{
/* Do we have it in the cache? (TODO: CSS cache) */
struct cache_entry *cached;
struct fragment *fragment;
if (!uri) { //|| css->import_level >= MAX_REDIRECTS)
return;
}
cached = get_redirected_cache_entry(uri);
if (!cached) return;
fragment = get_cache_fragment(cached);
if (fragment) {
// css->import_level++;
parse_css_common(html_context, fragment->data, fragment->length, uri);
// css_parse_stylesheet(css, uri, fragment->data, end);
// css->import_level--;
}
}
void *
el_match_selector(const char *selector, void *node)
{
struct string text;
css_error code;
size_t size;
uint32_t count;
css_stylesheet_params params;
css_stylesheet *sheet = NULL;
css_select_ctx *select_ctx = NULL;
css_select_results *style = NULL;
uint8_t color_type;
css_color color_shade;
css_media media = {
.type = CSS_MEDIA_SCREEN,
};
css_unit_ctx unit_len_ctx = {0};
unit_len_ctx.viewport_width = 800; // TODO
unit_len_ctx.viewport_height = 600; // TODO
unit_len_ctx.device_dpi = F_90; //device_dpi;
/** \todo Change nsoption font sizes to px. */
/// f_size = FDIV(FMUL(F_96, FDIV(INTTOFIX(nsoption_int(font_size)), F_10)), F_72);
/// f_min = FDIV(FMUL(F_96, FDIV(INTTOFIX(nsoption_int(font_min_size)), F_10)), F_72);
unsigned int f_size = FDIV(FMUL(F_96, FDIV(INTTOFIX(50), F_10)), F_72); // TODO
unsigned int f_min = FDIV(FMUL(F_96, FDIV(INTTOFIX(50), F_10)), F_72); // TODO
unit_len_ctx.font_size_default = f_size;
unit_len_ctx.font_size_minimum = f_min;
void *ret = NULL;
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_empty;
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;
if (!selector) {
return NULL;
}
if (!init_string(&text)) {
return NULL;
}
add_to_string(&text, selector);
add_to_string(&text, "{color:#123456}");
/* create a stylesheet */
code = css_stylesheet_create(&params, &sheet);
if (code != CSS_OK) {
goto empty;
}
code = css_stylesheet_append_data(sheet, (const uint8_t *) text.source, text.length);
if (code != CSS_OK && code != CSS_NEEDDATA) {
goto empty;
}
code = css_stylesheet_data_done(sheet);
if (code != CSS_OK) {
goto empty;
}
code = css_stylesheet_size(sheet, &size);
/* prepare a selection context containing the stylesheet */
code = css_select_ctx_create(&select_ctx);
if (code != CSS_OK) {
goto empty;
}
code = css_select_ctx_append_sheet(select_ctx, sheet, CSS_ORIGIN_AUTHOR, NULL);
if (code != CSS_OK) {
goto empty;
}
code = css_select_ctx_count_sheets(select_ctx, &count);
if (code != CSS_OK) {
goto empty;
}
code = css_select_style(select_ctx, node, &unit_len_ctx, &media, NULL, &selection_handler, 0, &style);
if (code != CSS_OK) {
goto empty;
}
color_type = css_computed_color(style->styles[CSS_PSEUDO_ELEMENT_NONE], &color_shade);
if (color_type && color_shade == 0xff123456) {
ret = node;
}
empty:
if (style) {
css_select_results_destroy(style);
}
if (select_ctx) {
css_select_ctx_destroy(select_ctx);
}
if (sheet) {
css_stylesheet_destroy(sheet);
}
done_string(&text);
return ret;
}