summaryrefslogtreecommitdiff
path: root/src/select/select.c
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2011-12-04 21:06:24 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2011-12-04 21:06:24 +0000
commitd5a183e14d42560632a6aa270aede226215bb3d3 (patch)
tree31d7c6692a8267237ff3caa34fa5958e280ead24 /src/select/select.c
parentd153cb125a6a69e08a49c93f9774f86705aa9898 (diff)
downloadlibcss-d5a183e14d42560632a6aa270aede226215bb3d3.tar.gz
libcss-d5a183e14d42560632a6aa270aede226215bb3d3.tar.bz2
@font-face support. Credit: James Montgomerie
Things missing: parser tests; the following descriptors: font-feature-settings, font-stretch, font-variant, unicode-range. svn path=/trunk/libcss/; revision=13244
Diffstat (limited to 'src/select/select.c')
-rw-r--r--src/select/select.c329
1 files changed, 310 insertions, 19 deletions
diff --git a/src/select/select.c b/src/select/select.c
index 7baac03..4243140 100644
--- a/src/select/select.c
+++ b/src/select/select.c
@@ -8,6 +8,8 @@
#include <assert.h>
#include <string.h>
+#include <libwapcaplet/libwapcaplet.h>
+
#include <libcss/select.h>
#include "bytecode/bytecode.h"
@@ -17,6 +19,7 @@
#include "select/dispatch.h"
#include "select/hash.h"
#include "select/propset.h"
+#include "select/font_face.h"
#include "select/select.h"
#include "utils/parserutilserror.h"
#include "utils/utils.h"
@@ -74,6 +77,27 @@ struct css_select_ctx {
lwc_string *after;
};
+/**
+ * Container for selected font faces
+ */
+typedef struct css_select_font_faces_list {
+ const css_font_face **font_faces;
+ size_t count;
+} css_select_font_faces_list;
+
+/**
+ * Font face selection state
+ */
+typedef struct css_select_font_faces_state {
+ lwc_string *font_family;
+ uint64_t media;
+
+ css_select_font_faces_list ua_font_faces;
+ css_select_font_faces_list user_font_faces;
+ css_select_font_faces_list author_font_faces;
+} css_select_font_faces_state;
+
+
static css_error set_hint(css_select_state *state, uint32_t prop);
static css_error set_initial(css_select_state *state,
uint32_t prop, css_pseudo_element pseudo,
@@ -104,6 +128,11 @@ static css_error match_detail(css_select_ctx *ctx, void *node,
bool *match, css_pseudo_element *pseudo_element);
static css_error cascade_style(const css_style *style, css_select_state *state);
+static css_error select_font_faces_from_sheet(css_select_ctx *ctx,
+ const css_stylesheet *sheet,
+ css_origin origin,
+ css_select_font_faces_state *state);
+
#ifdef DEBUG_CHAIN_MATCHING
static void dump_chain(const css_selector *selector);
#endif
@@ -558,6 +587,144 @@ css_error css_select_results_destroy(css_select_results *results)
return CSS_OK;
}
+/**
+ * Search a selection context for defined font faces
+ *
+ * \param ctx Selection context
+ * \param media Currently active media types
+ * \param font_family Font family to search for
+ * \param result Pointer to location to receive result
+ * \return CSS_OK on success, appropriate error otherwise.
+ */
+css_error css_select_font_faces(css_select_ctx *ctx,
+ uint64_t media, lwc_string *font_family,
+ css_select_font_faces_results **result)
+{
+ uint32_t i;
+ css_error error;
+ css_select_font_faces_state state;
+ uint32_t n_font_faces;
+
+ if (ctx == NULL || font_family == NULL || result == NULL)
+ return CSS_BADPARM;
+
+ memset(&state, 0, sizeof(css_select_font_faces_state));
+ state.font_family = font_family;
+ state.media = media;
+
+ /* Iterate through the top-level stylesheets, selecting font-faces
+ * from those which apply to our current media requirements and
+ * are not disabled */
+ for (i = 0; i < ctx->n_sheets; i++) {
+ const css_select_sheet s = ctx->sheets[i];
+
+ if ((s.media & media) != 0 &&
+ s.sheet->disabled == false) {
+ error = select_font_faces_from_sheet(ctx, s.sheet,
+ s.origin, &state);
+ if (error != CSS_OK)
+ goto cleanup;
+ }
+ }
+
+ n_font_faces = state.ua_font_faces.count +
+ state.user_font_faces.count +
+ state.author_font_faces.count;
+
+
+ if (n_font_faces > 0) {
+ /* We found some matching faces. Make a results structure with
+ * the font faces in priority order. */
+ css_select_font_faces_results *results;
+
+ results = ctx->alloc(NULL,
+ sizeof(css_select_font_faces_results),
+ ctx->pw);
+ if (results == NULL) {
+ error = CSS_NOMEM;
+ goto cleanup;
+ }
+
+ results->alloc = ctx->alloc;
+ results->pw = ctx->pw;
+
+ results->font_faces = ctx->alloc(NULL,
+ n_font_faces * sizeof(css_font_face *),
+ ctx->pw);
+ if (results->font_faces == NULL) {
+ ctx->alloc(results, 0, ctx->pw);
+ error = CSS_NOMEM;
+ goto cleanup;
+ }
+
+ results->n_font_faces = n_font_faces;
+
+ i = 0;
+ if (state.ua_font_faces.count != 0) {
+ memcpy(results->font_faces,
+ state.ua_font_faces.font_faces,
+ sizeof(css_font_face *) *
+ state.ua_font_faces.count);
+
+ i += state.ua_font_faces.count;
+ }
+
+ if (state.user_font_faces.count != 0) {
+ memcpy(results->font_faces + i,
+ state.user_font_faces.font_faces,
+ sizeof(css_font_face *) *
+ state.user_font_faces.count);
+ i += state.user_font_faces.count;
+ }
+
+ if (state.author_font_faces.count != 0) {
+ memcpy(results->font_faces + i,
+ state.author_font_faces.font_faces,
+ sizeof(css_font_face *) *
+ state.author_font_faces.count);
+ }
+
+ *result = results;
+ }
+
+ error = CSS_OK;
+
+cleanup:
+ if (state.ua_font_faces.count != 0)
+ ctx->alloc(state.ua_font_faces.font_faces, 0, ctx->pw);
+
+ if (state.user_font_faces.count != 0)
+ ctx->alloc(state.user_font_faces.font_faces, 0, ctx->pw);
+
+ if (state.author_font_faces.count != 0)
+ ctx->alloc(state.author_font_faces.font_faces, 0, ctx->pw);
+
+ return error;
+}
+
+/**
+ * Destroy a font-face result set
+ *
+ * \param results Result set to destroy
+ * \return CSS_OK on success, appropriate error otherwise
+ */
+css_error css_select_font_faces_results_destroy(
+ css_select_font_faces_results *results)
+{
+ if (results == NULL)
+ return CSS_BADPARM;
+
+ if (results->font_faces != NULL) {
+ /* Don't destroy the individual css_font_faces, they're owned
+ by their respective sheets */
+ results->alloc(results->font_faces, 0, results->pw);
+ }
+
+ results->alloc(results, 0, results->pw);
+
+ return CSS_OK;
+}
+
/******************************************************************************
* Selection engine internals below here *
******************************************************************************/
@@ -870,13 +1037,14 @@ css_error set_initial(css_select_state *state,
return CSS_OK;
}
+#define IMPORT_STACK_SIZE 256
+
css_error select_from_sheet(css_select_ctx *ctx, const css_stylesheet *sheet,
css_origin origin, css_select_state *state)
{
const css_stylesheet *s = sheet;
const css_rule *rule = s->rule_list;
uint32_t sp = 0;
-#define IMPORT_STACK_SIZE 256
const css_rule *import_stack[IMPORT_STACK_SIZE];
do {
@@ -894,7 +1062,8 @@ css_error select_from_sheet(css_select_ctx *ctx, const css_stylesheet *sheet,
if (import->sheet != NULL &&
(import->media & state->media) != 0) {
/* It's applicable, so process it */
- assert(sp < IMPORT_STACK_SIZE - 1);
+ if (sp >= IMPORT_STACK_SIZE)
+ return CSS_NOMEM;
import_stack[sp++] = rule;
@@ -930,6 +1099,144 @@ css_error select_from_sheet(css_select_ctx *ctx, const css_stylesheet *sheet,
return CSS_OK;
}
+static inline bool _rule_applies_to_media(const css_rule *rule, uint64_t media)
+{
+ bool applies = true;
+ const css_rule *ancestor = rule;
+
+ while (ancestor != NULL) {
+ const css_rule_media *m = (const css_rule_media *) ancestor;
+
+ if (ancestor->type == CSS_RULE_MEDIA &&
+ (m->media & media) == 0) {
+ applies = false;
+ break;
+ }
+
+ if (ancestor->ptype != CSS_RULE_PARENT_STYLESHEET)
+ ancestor = ancestor->parent;
+ else
+ ancestor = NULL;
+ }
+
+ return applies;
+}
+
+static css_error _select_font_face_from_rule(css_select_ctx *ctx,
+ const css_rule_font_face *rule, css_origin origin,
+ css_select_font_faces_state *state)
+{
+ if (_rule_applies_to_media((const css_rule *) rule, state->media)) {
+ bool correct_family = false;
+
+ if (lwc_string_isequal(
+ rule->font_face->font_family,
+ state->font_family,
+ &correct_family) == lwc_error_ok &&
+ correct_family) {
+ css_select_font_faces_list *faces = NULL;
+ const css_font_face **new_faces;
+ uint32_t index;
+ size_t new_size;
+
+ switch (origin) {
+ case CSS_ORIGIN_UA:
+ faces = &state->ua_font_faces;
+ break;
+ case CSS_ORIGIN_USER:
+ faces = &state->user_font_faces;
+ break;
+ case CSS_ORIGIN_AUTHOR:
+ faces = &state->author_font_faces;
+ break;
+ }
+
+ index = faces->count++;
+ new_size = faces->count * sizeof(css_font_face *);
+
+ new_faces = ctx->alloc(faces->font_faces,
+ new_size, ctx->pw);
+ if (new_faces == NULL) {
+ faces->count = 0;
+ return CSS_NOMEM;
+ }
+ faces->font_faces = new_faces;
+
+ faces->font_faces[index] = rule->font_face;
+ }
+ }
+
+ return CSS_OK;
+}
+
+static css_error select_font_faces_from_sheet(css_select_ctx *ctx,
+ const css_stylesheet *sheet,
+ css_origin origin,
+ css_select_font_faces_state *state)
+{
+ const css_stylesheet *s = sheet;
+ const css_rule *rule = s->rule_list;
+ uint32_t sp = 0;
+ const css_rule *import_stack[IMPORT_STACK_SIZE];
+
+ do {
+ /* Find first non-charset rule, if we're at the list head */
+ if (rule == s->rule_list) {
+ while (rule != NULL && rule->type == CSS_RULE_CHARSET)
+ rule = rule->next;
+ }
+
+ if (rule != NULL && rule->type == CSS_RULE_IMPORT) {
+ /* Current rule is an import */
+ const css_rule_import *import =
+ (const css_rule_import *) rule;
+
+ if (import->sheet != NULL &&
+ (import->media & state->media) != 0) {
+ /* It's applicable, so process it */
+ if (sp >= IMPORT_STACK_SIZE)
+ return CSS_NOMEM;
+
+ import_stack[sp++] = rule;
+
+ s = import->sheet;
+ rule = s->rule_list;
+ } else {
+ /* Not applicable; skip over it */
+ rule = rule->next;
+ }
+ } else if (rule != NULL && rule->type == CSS_RULE_FONT_FACE) {
+ css_error error;
+
+ error = _select_font_face_from_rule(
+ ctx,
+ (const css_rule_font_face *) rule,
+ origin,
+ state);
+
+ if (error != CSS_OK)
+ return error;
+
+ rule = rule->next;
+ } else if (rule == NULL) {
+ /* Find next sheet to process */
+ if (sp > 0) {
+ sp--;
+ rule = import_stack[sp]->next;
+ s = import_stack[sp]->parent;
+ } else {
+ s = NULL;
+ }
+ } else {
+ rule = rule->next;
+ }
+ } while (s != NULL);
+
+ return CSS_OK;
+}
+
+#undef IMPORT_STACK_SIZE
+
static inline bool _selectors_pending(const css_selector **node,
const css_selector **id, const css_selector ***classes,
uint32_t n_classes, const css_selector **univ)
@@ -1063,8 +1370,6 @@ css_error match_selectors_in_sheet(css_select_ctx *ctx,
while (_selectors_pending(node_selectors, id_selectors,
class_selectors, n_classes, univ_selectors)) {
const css_selector *selector;
- css_rule *rule, *parent;
- bool process = true;
/* Selectors must be matched in ascending order of specificity
* and rule index. (c.f. css__outranks_existing())
@@ -1077,21 +1382,7 @@ css_error match_selectors_in_sheet(css_select_ctx *ctx,
/* Ignore any selectors contained in rules which are a child
* of an @media block that doesn't match the current media
* requirements. */
- for (rule = selector->rule; rule != NULL; rule = parent) {
- if (rule->type == CSS_RULE_MEDIA &&
- (((css_rule_media *) rule)->media &
- state->media) == 0) {
- process = false;
- break;
- }
-
- if (rule->ptype != CSS_RULE_PARENT_STYLESHEET)
- parent = rule->parent;
- else
- parent = NULL;
- }
-
- if (process) {
+ if (_rule_applies_to_media(selector->rule, state->media)) {
error = match_selector_chain(ctx, selector, state);
if (error != CSS_OK)
goto cleanup;