From: yu.dongliang <18588496441@163.com> Date: Wed, 3 Jun 2026 08:21:47 +0000 (+0800) Subject: css: selectors priority: #id > .class > [attr] > :pse_class > ::pse_element > combina... X-Git-Url: http://baseworks.info/?a=commitdiff_plain;h=1b53cbe14bdb41d96eac65c973862b26ae544b7c;p=abc.git css: selectors priority: #id > .class > [attr] > :pse_class > ::pse_element > combinator > tag, 1, the last selector is 'key' selector, the highest priority selector is 'priority' selector. 2, the higher priority is, the later selector runs. if priority same, then check the type of 'key' selector: tag runs first, PSE class / attr / class / ID runs later one by one, so 'p .class {}' has higher priority, as it run later than '.class p {}'. -------------------------------------------------------------------------- I don't want to calculate the priority of combinators with the numbers of ID, class, attr, PSE class, tag, etc. Even if the browser kernel code can do it, but the CSS programer could NOT do it fast when write the CSS code, so we only see the 'key' selector (the last right) & the highest selector, it is easy! as this, it is NOT useful to write a long '.class list' to improve the priority, maybe no one remember these .classes later. -------------------------------------------------------------------------- for PSE class > PSE element, because PSE class is usually used for an action, PSE element used for an position in HTML. of course PSE element is static & PSE class is dynamic, so if a combinator has or has not an PSE in 'key' selector, the nature order is: normal selector < PSE element < PSE class, like below: .class:hover {} > .class::first-child {} > .class {}, Even if a new .class2 added to the 3rd like .class2 .class {}, .class:hover {} will run latest also, make sure the 'mouse move event' has the highest priority. --- diff --git a/html/abc_css.c b/html/abc_css.c index ddf0e17..ca18d4c 100644 --- a/html/abc_css.c +++ b/html/abc_css.c @@ -350,12 +350,35 @@ static int __css_parse_type(css_rule_t* css) css->flags = label->flags; if (css->text) { - css->last_key = j < 0 ? 0 : j; - css->last_key_end = k; + if (j < 0) + j = 0; - scf_logi("css selector '%s', last key: %.*s\n", css->text->data, - (int)(css->last_key_end - css->last_key), - css->text->data + css->last_key); + switch (css->text->data[j]) { + case '#': + case '.': + case '[': + case ':': + j++; + css->key_type = css->type; + break; + case ' ': + case '>': + case '+': + case '~': + j++; + default: + c = css->text->data[k]; + css->text->data[k] = '\0'; + + label = __html_find_label(css->text->data + j); + css->text->data[k] = c; + + css->key_type = label->type; + break; + }; + + css->last_key = j; + css->last_key_end = k; } return 0; } @@ -1372,10 +1395,10 @@ int css_hash_update_attrs(css_hash_t* h, css_rule_t* r) copy->keys = attr->keys; copy->flags = attr->flags; - if (h->attrs[i]) - abc_attr_free(h->attrs[i]); + if (h->cache_attrs[i]) + abc_attr_free(h->cache_attrs[i]); - h->attrs[i] = copy; + h->cache_attrs[i] = copy; } return 0; @@ -1399,11 +1422,139 @@ int abc_css_merge(abc_html_t* html, abc_obj_t* obj) for (l = scf_list_head(&obj->css); l != scf_list_sentinel(&obj->css); l = scf_list_next(l)) { css = scf_list_data(l, css_rule_t, list); - h = &(html->css->hash[css->type]); + if (css->type < ABC_HTML_NB) { + h = &(html->css->tag_selectors[css->type][0]); + + scf_list_add_tail(&h->rules, &css->hash); + } else if (ABC_CSS_COMBINATOR == css->type) { + h = &(html->css->tag_selectors[css->key_type][1]); + + scf_list_add_tail(&h->rules, &css->hash); + } else if (ABC_CSS_PSE_ELEMENT == css->type) { + assert(css->key_type < ABC_HTML_NB); + + h = &(html->css->pse_selectors[css->key_type][0]); + + scf_list_add_tail(&h->rules, &css->hash); + } else if (ABC_CSS_PSE_CLASS == css->type) { + assert(css->key_type < ABC_HTML_NB); - scf_list_add_tail(&h->rules, &css->hash); + h = &(html->css->pse_selectors[css->key_type][1]); - css_hash_update_attrs(h, css); + scf_list_add_tail(&h->rules, &css->hash); + } else { + int pse_type = ABC_CSS_COMBINATOR; + int key_type = css->key_type; + + if (':' == css->text->data[css->last_key_end]) + { + if (':' == css->text->data[css->last_key_end + 1]) + pse_type = ABC_CSS_PSE_ELEMENT; + else + pse_type = ABC_CSS_PSE_CLASS; + } + + if (ABC_CSS_ATTR == css->type) { + if (key_type < ABC_HTML_NB) + h = &(html->css->attr_selectors[key_type][pse_type - ABC_CSS_COMBINATOR]); + else + h = &(html->css->attr_selectors[ABC_HTML_NB][pse_type - ABC_CSS_COMBINATOR]); + + scf_list_add_tail(&h->rules, &css->hash); + } else { + if (key_type >= ABC_HTML_NB) + { + int c = css->text->data[css->last_key_end]; + + css->text->data[css->last_key_end] = '\0'; + uint32_t hash = elf_new_hash(css->text->data + css->last_key); + css->text->data[css->last_key_end] = c; + + key_type = hash % CSS_HASH_NB + ABC_HTML_NB; + scf_logi("key_type: %d, css->key_type: %d, pse_type - ABC_CSS_COMBINATOR: %d\n", + key_type, css->key_type, pse_type - ABC_CSS_COMBINATOR); + } + + if (ABC_CSS_CLASS == css->type) + h = &(html->css->class_selectors[key_type][pse_type - ABC_CSS_COMBINATOR]); + else + h = &(html->css->id_selectors [key_type][pse_type - ABC_CSS_COMBINATOR]); + + scf_list_add_tail(&h->rules, &css->hash); + } + } + + if (css->type <= ABC_CSS_COMBINATOR) + css_hash_update_attrs(h, css); + } + + return 0; +} + +int css_hash_use_caches(css_hash_t* h, abc_obj_t* obj) +{ + int j; + for (j = 0; j < sizeof(h->cache_attrs) / sizeof(h->cache_attrs[0]); j++) { + abc_attr_t* attr = h->cache_attrs[j]; + if (attr) + __css_set_attr(obj, attr); + } + + return 0; +} + +int css_hash_use_rules(css_hash_t* h, abc_obj_t* obj) +{ + scf_list_t* l; + css_rule_t* r; + + for (l = scf_list_head(&h->rules); l != scf_list_sentinel(&h->rules); l = scf_list_next(l)) { + r = scf_list_data(l, css_rule_t, hash); + + __css_use_complex(r, obj); + } + + return 0; +} + +int css_hash_use_selectors(css_hash_t selectors[][ABC_CSS_PSE_NB - ABC_CSS_COMBINATOR], int attr_type, int hash_max, abc_obj_t* obj) +{ + css_hash_t* h; + abc_attr_t* attr = abc_obj_get_attr(obj, attr_type); + + int i; + int j = -1; + + if (attr && attr->value && attr->value > 0) + j = elf_new_hash(attr->value->data) % hash_max; + + for (i = 0; i < ABC_CSS_PSE_NB - ABC_CSS_COMBINATOR; i++) { + + h = &(selectors[obj->type][i]); + css_hash_use_rules(h, obj); + + if (j >= 0) { + h = &(selectors[ABC_HTML_NB + j][i]); + css_hash_use_rules(h, obj); + } + } + + return 0; +} + +static int __css_use_attr_selectors(abc_css_t* css, abc_obj_t* obj) +{ + css_hash_t* h0; + css_hash_t* h1; + int i; + + for (i = 0; i < ABC_CSS_PSE_NB - ABC_CSS_COMBINATOR; i++) { + + h0 = &(css->id_selectors[obj->type ][i]); + h1 = &(css->id_selectors[ABC_HTML_NB][i]); + + css_hash_use_rules(h0, obj); + css_hash_use_rules(h1, obj); } return 0; @@ -1420,25 +1571,16 @@ int abc_css_use(abc_html_t* html, abc_obj_t* obj) int64_t tv0 = gettime(); if (html->css) { - css_hash_t* h = &(html->css->hash[obj->type]); - - int i; - for (i = 0; i < sizeof(h->attrs) / sizeof(h->attrs[0]); i++) { - attr = h->attrs[i]; - if (attr) - __css_set_attr(obj, attr); - } + css_hash_use_caches(&(html->css->tag_selectors[obj->type][0]), obj); + css_hash_use_caches(&(html->css->tag_selectors[obj->type][1]), obj); - for (i = ABC_CSS_COMBINATOR; i < ABC_CSS_SELECTOR_NB; i++) - { - h = &(html->css->hash[i]); + css_hash_use_rules(&(html->css->pse_selectors[obj->type][0]), obj); + css_hash_use_rules(&(html->css->pse_selectors[obj->type][1]), obj); - for (l = scf_list_head(&h->rules); l != scf_list_sentinel(&h->rules); l = scf_list_next(l)) { - css = scf_list_data(l, css_rule_t, hash); + __css_use_attr_selectors(html->css, obj); - __css_use_complex(css, obj); - } - } + css_hash_use_selectors(html->css->class_selectors, ABC_CSS_CLASS, CSS_HASH_NB, obj); + css_hash_use_selectors(html->css->id_selectors, ABC_CSS_ID, CSS_HASH_NB, obj); } // for inline css below diff --git a/html/abc_obj.c b/html/abc_obj.c index 40b0261..7378001 100644 --- a/html/abc_obj.c +++ b/html/abc_obj.c @@ -64,28 +64,48 @@ void abc_obj_free(abc_obj_t* obj) } } +void css_hash_free(css_hash_t* h) +{ + scf_list_t* l; + css_rule_t* r; + int k; + + for (l = scf_list_head(&h->rules); l != scf_list_sentinel(&h->rules); ) { + r = scf_list_data(l, css_rule_t, hash); + l = scf_list_next(l); + + scf_list_del(&r->hash); + } + + for (k = 0; k < sizeof(h->cache_attrs) / sizeof(h->cache_attrs); k++) { + if (h->cache_attrs[k]) + abc_attr_free(h->cache_attrs[k]); + } +} + void abc_css_free(abc_css_t* css) { if (css) { - scf_list_t* l; - css_rule_t* r; css_hash_t* h; int i; int j; - for (i = 0; i < ABC_CSS_SELECTOR_NB; i++) { - h = &(css->hash[i]); - - for (l = scf_list_head(&h->rules); l != scf_list_sentinel(&h->rules); ) { - r = scf_list_data(l, css_rule_t, hash); - l = scf_list_next(l); - - scf_list_del(&r->hash); + for (i = 0; i < ABC_HTML_NB; i++) { + for (j = 0; j < 2; j++) { + css_hash_free(&(css->tag_selectors[i][j])); + css_hash_free(&(css->pse_selectors[i][j])); } + } + + for (i = 0; i < ABC_HTML_NB + 1; i++) { + for (j = 0; j < ABC_CSS_PSE_NB - ABC_CSS_COMBINATOR; j++) + css_hash_free(&(css->attr_selectors[i][j])); + } - for (j = 0; j < sizeof(h->attrs) / sizeof(h->attrs); j++) { - if (h->attrs[j]) - abc_attr_free(h->attrs[j]); + for (i = 0; i < ABC_HTML_NB + CSS_HASH_NB; i++) { + for (j = 0; j < ABC_CSS_PSE_NB - ABC_CSS_COMBINATOR; j++) { + css_hash_free(&(css->class_selectors[i][j])); + css_hash_free(&(css->id_selectors [i][j])); } } @@ -100,8 +120,25 @@ abc_css_t* abc_css_alloc() return NULL; int i; - for (i = 0; i < ABC_CSS_SELECTOR_NB; i++) - scf_list_init(&(css->hash[i].rules)); + int j; + for (i = 0; i < ABC_HTML_NB; i++) { + for (j = 0; j < 2; j++) { + scf_list_init(&(css->tag_selectors[i][j].rules)); + scf_list_init(&(css->pse_selectors[i][j].rules)); + } + } + + for (i = 0; i < ABC_HTML_NB + 1; i++) { + for (j = 0; j < ABC_CSS_PSE_NB - ABC_CSS_COMBINATOR; j++) + scf_list_init(&(css->attr_selectors[i][j].rules)); + } + + for (i = 0; i < ABC_HTML_NB + CSS_HASH_NB; i++) { + for (j = 0; j < ABC_CSS_PSE_NB - ABC_CSS_COMBINATOR; j++) { + scf_list_init(&(css->class_selectors[i][j].rules)); + scf_list_init(&(css->id_selectors [i][j].rules)); + } + } return css; } diff --git a/html/abc_obj.h b/html/abc_obj.h index 452ea0d..16016c7 100644 --- a/html/abc_obj.h +++ b/html/abc_obj.h @@ -79,7 +79,9 @@ enum abc_objs ABC_CSS_COMBINATOR = ABC_HTML_NB, ABC_CSS_PSE_ELEMENT, ABC_CSS_PSE_CLASS, - ABC_CSS_ATTR, + ABC_CSS_PSE_NB, + + ABC_CSS_ATTR = ABC_CSS_PSE_NB, ABC_CSS_CLASS, // also for html attr 'class' ABC_CSS_ID, // also for html attr 'id' @@ -284,20 +286,31 @@ struct css_rule_s int line; // line int pos; // position + scf_list_t hash; + int key_type; + int last_key; int last_key_end; - scf_list_t hash; }; struct css_hash_s { scf_list_t rules; - abc_attr_t* attrs[ABC_HTML_CSS_NB - ABC_CSS_CLASS]; + abc_attr_t* cache_attrs[ABC_HTML_CSS_NB - ABC_CSS_CLASS]; + + uint32_t hash; + uint32_t flags; }; struct abc_css_s { - css_hash_t hash[ABC_CSS_SELECTOR_NB]; + css_hash_t tag_selectors [ABC_HTML_NB][2]; + css_hash_t pse_selectors [ABC_HTML_NB][2]; + +#define CSS_HASH_NB 256 + css_hash_t attr_selectors [ABC_HTML_NB + 1 ][ABC_CSS_PSE_NB - ABC_CSS_COMBINATOR]; + css_hash_t class_selectors[ABC_HTML_NB + CSS_HASH_NB][ABC_CSS_PSE_NB - ABC_CSS_COMBINATOR]; + css_hash_t id_selectors [ABC_HTML_NB + CSS_HASH_NB][ABC_CSS_PSE_NB - ABC_CSS_COMBINATOR]; }; struct abc_obj_s @@ -318,13 +331,13 @@ struct abc_obj_s int line; // line int pos; // position + scf_list_t css; int text_line; int text_pos; - scf_list_t css; - scf_list_t childs; - int n_childs; int n_texts; + int n_childs; + scf_list_t childs; void* gtk_builder; abc_filter_t* av_filter; @@ -457,4 +470,14 @@ static inline abc_attr_t* abc_obj_get_attr(abc_obj_t* obj, int key) return obj->attrs[key - ABC_CSS_CLASS]; } +static uint32_t elf_new_hash(const char *s) +{ + uint32_t h = 5381; + + for (unsigned char c = *s; c != '\0'; c = *++s) + h = (h << 5) + h + c; + + return h; +} + #endif