|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
Content-CSS stylesheet scanner, cascade, and face lookup (#111). More...
#include <stddef.h>#include <string.h>#include "ra8_attributes.h"#include "reflow.h"#include "reflow_css.h"#include "reflow_css_internal.h"Go to the source code of this file.
Enumerations | |
| enum | priv_css_consts_t : uint16_t { k_priv_rank_inherited = 0U , k_priv_spec_type = 1U , k_priv_spec_class = 100U , k_priv_spec_id = 10000U } |
| Cascade specificity weights (packed id > class > type > universal). More... | |
| enum | priv_css_scan_t : uint8_t { k_priv_cmt_marker = 2U } |
| Token lengths used by the top-level stylesheet scanner. More... | |
Functions | |
| ra8_err_t | ra8_css_sheet_reset (ra8_css_sheet_t *sheet) |
| Reset a stylesheet to empty (no rules, no names, order 0). | |
| static size_t | internal_skip_comment (const char *css, size_t len, size_t start) |
Step over a C-style comment starting at start. | |
| static bool | internal_find_block (const char *css, size_t len, size_t i, size_t *out_open, size_t *out_close) |
Locate the { ... } block beginning at selector offset i. | |
| ra8_err_t | ra8_css_parse (ra8_css_sheet_t *sheet, const char *css, uint32_t len) |
Parse one <style> block's CSS text, appending rules to sheet. | |
| static bool | internal_class_list_has (const char *list, size_t list_len, const char *name, size_t nlen) |
True iff a space-separated class list contains name exactly. | |
| bool | ra8_css_rule_matches (const ra8_css_rule_t *rule, const ra8_css_element_t *el, const ra8_css_sheet_t *sheet) |
Test whether rule rule's selector matches element el. | |
| static bool | internal_anc_matches (const ra8_css_anc_t *anc, const ra8_css_element_t *el, const ra8_css_sheet_t *sheet) |
True if descendant ancestor part anc matches element el. | |
| static bool | internal_rule_matches_ctx (const ra8_css_rule_t *rule, const ra8_css_element_t *el, const ra8_css_element_t *ancestors, uint8_t n_anc, const ra8_css_sheet_t *sheet) |
Full match of a (possibly descendant) rule against el + ancestors. | |
| static uint16_t | internal_rule_rank (const ra8_css_rule_t *rule) |
| Packed specificity (id*10000 + class*100 + type), summing all parts. | |
| static const ra8_css_style_t * | internal_resolve (uint8_t setbit, const ra8_css_style_t *inherited, const ra8_css_sheet_t *sheet, const bool *matched, const ra8_css_style_t *inl) |
| Resolve the winning declaration for one property across all sources. | |
| static void | internal_cascade_emphasis (ra8_css_style_t *out, const ra8_css_sheet_t *sheet, const bool *matched, const ra8_css_style_t *inherited, const ra8_css_style_t *inl) |
Resolve the bold / italic / underline emphasis bits into out. | |
| static void | internal_cascade_scalars (ra8_css_style_t *out, const ra8_css_sheet_t *sheet, const bool *matched, const ra8_css_style_t *inherited, const ra8_css_style_t *inl) |
| Resolve the scalar properties (align / colour / font-size / display). | |
| static void | internal_cascade_family (ra8_css_style_t *out, const ra8_css_sheet_t *sheet, const bool *matched, const ra8_css_style_t *inherited, const ra8_css_style_t *inl) |
Resolve the inherited font-family slice into out. | |
| ra8_css_style_t | ra8_css_cascade_ctx (const ra8_css_sheet_t *sheet, const ra8_css_element_t *el, ra8_css_style_t inherited, ra8_css_style_t inline_decl, const ra8_css_element_t *ancestors, uint8_t n_anc) |
| Compute an element's cascaded style with descendant-selector context. | |
| ra8_css_style_t | ra8_css_cascade (const ra8_css_sheet_t *sheet, const ra8_css_element_t *el, ra8_css_style_t inherited, ra8_css_style_t inline_decl) |
| Compute an element's cascaded style. | |
| static bool | internal_ci_eq_span (const char *a, size_t alen, const char *b, size_t blen) |
| Case-insensitive equality of two byte spans. | |
| static bool | internal_family_eq (const ra8_css_sheet_t *sheet, const ra8_css_fontface_t *f, const char *family, size_t family_len) |
True iff face f's family equals family (case-insensitive). | |
| int16_t | ra8_css_match_face (const ra8_css_sheet_t *sheet, const char *family, uint16_t family_len, bool want_bold, bool want_italic) |
| Pick the @font-face whose family + emphasis best fits a run. | |
| bool | ra8_css_face_src (const ra8_css_sheet_t *sheet, uint16_t idx, const char **out_src, uint16_t *out_len) |
| Read a parsed @font-face entry's src href bytes. | |
Content-CSS stylesheet scanner, cascade, and face lookup (#111).
The top-level stylesheet scanner (ra8_css_parse, comment + block skipping), the selector-matching and specificity cascade, and the @font-face lookup API for the v1 CSS subset documented in reflow_css.h. The scanner walks selector { ... } blocks and hands each to priv_reflow_css_parse_one_block in a sibling translation unit; the cascade resolves inherited / rule / inline sources into a single ra8_css_style_t. No MMIO, no heap; all state lives in the caller-owned ra8_css_sheet_t.
[Ring 4 / Reflow] {World: NS}
Definition in file reflow_css_cascade.c.
| enum priv_css_consts_t : uint16_t |
Cascade specificity weights (packed id > class > type > universal).
A rule's specificity is the sum of one weight per present constraint (id + class + type), each at most 1 in our compound model; the packed value orders the cascade. Inheritance is the lowest priority (weight 0); a universal selector (no constraints) is also 0 but beats inheritance on source order.
Definition at line 45 of file reflow_css_cascade.c.
| enum priv_css_scan_t : uint8_t |
Token lengths used by the top-level stylesheet scanner.
Stand-ins for the / + * comment delimiters so the parse loop carries no bare numeric literals when stepping over comments and blocks.
| Enumerator | |
|---|---|
| k_priv_cmt_marker | Byte length of a CSS comment delimiter pair. |
Definition at line 63 of file reflow_css_cascade.c.
|
static |
True if descendant ancestor part anc matches element el.
Checks each non-zero constraint field of anc against el:
el->tag must equal anc->tag when the tag is not unknown.el->class_str must contain the interned class name via internal_class_list_has().el->id must equal the interned id byte for byte. Returns true only when all present constraints match. An anc with all fields zero (universal) always returns true.| [in] | anc | Ancestor constraint part to test. |
| [in] | el | Element to test the constraint against. |
| [in] | sheet | Sheet owning the name pool for interned class/id names. |
anc matches el. | true | All constraints in anc are satisfied by el. |
| false | At least one constraint in anc is not satisfied. |
anc, el, and sheet are non-NULL. anc are valid indices into sheet->names. anc, el, and sheet are not modified.Definition at line 320 of file reflow_css_cascade.c.
References ra8_css_anc_t::class_len, ra8_css_element_t::class_len, ra8_css_anc_t::class_off, ra8_css_element_t::class_str, ra8_css_element_t::id, ra8_css_anc_t::id_len, ra8_css_element_t::id_len, ra8_css_anc_t::id_off, internal_class_list_has(), k_reflow_tag_unknown, ra8_css_sheet_t::names, ra8_css_anc_t::tag, and ra8_css_element_t::tag.
Referenced by internal_rule_matches_ctx().
|
static |
Resolve the bold / italic / underline emphasis bits into out.
Iterates over a fixed table of three emphasis properties (bold, italic, underline) and calls internal_resolve() for each to find the winning style source. When a winner is found, the corresponding set bit is OR'd into out->set and the style bit is conditionally OR'd into out->style based on the winner's style field. The table has a statically known size of 3, making the loop provably bounded.
| [in,out] | out | Style record to write the resolved emphasis bits into. |
| [in] | sheet | Sheet providing the matching rules. |
| [in] | matched | Boolean array parallel to sheet->rules[]; true for each rule that matched the current element. |
| [in] | inherited | Style inherited from the parent element. |
| [in] | inl | Inline style from the element's style attribute. |
out, sheet, matched, inherited, and inl are non-NULL. matched has at least sheet->rule_count elements. out->set and out->style have the resolved emphasis bits written. inherited and inl are not modified.out instances. < Setbit.
< Stylebit.
Definition at line 531 of file reflow_css_cascade.c.
References internal_resolve(), k_ra8_css_set_bold, k_ra8_css_set_italic, k_ra8_css_set_underline, k_reflow_style_bold, k_reflow_style_italic, k_reflow_style_underline, ra8_css_style_t::set, and ra8_css_style_t::style.
Referenced by ra8_css_cascade_ctx().
|
static |
Resolve the inherited font-family slice into out.
Calls internal_resolve() for the k_ra8_css_set_family property and, when a winning style is found, copies (family_off, family_len) from the winner into out and OR's k_ra8_css_set_family into out->set. The family name itself lives in the sheet's name pool and is referenced by offset and length; no string copy is performed.
| [in,out] | out | Style record to write the resolved family reference into. |
| [in] | sheet | Sheet providing the matching rules and name pool. |
| [in] | matched | Boolean array parallel to sheet->rules[]; true for each rule that matched the current element. |
| [in] | inherited | Style inherited from the parent element. |
| [in] | inl | Inline style from the element's style attribute. |
out, sheet, matched, inherited, and inl are non-NULL. matched has at least sheet->rule_count elements. out->family_off and out->family_len are set and k_ra8_css_set_family is OR'd into out->set. inherited and inl are not modified.out instances. Definition at line 649 of file reflow_css_cascade.c.
References ra8_css_style_t::family_len, ra8_css_style_t::family_off, internal_resolve(), k_ra8_css_set_family, and ra8_css_style_t::set.
Referenced by ra8_css_cascade_ctx().
|
static |
Resolve the scalar properties (align / colour / font-size / display).
Calls internal_resolve() for each of the four scalar CSS properties (text-align, color, font-size, display) and, when a winner is found, copies the winning value into out and OR's the corresponding set bit. font-size and display are resolved from rules and inline styles only; inheritance for those is handled by the caller to avoid double-application of percentage font-size values.
| [in,out] | out | Style record to write the resolved scalar values into. |
| [in] | sheet | Sheet providing the matching rules. |
| [in] | matched | Boolean array parallel to sheet->rules[]; true for each rule that matched the current element. |
| [in] | inherited | Style inherited from the parent element. |
| [in] | inl | Inline style from the element's style attribute. |
out, sheet, matched, inherited, and inl are non-NULL. matched has at least sheet->rule_count elements. out reflects the winning scalar values for all four properties. inherited and inl are not modified.out instances. Definition at line 585 of file reflow_css_cascade.c.
References ra8_css_style_t::align, ra8_css_style_t::color, ra8_css_style_t::display, ra8_css_style_t::font_unit, ra8_css_style_t::font_val, internal_resolve(), k_ra8_css_set_align, k_ra8_css_set_color, k_ra8_css_set_display, k_ra8_css_set_fontsize, and ra8_css_style_t::set.
Referenced by ra8_css_cascade_ctx().
|
static |
Case-insensitive equality of two byte spans.
Compares a[0..alen) and b[0..blen) byte-by-byte after ASCII case-folding via priv_reflow_css_lower(). Returns false immediately when either pointer is NULL or the lengths differ. The comparison loop is bounded by alen (which equals blen when lengths match).
| [in] | a | First byte span (need not be NUL-terminated). |
| [in] | alen | Length of a in bytes. |
| [in] | b | Second byte span (need not be NUL-terminated). |
| [in] | blen | Length of b in bytes. |
| true | Both spans have equal length and compare equal case-insensitively. |
| false | Either pointer is NULL, lengths differ, or any byte differs. |
a points to at least alen readable bytes, or is NULL. b points to at least blen readable bytes, or is NULL. a and b are not modified.Definition at line 720 of file reflow_css_cascade.c.
References priv_reflow_css_lower().
Referenced by internal_family_eq().
|
static |
True iff a space-separated class list contains name exactly.
Tokenises list[0..list_len) on whitespace and performs a byte-exact (not case-folded) comparison of each token against name[0..nlen), byte by byte. The outer loop is bounded by list_len; each pass skips leading whitespace, extracts the next token, and compares it. Returns true on the first matching token; false if no token matches.
| [in] | list | Space-separated class attribute value span. |
| [in] | list_len | Number of bytes in list. |
| [in] | name | Interned class name to search for. |
| [in] | nlen | Length of name in bytes. |
| true | At least one whitespace-delimited token in list equals name. |
| false | No token in list matches name, or list is empty. |
list points to at least list_len readable bytes. name points to at least nlen readable bytes. list and name are not modified.Definition at line 233 of file reflow_css_cascade.c.
References priv_reflow_css_is_ws().
Referenced by internal_anc_matches(), and ra8_css_rule_matches().
|
static |
True iff face f's family equals family (case-insensitive).
Retrieves the interned family name from sheet->names using f->family_off and f->family_len, then delegates to internal_ci_eq_span() for a case-insensitive comparison against family[0..family_len).
| [in] | sheet | Sheet owning the name pool referenced by f. |
| [in] | f | Font face whose interned family name to compare. |
| [in] | family | Byte span of the family name to match. |
| [in] | family_len | Number of bytes in family. |
| true | f's interned family name equals family case-insensitively. |
| false | The names differ in length or any byte differs. |
sheet and f are non-NULL. f->family_off + f->family_len <= sheet->names_used. sheet. family is not modified.Definition at line 760 of file reflow_css_cascade.c.
References ra8_css_fontface_t::family_len, ra8_css_fontface_t::family_off, internal_ci_eq_span(), and ra8_css_sheet_t::names.
Referenced by ra8_css_match_face().
|
static |
Locate the { ... } block beginning at selector offset i.
Scans forward for the block-open byte, then the block-close byte, writing both offsets out. Returns false when no block-open is present, which the caller treats as end-of-input.
| [in] | css | Stylesheet text (non-NULL, validated by the caller). |
| [in] | len | Total length of css, bytes. |
| [in] | i | Offset of the selector list start. |
| [out] | out_open | Offset of the block-open byte (valid only on true). |
| [out] | out_close | Offset of the block-close byte, or len if missing. |
i. | true | A block-open was found; out_open / out_close are set. |
| false | No block-open exists in [i, len). |
css, out_open, out_close are non-NULL. i <= len. Definition at line 145 of file reflow_css_cascade.c.
Referenced by ra8_css_parse().
|
static |
Resolve the winning declaration for one property across all sources.
setbit. Definition at line 467 of file reflow_css_cascade.c.
References ra8_css_rule_t::decl, internal_rule_rank(), k_priv_rank_inherited, ra8_css_rule_t::order, ra8_css_sheet_t::rule_count, ra8_css_sheet_t::rules, and ra8_css_style_t::set.
Referenced by internal_cascade_emphasis(), internal_cascade_family(), and internal_cascade_scalars().
|
static |
Full match of a (possibly descendant) rule against el + ancestors.
The subject must match el via ra8_css_rule_matches(); when rule->anc_count is 0 that check is sufficient. For rules with ancestor constraints, a greedy right-to-left scan walks the ancestor stack from innermost (ancestors[n_anc-1]) to outermost (ancestors[0]), consuming one rule ancestor part per stack element that matches via internal_anc_matches(). The descendant combinator allows any depth between parts. Returns true only when all ancestor parts are consumed (ai < 0 at loop exit).
| [in] | rule | Rule to match (subject + ancestor constraints). |
| [in] | el | Subject element to match the rule's subject compound. |
| [in] | ancestors | Array of ancestor elements; index 0 is outermost. |
| [in] | n_anc | Number of elements in ancestors. |
| [in] | sheet | Sheet owning the name pool for interned names. |
| true | The rule's subject and all ancestor constraints are satisfied. |
| false | The subject does not match el, or at least one ancestor constraint has no matching ancestor in the stack. |
rule, el, and sheet are non-NULL. ancestors points to at least n_anc readable elements, or is NULL when n_anc is 0. rule, el, ancestors, and sheet are not modified.Definition at line 381 of file reflow_css_cascade.c.
References ra8_css_rule_t::anc, ra8_css_rule_t::anc_count, internal_anc_matches(), and ra8_css_rule_matches().
Referenced by ra8_css_cascade_ctx().
|
static |
Packed specificity (id*10000 + class*100 + type), summing all parts.
Computes the CSS cascade specificity of rule by summing weights for each present constraint across the subject and all ancestor parts:
rule->anc_count. The result is used by internal_resolve() to determine which rule wins during the cascade.| [in] | rule | Rule whose specificity to compute. |
| 0 | When no type, class, or id constraints are present (universal). |
| >0 | The summed specificity per CSS cascade rules. |
rule is non-NULL. rule->anc_count is accurate (within bounds of rule->anc[]). rule is not modified.Definition at line 433 of file reflow_css_cascade.c.
References ra8_css_rule_t::anc, ra8_css_rule_t::anc_count, ra8_css_anc_t::class_len, ra8_css_rule_t::class_len, ra8_css_anc_t::id_len, ra8_css_rule_t::id_len, k_priv_spec_class, k_priv_spec_id, k_priv_spec_type, k_reflow_tag_unknown, ra8_css_rule_t::sel_tag, and ra8_css_anc_t::tag.
Referenced by internal_resolve().
|
static |
Step over a C-style comment starting at start.
Assumes css[start..start+1] is the open delimiter and scans to the matching close delimiter, returning the offset just past it (clamped to len when the comment is unterminated).
| [in] | css | Stylesheet text (non-NULL, validated by the caller). |
| [in] | len | Total length of css, bytes. |
| [in] | start | Offset of the comment open delimiter. |
| len | The comment ran to the end of the buffer (unterminated). |
css is non-NULL. start < len. Definition at line 106 of file reflow_css_cascade.c.
References k_priv_cmt_marker.
Referenced by ra8_css_parse().
|
nodiscard |
Compute an element's cascaded style.
Folds, in increasing priority: inherited (inheritable properties only), every matching rule in sheet by (specificity, source order), then inline_decl (always wins). Each property resolves independently to its highest-priority setter.
| [in] | sheet | Parsed stylesheet (may be empty). |
| [in] | el | Element identity. |
| [in] | inherited | Parent's computed style (inheritable props flow in). |
| [in] | inline_decl | Inline style="" declaration (set == 0 if none). |
el.sheet, el non-NULL (NULL sheet/el -> returns inherited). Definition at line 686 of file reflow_css_cascade.c.
References ra8_css_cascade_ctx().
|
nodiscard |
Compute an element's cascaded style with descendant-selector context.
Identical to ra8_css_cascade but also resolves descendant selectors (.chapter p, div p): a rule with ancestor parts matches only when its subject matches el AND each ancestor part matches some element in ancestors (the open-element stack, outermost first), in order. ra8_css_cascade is the n_anc == 0 case (descendant rules never match without ancestor context). Each matched ancestor part also adds to the rule's specificity.
| [in] | sheet | Parsed stylesheet (may be empty). |
| [in] | el | Element identity (the selector subject). |
| [in] | inherited | Parent's computed style (inheritable props flow in). |
| [in] | inline_decl | Inline style="" declaration (set == 0 if none). |
| [in] | ancestors | Open-element ancestors, outermost (root) first; may be NULL iff n_anc is 0. |
| [in] | n_anc | Number of entries in ancestors (0 = no context). |
el.sheet, el non-NULL (NULL -> returns inherited). ancestors non-NULL when n_anc > 0. Definition at line 664 of file reflow_css_cascade.c.
References internal_cascade_emphasis(), internal_cascade_family(), internal_cascade_scalars(), internal_rule_matches_ctx(), k_ra8_css_max_rules, ra8_css_sheet_t::rule_count, and ra8_css_sheet_t::rules.
Referenced by internal_open_styled(), and ra8_css_cascade().
|
nodiscard |
Read a parsed @font-face entry's src href bytes.
Returns the de-url()-wrapped manifest href slice for face idx (the value ra8_css_match_face's result indexes), so the caller can resolve it against the EPUB manifest. The bytes live in sheet's name pool and are valid for the sheet's lifetime; they are not NUL-terminated.
| [in] | sheet | Parsed stylesheet owning the face table. |
| [in] | idx | Face index in [0, face_count). |
| [out] | out_src | Receives a pointer to the href bytes. |
| [out] | out_len | Receives the href length, bytes. |
idx is valid and the href was returned. | false | A NULL pointer, or idx out of range. |
sheet, out_src, out_len non-NULL. Definition at line 799 of file reflow_css_cascade.c.
References ra8_css_sheet_t::face_count, ra8_css_sheet_t::faces, ra8_css_sheet_t::names, ra8_css_fontface_t::src_len, and ra8_css_fontface_t::src_off.
|
nodiscard |
Pick the @font-face whose family + emphasis best fits a run.
Scans sheet's parsed @font-face table for entries whose font-family equals family (case-insensitive, exact). Among those it prefers an exact (weight, style) match; failing that it falls back to the family's regular (non-bold, non-italic) face; failing that it reports k_ra8_css_no_face so the caller uses its default face. This is the face-selection decision #109's remainder consumes (it then resolves the winning entry's ra8_css_face_src href to a loaded typeface).
| [in] | sheet | Parsed stylesheet (its faces table is scanned). |
| [in] | family | Resolved font-family bytes (e.g. a cascade result's name slice); need not be NUL-terminated. |
| [in] | family_len | Length of family, bytes. |
| [in] | want_bold | Whether the run is bold. |
| [in] | want_italic | Whether the run is italic. |
sheet, family non-NULL and family_len > 0 (else no match). Definition at line 771 of file reflow_css_cascade.c.
References ra8_css_sheet_t::face_count, ra8_css_sheet_t::faces, internal_family_eq(), k_ra8_css_no_face, ra8_css_fontface_t::style_italic, and ra8_css_fontface_t::weight_bold.
Referenced by priv_reflow_tok_resolve_face_slot().
|
nodiscard |
Parse one <style> block's CSS text, appending rules to sheet.
Handles /<star> comments <star>/, comma-grouped simple selectors, and the v1 property set. Unsupported selectors (compound / descendant / pseudo) and unknown properties are skipped, not errored. Rules beyond k_ra8_css_max_rules (or names beyond the pool) are dropped silently.
| [in,out] | sheet | Stylesheet to grow. |
| [in] | css | CSS source bytes (a <style> block body). |
| [in] | len | Length of css, bytes. |
| k_ra8_ok | Parsed (possibly with truncation). |
| k_ra8_err_null_ptr | sheet or css is NULL. |
sheet, css non-NULL. sheet. Definition at line 167 of file reflow_css_cascade.c.
References internal_find_block(), internal_skip_comment(), k_ra8_err_null_ptr, k_ra8_ok, priv_reflow_css_is_ws(), and priv_reflow_css_parse_one_block().
Referenced by internal_handle_link(), and internal_handle_raw_text().
|
nodiscard |
Test whether rule rule's selector matches element el.
| [in] | rule | Rule whose selector is tested. |
| [in] | el | Element identity (tag + id + class list). |
| [in] | sheet | Sheet owning the rule's name pool. |
el. | false | A required pointer is NULL, or the selector does not match. |
rule, el, sheet non-NULL (NULL -> false). Definition at line 259 of file reflow_css_cascade.c.
References ra8_css_element_t::class_len, ra8_css_rule_t::class_len, ra8_css_rule_t::class_off, ra8_css_element_t::class_str, ra8_css_element_t::id, ra8_css_element_t::id_len, ra8_css_rule_t::id_len, ra8_css_rule_t::id_off, internal_class_list_has(), k_reflow_tag_unknown, ra8_css_sheet_t::names, ra8_css_rule_t::sel_tag, and ra8_css_element_t::tag.
Referenced by internal_rule_matches_ctx().
|
nodiscard |
Reset a stylesheet to empty (no rules, no names, order 0).
| [out] | sheet | Stylesheet to clear. |
| k_ra8_ok | Cleared. |
| k_ra8_err_null_ptr | sheet is NULL. |
sheet is non-NULL. sheet; not thread-safe relative to it. Definition at line 72 of file reflow_css_cascade.c.
References ra8_css_sheet_t::face_count, k_ra8_err_null_ptr, k_ra8_ok, ra8_css_sheet_t::names_used, ra8_css_sheet_t::next_order, and ra8_css_sheet_t::rule_count.
Referenced by priv_reflow_xml_walk().