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