css: selectors priority: #id > .class > [attr] > :pse_class > ::pse_element > combina...
authoryu.dongliang <18588496441@163.com>
Wed, 3 Jun 2026 08:21:47 +0000 (16:21 +0800)
committeryu.dongliang <18588496441@163.com>
Wed, 3 Jun 2026 08:22:11 +0000 (16:22 +0800)
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.

html/abc_css.c
html/abc_obj.c
html/abc_obj.h

index ddf0e17bcb347693492a71db2257dee1cb6f3903..ca18d4cdbfc9a7a7d007339686f6e281932abab9 100644 (file)
@@ -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
index 40b026116698cf6168f24a500883a9e6ab9b8536..73780010ece8fa2d3829da70236141820b3cdc71 100644 (file)
@@ -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;
 }
index 452ea0d73a017ee692093458e9e654e593b46597..16016c7d2cb0f4e78717e2aa4fce59863accc330 100644 (file)
@@ -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