/* * Copyright (C) 2018-2024 Alexander Borisov * * Author: Alexander Borisov */ #include "lexbor/core/str.h" #include "lexbor/html/interfaces/document.h" #include "lexbor/html/interfaces/title_element.h" #include "lexbor/html/interfaces/style_element.h" #include "lexbor/html/node.h" #include "lexbor/html/parser.h" #include "lexbor/html/style.h" #include "lexbor/tag/tag.h" #include "lexbor/dom/interfaces/text.h" #include "lexbor/dom/interfaces/element.h" #define LXB_HTML_TAG_RES_DATA #define LXB_HTML_TAG_RES_SHS_DATA #include "lexbor/html/tag_res.h" static const lexbor_hash_search_t lxb_html_document_css_customs_se = { .cmp = lexbor_str_data_ncasecmp, .hash = lexbor_hash_make_id }; static const lexbor_hash_insert_t lxb_html_document_css_customs_in = { .copy = lexbor_hash_copy, .cmp = lexbor_str_data_ncasecmp, .hash = lexbor_hash_make_id }; typedef struct { lexbor_hash_entry_t entry; uintptr_t id; } lxb_html_document_css_custom_entry_t; typedef struct { lxb_html_document_t *doc; bool all; } lxb_html_document_event_ctx_t; typedef struct { lxb_html_document_t *doc; lxb_css_rule_declaration_list_t *list; } lxb_html_document_remove_ctx_t; static lxb_html_document_css_custom_entry_t * lxb_html_document_css_customs_insert(lxb_html_document_t *document, const lxb_char_t *key, size_t length); #if 0 static lxb_status_t lxb_html_document_style_remove_by_rule_cb(lxb_dom_node_t *node, lxb_css_selector_specificity_t spec, void *ctx); static lxb_status_t lxb_html_document_style_remove_avl_cb(lexbor_avl_t *avl, lexbor_avl_node_t **root, lexbor_avl_node_t *node, void *ctx); static lxb_status_t lxb_html_document_style_cb(lxb_dom_node_t *node, lxb_css_selector_specificity_t spec, void *ctx); static lxb_status_t lxb_html_document_done(lxb_html_document_t *document); #endif lxb_status_t lxb_html_parse_chunk_prepare(lxb_html_parser_t *parser, lxb_html_document_t *document); lxb_inline lxb_status_t lxb_html_document_parser_prepare(lxb_html_document_t *document); static lexbor_action_t lxb_html_document_title_walker(lxb_dom_node_t *node, void *ctx); #if 0 static lxb_status_t lxb_html_document_event_insert(lxb_dom_node_t *node); static lxb_status_t lxb_html_document_event_insert_attribute(lxb_dom_node_t *node); static lxb_status_t lxb_html_document_event_remove(lxb_dom_node_t *node); static lxb_status_t lxb_html_document_style_remove_cb(lexbor_avl_t *avl, lexbor_avl_node_t **root, lexbor_avl_node_t *node, void *ctx); static lxb_status_t lxb_html_document_event_remove_attribute(lxb_dom_node_t *node); static lxb_status_t lxb_html_document_style_remove_my_cb(lexbor_avl_t *avl, lexbor_avl_node_t **root, lexbor_avl_node_t *node, void *ctx); static lxb_status_t lxb_html_document_event_destroy(lxb_dom_node_t *node); static lxb_status_t lxb_html_document_event_set_value(lxb_dom_node_t *node, const lxb_char_t *value, size_t length); #endif lxb_html_document_t * lxb_html_document_interface_create(lxb_html_document_t *document) { lxb_status_t status; lxb_dom_document_t *doc; lxb_html_document_t *hdoc; lxb_dom_interface_create_f icreator; if (document != NULL) { doc = lexbor_mraw_calloc(lxb_html_document_mraw(document), sizeof(lxb_html_document_t)); } else { doc = lexbor_calloc(1, sizeof(lxb_html_document_t)); } if (doc == NULL) { return NULL; } icreator = (lxb_dom_interface_create_f) lxb_html_interface_create; status = lxb_dom_document_init(doc, lxb_dom_interface_document(document), icreator, lxb_html_interface_clone, lxb_html_interface_destroy, LXB_DOM_DOCUMENT_DTYPE_HTML, LXB_NS_HTML); if (status != LXB_STATUS_OK) { (void) lxb_dom_document_destroy(doc); return NULL; } hdoc = lxb_html_interface_document(doc); if (document == NULL) { hdoc->css_init = false; } else { hdoc->css = document->css; hdoc->css_init = document->css_init; } return hdoc; } lxb_html_document_t * lxb_html_document_interface_destroy(lxb_html_document_t *document) { lxb_dom_document_t *doc; if (document == NULL) { return NULL; } doc = lxb_dom_interface_document(document); if (doc->node.owner_document == doc) { (void) lxb_html_parser_unref(doc->parser); #if 0 lxb_html_document_css_destroy(document); #endif } (void) lxb_dom_document_destroy(doc); return NULL; } lxb_html_document_t * lxb_html_document_create(void) { return lxb_html_document_interface_create(NULL); } void lxb_html_document_clean(lxb_html_document_t *document) { document->body = NULL; document->head = NULL; document->iframe_srcdoc = NULL; document->ready_state = LXB_HTML_DOCUMENT_READY_STATE_UNDEF; #if 0 lxb_html_document_css_clean(document); #endif lxb_dom_document_clean(lxb_dom_interface_document(document)); } #if 0 lxb_status_t lxb_html_document_css_init(lxb_html_document_t *document) { lxb_status_t status; lxb_html_document_css_t *css = &document->css; if (document->css_init) { return LXB_HTML_STATUS_OK; } css->memory = lxb_css_memory_create(); status = lxb_css_memory_init(css->memory, 1024); if (status != LXB_STATUS_OK) { goto failed; } css->css_selectors = lxb_css_selectors_create(); status = lxb_css_selectors_init(css->css_selectors); if (status != LXB_STATUS_OK) { goto failed; } css->parser = lxb_css_parser_create(); status = lxb_css_parser_init(css->parser, NULL); if (status != LXB_STATUS_OK) { goto failed; } lxb_css_parser_memory_set(css->parser, css->memory); lxb_css_parser_selectors_set(css->parser, css->css_selectors); css->selectors = lxb_selectors_create(); status = lxb_selectors_init(css->selectors); if (status != LXB_STATUS_OK) { goto failed; } css->styles = lexbor_avl_create(); status = lexbor_avl_init(css->styles, 2048, sizeof(lxb_html_style_node_t)); if (status != LXB_STATUS_OK) { goto failed; } css->stylesheets = lexbor_array_create(); status = lexbor_array_init(css->stylesheets, 16); if (status != LXB_STATUS_OK) { goto failed; } css->weak = lexbor_dobject_create(); status = lexbor_dobject_init(css->weak, 2048, sizeof(lxb_html_style_weak_t)); if (status != LXB_STATUS_OK) { goto failed; } status = lxb_html_document_css_customs_init(document); if (status != LXB_STATUS_OK) { goto failed; } document->css_init = true; document->dom_document.ev_insert = lxb_html_document_event_insert; document->dom_document.ev_remove = lxb_html_document_event_remove; document->dom_document.ev_destroy = lxb_html_document_event_destroy; document->dom_document.ev_set_value = lxb_html_document_event_set_value; document->done = lxb_html_document_done; return LXB_STATUS_OK; failed: lxb_html_document_css_destroy(document); return status; } void lxb_html_document_css_destroy(lxb_html_document_t *document) { lxb_html_document_css_t *css = &document->css; if (!document->css_init || lxb_dom_interface_node(document)->owner_document != lxb_dom_interface_document(document)) { return; } css->memory = lxb_css_memory_destroy(css->memory, true); css->css_selectors = lxb_css_selectors_destroy(css->css_selectors, true); css->parser = lxb_css_parser_destroy(css->parser, true); css->selectors = lxb_selectors_destroy(css->selectors, true); css->styles = lexbor_avl_destroy(css->styles, true); css->stylesheets = lexbor_array_destroy(css->stylesheets, true); css->weak = lexbor_dobject_destroy(css->weak, true); document->dom_document.ev_insert = NULL; document->dom_document.ev_remove = NULL; document->dom_document.ev_destroy = NULL; document->dom_document.ev_set_value = NULL; document->done = NULL; lxb_html_document_css_customs_destroy(document); } void lxb_html_document_css_clean(lxb_html_document_t *document) { lxb_html_document_css_t *css; if (lxb_dom_interface_node(document)->owner_document == lxb_dom_interface_document(document)) { if (!document->css_init) { return; } css = &document->css; lxb_css_memory_clean(css->memory); lxb_css_selectors_clean(css->css_selectors); lxb_css_parser_clean(css->parser); lxb_selectors_clean(css->selectors); lexbor_avl_clean(css->styles); lexbor_array_clean(css->stylesheets); lexbor_dobject_clean(css->weak); } } #endif void lxb_html_document_css_parser_attach(lxb_html_document_t *document, lxb_css_parser_t *parser) { document->css.parser = parser; } void lxb_html_document_css_memory_attach(lxb_html_document_t *document, lxb_css_memory_t *memory) { document->css.memory = memory; } lxb_status_t lxb_html_document_css_customs_init(lxb_html_document_t *document) { lxb_html_document_css_t *css = &document->css; css->customs_id = LXB_CSS_PROPERTY__LAST_ENTRY; css->customs = lexbor_hash_create(); return lexbor_hash_init(css->customs, 512, sizeof(lxb_html_document_css_custom_entry_t)); } void lxb_html_document_css_customs_destroy(lxb_html_document_t *document) { document->css.customs = lexbor_hash_destroy(document->css.customs, true); } uintptr_t lxb_html_document_css_customs_find_id(lxb_html_document_t *document, const lxb_char_t *key, size_t length) { const lxb_html_document_css_custom_entry_t *entry; entry = lexbor_hash_search(document->css.customs, &lxb_html_document_css_customs_se, key, length); return (entry != NULL) ? entry->id : 0; } static lxb_html_document_css_custom_entry_t * lxb_html_document_css_customs_insert(lxb_html_document_t *document, const lxb_char_t *key, size_t length) { lxb_html_document_css_custom_entry_t *entry; if (UINTPTR_MAX - document->css.customs_id == 0) { return NULL; } entry = lexbor_hash_insert(document->css.customs, &lxb_html_document_css_customs_in, key, length); if (entry == NULL) { return NULL; } entry->id = document->css.customs_id++; return entry; } uintptr_t lxb_html_document_css_customs_id(lxb_html_document_t *document, const lxb_char_t *key, size_t length) { lxb_html_document_css_custom_entry_t *entry; entry = lexbor_hash_search(document->css.customs, &lxb_html_document_css_customs_se, key, length); if (entry != NULL) { return entry->id; } entry = lxb_html_document_css_customs_insert(document, key, length); if (entry == NULL) { return 0; } return entry->id; } lxb_status_t lxb_html_document_stylesheet_attach(lxb_html_document_t *document, lxb_css_stylesheet_t *sst) { lxb_status_t status; status = lexbor_array_push(document->css.stylesheets, sst); if (status != LXB_STATUS_OK) { return status; } return lxb_html_document_stylesheet_apply(document, sst); } lxb_status_t lxb_html_document_stylesheet_apply(lxb_html_document_t *document, lxb_css_stylesheet_t *sst) { lxb_status_t status = LXB_STATUS_OK; lxb_css_rule_t *rule; lxb_css_rule_list_t *list; rule = sst->root; if (rule->type != LXB_CSS_RULE_LIST) { return LXB_STATUS_ERROR_WRONG_ARGS; } list = lxb_css_rule_list(rule); rule = list->first; while (rule != NULL) { switch (rule->type) { case LXB_CSS_RULE_STYLE: status = lxb_html_document_style_attach(document, lxb_css_rule_style(rule)); break; default: break; } if (status != LXB_STATUS_OK) { /* FIXME: what to do with an error? */ } rule = rule->next; } return LXB_STATUS_OK; } lxb_status_t lxb_html_document_stylesheet_add(lxb_html_document_t *document, lxb_css_stylesheet_t *sst) { if (sst == NULL) { return LXB_STATUS_OK; } return lexbor_array_push(document->css.stylesheets, sst); } lxb_status_t lxb_html_document_stylesheet_remove(lxb_html_document_t *document, lxb_css_stylesheet_t *sst) { size_t i, length; lxb_status_t status = LXB_STATUS_OK; lxb_css_rule_t *rule; lxb_css_rule_list_t *list; lxb_css_stylesheet_t *sst_in; rule = sst->root; if (rule->type != LXB_CSS_RULE_LIST) { return LXB_STATUS_ERROR_WRONG_ARGS; } list = lxb_css_rule_list(rule); rule = list->first; while (rule != NULL) { switch (rule->type) { case LXB_CSS_RULE_STYLE: status = lxb_html_document_style_remove(document, lxb_css_rule_style(rule)); break; default: break; } if (status != LXB_STATUS_OK) { /* FIXME: what to do with an error? */ } rule = rule->next; } length = lexbor_array_length(document->css.stylesheets); for (i = 0; i < length; i++) { sst_in = lexbor_array_get(document->css.stylesheets, i); if (sst_in == sst) { lexbor_array_delete(document->css.stylesheets, i, 1); length = lexbor_array_length(document->css.stylesheets); } } return LXB_STATUS_OK; } lxb_status_t lxb_html_document_element_styles_attach(lxb_html_element_t *element) { lxb_status_t status = LXB_STATUS_OK; lxb_css_rule_t *rule; lexbor_array_t *ssts; lxb_css_rule_list_t *list; lxb_css_stylesheet_t *sst; lxb_html_document_t *document; document = lxb_html_element_document(element); ssts = document->css.stylesheets; for (size_t i = 0; i < lexbor_array_length(ssts); i++) { sst = lexbor_array_get(ssts, i); list = lxb_css_rule_list(sst->root); rule = list->first; while (rule != NULL) { switch (rule->type) { case LXB_CSS_RULE_STYLE: status = lxb_html_document_style_attach_by_element(document, element, lxb_css_rule_style(rule)); break; default: break; } if (status != LXB_STATUS_OK) { /* FIXME: what to do with an error? */ } rule = rule->next; } } return LXB_STATUS_OK; } void lxb_html_document_stylesheet_destroy_all(lxb_html_document_t *document, bool destroy_memory) { #if 0 size_t length; lxb_css_stylesheet_t *sst; lxb_html_document_css_t *css = &document->css; length = lexbor_array_length(css->stylesheets); for (size_t i = 0; i < length; i++) { sst = lexbor_array_pop(css->stylesheets); (void) lxb_css_stylesheet_destroy(sst, destroy_memory); } #endif } lxb_status_t lxb_html_document_style_attach(lxb_html_document_t *document, lxb_css_rule_style_t *style) { #if 0 lxb_html_document_css_t *css = &document->css; return lxb_selectors_find(css->selectors, lxb_dom_interface_node(document), style->selector, lxb_html_document_style_cb, style); #endif return LXB_STATUS_OK; } lxb_status_t lxb_html_document_style_remove(lxb_html_document_t *document, lxb_css_rule_style_t *style) { #if 0 lxb_html_document_css_t *css = &document->css; return lxb_selectors_find(css->selectors, lxb_dom_interface_node(document), style->selector, lxb_html_document_style_remove_by_rule_cb, style); #endif return LXB_STATUS_OK; } #if 0 static lxb_status_t lxb_html_document_style_remove_by_rule_cb(lxb_dom_node_t *node, lxb_css_selector_specificity_t spec, void *ctx) { lxb_html_element_t *el; lxb_html_document_t *doc; lxb_css_rule_style_t *style = ctx; lxb_html_document_remove_ctx_t context; /* FIXME: we don't have support for anything other than HTML. */ if (node->ns != LXB_NS_HTML) { return LXB_STATUS_OK; } el = lxb_html_interface_element(node); if (el->style == NULL) { return LXB_STATUS_OK; } doc = lxb_html_interface_document(node->owner_document); context.doc = doc; context.list = style->declarations; return lexbor_avl_foreach(doc->css.styles, &el->style, lxb_html_document_style_remove_avl_cb, &context); } static lxb_status_t lxb_html_document_style_remove_avl_cb(lexbor_avl_t *avl, lexbor_avl_node_t **root, lexbor_avl_node_t *node, void *ctx) { lxb_html_document_remove_ctx_t *context = ctx; lxb_html_style_node_t *style = (lxb_html_style_node_t *) node; if (context->list == NULL) { return LXB_STATUS_OK; } lxb_html_element_style_remove_by_list(context->doc, root, style, context->list); return LXB_STATUS_OK; } #endif lxb_status_t lxb_html_document_style_attach_by_element(lxb_html_document_t *document, lxb_html_element_t *element, lxb_css_rule_style_t *style) { #if 0 lxb_html_document_css_t *css = &document->css; return lxb_selectors_match_node(css->selectors, lxb_dom_interface_node(element), style->selector, lxb_html_document_style_cb, style); #endif return LXB_STATUS_OK; } #if 0 static lxb_status_t lxb_html_document_style_cb(lxb_dom_node_t *node, lxb_css_selector_specificity_t spec, void *ctx) { lxb_css_rule_style_t *style = ctx; // FIXME: we don't have support for anything other than HTML. if (node->ns != LXB_NS_HTML) { return LXB_STATUS_OK; } /* Valid behavior when there are no declarations in the style. */ if (style->declarations == NULL) { return LXB_STATUS_OK; } return lxb_html_element_style_list_append(lxb_html_interface_element(node), style->declarations, spec); } #endif lxb_html_document_t * lxb_html_document_destroy(lxb_html_document_t *document) { return lxb_html_document_interface_destroy(document); } lxb_status_t lxb_html_document_parse(lxb_html_document_t *document, const lxb_char_t *html, size_t size) { lxb_status_t status; lxb_dom_document_t *doc; lxb_html_document_opt_t opt; if (document->ready_state != LXB_HTML_DOCUMENT_READY_STATE_UNDEF && document->ready_state != LXB_HTML_DOCUMENT_READY_STATE_LOADING) { lxb_html_document_clean(document); } opt = document->opt; doc = lxb_dom_interface_document(document); status = lxb_html_document_parser_prepare(document); if (status != LXB_STATUS_OK) { goto failed; } status = lxb_html_parse_chunk_prepare(doc->parser, document); if (status != LXB_STATUS_OK) { goto failed; } status = lxb_html_parse_chunk_process(doc->parser, html, size); if (status != LXB_STATUS_OK) { goto failed; } document->opt = opt; return lxb_html_parse_chunk_end(doc->parser); failed: document->opt = opt; return status; } lxb_status_t lxb_html_document_parse_chunk_begin(lxb_html_document_t *document) { if (document->ready_state != LXB_HTML_DOCUMENT_READY_STATE_UNDEF && document->ready_state != LXB_HTML_DOCUMENT_READY_STATE_LOADING) { lxb_html_document_clean(document); } lxb_status_t status = lxb_html_document_parser_prepare(document); if (status != LXB_STATUS_OK) { return status; } return lxb_html_parse_chunk_prepare(document->dom_document.parser, document); } lxb_status_t lxb_html_document_parse_chunk(lxb_html_document_t *document, const lxb_char_t *html, size_t size) { return lxb_html_parse_chunk_process(document->dom_document.parser, html, size); } lxb_status_t lxb_html_document_parse_chunk_end(lxb_html_document_t *document) { return lxb_html_parse_chunk_end(document->dom_document.parser); } lxb_dom_node_t * lxb_html_document_parse_fragment(lxb_html_document_t *document, lxb_dom_element_t *element, const lxb_char_t *html, size_t size) { lxb_status_t status; lxb_html_parser_t *parser; lxb_html_document_opt_t opt = document->opt; status = lxb_html_document_parser_prepare(document); if (status != LXB_STATUS_OK) { goto failed; } parser = document->dom_document.parser; status = lxb_html_parse_fragment_chunk_begin(parser, document, element->node.local_name, element->node.ns); if (status != LXB_STATUS_OK) { goto failed; } status = lxb_html_parse_fragment_chunk_process(parser, html, size); if (status != LXB_STATUS_OK) { goto failed; } document->opt = opt; return lxb_html_parse_fragment_chunk_end(parser); failed: document->opt = opt; return NULL; } lxb_status_t lxb_html_document_parse_fragment_chunk_begin(lxb_html_document_t *document, lxb_dom_element_t *element) { lxb_status_t status; lxb_html_parser_t *parser; status = lxb_html_document_parser_prepare(document); if (status != LXB_STATUS_OK) { return status; } parser = document->dom_document.parser; return lxb_html_parse_fragment_chunk_begin(parser, document, element->node.local_name, element->node.ns); } lxb_status_t lxb_html_document_parse_fragment_chunk(lxb_html_document_t *document, const lxb_char_t *html, size_t size) { return lxb_html_parse_fragment_chunk_process(document->dom_document.parser, html, size); } lxb_dom_node_t * lxb_html_document_parse_fragment_chunk_end(lxb_html_document_t *document) { return lxb_html_parse_fragment_chunk_end(document->dom_document.parser); } lxb_inline lxb_status_t lxb_html_document_parser_prepare(lxb_html_document_t *document) { lxb_status_t status; lxb_dom_document_t *doc; doc = lxb_dom_interface_document(document); if (doc->parser == NULL) { doc->parser = lxb_html_parser_create(); status = lxb_html_parser_init(doc->parser); if (status != LXB_STATUS_OK) { lxb_html_parser_destroy(doc->parser); return status; } } else if (lxb_html_parser_state(doc->parser) != LXB_HTML_PARSER_STATE_BEGIN) { lxb_html_parser_clean(doc->parser); } return LXB_STATUS_OK; } #if 0 static lxb_status_t lxb_html_document_done(lxb_html_document_t *document) { size_t i, length; lxb_status_t status; lxb_css_stylesheet_t *sst; if (!document->css_init) { return LXB_STATUS_OK; } length = lexbor_array_length(document->css.stylesheets); for (i = 0; i < length; i++) { sst = lexbor_array_get(document->css.stylesheets, i); status = lxb_html_document_stylesheet_apply(document, sst); if (status != LXB_STATUS_OK) { return status; } } return LXB_STATUS_OK; } #endif const lxb_char_t * lxb_html_document_title(lxb_html_document_t *document, size_t *len) { lxb_html_title_element_t *title = NULL; lxb_dom_node_simple_walk(lxb_dom_interface_node(document), lxb_html_document_title_walker, &title); if (title == NULL) { return NULL; } return lxb_html_title_element_strict_text(title, len); } lxb_status_t lxb_html_document_title_set(lxb_html_document_t *document, const lxb_char_t *title, size_t len) { lxb_status_t status; /* TODO: If the document element is an SVG svg element */ /* If the document element is in the HTML namespace */ if (document->head == NULL) { return LXB_STATUS_OK; } lxb_html_title_element_t *el_title = NULL; lxb_dom_node_simple_walk(lxb_dom_interface_node(document), lxb_html_document_title_walker, &el_title); if (el_title == NULL) { el_title = (void *) lxb_html_document_create_element(document, (const lxb_char_t *) "title", 5, NULL); if (el_title == NULL) { return LXB_STATUS_ERROR_MEMORY_ALLOCATION; } lxb_dom_node_insert_child(lxb_dom_interface_node(document->head), lxb_dom_interface_node(el_title)); } status = lxb_dom_node_text_content_set(lxb_dom_interface_node(el_title), title, len); if (status != LXB_STATUS_OK) { lxb_html_document_destroy_element(&el_title->element.element); return status; } return LXB_STATUS_OK; } const lxb_char_t * lxb_html_document_title_raw(lxb_html_document_t *document, size_t *len) { lxb_html_title_element_t *title = NULL; lxb_dom_node_simple_walk(lxb_dom_interface_node(document), lxb_html_document_title_walker, &title); if (title == NULL) { return NULL; } return lxb_html_title_element_text(title, len); } static lexbor_action_t lxb_html_document_title_walker(lxb_dom_node_t *node, void *ctx) { if (node->local_name == LXB_TAG_TITLE && node->ns == LXB_NS_HTML) { *((void **) ctx) = node; return LEXBOR_ACTION_STOP; } return LEXBOR_ACTION_OK; } lxb_dom_node_t * lxb_html_document_import_node(lxb_html_document_t *doc, lxb_dom_node_t *node, bool deep) { return lxb_dom_document_import_node(&doc->dom_document, node, deep); } #if 0 static lxb_status_t lxb_html_document_event_insert(lxb_dom_node_t *node) { lxb_status_t status; lxb_html_document_t *doc; lxb_html_style_element_t *style; if (node->type == LXB_DOM_NODE_TYPE_ATTRIBUTE) { return lxb_html_document_event_insert_attribute(node); } else if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) { return LXB_STATUS_OK; } // FIXME: we don't have support for anything other than HTML. if (node->ns != LXB_NS_HTML) { return LXB_STATUS_OK; } if (node->local_name == LXB_TAG_STYLE) { style = lxb_html_interface_style(node); status = lxb_html_style_element_parse(style); if (status != LXB_STATUS_OK) { return status; } doc = lxb_html_interface_document(node->owner_document); status = lxb_html_document_stylesheet_attach(doc, style->stylesheet); if (status != LXB_STATUS_OK) { return status; } } return lxb_html_document_element_styles_attach(lxb_html_interface_element(node)); } static lxb_status_t lxb_html_document_event_insert_attribute(lxb_dom_node_t *node) { lxb_status_t status; lxb_dom_attr_t *attr; lxb_html_element_t *el; if (node->type != LXB_DOM_NODE_TYPE_ATTRIBUTE || node->local_name != LXB_DOM_ATTR_STYLE) { return LXB_STATUS_OK; } // FIXME: we don't have support for anything other than HTML. if (node->ns != LXB_NS_HTML) { return LXB_STATUS_OK; } attr = lxb_dom_interface_attr(node); el = lxb_html_interface_element(attr->owner); if (el != NULL && el->list != NULL) { status = lxb_html_document_event_remove_attribute(node); if (status != LXB_STATUS_OK) { return status; } } if (attr->value == NULL || attr->value->data == NULL) { return LXB_STATUS_OK; } return lxb_html_element_style_parse(el, attr->value->data, attr->value->length); } static lxb_status_t lxb_html_document_event_remove(lxb_dom_node_t *node) { lxb_status_t status; lxb_html_element_t *el; lxb_html_document_t *doc; lxb_html_document_event_ctx_t context; if (node->type == LXB_DOM_NODE_TYPE_ATTRIBUTE) { return lxb_html_document_event_remove_attribute(node); } else if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) { return LXB_STATUS_OK; } // FIXME: we don't have support for anything other than HTML. if (node->ns != LXB_NS_HTML) { return LXB_STATUS_OK; } if (node->local_name == LXB_TAG_STYLE) { status = lxb_html_element_style_remove((lxb_html_style_element_t *) node); if (status != LXB_STATUS_OK) { return status; } } el = lxb_html_interface_element(node); if (el->style == NULL) { return LXB_STATUS_OK; } doc = lxb_html_interface_document(node->owner_document); context.doc = doc; context.all = false; return lexbor_avl_foreach(doc->css.styles, &el->style, lxb_html_document_style_remove_cb, &context); } static lxb_status_t lxb_html_document_style_remove_cb(lexbor_avl_t *avl, lexbor_avl_node_t **root, lexbor_avl_node_t *node, void *ctx) { lxb_html_document_event_ctx_t *context = ctx; lxb_html_style_node_t *style = (lxb_html_style_node_t *) node; if (context->all) { lxb_html_element_style_remove_all(context->doc, root, style); } lxb_html_element_style_remove_all_not(context->doc, root, style, false); return LXB_STATUS_OK; } static lxb_status_t lxb_html_document_event_remove_attribute(lxb_dom_node_t *node) { lxb_status_t status; lxb_dom_attr_t *attr; lxb_html_element_t *el; lxb_html_document_t *doc; lxb_html_document_event_ctx_t context; // FIXME: we don't have support for anything other than HTML. if (node->local_name != LXB_DOM_ATTR_STYLE || node->ns != LXB_NS_HTML) { return LXB_STATUS_OK; } attr = lxb_dom_interface_attr(node); el = lxb_html_interface_element(attr->owner); if (el == NULL || el->list == NULL) { return LXB_STATUS_OK; } doc = lxb_html_interface_document(node->owner_document); context.doc = doc; status = lexbor_avl_foreach(doc->css.styles, &el->style, lxb_html_document_style_remove_my_cb, &context); if (status != LXB_STATUS_OK) { return status; } el->list->first = NULL; el->list->last = NULL; el->list = lxb_css_rule_declaration_list_destroy(el->list, true); return LXB_STATUS_OK; } static lxb_status_t lxb_html_document_style_remove_my_cb(lexbor_avl_t *avl, lexbor_avl_node_t **root, lexbor_avl_node_t *node, void *ctx) { lxb_html_document_event_ctx_t *context = ctx; lxb_html_style_node_t *style = (lxb_html_style_node_t *) node; lxb_html_element_style_remove_all_not(context->doc, root, style, true); return LXB_STATUS_OK; } static lxb_status_t lxb_html_document_event_destroy(lxb_dom_node_t *node) { lxb_status_t status; lxb_html_element_t *el; lxb_html_document_t *doc; lxb_html_document_event_ctx_t context; if (node->type == LXB_DOM_NODE_TYPE_ATTRIBUTE) { return lxb_html_document_event_remove_attribute(node); } else if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) { return LXB_STATUS_OK; } // FIXME: we don't have support for anything other than HTML. if (node->ns != LXB_NS_HTML) { return LXB_STATUS_OK; } el = lxb_html_interface_element(node); if (el->style == NULL) { if (el->list != NULL) { goto destroy; } return LXB_STATUS_OK; } doc = lxb_html_interface_document(node->owner_document); context.doc = doc; context.all = true; status = lexbor_avl_foreach(doc->css.styles, &el->style, lxb_html_document_style_remove_cb, &context); if (status != LXB_STATUS_OK) { return status; } destroy: el->list->first = NULL; el->list->last = NULL; el->list = lxb_css_rule_declaration_list_destroy(el->list, true); return LXB_STATUS_OK; } static lxb_status_t lxb_html_document_event_set_value(lxb_dom_node_t *node, const lxb_char_t *value, size_t length) { lxb_status_t status; lxb_dom_attr_t *attr = lxb_dom_interface_attr(node); if (node->type != LXB_DOM_NODE_TYPE_ATTRIBUTE || node->local_name != LXB_DOM_ATTR_STYLE) { return LXB_STATUS_OK; } // FIXME: we don't have support for anything other than HTML. if (node->ns != LXB_NS_HTML) { return LXB_STATUS_OK; } if (attr->owner == NULL) { return LXB_STATUS_OK; } status = lxb_html_document_event_remove_attribute(node); if (status != LXB_STATUS_OK) { return status; } return lxb_html_element_style_parse(lxb_html_interface_element(node), value, length); } #endif /* * No inline functions for ABI. */ lxb_html_head_element_t * lxb_html_document_head_element_noi(lxb_html_document_t *document) { return lxb_html_document_head_element(document); } lxb_html_body_element_t * lxb_html_document_body_element_noi(lxb_html_document_t *document) { return lxb_html_document_body_element(document); } lxb_dom_document_t * lxb_html_document_original_ref_noi(lxb_html_document_t *document) { return lxb_html_document_original_ref(document); } bool lxb_html_document_is_original_noi(lxb_html_document_t *document) { return lxb_html_document_is_original(document); } lexbor_mraw_t * lxb_html_document_mraw_noi(lxb_html_document_t *document) { return lxb_html_document_mraw(document); } lexbor_mraw_t * lxb_html_document_mraw_text_noi(lxb_html_document_t *document) { return lxb_html_document_mraw_text(document); } void lxb_html_document_opt_set_noi(lxb_html_document_t *document, lxb_html_document_opt_t opt) { lxb_html_document_opt_set(document, opt); } lxb_html_document_opt_t lxb_html_document_opt_noi(lxb_html_document_t *document) { return lxb_html_document_opt(document); } void * lxb_html_document_create_struct_noi(lxb_html_document_t *document, size_t struct_size) { return lxb_html_document_create_struct(document, struct_size); } void * lxb_html_document_destroy_struct_noi(lxb_html_document_t *document, void *data) { return lxb_html_document_destroy_struct(document, data); } lxb_html_element_t * lxb_html_document_create_element_noi(lxb_html_document_t *document, const lxb_char_t *local_name, size_t lname_len, void *reserved_for_opt) { return lxb_html_document_create_element(document, local_name, lname_len, reserved_for_opt); } lxb_dom_element_t * lxb_html_document_destroy_element_noi(lxb_dom_element_t *element) { return lxb_html_document_destroy_element(element); }