1 /*
2  * Copyright (C) 2018-2022 Alexander Borisov
3  *
4  * Author: Alexander Borisov <borisov@lexbor.com>
5  */
6 
7 #include "lexbor/html/style.h"
8 #include "lexbor/html/interfaces/element.h"
9 #include "lexbor/html/interfaces/document.h"
10 
11 
12 typedef struct {
13     lexbor_str_t  *str;
14     lexbor_mraw_t *mraw;
15 }
16 lxb_html_element_style_ctx_t;
17 
18 typedef struct {
19     lxb_html_element_t          *element;
20     lxb_html_element_style_cb_f cb;
21     void                        *ctx;
22     bool                        weak;
23 }
24 lxb_html_element_walk_ctx_t;
25 
26 
27 static lxb_status_t
28 lxb_html_element_style_walk_cb(lexbor_avl_t *avl, lexbor_avl_node_t **root,
29                                lexbor_avl_node_t *node, void *ctx);
30 
31 static lxb_status_t
32 lxb_html_element_style_weak_append(lxb_html_document_t *doc,
33                                    lxb_html_style_node_t *node,
34                                    lxb_css_rule_declaration_t *declr,
35                                    lxb_css_selector_specificity_t spec);
36 
37 static lxb_status_t
38 lxb_html_element_style_serialize_cb(lexbor_avl_t *avl, lexbor_avl_node_t **root,
39                                     lexbor_avl_node_t *node, void *ctx);
40 
41 #if 0
42 static lxb_status_t
43 lxb_html_element_style_serialize_str_cb(const lxb_char_t *data,
44                                         size_t len, void *ctx);
45 #endif
46 
47 
48 lxb_html_element_t *
lxb_html_element_interface_create(lxb_html_document_t * document)49 lxb_html_element_interface_create(lxb_html_document_t *document)
50 {
51     lxb_html_element_t *element;
52 
53     element = lexbor_mraw_calloc(document->dom_document.mraw,
54                                  sizeof(lxb_html_element_t));
55     if (element == NULL) {
56         return NULL;
57     }
58 
59     lxb_dom_node_t *node = lxb_dom_interface_node(element);
60 
61     node->owner_document = lxb_html_document_original_ref(document);
62     node->type = LXB_DOM_NODE_TYPE_ELEMENT;
63 
64     return element;
65 }
66 
67 lxb_html_element_t *
lxb_html_element_interface_destroy(lxb_html_element_t * element)68 lxb_html_element_interface_destroy(lxb_html_element_t *element)
69 {
70     (void) lxb_dom_node_interface_destroy(lxb_dom_interface_node(element));
71     return NULL;
72 }
73 
74 lxb_html_element_t *
lxb_html_element_inner_html_set(lxb_html_element_t * element,const lxb_char_t * html,size_t size)75 lxb_html_element_inner_html_set(lxb_html_element_t *element,
76                                 const lxb_char_t *html, size_t size)
77 {
78     lxb_dom_node_t *node, *child;
79     lxb_dom_node_t *root = lxb_dom_interface_node(element);
80     lxb_html_document_t *doc = lxb_html_interface_document(root->owner_document);
81 
82     node = lxb_html_document_parse_fragment(doc, &element->element, html, size);
83     if (node == NULL) {
84         return NULL;
85     }
86 
87     while (root->first_child != NULL) {
88         lxb_dom_node_destroy_deep(root->first_child);
89     }
90 
91     while (node->first_child != NULL) {
92         child = node->first_child;
93 
94         lxb_dom_node_remove(child);
95         lxb_dom_node_insert_child(root, child);
96     }
97 
98     lxb_dom_node_destroy(node);
99 
100     return lxb_html_interface_element(root);
101 }
102 
103 const lxb_css_rule_declaration_t *
lxb_html_element_style_by_name(lxb_html_element_t * element,const lxb_char_t * name,size_t size)104 lxb_html_element_style_by_name(lxb_html_element_t *element,
105                                const lxb_char_t *name, size_t size)
106 {
107 #if 0
108     uintptr_t id;
109     lxb_html_style_node_t *node;
110     lxb_dom_document_t *ddoc = lxb_dom_interface_node(element)->owner_document;
111     lxb_html_document_t *doc = lxb_html_interface_document(ddoc);
112 
113     id = lxb_html_style_id_by_name(doc, name, size);
114     if (id == LXB_CSS_PROPERTY__UNDEF) {
115         return NULL;
116     }
117 
118     node = (void *) lexbor_avl_search(doc->css.styles, element->style, id);
119 
120     return (node != NULL) ? node->entry.value : NULL;
121 #endif
122     return NULL;
123 }
124 
125 const lxb_css_rule_declaration_t *
lxb_html_element_style_by_id(lxb_html_element_t * element,uintptr_t id)126 lxb_html_element_style_by_id(lxb_html_element_t *element, uintptr_t id)
127 {
128 #if 0
129     const lxb_html_style_node_t *node;
130 
131     node = lxb_html_element_style_node_by_id(element, id);
132     if (node == NULL) {
133         return NULL;
134     }
135 
136     return node->entry.value;
137 #endif
138     return NULL;
139 }
140 
141 const lxb_html_style_node_t *
lxb_html_element_style_node_by_id(lxb_html_element_t * element,uintptr_t id)142 lxb_html_element_style_node_by_id(lxb_html_element_t *element, uintptr_t id)
143 {
144     lxb_dom_document_t *ddoc = lxb_dom_interface_node(element)->owner_document;
145     lxb_html_document_t *doc = lxb_html_interface_document(ddoc);
146 
147     return (lxb_html_style_node_t *) lexbor_avl_search(doc->css.styles,
148                                                        element->style, id);
149 }
150 
151 const lxb_html_style_node_t *
lxb_html_element_style_node_by_name(lxb_html_element_t * element,const lxb_char_t * name,size_t size)152 lxb_html_element_style_node_by_name(lxb_html_element_t *element,
153                                     const lxb_char_t *name, size_t size)
154 {
155 #if 0
156     uintptr_t id;
157     lxb_dom_document_t *ddoc = lxb_dom_interface_node(element)->owner_document;
158     lxb_html_document_t *doc = lxb_html_interface_document(ddoc);
159 
160     id = lxb_html_style_id_by_name(doc, name, size);
161     if (id == LXB_CSS_PROPERTY__UNDEF) {
162         return NULL;
163     }
164 
165     return (lxb_html_style_node_t *) lexbor_avl_search(doc->css.styles,
166                                                        element->style, id);
167 #endif
168     return NULL;
169 }
170 
171 const void *
lxb_html_element_css_property_by_id(lxb_html_element_t * element,uintptr_t id)172 lxb_html_element_css_property_by_id(lxb_html_element_t *element, uintptr_t id)
173 {
174 #if 0
175     lxb_css_rule_declaration_t *declr;
176     const lxb_html_style_node_t *node;
177 
178     node = lxb_html_element_style_node_by_id(element, id);
179     if (node == NULL) {
180         return lxb_css_property_initial_by_id(id);
181     }
182 
183     declr = node->entry.value;
184 
185     return declr->u.user;
186 #endif
187     return NULL;
188 }
189 
190 lxb_status_t
lxb_html_element_style_walk(lxb_html_element_t * element,lxb_html_element_style_cb_f cb,void * ctx,bool with_weak)191 lxb_html_element_style_walk(lxb_html_element_t *element,
192                             lxb_html_element_style_cb_f cb, void *ctx,
193                             bool with_weak)
194 {
195     lxb_html_element_walk_ctx_t walk;
196 
197     walk.element = element;
198     walk.cb = cb;
199     walk.ctx = ctx;
200     walk.weak = with_weak;
201 
202     return lexbor_avl_foreach(NULL, &element->style,
203                               lxb_html_element_style_walk_cb, &walk);
204 }
205 
206 static lxb_status_t
lxb_html_element_style_walk_cb(lexbor_avl_t * avl,lexbor_avl_node_t ** root,lexbor_avl_node_t * node,void * ctx)207 lxb_html_element_style_walk_cb(lexbor_avl_t *avl, lexbor_avl_node_t **root,
208                                lexbor_avl_node_t *node, void *ctx)
209 {
210     lxb_status_t status;
211     lxb_html_style_weak_t *weak;
212     lxb_html_style_node_t *style;
213     lxb_html_element_walk_ctx_t *walk = ctx;
214 
215     style = (lxb_html_style_node_t *) node;
216 
217     status = walk->cb(walk->element, node->value, walk->ctx, style->sp, false);
218     if (status != LXB_STATUS_OK) {
219         return status;
220     }
221 
222     weak = style->weak;
223 
224     while (weak != NULL) {
225         status = walk->cb(walk->element, weak->value, walk->ctx,
226                           weak->sp, true);
227         if (status != LXB_STATUS_OK) {
228             return status;
229         }
230 
231         weak = weak->next;
232     }
233 
234     return LXB_STATUS_OK;
235 }
236 
237 lxb_status_t
lxb_html_element_style_parse(lxb_html_element_t * element,const lxb_char_t * style,size_t size)238 lxb_html_element_style_parse(lxb_html_element_t *element,
239                              const lxb_char_t *style, size_t size)
240 {
241 #if 0
242     lxb_css_rule_declaration_list_t *list;
243 
244     lxb_dom_document_t *ddoc = lxb_dom_interface_node(element)->owner_document;
245     lxb_html_document_t *doc = lxb_html_interface_document(ddoc);
246     lxb_html_document_css_t *css = &doc->css;
247 
248     list = lxb_css_declaration_list_parse(css->parser, css->memory,
249                                           style, size);
250     if (list == NULL) {
251         return css->parser->status;
252     }
253 
254     element->list = list;
255 
256     return lxb_html_element_style_list_append(element, list,
257                                               lxb_css_selector_sp_up_s(0));
258 #endif
259     return LXB_STATUS_ERROR;
260 }
261 
262 lxb_status_t
lxb_html_element_style_append(lxb_html_element_t * element,lxb_css_rule_declaration_t * declr,lxb_css_selector_specificity_t spec)263 lxb_html_element_style_append(lxb_html_element_t *element,
264                               lxb_css_rule_declaration_t *declr,
265                               lxb_css_selector_specificity_t spec)
266 {
267     uintptr_t id;
268     lxb_status_t status;
269     lexbor_str_t *name;
270     lxb_html_style_node_t *node;
271 
272     lxb_dom_document_t *ddoc = lxb_dom_interface_node(element)->owner_document;
273     lxb_html_document_t *doc = lxb_html_interface_document(ddoc);
274     lxb_html_document_css_t *css = &doc->css;
275 
276     id = declr->type;
277 
278     lxb_css_selector_sp_set_i(spec, declr->important);
279 
280     if (id == LXB_CSS_PROPERTY__CUSTOM) {
281         name = &declr->u.custom->name;
282 
283         id = lxb_html_document_css_customs_id(doc, name->data,
284                                               name->length);
285         if (id == 0) {
286             /* FIXME: what to do with an error? */
287             return LXB_STATUS_ERROR;
288         }
289     }
290 
291     node = (void *) lexbor_avl_search(css->styles, element->style, id);
292     if (node != NULL) {
293         if (spec < node->sp) {
294             return lxb_html_element_style_weak_append(doc, node, declr, spec);
295         }
296 
297         status = lxb_html_element_style_weak_append(doc, node,
298                                                     node->entry.value, node->sp);
299         if (status != LXB_STATUS_OK) {
300             return status;
301         }
302 
303         lxb_css_rule_ref_dec(node->entry.value);
304 
305         node->entry.value = declr;
306         node->sp = spec;
307 
308         return LXB_STATUS_OK;
309     }
310 
311     node = (void *) lexbor_avl_insert(css->styles, &element->style,
312                                       id, declr);
313     if (node == NULL) {
314         /* FIXME: what to do with an error? */
315         return LXB_STATUS_ERROR;
316     }
317 
318     node->sp = spec;
319 
320     return lxb_css_rule_ref_inc(lxb_css_rule(declr));
321 }
322 
323 static lxb_status_t
lxb_html_element_style_weak_append(lxb_html_document_t * doc,lxb_html_style_node_t * node,lxb_css_rule_declaration_t * declr,lxb_css_selector_specificity_t spec)324 lxb_html_element_style_weak_append(lxb_html_document_t *doc,
325                                    lxb_html_style_node_t *node,
326                                    lxb_css_rule_declaration_t *declr,
327                                    lxb_css_selector_specificity_t spec)
328 {
329     lxb_html_style_weak_t *weak, *prev, *new_weak;
330 
331     new_weak = lexbor_dobject_alloc(doc->css.weak);
332     if (new_weak == NULL) {
333         return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
334     }
335 
336     new_weak->value = declr;
337     new_weak->sp = spec;
338 
339     if (node->weak == NULL) {
340         node->weak = new_weak;
341         new_weak->next = NULL;
342 
343         goto done;
344     }
345 
346     weak = node->weak;
347 
348     if (weak->sp <= spec) {
349         node->weak = new_weak;
350         new_weak->next = weak;
351 
352         goto done;
353     }
354 
355     prev = weak;
356     weak = weak->next;
357 
358     while (weak != NULL) {
359         if (weak->sp <= spec) {
360             prev->next = new_weak;
361             new_weak->next = weak;
362 
363             goto done;
364         }
365 
366         prev = weak;
367         weak = weak->next;
368     }
369 
370     prev->next = new_weak;
371     new_weak->next = NULL;
372 
373 done:
374 
375     return lxb_css_rule_ref_inc(lxb_css_rule(declr));
376 }
377 
378 lxb_status_t
lxb_html_element_style_list_append(lxb_html_element_t * element,lxb_css_rule_declaration_list_t * list,lxb_css_selector_specificity_t spec)379 lxb_html_element_style_list_append(lxb_html_element_t *element,
380                                    lxb_css_rule_declaration_list_t *list,
381                                    lxb_css_selector_specificity_t spec)
382 {
383     lxb_status_t status;
384     lxb_css_rule_t *rule;
385     lxb_css_rule_declaration_t *declr;
386 
387     rule = list->first;
388 
389     while (rule != NULL) {
390         if (rule->type != LXB_CSS_RULE_DECLARATION) {
391             goto next;
392         }
393 
394         declr = lxb_css_rule_declaration(rule);
395 
396         status = lxb_html_element_style_append(element, declr, spec);
397         if (status != LXB_STATUS_OK) {
398             /* FIXME: what to do with an error? */
399         }
400 
401     next:
402 
403         rule = rule->next;
404     }
405 
406     return LXB_STATUS_OK;
407 }
408 
409 void
lxb_html_element_style_remove_by_name(lxb_html_element_t * element,const lxb_char_t * name,size_t size)410 lxb_html_element_style_remove_by_name(lxb_html_element_t *element,
411                                       const lxb_char_t *name, size_t size)
412 {
413 #if 0
414     uintptr_t id;
415     lxb_dom_document_t *ddoc = lxb_dom_interface_node(element)->owner_document;
416     lxb_html_document_t *doc = lxb_html_interface_document(ddoc);
417 
418     id = lxb_html_style_id_by_name(doc, name, size);
419     if (id == LXB_CSS_PROPERTY__UNDEF) {
420         return;
421     }
422 
423     lxb_html_element_style_remove_by_id(element, id);
424 #endif
425 }
426 
427 void
lxb_html_element_style_remove_by_id(lxb_html_element_t * element,uintptr_t id)428 lxb_html_element_style_remove_by_id(lxb_html_element_t *element, uintptr_t id)
429 {
430 #if 0
431     lxb_html_style_node_t *node;
432     lxb_dom_document_t *ddoc = lxb_dom_interface_node(element)->owner_document;
433     lxb_html_document_t *doc = lxb_html_interface_document(ddoc);
434 
435     node = (lxb_html_style_node_t *) lexbor_avl_search(doc->css.styles,
436                                                        element->style, id);
437     if (node != NULL) {
438         lxb_html_element_style_remove_all(doc, &element->style, node);
439     }
440 #endif
441 }
442 
443 lxb_html_style_node_t *
lxb_html_element_style_remove_all_not(lxb_html_document_t * doc,lexbor_avl_node_t ** root,lxb_html_style_node_t * style,bool bs)444 lxb_html_element_style_remove_all_not(lxb_html_document_t *doc,
445                                       lexbor_avl_node_t **root,
446                                       lxb_html_style_node_t *style, bool bs)
447 {
448     lxb_html_style_weak_t *weak, *prev, *next;
449 
450     weak = style->weak;
451     prev = NULL;
452 
453     while (weak != NULL) {
454         next = weak->next;
455 
456         if (lxb_css_selector_sp_s(weak->sp) == bs) {
457             lxb_css_rule_ref_dec_destroy(weak->value);
458             lexbor_dobject_free(doc->css.weak, weak);
459 
460             if (prev != NULL) {
461                 prev->next = next;
462             }
463             else {
464                 style->weak = next;
465             }
466         }
467         else {
468             prev = weak;
469         }
470 
471         weak = next;
472     }
473 
474     if (lxb_css_selector_sp_s(style->sp) != bs) {
475         return style;
476     }
477 
478     lxb_css_rule_ref_dec_destroy(style->entry.value);
479 
480     if (style->weak == NULL) {
481         lexbor_avl_remove_by_node(doc->css.styles, root,
482                                   (lexbor_avl_node_t *) style);
483         return NULL;
484     }
485 
486     weak = style->weak;
487 
488     style->entry.value = weak->value;
489     style->sp = weak->sp;
490     style->weak = weak->next;
491 
492     lexbor_dobject_free(doc->css.weak, weak);
493 
494     return style;
495 }
496 
497 lxb_html_style_node_t *
lxb_html_element_style_remove_all(lxb_html_document_t * doc,lexbor_avl_node_t ** root,lxb_html_style_node_t * style)498 lxb_html_element_style_remove_all(lxb_html_document_t *doc,
499                                   lexbor_avl_node_t **root,
500                                   lxb_html_style_node_t *style)
501 {
502     lxb_html_style_weak_t *weak, *next;
503 
504     weak = style->weak;
505 
506     while (weak != NULL) {
507         next = weak->next;
508 
509         lxb_css_rule_ref_dec_destroy(weak->value);
510         lexbor_dobject_free(doc->css.weak, weak);
511 
512         weak = next;
513     }
514 
515     lxb_css_rule_ref_dec_destroy(style->entry.value);
516     lexbor_avl_remove_by_node(doc->css.styles, root,
517                               (lexbor_avl_node_t *) style);
518     return NULL;
519 }
520 
521 lxb_html_style_node_t *
lxb_html_element_style_remove_by_list(lxb_html_document_t * doc,lexbor_avl_node_t ** root,lxb_html_style_node_t * style,lxb_css_rule_declaration_list_t * list)522 lxb_html_element_style_remove_by_list(lxb_html_document_t *doc,
523                                       lexbor_avl_node_t **root,
524                                       lxb_html_style_node_t *style,
525                                       lxb_css_rule_declaration_list_t *list)
526 {
527     lxb_html_style_weak_t *weak, *prev, *next;
528 
529     weak = style->weak;
530     prev = NULL;
531 
532     while (weak != NULL) {
533         next = weak->next;
534 
535         if (((lxb_css_rule_declaration_t *) weak->value)->rule.parent
536             == (lxb_css_rule_t *) list)
537         {
538             lxb_css_rule_ref_dec_destroy(weak->value);
539             lexbor_dobject_free(doc->css.weak, weak);
540 
541             if (prev != NULL) {
542                 prev->next = next;
543             }
544             else {
545                 style->weak = next;
546             }
547         }
548         else {
549             prev = weak;
550         }
551 
552         weak = next;
553     }
554 
555     if (((lxb_css_rule_declaration_t *) style->entry.value)->rule.parent
556         != (lxb_css_rule_t *) list)
557     {
558         return style;
559     }
560 
561     lxb_css_rule_ref_dec_destroy(style->entry.value);
562 
563     if (style->weak == NULL) {
564         lexbor_avl_remove_by_node(doc->css.styles, root,
565                                   (lexbor_avl_node_t *) style);
566         return NULL;
567     }
568 
569     weak = style->weak;
570 
571     style->entry.value = weak->value;
572     style->sp = weak->sp;
573     style->weak = weak->next;
574 
575     lexbor_dobject_free(doc->css.weak, weak);
576 
577     return style;
578 }
579 
580 lxb_status_t
lxb_html_element_style_serialize(lxb_html_element_t * element,lxb_html_element_style_opt_t opt,lexbor_serialize_cb_f cb,void * ctx)581 lxb_html_element_style_serialize(lxb_html_element_t *element,
582                                  lxb_html_element_style_opt_t opt,
583                                  lexbor_serialize_cb_f cb, void *ctx)
584 {
585     lexbor_serialize_ctx_t context;
586 
587     context.cb = cb;
588     context.ctx = ctx;
589     context.opt = opt;
590     context.count = 0;
591 
592     return lexbor_avl_foreach(NULL, &element->style,
593                               lxb_html_element_style_serialize_cb, &context);
594 }
595 
596 static lxb_status_t
lxb_html_element_style_serialize_cb(lexbor_avl_t * avl,lexbor_avl_node_t ** root,lexbor_avl_node_t * node,void * ctx)597 lxb_html_element_style_serialize_cb(lexbor_avl_t *avl, lexbor_avl_node_t **root,
598                                     lexbor_avl_node_t *node, void *ctx)
599 {
600 #if 0
601     lxb_status_t status;
602     lexbor_serialize_ctx_t *context = ctx;
603 
604     static const lexbor_str_t splt = lexbor_str("; ");
605 
606     if (context->count > 0) {
607         lexbor_serialize_write(context->cb, splt.data, splt.length,
608                                context->ctx, status);
609     }
610 
611     context->count = 1;
612 
613     return lxb_css_rule_serialize(node->value, context->cb, context->ctx);
614 #endif
615     return LXB_STATUS_ERROR;
616 }
617 
618 lxb_status_t
lxb_html_element_style_serialize_str(lxb_html_element_t * element,lexbor_str_t * str,lxb_html_element_style_opt_t opt)619 lxb_html_element_style_serialize_str(lxb_html_element_t *element,
620                                      lexbor_str_t *str,
621                                      lxb_html_element_style_opt_t opt)
622 {
623 #if 0
624     lxb_dom_document_t *doc;
625     lxb_html_element_style_ctx_t context;
626 
627     doc = lxb_dom_interface_node(element)->owner_document;
628 
629     if (str->data == NULL) {
630         lexbor_str_init(str, doc->text, 1024);
631 
632         if (str->data == NULL) {
633             return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
634         }
635     }
636 
637     context.str = str;
638     context.mraw = doc->text;
639 
640     return lxb_html_element_style_serialize(element, opt,
641                             lxb_html_element_style_serialize_str_cb, &context);
642 #endif
643     return LXB_STATUS_ERROR;
644 }
645 
646 #if 0
647 static lxb_status_t
648 lxb_html_element_style_serialize_str_cb(const lxb_char_t *data,
649                                         size_t len, void *ctx)
650 {
651     lxb_char_t *ret;
652     lxb_html_element_style_ctx_t *context = ctx;
653 
654     ret = lexbor_str_append(context->str, context->mraw, data, len);
655     if (ret == NULL) {
656         return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
657     }
658 
659     return LXB_STATUS_OK;
660 }
661 #endif
662