mirror of
https://github.com/lexborisov/Modest
synced 2024-11-25 07:09:35 +03:00
2581 lines
79 KiB
C
2581 lines
79 KiB
C
/*
|
|
Copyright (C) 2015-2016 Alexander Borisov
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
Author: lex.borisov@gmail.com (Alexander Borisov)
|
|
*/
|
|
|
|
#include "myhtml/tree.h"
|
|
|
|
myhtml_tree_t * myhtml_tree_create(void)
|
|
{
|
|
return (myhtml_tree_t*)mycore_calloc(1, sizeof(myhtml_tree_t));
|
|
}
|
|
|
|
mystatus_t myhtml_tree_init(myhtml_tree_t* tree, myhtml_t* myhtml)
|
|
{
|
|
mystatus_t status = MyHTML_STATUS_OK;
|
|
|
|
tree->myhtml = myhtml;
|
|
tree->token = myhtml_token_create(tree, 512);
|
|
|
|
if(tree->token == NULL)
|
|
return MyHTML_STATUS_TOKENIZER_ERROR_MEMORY_ALLOCATION;
|
|
|
|
tree->temp_tag_name.data = NULL;
|
|
tree->stream_buffer = NULL;
|
|
tree->parse_flags = MyHTML_TREE_PARSE_FLAGS_CLEAN;
|
|
tree->context = NULL;
|
|
|
|
tree->callback_before_token = NULL;
|
|
tree->callback_after_token = NULL;
|
|
tree->callback_before_token_ctx = NULL;
|
|
tree->callback_after_token_ctx = NULL;
|
|
|
|
tree->callback_tree_node_insert = NULL;
|
|
tree->callback_tree_node_remove = NULL;
|
|
tree->callback_tree_node_insert_ctx = NULL;
|
|
tree->callback_tree_node_remove_ctx = NULL;
|
|
|
|
if(status)
|
|
return status;
|
|
|
|
/* Thread Queue */
|
|
tree->queue = mythread_queue_create();
|
|
if(tree->queue == NULL)
|
|
return MyHTML_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
|
|
status = mythread_queue_init(tree->queue, 9182);
|
|
if(status)
|
|
return status;
|
|
|
|
/* Init Incoming Buffer objects */
|
|
tree->mcobject_incoming_buf = mcobject_create();
|
|
if(tree->mcobject_incoming_buf == NULL)
|
|
return MyHTML_STATUS_TREE_ERROR_INCOMING_BUFFER_CREATE;
|
|
|
|
status = mcobject_init(tree->mcobject_incoming_buf, 256, sizeof(mycore_incoming_buffer_t));
|
|
if(status)
|
|
return status;
|
|
|
|
/* init Tree Node objects */
|
|
tree->tree_obj = mcobject_async_create();
|
|
if(tree->tree_obj == NULL)
|
|
return MyHTML_STATUS_TREE_ERROR_MCOBJECT_CREATE;
|
|
|
|
mcobject_async_status_t mcstatus = mcobject_async_init(tree->tree_obj, 128, 1024, sizeof(myhtml_tree_node_t));
|
|
if(mcstatus)
|
|
return MyHTML_STATUS_TREE_ERROR_MCOBJECT_INIT;
|
|
|
|
tree->mchar = mchar_async_create();
|
|
tree->active_formatting = myhtml_tree_active_formatting_init(tree);
|
|
tree->open_elements = myhtml_tree_open_elements_init(tree);
|
|
tree->other_elements = myhtml_tree_list_init();
|
|
tree->token_list = myhtml_tree_token_list_init();
|
|
tree->template_insertion = myhtml_tree_template_insertion_init(tree);
|
|
|
|
if(tree->mchar == NULL)
|
|
return MyHTML_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
|
|
if((status = mchar_async_init(tree->mchar, 128, (4096 * 5))))
|
|
return status;
|
|
|
|
tree->mcasync_tree_id = mcobject_async_node_add(tree->tree_obj, &mcstatus);
|
|
if(mcstatus)
|
|
return MyHTML_STATUS_TREE_ERROR_MCOBJECT_CREATE_NODE;
|
|
|
|
tree->mcasync_rules_token_id = mcobject_async_node_add(tree->token->nodes_obj, &mcstatus);
|
|
if(mcstatus)
|
|
return MyHTML_STATUS_TREE_ERROR_MCOBJECT_CREATE_NODE;
|
|
|
|
tree->mcasync_rules_attr_id = mcobject_async_node_add(tree->token->attr_obj, &mcstatus);
|
|
if(mcstatus)
|
|
return MyHTML_STATUS_TREE_ERROR_MCOBJECT_CREATE_NODE;
|
|
|
|
#ifndef MyCORE_BUILD_WITHOUT_THREADS
|
|
tree->async_args = (myhtml_async_args_t*)mycore_calloc(myhtml->thread_total, sizeof(myhtml_async_args_t));
|
|
if(tree->async_args == NULL)
|
|
return MyHTML_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
|
|
// for batch thread
|
|
for(size_t i = 0; i < myhtml->thread_total; i++) {
|
|
tree->async_args[i].mchar_node_id = mchar_async_node_add(tree->mchar, &status);
|
|
|
|
if(status)
|
|
return status;
|
|
}
|
|
#else /* MyCORE_BUILD_WITHOUT_THREADS */
|
|
tree->async_args = (myhtml_async_args_t*)mycore_calloc(1, sizeof(myhtml_async_args_t));
|
|
|
|
if(tree->async_args == NULL)
|
|
return MyHTML_STATUS_TREE_ERROR_MEMORY_ALLOCATION;
|
|
|
|
tree->async_args->mchar_node_id = mchar_async_node_add(tree->mchar, &status);
|
|
|
|
if(status)
|
|
return status;
|
|
|
|
#endif /* MyCORE_BUILD_WITHOUT_THREADS */
|
|
|
|
/* for main thread only after parsing */
|
|
tree->mchar_node_id = tree->async_args->mchar_node_id;
|
|
|
|
tree->sync = mcsync_create();
|
|
if(tree->sync == NULL)
|
|
return MyHTML_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
|
|
if(mcsync_init(tree->sync))
|
|
return MyHTML_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
|
|
/* init Tags after create and init mchar */
|
|
tree->tags = myhtml_tag_create();
|
|
status = myhtml_tag_init(tree, tree->tags);
|
|
|
|
myhtml_tree_clean(tree);
|
|
|
|
return status;
|
|
}
|
|
|
|
void myhtml_tree_clean(myhtml_tree_t* tree)
|
|
{
|
|
#ifndef MyCORE_BUILD_WITHOUT_THREADS
|
|
myhtml_t* myhtml = tree->myhtml;
|
|
|
|
for(size_t i = 0; i < myhtml->thread_total; i++) {
|
|
mchar_async_node_clean(tree->mchar, tree->async_args[i].mchar_node_id);
|
|
}
|
|
#else
|
|
mchar_async_node_clean(tree->mchar, tree->mchar_node_id);
|
|
#endif
|
|
|
|
mcobject_async_node_clean(tree->tree_obj, tree->mcasync_tree_id);
|
|
mcobject_async_node_clean(tree->token->nodes_obj, tree->mcasync_rules_token_id);
|
|
mcobject_async_node_clean(tree->token->attr_obj, tree->mcasync_rules_attr_id);
|
|
|
|
#ifndef MyCORE_BUILD_WITHOUT_THREADS
|
|
mythread_queue_list_entry_clean(tree->queue_entry);
|
|
#endif /* MyCORE_BUILD_WITHOUT_THREADS */
|
|
|
|
myhtml_token_clean(tree->token);
|
|
|
|
// null root
|
|
myhtml_tree_node_create(tree);
|
|
|
|
tree->document = myhtml_tree_node_create(tree);
|
|
tree->fragment = NULL;
|
|
|
|
tree->doctype.is_html = false;
|
|
tree->doctype.attr_name = NULL;
|
|
tree->doctype.attr_public = NULL;
|
|
tree->doctype.attr_system = NULL;
|
|
|
|
tree->node_html = 0;
|
|
tree->node_body = 0;
|
|
tree->node_head = 0;
|
|
tree->node_form = 0;
|
|
|
|
tree->state = MyHTML_TOKENIZER_STATE_DATA;
|
|
tree->state_of_builder = MyHTML_TOKENIZER_STATE_DATA;
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_INITIAL;
|
|
tree->orig_insert_mode = MyHTML_INSERTION_MODE_INITIAL;
|
|
tree->compat_mode = MyHTML_TREE_COMPAT_MODE_NO_QUIRKS;
|
|
tree->tmp_tag_id = MyHTML_TAG__UNDEF;
|
|
tree->flags = MyHTML_TREE_FLAGS_CLEAN|MyHTML_TREE_FLAGS_FRAMESET_OK;
|
|
tree->foster_parenting = false;
|
|
tree->token_namespace = NULL;
|
|
tree->incoming_buf = NULL;
|
|
tree->incoming_buf_first = NULL;
|
|
tree->global_offset = 0;
|
|
tree->current_qnode = NULL;
|
|
tree->token_last_done = NULL;
|
|
tree->tokenizer_status = MyHTML_STATUS_OK;
|
|
|
|
tree->encoding = MyENCODING_UTF_8;
|
|
tree->encoding_usereq = MyENCODING_DEFAULT;
|
|
|
|
myhtml_stream_buffer_clean(tree->stream_buffer);
|
|
|
|
myhtml_tree_active_formatting_clean(tree);
|
|
myhtml_tree_open_elements_clean(tree);
|
|
myhtml_tree_list_clean(tree->other_elements);
|
|
myhtml_tree_token_list_clean(tree->token_list);
|
|
myhtml_tree_template_insertion_clean(tree);
|
|
mcobject_clean(tree->mcobject_incoming_buf);
|
|
myhtml_tag_clean(tree->tags);
|
|
mythread_queue_clean(tree->queue);
|
|
|
|
tree->attr_current = myhtml_token_attr_create(tree->token, tree->token->mcasync_attr_id);
|
|
}
|
|
|
|
void myhtml_tree_clean_all(myhtml_tree_t* tree)
|
|
{
|
|
mcobject_async_clean(tree->tree_obj);
|
|
myhtml_token_clean(tree->token);
|
|
mchar_async_clean(tree->mchar);
|
|
|
|
// null root
|
|
myhtml_tree_node_create(tree);
|
|
|
|
tree->document = myhtml_tree_node_create(tree);
|
|
tree->fragment = NULL;
|
|
|
|
tree->doctype.is_html = false;
|
|
tree->doctype.attr_name = NULL;
|
|
tree->doctype.attr_public = NULL;
|
|
tree->doctype.attr_system = NULL;
|
|
|
|
tree->node_html = 0;
|
|
tree->node_body = 0;
|
|
tree->node_head = 0;
|
|
tree->node_form = 0;
|
|
|
|
tree->state = MyHTML_TOKENIZER_STATE_DATA;
|
|
tree->state_of_builder = MyHTML_TOKENIZER_STATE_DATA;
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_INITIAL;
|
|
tree->orig_insert_mode = MyHTML_INSERTION_MODE_INITIAL;
|
|
tree->compat_mode = MyHTML_TREE_COMPAT_MODE_NO_QUIRKS;
|
|
tree->tmp_tag_id = MyHTML_TAG__UNDEF;
|
|
tree->flags = MyHTML_TREE_FLAGS_CLEAN|MyHTML_TREE_FLAGS_FRAMESET_OK;
|
|
tree->foster_parenting = false;
|
|
tree->token_namespace = NULL;
|
|
tree->incoming_buf = NULL;
|
|
tree->incoming_buf_first = NULL;
|
|
tree->global_offset = 0;
|
|
tree->current_qnode = NULL;
|
|
tree->token_last_done = NULL;
|
|
tree->tokenizer_status = MyHTML_STATUS_OK;
|
|
|
|
tree->encoding = MyENCODING_UTF_8;
|
|
tree->encoding_usereq = MyENCODING_DEFAULT;
|
|
|
|
myhtml_stream_buffer_clean(tree->stream_buffer);
|
|
|
|
myhtml_tree_active_formatting_clean(tree);
|
|
myhtml_tree_open_elements_clean(tree);
|
|
myhtml_tree_list_clean(tree->other_elements);
|
|
myhtml_tree_token_list_clean(tree->token_list);
|
|
myhtml_tree_template_insertion_clean(tree);
|
|
mcobject_clean(tree->mcobject_incoming_buf);
|
|
myhtml_tag_clean(tree->tags);
|
|
|
|
#ifndef MyCORE_BUILD_WITHOUT_THREADS
|
|
mythread_queue_list_entry_clean(tree->queue_entry);
|
|
#endif
|
|
|
|
tree->attr_current = myhtml_token_attr_create(tree->token, tree->token->mcasync_attr_id);
|
|
}
|
|
|
|
myhtml_tree_t * myhtml_tree_destroy(myhtml_tree_t* tree)
|
|
{
|
|
if(tree == NULL)
|
|
return NULL;
|
|
|
|
/* destroy tags before other objects */
|
|
tree->tags = myhtml_tag_destroy(tree->tags);
|
|
tree->active_formatting = myhtml_tree_active_formatting_destroy(tree);
|
|
tree->open_elements = myhtml_tree_open_elements_destroy(tree);
|
|
tree->other_elements = myhtml_tree_list_destroy(tree->other_elements, true);
|
|
tree->token_list = myhtml_tree_token_list_destroy(tree->token_list, true);
|
|
tree->template_insertion = myhtml_tree_template_insertion_destroy(tree);
|
|
tree->sync = mcsync_destroy(tree->sync, true);
|
|
tree->tree_obj = mcobject_async_destroy(tree->tree_obj, true);
|
|
tree->token = myhtml_token_destroy(tree->token);
|
|
tree->mchar = mchar_async_destroy(tree->mchar, 1);
|
|
tree->stream_buffer = myhtml_stream_buffer_destroy(tree->stream_buffer, true);
|
|
tree->queue = mythread_queue_destroy(tree->queue);
|
|
tree->mcobject_incoming_buf = mcobject_destroy(tree->mcobject_incoming_buf, true);
|
|
|
|
myhtml_tree_temp_tag_name_destroy(&tree->temp_tag_name, false);
|
|
|
|
mycore_free(tree->async_args);
|
|
mycore_free(tree);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void myhtml_tree_node_clean(myhtml_tree_node_t* tree_node)
|
|
{
|
|
memset(tree_node, 0, sizeof(myhtml_tree_node_t));
|
|
tree_node->ns = MyHTML_NAMESPACE_HTML;
|
|
}
|
|
|
|
/* parse flags */
|
|
myhtml_tree_parse_flags_t myhtml_tree_parse_flags(myhtml_tree_t* tree)
|
|
{
|
|
return tree->parse_flags;
|
|
}
|
|
|
|
void myhtml_tree_parse_flags_set(myhtml_tree_t* tree, myhtml_tree_parse_flags_t flags)
|
|
{
|
|
tree->parse_flags = flags;
|
|
}
|
|
|
|
myhtml_t * myhtml_tree_get_myhtml(myhtml_tree_t* tree)
|
|
{
|
|
if(tree)
|
|
return tree->myhtml;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
myhtml_tag_t * myhtml_tree_get_tag(myhtml_tree_t* tree)
|
|
{
|
|
if(tree)
|
|
return tree->tags;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_get_document(myhtml_tree_t* tree)
|
|
{
|
|
return tree->document;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_get_node_html(myhtml_tree_t* tree)
|
|
{
|
|
return tree->node_html;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_get_node_body(myhtml_tree_t* tree)
|
|
{
|
|
return tree->node_body;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_get_node_head(myhtml_tree_t* tree)
|
|
{
|
|
return tree->node_head;
|
|
}
|
|
|
|
mchar_async_t * myhtml_tree_get_mchar(myhtml_tree_t* tree)
|
|
{
|
|
return tree->mchar;
|
|
}
|
|
|
|
size_t myhtml_tree_get_mchar_node_id(myhtml_tree_t* tree)
|
|
{
|
|
return tree->mchar_node_id;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_node_create(myhtml_tree_t* tree)
|
|
{
|
|
myhtml_tree_node_t* node = (myhtml_tree_node_t*)mcobject_async_malloc(tree->tree_obj, tree->mcasync_tree_id, NULL);
|
|
myhtml_tree_node_clean(node);
|
|
|
|
node->tree = tree;
|
|
|
|
return node;
|
|
}
|
|
|
|
void myhtml_tree_node_add_child(myhtml_tree_node_t* root, myhtml_tree_node_t* node)
|
|
{
|
|
if(root->last_child) {
|
|
root->last_child->next = node;
|
|
node->prev = root->last_child;
|
|
}
|
|
else {
|
|
root->child = node;
|
|
}
|
|
|
|
node->parent = root;
|
|
root->last_child = node;
|
|
|
|
myhtml_tree_node_callback_insert(node->tree, node);
|
|
}
|
|
|
|
void myhtml_tree_node_insert_before(myhtml_tree_node_t* root, myhtml_tree_node_t* node)
|
|
{
|
|
if(root->prev) {
|
|
root->prev->next = node;
|
|
node->prev = root->prev;
|
|
}
|
|
else {
|
|
root->parent->child = node;
|
|
}
|
|
|
|
node->parent = root->parent;
|
|
node->next = root;
|
|
root->prev = node;
|
|
|
|
myhtml_tree_node_callback_insert(node->tree, node);
|
|
}
|
|
|
|
void myhtml_tree_node_insert_after(myhtml_tree_node_t* root, myhtml_tree_node_t* node)
|
|
{
|
|
if(root->next) {
|
|
root->next->prev = node;
|
|
node->next = root->next;
|
|
}
|
|
else {
|
|
root->parent->last_child = node;
|
|
}
|
|
|
|
node->parent = root->parent;
|
|
node->prev = root;
|
|
root->next = node;
|
|
|
|
myhtml_tree_node_callback_insert(node->tree, node);
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_node_find_parent_by_tag_id(myhtml_tree_node_t* node, myhtml_tag_id_t tag_id)
|
|
{
|
|
node = node->parent;
|
|
|
|
while (node && node->tag_id != tag_id) {
|
|
node = node->parent;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_node_remove(myhtml_tree_node_t* node)
|
|
{
|
|
if(node->next)
|
|
node->next->prev = node->prev;
|
|
else if(node->parent)
|
|
node->parent->last_child = node->prev;
|
|
|
|
if(node->prev) {
|
|
node->prev->next = node->next;
|
|
node->prev = NULL;
|
|
} else if(node->parent)
|
|
node->parent->child = node->next;
|
|
|
|
node->parent = NULL;
|
|
|
|
if(node->next)
|
|
node->next = NULL;
|
|
|
|
myhtml_tree_node_callback_remove(node->tree, node);
|
|
|
|
return node;
|
|
}
|
|
|
|
void myhtml_tree_node_free(myhtml_tree_node_t* node)
|
|
{
|
|
if(node == NULL)
|
|
return;
|
|
|
|
if(node->token) {
|
|
myhtml_token_attr_delete_all(node->tree->token, node->token);
|
|
myhtml_token_delete(node->tree->token, node->token);
|
|
}
|
|
|
|
mcobject_async_free(node->tree->tree_obj, node);
|
|
}
|
|
|
|
void myhtml_tree_node_delete(myhtml_tree_node_t* node)
|
|
{
|
|
if(node == NULL)
|
|
return;
|
|
|
|
myhtml_tree_node_remove(node);
|
|
myhtml_tree_node_free(node);
|
|
}
|
|
|
|
static void _myhtml_tree_node_delete_recursive(myhtml_tree_node_t* node)
|
|
{
|
|
while(node)
|
|
{
|
|
if(node->child)
|
|
_myhtml_tree_node_delete_recursive(node->child);
|
|
|
|
node = node->next;
|
|
myhtml_tree_node_delete(node);
|
|
}
|
|
}
|
|
|
|
void myhtml_tree_node_delete_recursive(myhtml_tree_node_t* node)
|
|
{
|
|
if(node == NULL)
|
|
return;
|
|
|
|
if(node->child)
|
|
_myhtml_tree_node_delete_recursive(node->child);
|
|
|
|
myhtml_tree_node_delete(node);
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_node_clone(myhtml_tree_node_t* node)
|
|
{
|
|
myhtml_tree_node_t* new_node = myhtml_tree_node_create(node->tree);
|
|
|
|
myhtml_token_node_wait_for_done(node->tree->token, node->token);
|
|
|
|
new_node->token = myhtml_token_node_clone(node->tree->token, node->token,
|
|
node->tree->mcasync_rules_token_id,
|
|
node->tree->mcasync_rules_attr_id);
|
|
new_node->tag_id = node->tag_id;
|
|
new_node->ns = node->ns;
|
|
new_node->token->type |= MyHTML_TOKEN_TYPE_DONE;
|
|
|
|
return new_node;
|
|
}
|
|
|
|
void myhtml_tree_node_insert_by_mode(myhtml_tree_node_t* adjusted_location,
|
|
myhtml_tree_node_t* node, enum myhtml_tree_insertion_mode mode)
|
|
{
|
|
if(mode == MyHTML_TREE_INSERTION_MODE_DEFAULT)
|
|
myhtml_tree_node_add_child(adjusted_location, node);
|
|
else if(mode == MyHTML_TREE_INSERTION_MODE_BEFORE)
|
|
myhtml_tree_node_insert_before(adjusted_location, node);
|
|
else
|
|
myhtml_tree_node_insert_after(adjusted_location, node);
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_node_insert_by_token(myhtml_tree_t* tree, myhtml_token_node_t* token, myhtml_namespace_t ns)
|
|
{
|
|
myhtml_tree_node_t* node = myhtml_tree_node_create(tree);
|
|
|
|
node->tag_id = token->tag_id;
|
|
node->token = token;
|
|
node->ns = ns;
|
|
|
|
enum myhtml_tree_insertion_mode mode;
|
|
myhtml_tree_node_t* adjusted_location = myhtml_tree_appropriate_place_inserting(tree, NULL, &mode);
|
|
myhtml_tree_node_insert_by_mode(adjusted_location, node, mode);
|
|
|
|
myhtml_tree_open_elements_append(tree, node);
|
|
return node;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_node_insert(myhtml_tree_t* tree, myhtml_tag_id_t tag_idx, myhtml_namespace_t ns)
|
|
{
|
|
myhtml_tree_node_t* node = myhtml_tree_node_create(tree);
|
|
|
|
node->token = NULL;
|
|
node->tag_id = tag_idx;
|
|
node->ns = ns;
|
|
|
|
enum myhtml_tree_insertion_mode mode;
|
|
myhtml_tree_node_t* adjusted_location = myhtml_tree_appropriate_place_inserting(tree, NULL, &mode);
|
|
myhtml_tree_node_insert_by_mode(adjusted_location, node, mode);
|
|
|
|
myhtml_tree_open_elements_append(tree, node);
|
|
return node;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_node_insert_comment(myhtml_tree_t* tree, myhtml_token_node_t* token, myhtml_tree_node_t* parent)
|
|
{
|
|
myhtml_tree_node_t* node = myhtml_tree_node_create(tree);
|
|
|
|
node->token = token;
|
|
node->tag_id = MyHTML_TAG__COMMENT;
|
|
|
|
enum myhtml_tree_insertion_mode mode = 0;
|
|
if(parent == NULL) {
|
|
parent = myhtml_tree_appropriate_place_inserting(tree, NULL, &mode);
|
|
}
|
|
|
|
myhtml_tree_node_insert_by_mode(parent, node, mode);
|
|
node->ns = parent->ns;
|
|
|
|
return node;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_node_insert_doctype(myhtml_tree_t* tree, myhtml_token_node_t* token)
|
|
{
|
|
myhtml_tree_node_t* node = myhtml_tree_node_create(tree);
|
|
|
|
node->token = token;
|
|
node->ns = MyHTML_NAMESPACE_HTML;
|
|
node->tag_id = MyHTML_TAG__DOCTYPE;
|
|
|
|
myhtml_tree_node_add_child(tree->document, node);
|
|
return node;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_node_insert_root(myhtml_tree_t* tree, myhtml_token_node_t* token, enum myhtml_namespace ns)
|
|
{
|
|
myhtml_tree_node_t* node = myhtml_tree_node_create(tree);
|
|
|
|
if(token)
|
|
node->tag_id = token->tag_id;
|
|
else
|
|
node->tag_id = MyHTML_TAG_HTML;
|
|
|
|
node->token = token;
|
|
node->ns = ns;
|
|
|
|
myhtml_tree_node_add_child(tree->document, node);
|
|
myhtml_tree_open_elements_append(tree, node);
|
|
|
|
tree->node_html = node;
|
|
return node;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_node_insert_text(myhtml_tree_t* tree, myhtml_token_node_t* token)
|
|
{
|
|
enum myhtml_tree_insertion_mode mode;
|
|
myhtml_tree_node_t* adjusted_location = myhtml_tree_appropriate_place_inserting(tree, NULL, &mode);
|
|
|
|
if(adjusted_location == tree->document)
|
|
return NULL;
|
|
|
|
if(mode == MyHTML_TREE_INSERTION_MODE_AFTER) {
|
|
if(adjusted_location->tag_id == MyHTML_TAG__TEXT && adjusted_location->token)
|
|
{
|
|
myhtml_token_merged_two_token_string(tree, adjusted_location->token, token, false);
|
|
return adjusted_location;
|
|
}
|
|
}
|
|
else if(mode == MyHTML_TREE_INSERTION_MODE_BEFORE) {
|
|
if(adjusted_location->tag_id == MyHTML_TAG__TEXT && adjusted_location->token) {
|
|
myhtml_token_merged_two_token_string(tree, token, adjusted_location->token, true);
|
|
return adjusted_location;
|
|
}
|
|
}
|
|
else {
|
|
if(adjusted_location->last_child && adjusted_location->last_child->tag_id == MyHTML_TAG__TEXT &&
|
|
adjusted_location->last_child->token)
|
|
{
|
|
myhtml_token_merged_two_token_string(tree, adjusted_location->last_child->token, token, false);
|
|
return adjusted_location->last_child;
|
|
}
|
|
}
|
|
|
|
myhtml_tree_node_t* node = myhtml_tree_node_create(tree);
|
|
|
|
node->tag_id = MyHTML_TAG__TEXT;
|
|
node->token = token;
|
|
node->ns = adjusted_location->ns;
|
|
|
|
myhtml_tree_node_insert_by_mode(adjusted_location, node, mode);
|
|
return node;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_node_insert_by_node(myhtml_tree_t* tree, myhtml_tree_node_t* node)
|
|
{
|
|
enum myhtml_tree_insertion_mode mode;
|
|
myhtml_tree_node_t* adjusted_location = myhtml_tree_appropriate_place_inserting(tree, NULL, &mode);
|
|
myhtml_tree_node_insert_by_mode(adjusted_location, node, mode);
|
|
|
|
myhtml_tree_open_elements_append(tree, node);
|
|
return node;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_node_insert_foreign_element(myhtml_tree_t* tree, myhtml_token_node_t* token)
|
|
{
|
|
enum myhtml_tree_insertion_mode mode;
|
|
myhtml_tree_node_t* adjusted_location = myhtml_tree_appropriate_place_inserting(tree, NULL, &mode);
|
|
|
|
myhtml_tree_node_t* node = myhtml_tree_node_create(tree);
|
|
|
|
node->tag_id = token->tag_id;
|
|
node->token = token;
|
|
node->ns = adjusted_location->ns;
|
|
|
|
myhtml_tree_node_insert_by_mode(adjusted_location, node, mode);
|
|
myhtml_tree_open_elements_append(tree, node);
|
|
return node;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_node_insert_html_element(myhtml_tree_t* tree, myhtml_token_node_t* token)
|
|
{
|
|
enum myhtml_tree_insertion_mode mode;
|
|
myhtml_tree_node_t* adjusted_location = myhtml_tree_appropriate_place_inserting(tree, NULL, &mode);
|
|
|
|
myhtml_tree_node_t* node = myhtml_tree_node_create(tree);
|
|
|
|
node->tag_id = token->tag_id;
|
|
node->token = token;
|
|
node->ns = MyHTML_NAMESPACE_HTML;
|
|
|
|
myhtml_tree_node_insert_by_mode(adjusted_location, node, mode);
|
|
myhtml_tree_open_elements_append(tree, node);
|
|
return node;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_element_in_scope(myhtml_tree_t* tree, myhtml_tag_id_t tag_idx,
|
|
myhtml_namespace_t mynamespace, enum myhtml_tag_categories category)
|
|
{
|
|
myhtml_tree_node_t** list = tree->open_elements->list;
|
|
|
|
const myhtml_tag_context_t *tag_ctx;
|
|
size_t i = tree->open_elements->length;
|
|
|
|
while(i) {
|
|
i--;
|
|
|
|
tag_ctx = myhtml_tag_get_by_id(tree->tags, list[i]->tag_id);
|
|
|
|
if(list[i]->tag_id == tag_idx &&
|
|
(mynamespace == MyHTML_NAMESPACE_UNDEF || list[i]->ns == mynamespace))
|
|
return list[i];
|
|
else if(category == MyHTML_TAG_CATEGORIES_SCOPE_SELECT) {
|
|
if((tag_ctx->cats[list[i]->ns] & category) == 0)
|
|
break;
|
|
}
|
|
else if(tag_ctx->cats[list[i]->ns] & category)
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool myhtml_tree_element_in_scope_by_node(myhtml_tree_node_t* node, enum myhtml_tag_categories category)
|
|
{
|
|
myhtml_tree_t* tree = node->tree;
|
|
myhtml_tree_node_t** list = tree->open_elements->list;
|
|
|
|
const myhtml_tag_context_t *tag_ctx;
|
|
size_t i = tree->open_elements->length;
|
|
|
|
while(i) {
|
|
i--;
|
|
|
|
tag_ctx = myhtml_tag_get_by_id(tree->tags, list[i]->tag_id);
|
|
|
|
if(list[i] == node)
|
|
return true;
|
|
else if(category == MyHTML_TAG_CATEGORIES_SCOPE_SELECT) {
|
|
if((tag_ctx->cats[list[i]->ns] & category) == 0)
|
|
break;
|
|
}
|
|
else if(tag_ctx->cats[list[i]->ns] & category)
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// list
|
|
myhtml_tree_list_t * myhtml_tree_list_init(void)
|
|
{
|
|
myhtml_tree_list_t* list = mycore_malloc(sizeof(myhtml_tree_list_t));
|
|
|
|
list->length = 0;
|
|
list->size = 4096;
|
|
list->list = (myhtml_tree_node_t**)mycore_malloc(sizeof(myhtml_tree_node_t*) * list->size);
|
|
|
|
return list;
|
|
}
|
|
|
|
void myhtml_tree_list_clean(myhtml_tree_list_t* list)
|
|
{
|
|
list->length = 0;
|
|
}
|
|
|
|
myhtml_tree_list_t * myhtml_tree_list_destroy(myhtml_tree_list_t* list, bool destroy_self)
|
|
{
|
|
if(list == NULL)
|
|
return NULL;
|
|
|
|
if(list->list)
|
|
mycore_free(list->list);
|
|
|
|
if(destroy_self && list) {
|
|
mycore_free(list);
|
|
return NULL;
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
void myhtml_tree_list_append(myhtml_tree_list_t* list, myhtml_tree_node_t* node)
|
|
{
|
|
if(list->length >= list->size) {
|
|
list->size <<= 1;
|
|
|
|
myhtml_tree_node_t** tmp = (myhtml_tree_node_t**)mycore_realloc(list->list, sizeof(myhtml_tree_node_t*) * list->size);
|
|
|
|
if(tmp)
|
|
list->list = tmp;
|
|
}
|
|
|
|
list->list[list->length] = node;
|
|
list->length++;
|
|
}
|
|
|
|
void myhtml_tree_list_append_after_index(myhtml_tree_list_t* list, myhtml_tree_node_t* node, size_t index)
|
|
{
|
|
myhtml_tree_list_insert_by_index(list, node, (index + 1));
|
|
}
|
|
|
|
void myhtml_tree_list_insert_by_index(myhtml_tree_list_t* list, myhtml_tree_node_t* node, size_t index)
|
|
{
|
|
if(list->length >= list->size) {
|
|
list->size <<= 1;
|
|
|
|
myhtml_tree_node_t** tmp = (myhtml_tree_node_t**)mycore_realloc(list->list, sizeof(myhtml_tree_node_t*) * list->size);
|
|
|
|
if(tmp)
|
|
list->list = tmp;
|
|
}
|
|
|
|
myhtml_tree_node_t** node_list = list->list;
|
|
|
|
memmove(&node_list[(index + 1)], &node_list[index], sizeof(myhtml_tree_node_t*) * (list->length - index));
|
|
|
|
list->list[index] = node;
|
|
list->length++;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_list_current_node(myhtml_tree_list_t* list)
|
|
{
|
|
if(list->length == 0)
|
|
return 0;
|
|
|
|
return list->list[ list->length - 1 ];
|
|
}
|
|
|
|
// stack of open elements
|
|
myhtml_tree_list_t * myhtml_tree_open_elements_init(myhtml_tree_t* tree)
|
|
{
|
|
return myhtml_tree_list_init();
|
|
}
|
|
|
|
void myhtml_tree_open_elements_clean(myhtml_tree_t* tree)
|
|
{
|
|
tree->open_elements->length = 0;
|
|
}
|
|
|
|
myhtml_tree_list_t * myhtml_tree_open_elements_destroy(myhtml_tree_t* tree)
|
|
{
|
|
return myhtml_tree_list_destroy(tree->open_elements, true);
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_current_node(myhtml_tree_t* tree)
|
|
{
|
|
if(tree->open_elements->length == 0) {
|
|
MyCORE_DEBUG("Current node; Open elements is 0");
|
|
return 0;
|
|
}
|
|
|
|
return tree->open_elements->list[ tree->open_elements->length - 1 ];
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_adjusted_current_node(myhtml_tree_t* tree)
|
|
{
|
|
if(tree->open_elements->length == 1 && tree->fragment)
|
|
return tree->fragment;
|
|
|
|
return myhtml_tree_current_node(tree);
|
|
}
|
|
|
|
void myhtml_tree_open_elements_append(myhtml_tree_t* tree, myhtml_tree_node_t* node)
|
|
{
|
|
myhtml_tree_list_append(tree->open_elements, node);
|
|
}
|
|
|
|
void myhtml_tree_open_elements_append_after_index(myhtml_tree_t* tree, myhtml_tree_node_t* node, size_t index)
|
|
{
|
|
myhtml_tree_list_append_after_index(tree->open_elements, node, index);
|
|
}
|
|
|
|
void myhtml_tree_open_elements_pop(myhtml_tree_t* tree)
|
|
{
|
|
if(tree->open_elements->length)
|
|
tree->open_elements->length--;
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(tree->open_elements->length == 0) {
|
|
MyCORE_DEBUG("Pop open elements; Now, Open Elements set 0; Good, if the end of parsing, otherwise is very bad");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void myhtml_tree_open_elements_remove(myhtml_tree_t* tree, myhtml_tree_node_t* node)
|
|
{
|
|
myhtml_tree_node_t** list = tree->open_elements->list;
|
|
|
|
size_t el_idx = tree->open_elements->length;
|
|
while(el_idx)
|
|
{
|
|
el_idx--;
|
|
|
|
if(list[el_idx] == node)
|
|
{
|
|
memmove(&list[el_idx], &list[el_idx + 1], sizeof(myhtml_tree_node_t*) * (tree->open_elements->length - el_idx));
|
|
tree->open_elements->length--;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(tree->open_elements->length == 0) {
|
|
MyCORE_DEBUG("Remove open elements; Now, Open Elements set 0; Good, if the end of parsing, otherwise is very bad");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void myhtml_tree_open_elements_pop_until(myhtml_tree_t* tree, myhtml_tag_id_t tag_idx, myhtml_namespace_t mynamespace, bool is_exclude)
|
|
{
|
|
myhtml_tree_node_t** list = tree->open_elements->list;
|
|
|
|
while(tree->open_elements->length)
|
|
{
|
|
tree->open_elements->length--;
|
|
|
|
if(list[ tree->open_elements->length ]->tag_id == tag_idx &&
|
|
// check namespace if set
|
|
(mynamespace == MyHTML_NAMESPACE_UNDEF ||
|
|
list[ tree->open_elements->length ]->ns == mynamespace))
|
|
{
|
|
if(is_exclude)
|
|
tree->open_elements->length++;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(tree->open_elements->length == 0) {
|
|
MyCORE_DEBUG("Until open elements; Now, Open Elements set 0; Good, if the end of parsing, otherwise is very bad");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void myhtml_tree_open_elements_pop_until_by_node(myhtml_tree_t* tree, myhtml_tree_node_t* node, bool is_exclude)
|
|
{
|
|
myhtml_tree_node_t** list = tree->open_elements->list;
|
|
|
|
while(tree->open_elements->length)
|
|
{
|
|
tree->open_elements->length--;
|
|
|
|
if(list[ tree->open_elements->length ] == node) {
|
|
if(is_exclude)
|
|
tree->open_elements->length++;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(tree->open_elements->length == 0) {
|
|
MyCORE_DEBUG("Until by node open elements; Now, Open Elements set 0; Good, if the end of parsing, otherwise is very bad");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void myhtml_tree_open_elements_pop_until_by_index(myhtml_tree_t* tree, size_t idx, bool is_exclude)
|
|
{
|
|
while(tree->open_elements->length)
|
|
{
|
|
tree->open_elements->length--;
|
|
|
|
if(tree->open_elements->length == idx) {
|
|
if(is_exclude)
|
|
tree->open_elements->length++;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(tree->open_elements->length == 0) {
|
|
MyCORE_DEBUG("Until by index open elements; Now, Open Elements set 0; Good, if the end of parsing, otherwise is very bad");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool myhtml_tree_open_elements_find_reverse(myhtml_tree_t* tree, myhtml_tree_node_t* idx, size_t* pos)
|
|
{
|
|
myhtml_tree_node_t** list = tree->open_elements->list;
|
|
|
|
size_t i = tree->open_elements->length;
|
|
while(i)
|
|
{
|
|
i--;
|
|
|
|
if(list[i] == idx) {
|
|
if(pos)
|
|
*pos = i;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool myhtml_tree_open_elements_find(myhtml_tree_t* tree, myhtml_tree_node_t* node, size_t* pos)
|
|
{
|
|
myhtml_tree_node_t** list = tree->open_elements->list;
|
|
|
|
for (size_t i = 0; i < tree->open_elements->length; i++)
|
|
{
|
|
if(list[i] == node) {
|
|
if(pos)
|
|
*pos = i;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_open_elements_find_by_tag_idx_reverse(myhtml_tree_t* tree, myhtml_tag_id_t tag_idx, myhtml_namespace_t mynamespace, size_t* return_index)
|
|
{
|
|
myhtml_tree_node_t** list = tree->open_elements->list;
|
|
|
|
size_t i = tree->open_elements->length;
|
|
while(i)
|
|
{
|
|
i--;
|
|
|
|
if(list[i]->tag_id == tag_idx &&
|
|
(mynamespace == MyHTML_NAMESPACE_UNDEF || list[i]->ns == mynamespace))
|
|
{
|
|
if(return_index)
|
|
*return_index = i;
|
|
|
|
return list[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_open_elements_find_by_tag_idx(myhtml_tree_t* tree, myhtml_tag_id_t tag_idx, myhtml_namespace_t mynamespace, size_t* return_index)
|
|
{
|
|
myhtml_tree_node_t** list = tree->open_elements->list;
|
|
|
|
for (size_t i = 0; i < tree->open_elements->length; i++)
|
|
{
|
|
if(list[i]->tag_id == tag_idx &&
|
|
(mynamespace == MyHTML_NAMESPACE_UNDEF || list[i]->ns == mynamespace))
|
|
{
|
|
if(return_index)
|
|
*return_index = i;
|
|
|
|
return list[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void myhtml_tree_generate_implied_end_tags(myhtml_tree_t* tree, myhtml_tag_id_t exclude_tag_idx, myhtml_namespace_t mynamespace)
|
|
{
|
|
if(tree->open_elements->length == 0) {
|
|
MyCORE_DEBUG("Generate implied end tags; Open elements is 0");
|
|
return;
|
|
}
|
|
|
|
while(tree->open_elements->length > 0)
|
|
{
|
|
myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(current_node == NULL) {
|
|
MyCORE_DEBUG_ERROR("Generate implied end tags; Current node is NULL! This is very bad");
|
|
}
|
|
#endif
|
|
|
|
switch (current_node->tag_id) {
|
|
case MyHTML_TAG_DD:
|
|
case MyHTML_TAG_DT:
|
|
case MyHTML_TAG_LI:
|
|
case MyHTML_TAG_MENUITEM:
|
|
case MyHTML_TAG_OPTGROUP:
|
|
case MyHTML_TAG_OPTION:
|
|
case MyHTML_TAG_P:
|
|
case MyHTML_TAG_RB:
|
|
case MyHTML_TAG_RP:
|
|
case MyHTML_TAG_RT:
|
|
case MyHTML_TAG_RTC:
|
|
if(exclude_tag_idx == current_node->tag_id &&
|
|
(mynamespace == MyHTML_NAMESPACE_UNDEF || current_node->ns == mynamespace))
|
|
return;
|
|
|
|
myhtml_tree_open_elements_pop(tree);
|
|
continue;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void myhtml_tree_generate_all_implied_end_tags(myhtml_tree_t* tree, myhtml_tag_id_t exclude_tag_idx, myhtml_namespace_t mynamespace)
|
|
{
|
|
if(tree->open_elements->length == 0) {
|
|
MyCORE_DEBUG("Generate all implied end tags; Open elements is 0");
|
|
return;
|
|
}
|
|
|
|
while(tree->open_elements->length > 0)
|
|
{
|
|
myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(current_node == NULL) {
|
|
MyCORE_DEBUG_ERROR("Generate all implied end tags; Current node is NULL! This is very bad");
|
|
}
|
|
#endif
|
|
|
|
switch (current_node->tag_id) {
|
|
case MyHTML_TAG_CAPTION:
|
|
case MyHTML_TAG_COLGROUP:
|
|
case MyHTML_TAG_DD:
|
|
case MyHTML_TAG_DT:
|
|
case MyHTML_TAG_LI:
|
|
case MyHTML_TAG_OPTGROUP:
|
|
case MyHTML_TAG_OPTION:
|
|
case MyHTML_TAG_P:
|
|
case MyHTML_TAG_RB:
|
|
case MyHTML_TAG_RP:
|
|
case MyHTML_TAG_RT:
|
|
case MyHTML_TAG_RTC:
|
|
case MyHTML_TAG_TBODY:
|
|
case MyHTML_TAG_TD:
|
|
case MyHTML_TAG_TFOOT:
|
|
case MyHTML_TAG_TH:
|
|
case MyHTML_TAG_THEAD:
|
|
case MyHTML_TAG_TR:
|
|
if(exclude_tag_idx == current_node->tag_id &&
|
|
(mynamespace == MyHTML_NAMESPACE_UNDEF || current_node->ns == mynamespace))
|
|
return;
|
|
|
|
myhtml_tree_open_elements_pop(tree);
|
|
continue;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void myhtml_tree_reset_insertion_mode_appropriately(myhtml_tree_t* tree)
|
|
{
|
|
if(tree->open_elements->length == 0) {
|
|
MyCORE_DEBUG("Reset insertion mode appropriately; Open elements is 0");
|
|
return;
|
|
}
|
|
|
|
size_t i = tree->open_elements->length;
|
|
|
|
// step 1
|
|
bool last = false;
|
|
myhtml_tree_node_t** list = tree->open_elements->list;
|
|
|
|
// step 3
|
|
while(i)
|
|
{
|
|
i--;
|
|
|
|
// step 2
|
|
myhtml_tree_node_t* node = list[i];
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(node == NULL) {
|
|
MyCORE_DEBUG_ERROR("Reset insertion mode appropriately; node is NULL! This is very bad");
|
|
}
|
|
#endif
|
|
|
|
if(i == 0) {
|
|
last = true;
|
|
|
|
if(tree->fragment) {
|
|
node = tree->fragment;
|
|
}
|
|
}
|
|
|
|
if(node->ns != MyHTML_NAMESPACE_HTML) {
|
|
if(last) {
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
|
|
return;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// step 4
|
|
if(node->tag_id == MyHTML_TAG_SELECT)
|
|
{
|
|
// step 4.1
|
|
if(last) {
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_SELECT;
|
|
return;
|
|
}
|
|
|
|
// step 4.2
|
|
size_t ancestor = i;
|
|
|
|
while(1)
|
|
{
|
|
// step 4.3
|
|
if(ancestor == 0) {
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_SELECT;
|
|
return;
|
|
}
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(ancestor == 0) {
|
|
MyCORE_DEBUG_ERROR("Reset insertion mode appropriately; Ancestor is 0! This is very, very bad");
|
|
}
|
|
#endif
|
|
|
|
// step 4.4
|
|
ancestor--;
|
|
|
|
// step 4.5
|
|
if(list[ancestor]->tag_id == MyHTML_TAG_TEMPLATE) {
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_SELECT;
|
|
return;
|
|
}
|
|
// step 4.6
|
|
else if(list[ancestor]->tag_id == MyHTML_TAG_TABLE) {
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_SELECT_IN_TABLE;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// step 5-15
|
|
switch (node->tag_id) {
|
|
case MyHTML_TAG_TD:
|
|
case MyHTML_TAG_TH:
|
|
if(last == false) {
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_CELL;
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case MyHTML_TAG_TR:
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_ROW;
|
|
return;
|
|
|
|
case MyHTML_TAG_TBODY:
|
|
case MyHTML_TAG_TFOOT:
|
|
case MyHTML_TAG_THEAD:
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE_BODY;
|
|
return;
|
|
|
|
case MyHTML_TAG_CAPTION:
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_CAPTION;
|
|
return;
|
|
|
|
case MyHTML_TAG_COLGROUP:
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_COLUMN_GROUP;
|
|
return;
|
|
|
|
case MyHTML_TAG_TABLE:
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE;
|
|
return;
|
|
|
|
case MyHTML_TAG_TEMPLATE:
|
|
tree->insert_mode = tree->template_insertion->list[(tree->template_insertion->length - 1)];
|
|
return;
|
|
|
|
case MyHTML_TAG_HEAD:
|
|
if(last == false) {
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_HEAD;
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case MyHTML_TAG_BODY:
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
|
|
return;
|
|
|
|
case MyHTML_TAG_FRAMESET:
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_FRAMESET;
|
|
return;
|
|
|
|
case MyHTML_TAG_HTML:
|
|
{
|
|
if(tree->node_head) {
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_AFTER_HEAD;
|
|
return;
|
|
}
|
|
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_BEFORE_HEAD;
|
|
return;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// step 16
|
|
if(last) {
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
|
|
return;
|
|
}
|
|
|
|
// step 17
|
|
}
|
|
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_INITIAL;
|
|
}
|
|
|
|
// stack of active formatting elements
|
|
myhtml_tree_list_t * myhtml_tree_active_formatting_init(myhtml_tree_t* tree)
|
|
{
|
|
return myhtml_tree_list_init();
|
|
}
|
|
|
|
void myhtml_tree_active_formatting_clean(myhtml_tree_t* tree)
|
|
{
|
|
tree->active_formatting->length = 0;
|
|
}
|
|
|
|
myhtml_tree_list_t * myhtml_tree_active_formatting_destroy(myhtml_tree_t* tree)
|
|
{
|
|
return myhtml_tree_list_destroy(tree->active_formatting, true);
|
|
}
|
|
|
|
bool myhtml_tree_active_formatting_is_marker(myhtml_tree_t* tree, myhtml_tree_node_t* node)
|
|
{
|
|
#ifdef DEBUG_MODE
|
|
if(node == NULL) {
|
|
MyCORE_DEBUG_ERROR("Active formatting is marker; node is NULL!");
|
|
}
|
|
#endif
|
|
|
|
if(tree->myhtml->marker == node)
|
|
return true;
|
|
|
|
switch (node->tag_id) {
|
|
case MyHTML_TAG_APPLET:
|
|
case MyHTML_TAG_BUTTON:
|
|
case MyHTML_TAG_OBJECT:
|
|
case MyHTML_TAG_MARQUEE:
|
|
case MyHTML_TAG_TD:
|
|
case MyHTML_TAG_TH:
|
|
case MyHTML_TAG_CAPTION:
|
|
return true;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void myhtml_tree_active_formatting_append(myhtml_tree_t* tree, myhtml_tree_node_t* node)
|
|
{
|
|
myhtml_tree_list_append( tree->active_formatting, node);
|
|
}
|
|
|
|
void myhtml_tree_active_formatting_pop(myhtml_tree_t* tree)
|
|
{
|
|
if(tree->active_formatting->length)
|
|
tree->active_formatting->length--;
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(tree->active_formatting->length == 0) {
|
|
MyCORE_DEBUG("Pop active formatting; length is 0");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void myhtml_tree_active_formatting_remove(myhtml_tree_t* tree, myhtml_tree_node_t* node)
|
|
{
|
|
myhtml_tree_node_t** list = tree->active_formatting->list;
|
|
|
|
size_t el_idx = tree->active_formatting->length;
|
|
while(el_idx)
|
|
{
|
|
el_idx--;
|
|
|
|
if(list[el_idx] == node)
|
|
{
|
|
memmove(&list[el_idx], &list[el_idx + 1], sizeof(myhtml_tree_node_t*) * (tree->active_formatting->length - el_idx));
|
|
tree->active_formatting->length--;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(tree->active_formatting->length == 0) {
|
|
// MyCORE_DEBUG("Remove active formatting; length is 0");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void myhtml_tree_active_formatting_remove_by_index(myhtml_tree_t* tree, size_t idx)
|
|
{
|
|
myhtml_tree_node_t** list = tree->active_formatting->list;
|
|
|
|
memmove(&list[idx], &list[idx + 1], sizeof(myhtml_tree_node_t*) * (tree->active_formatting->length - idx));
|
|
tree->active_formatting->length--;
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(tree->active_formatting->length == 0) {
|
|
MyCORE_DEBUG("Remove active formatting by index; length is 0");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void myhtml_tree_active_formatting_append_with_check(myhtml_tree_t* tree, myhtml_tree_node_t* node)
|
|
{
|
|
// if(myhtml_tree_active_formatting_is_marker(tree, node)) {
|
|
// myhtml_tree_active_formatting_append(tree, node);
|
|
// return;
|
|
// }
|
|
|
|
myhtml_tree_node_t** list = tree->active_formatting->list;
|
|
size_t i = tree->active_formatting->length;
|
|
size_t earliest_idx = (i ? (i - 1) : 0);
|
|
size_t count = 0;
|
|
|
|
while(i)
|
|
{
|
|
i--;
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(list[i] == NULL) {
|
|
MyCORE_DEBUG("Appen active formatting with check; list[" MyCORE_FORMAT_Z "] is NULL", i);
|
|
}
|
|
#endif
|
|
|
|
if(myhtml_tree_active_formatting_is_marker(tree, list[i]))
|
|
break;
|
|
|
|
if(list[i]->token && node->token)
|
|
{
|
|
myhtml_token_node_wait_for_done(tree->token, list[i]->token);
|
|
myhtml_token_node_wait_for_done(tree->token, node->token);
|
|
|
|
if(list[i]->ns == node->ns &&
|
|
list[i]->tag_id == node->tag_id &&
|
|
myhtml_token_attr_compare(list[i]->token, node->token))
|
|
{
|
|
count++;
|
|
earliest_idx = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(count >= 3)
|
|
myhtml_tree_active_formatting_remove_by_index(tree, earliest_idx);
|
|
|
|
myhtml_tree_active_formatting_append(tree, node);
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_active_formatting_current_node(myhtml_tree_t* tree)
|
|
{
|
|
if(tree->active_formatting->length == 0) {
|
|
MyCORE_DEBUG("Current node active formatting; length is 0");
|
|
return 0;
|
|
}
|
|
|
|
return tree->active_formatting->list[ tree->active_formatting->length - 1 ];
|
|
}
|
|
|
|
bool myhtml_tree_active_formatting_find(myhtml_tree_t* tree, myhtml_tree_node_t* node, size_t* return_idx)
|
|
{
|
|
myhtml_tree_node_t** list = tree->active_formatting->list;
|
|
|
|
size_t i = tree->active_formatting->length;
|
|
while(i)
|
|
{
|
|
i--;
|
|
|
|
if(list[i] == node) {
|
|
if(return_idx)
|
|
*return_idx = i;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void myhtml_tree_active_formatting_up_to_last_marker(myhtml_tree_t* tree)
|
|
{
|
|
// Step 1: Let entry be the last (most recently added) entry in the list of active formatting elements.
|
|
myhtml_tree_node_t** list = tree->active_formatting->list;
|
|
|
|
// Step 2: Remove entry from the list of active formatting elements.
|
|
if(tree->active_formatting->length == 0)
|
|
return;
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(list[ tree->active_formatting->length ] == NULL) {
|
|
MyCORE_DEBUG("Up to last marker active formatting; list[" MyCORE_FORMAT_Z "] is NULL", tree->active_formatting->length);
|
|
}
|
|
#endif
|
|
|
|
while(tree->active_formatting->length)
|
|
{
|
|
tree->active_formatting->length--;
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(list[ tree->active_formatting->length ] == NULL) {
|
|
MyCORE_DEBUG("Up to last marker active formatting; list[" MyCORE_FORMAT_Z "] is NULL", tree->active_formatting->length);
|
|
}
|
|
#endif
|
|
|
|
if(myhtml_tree_active_formatting_is_marker(tree, list[ tree->active_formatting->length ])) {
|
|
// include marker
|
|
//tree->active_formatting->length++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_active_formatting_between_last_marker(myhtml_tree_t* tree, myhtml_tag_id_t tag_idx, size_t* return_idx)
|
|
{
|
|
myhtml_tree_node_t** list = tree->active_formatting->list;
|
|
|
|
size_t i = tree->active_formatting->length;
|
|
while(i)
|
|
{
|
|
i--;
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(list[i] == NULL) {
|
|
MyCORE_DEBUG("Between last marker active formatting; list[" MyCORE_FORMAT_Z "] is NULL", i);
|
|
}
|
|
#endif
|
|
|
|
if(myhtml_tree_active_formatting_is_marker(tree, list[i]))
|
|
break;
|
|
else if(list[i]->tag_id == tag_idx && list[i]->ns == MyHTML_NAMESPACE_HTML) {
|
|
if(return_idx)
|
|
*return_idx = i;
|
|
return list[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void myhtml_tree_active_formatting_reconstruction(myhtml_tree_t* tree)
|
|
{
|
|
myhtml_tree_list_t* af = tree->active_formatting;
|
|
myhtml_tree_node_t** list = af->list;
|
|
|
|
// step 1
|
|
if(af->length == 0)
|
|
return;
|
|
|
|
// step 2--3
|
|
size_t af_idx = af->length - 1;
|
|
|
|
if(myhtml_tree_active_formatting_is_marker(tree, list[af_idx]) ||
|
|
myhtml_tree_open_elements_find(tree, list[af_idx], NULL))
|
|
return;
|
|
|
|
// step 4--6
|
|
while (af_idx)
|
|
{
|
|
af_idx--;
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(list[af_idx] == NULL) {
|
|
MyCORE_DEBUG("Formatting reconstruction; Step 4--6; list[" MyCORE_FORMAT_Z "] is NULL", af_idx);
|
|
}
|
|
#endif
|
|
|
|
if(myhtml_tree_active_formatting_is_marker(tree, list[af_idx]) ||
|
|
myhtml_tree_open_elements_find(tree, list[af_idx], NULL))
|
|
{
|
|
af_idx++; // need if 0
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (af_idx < af->length)
|
|
{
|
|
#ifdef DEBUG_MODE
|
|
if(list[af_idx] == NULL) {
|
|
MyCORE_DEBUG("Formatting reconstruction; Next steps; list[" MyCORE_FORMAT_Z "] is NULL", af_idx);
|
|
}
|
|
#endif
|
|
|
|
myhtml_tree_node_t* node = myhtml_tree_node_clone(list[af_idx]);
|
|
myhtml_tree_node_insert_by_node(tree, node);
|
|
|
|
list[af_idx] = node;
|
|
|
|
af_idx++;
|
|
}
|
|
}
|
|
|
|
bool myhtml_tree_adoption_agency_algorithm(myhtml_tree_t* tree, myhtml_token_node_t* token, myhtml_tag_id_t subject_tag_idx)
|
|
{
|
|
if(tree->open_elements->length == 0)
|
|
return false;
|
|
|
|
size_t oel_curr_index = tree->open_elements->length - 1;
|
|
|
|
myhtml_tree_node_t** oel_list = tree->open_elements->list;
|
|
myhtml_tree_node_t** afe_list = tree->active_formatting->list;
|
|
myhtml_tree_node_t* current_node = oel_list[oel_curr_index];
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(current_node == NULL) {
|
|
MyCORE_DEBUG_ERROR("Adoption agency algorithm; Current node is NULL");
|
|
}
|
|
#endif
|
|
|
|
// step 1
|
|
if(current_node->ns == MyHTML_NAMESPACE_HTML && current_node->tag_id == subject_tag_idx &&
|
|
myhtml_tree_active_formatting_find(tree, current_node, NULL) == false)
|
|
{
|
|
myhtml_tree_open_elements_pop(tree);
|
|
return false;
|
|
}
|
|
|
|
// step 2, 3
|
|
int loop = 0;
|
|
|
|
while (loop < 8)
|
|
{
|
|
// step 4
|
|
loop++;
|
|
|
|
// step 5
|
|
size_t afe_index = 0;
|
|
myhtml_tree_node_t* formatting_element = NULL;
|
|
{
|
|
myhtml_tree_node_t** list = tree->active_formatting->list;
|
|
|
|
size_t i = tree->active_formatting->length;
|
|
while(i)
|
|
{
|
|
i--;
|
|
|
|
if(myhtml_tree_active_formatting_is_marker(tree, list[i]))
|
|
return false;
|
|
else if(list[i]->tag_id == subject_tag_idx) {
|
|
afe_index = i;
|
|
formatting_element = list[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//myhtml_tree_node_t* formatting_element = myhtml_tree_active_formatting_between_last_marker(tree, subject_tag_idx, &afe_index);
|
|
|
|
// If there is no such element, then abort these steps and instead act as described in the
|
|
// ===> "any other end tag" entry above.
|
|
if(formatting_element == NULL) {
|
|
return true;
|
|
}
|
|
|
|
// step 6
|
|
size_t oel_format_el_idx;
|
|
if(myhtml_tree_open_elements_find(tree, formatting_element, &oel_format_el_idx) == false) {
|
|
myhtml_tree_active_formatting_remove(tree, formatting_element);
|
|
return false;
|
|
}
|
|
|
|
// step 7
|
|
if(myhtml_tree_element_in_scope_by_node(formatting_element, MyHTML_TAG_CATEGORIES_SCOPE) == false) {
|
|
/* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:AAA_FORMATTING_ELEMENT_NOT_FOUND LEVEL:ERROR NODE:formatting_element */
|
|
return false;
|
|
}
|
|
|
|
// step 8
|
|
//if(afe_last != list[i])
|
|
// fprintf(stderr, "oh");
|
|
|
|
// step 9
|
|
myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
|
|
if(current_node->ns != formatting_element->ns ||
|
|
current_node->tag_id != formatting_element->tag_id) {
|
|
// parse error
|
|
/* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
|
|
/* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:formatting_element->token HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:formatting_element->tag_id NEED_NS:formatting_element->ns */
|
|
}
|
|
|
|
// 10
|
|
// Let furthest block be the topmost node in the stack of open elements
|
|
// that is lower in the stack than formatting element, and is an element in the special category. T
|
|
// here might not be one.
|
|
myhtml_tree_node_t* furthest_block = NULL;
|
|
size_t idx_furthest_block = 0;
|
|
for (idx_furthest_block = oel_format_el_idx; idx_furthest_block < tree->open_elements->length; idx_furthest_block++)
|
|
{
|
|
const myhtml_tag_context_t *tag_ctx = myhtml_tag_get_by_id(tree->tags, oel_list[idx_furthest_block]->tag_id);
|
|
|
|
if(tag_ctx->cats[oel_list[idx_furthest_block]->ns] & MyHTML_TAG_CATEGORIES_SPECIAL) {
|
|
furthest_block = oel_list[idx_furthest_block];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// step 11
|
|
// If there is no furthest block, then the UA must first pop all the nodes from the bottom
|
|
// of the stack of open elements, from the current node up to and including formatting element,
|
|
// then remove formatting element from the list of active formatting elements, and finally abort these steps.
|
|
if(furthest_block == NULL)
|
|
{
|
|
while(myhtml_tree_current_node(tree) != formatting_element) {
|
|
myhtml_tree_open_elements_pop(tree);
|
|
}
|
|
|
|
myhtml_tree_open_elements_pop(tree); // and including formatting element
|
|
myhtml_tree_active_formatting_remove(tree, formatting_element);
|
|
|
|
return false;
|
|
}
|
|
|
|
/* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:AAA_BEGIN LEVEL:INFO */
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(oel_format_el_idx == 0) {
|
|
MyCORE_DEBUG_ERROR("Adoption agency algorithm; Step 11; oel_format_el_idx is 0; Bad!");
|
|
}
|
|
#endif
|
|
|
|
// step 12
|
|
myhtml_tree_node_t* common_ancestor = oel_list[oel_format_el_idx - 1];
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(common_ancestor == NULL) {
|
|
MyCORE_DEBUG_ERROR("Adoption agency algorithm; Step 11; common_ancestor is NULL");
|
|
}
|
|
#endif
|
|
|
|
// step 13
|
|
size_t bookmark = afe_index + 1;
|
|
|
|
// step 14
|
|
myhtml_tree_node_t *node = furthest_block, *last = furthest_block;
|
|
size_t index_oel_node = idx_furthest_block;
|
|
|
|
// step 14.1
|
|
for(int inner_loop = 0;;)
|
|
{
|
|
// step 14.2
|
|
inner_loop++;
|
|
|
|
// step 14.3
|
|
size_t node_index;
|
|
if(myhtml_tree_open_elements_find(tree, node, &node_index) == false) {
|
|
node_index = index_oel_node;
|
|
}
|
|
|
|
if(node_index > 0)
|
|
node_index--;
|
|
else {
|
|
MyCORE_DEBUG_ERROR("Adoption agency algorithm; decrement node_index, node_index is null");
|
|
return false;
|
|
}
|
|
|
|
index_oel_node = node_index;
|
|
|
|
node = oel_list[node_index];
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(node == NULL) {
|
|
MyCORE_DEBUG_ERROR("Adoption agency algorithm; Step 13.3; node is NULL");
|
|
}
|
|
#endif
|
|
// step 14.4
|
|
if(node == formatting_element)
|
|
break;
|
|
|
|
// step 14.5
|
|
size_t afe_node_index;
|
|
bool is_exists = myhtml_tree_active_formatting_find(tree, node, &afe_node_index);
|
|
if(inner_loop > 3 && is_exists) {
|
|
myhtml_tree_active_formatting_remove_by_index(tree, afe_node_index);
|
|
|
|
if(afe_node_index < bookmark)
|
|
bookmark--;
|
|
|
|
// If inner loop counter is greater than three and node is in the list of active formatting elements,
|
|
// then remove node from the list of active formatting elements.
|
|
continue;
|
|
}
|
|
|
|
// step 14.6
|
|
if(is_exists == false) {
|
|
myhtml_tree_open_elements_remove(tree, node);
|
|
continue;
|
|
}
|
|
|
|
// step 14.7
|
|
myhtml_tree_node_t* clone = myhtml_tree_node_clone(node);
|
|
|
|
clone->ns = MyHTML_NAMESPACE_HTML;
|
|
|
|
afe_list[afe_node_index] = clone;
|
|
oel_list[node_index] = clone;
|
|
|
|
node = clone;
|
|
|
|
// step 14.8
|
|
if(last == furthest_block) {
|
|
bookmark = afe_node_index + 1;
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(bookmark >= tree->active_formatting->length) {
|
|
MyCORE_DEBUG_ERROR("Adoption agency algorithm; Step 13.8; bookmark >= open_elements length");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// step 14.9
|
|
if(last->parent)
|
|
myhtml_tree_node_remove(last);
|
|
|
|
myhtml_tree_node_add_child(node, last);
|
|
|
|
// step 14.10
|
|
last = node;
|
|
}
|
|
|
|
if(last->parent)
|
|
myhtml_tree_node_remove(last);
|
|
|
|
// step 15
|
|
enum myhtml_tree_insertion_mode insert_mode;
|
|
common_ancestor = myhtml_tree_appropriate_place_inserting(tree, common_ancestor, &insert_mode);
|
|
myhtml_tree_node_insert_by_mode(common_ancestor, last, insert_mode);
|
|
|
|
// step 16
|
|
myhtml_tree_node_t* new_formatting_element = myhtml_tree_node_clone(formatting_element);
|
|
|
|
new_formatting_element->ns = MyHTML_NAMESPACE_HTML;
|
|
|
|
// step 17
|
|
myhtml_tree_node_t * furthest_block_child = furthest_block->child;
|
|
|
|
while (furthest_block_child) {
|
|
myhtml_tree_node_t *next = furthest_block_child->next;
|
|
myhtml_tree_node_remove(furthest_block_child);
|
|
|
|
myhtml_tree_node_add_child(new_formatting_element, furthest_block_child);
|
|
furthest_block_child = next;
|
|
}
|
|
|
|
// step 18
|
|
myhtml_tree_node_add_child(furthest_block, new_formatting_element);
|
|
|
|
// step 19
|
|
if(myhtml_tree_active_formatting_find(tree, formatting_element, &afe_index) == false)
|
|
return false;
|
|
|
|
if(afe_index < bookmark)
|
|
bookmark--;
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(bookmark >= tree->active_formatting->length) {
|
|
MyCORE_DEBUG_ERROR("Adoption agency algorithm; Before Step 18; bookmark (" MyCORE_FORMAT_Z ") >= open_elements length", bookmark);
|
|
}
|
|
#endif
|
|
|
|
myhtml_tree_active_formatting_remove_by_index(tree, afe_index);
|
|
myhtml_tree_list_insert_by_index(tree->active_formatting, new_formatting_element, bookmark);
|
|
|
|
// step 20
|
|
myhtml_tree_open_elements_remove(tree, formatting_element);
|
|
|
|
if(myhtml_tree_open_elements_find(tree, furthest_block, &idx_furthest_block)) {
|
|
myhtml_tree_list_insert_by_index(tree->open_elements, new_formatting_element, idx_furthest_block + 1);
|
|
}
|
|
else {
|
|
MyCORE_DEBUG_ERROR("Adoption agency algorithm; Step 19; can't find furthest_block in open elements");
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_appropriate_place_inserting(myhtml_tree_t* tree, myhtml_tree_node_t* override_target,
|
|
enum myhtml_tree_insertion_mode* mode)
|
|
{
|
|
*mode = MyHTML_TREE_INSERTION_MODE_DEFAULT;
|
|
|
|
// step 1
|
|
myhtml_tree_node_t* target = override_target ? override_target : myhtml_tree_current_node(tree);
|
|
|
|
// step 2
|
|
myhtml_tree_node_t* adjusted_location;
|
|
|
|
if(tree->foster_parenting) {
|
|
#ifdef DEBUG_MODE
|
|
if(target == NULL) {
|
|
MyCORE_DEBUG_ERROR("Appropriate place inserting; Step 2; target is NULL in return value! This IS very bad");
|
|
}
|
|
#endif
|
|
if(target->ns != MyHTML_NAMESPACE_HTML)
|
|
return target;
|
|
|
|
switch (target->tag_id) {
|
|
case MyHTML_TAG_TABLE:
|
|
case MyHTML_TAG_TBODY:
|
|
case MyHTML_TAG_TFOOT:
|
|
case MyHTML_TAG_THEAD:
|
|
case MyHTML_TAG_TR:
|
|
{
|
|
size_t idx_template, idx_table;
|
|
|
|
// step 2.1-2
|
|
myhtml_tree_node_t* last_template = myhtml_tree_open_elements_find_by_tag_idx_reverse(tree, MyHTML_TAG_TEMPLATE, MyHTML_NAMESPACE_HTML, &idx_template);
|
|
myhtml_tree_node_t* last_table = myhtml_tree_open_elements_find_by_tag_idx_reverse(tree, MyHTML_TAG_TABLE, MyHTML_NAMESPACE_HTML, &idx_table);
|
|
|
|
// step 2.3
|
|
if(last_template && (last_table == NULL || idx_template > idx_table))
|
|
{
|
|
return last_template;
|
|
}
|
|
|
|
// step 2.4
|
|
else if(last_table == NULL)
|
|
{
|
|
adjusted_location = tree->open_elements->list[0];
|
|
break;
|
|
}
|
|
|
|
// step 2.5
|
|
else if(last_table->parent)
|
|
{
|
|
//adjusted_location = last_table->parent;
|
|
|
|
if(last_table->prev) {
|
|
adjusted_location = last_table->prev;
|
|
*mode = MyHTML_TREE_INSERTION_MODE_AFTER;
|
|
}
|
|
else {
|
|
adjusted_location = last_table;
|
|
*mode = MyHTML_TREE_INSERTION_MODE_BEFORE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(idx_table == 0) {
|
|
MyCORE_DEBUG_ERROR("Appropriate place inserting; Step 2.5; idx_table is 0");
|
|
}
|
|
#endif
|
|
|
|
// step 2.6-7
|
|
adjusted_location = tree->open_elements->list[idx_table - 1];
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
adjusted_location = target;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
#ifdef DEBUG_MODE
|
|
if(target == NULL) {
|
|
MyCORE_DEBUG_ERROR("Appropriate place inserting; Step 3-5; target is NULL in return value! This IS very bad");
|
|
}
|
|
#endif
|
|
|
|
// step 3-4
|
|
return target;
|
|
}
|
|
|
|
// step 3-4
|
|
return adjusted_location;
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_appropriate_place_inserting_in_tree(myhtml_tree_node_t* target, enum myhtml_tree_insertion_mode* mode)
|
|
{
|
|
*mode = MyHTML_TREE_INSERTION_MODE_BEFORE;
|
|
|
|
// step 2
|
|
myhtml_tree_node_t* adjusted_location;
|
|
|
|
if(target->tree->foster_parenting) {
|
|
#ifdef DEBUG_MODE
|
|
if(target == NULL) {
|
|
MyCORE_DEBUG_ERROR("Appropriate place inserting; Step 2; target is NULL in return value! This IS very bad");
|
|
}
|
|
#endif
|
|
|
|
if(target->ns != MyHTML_NAMESPACE_HTML)
|
|
return target;
|
|
|
|
switch (target->tag_id) {
|
|
case MyHTML_TAG_TABLE:
|
|
case MyHTML_TAG_TBODY:
|
|
case MyHTML_TAG_TFOOT:
|
|
case MyHTML_TAG_THEAD:
|
|
case MyHTML_TAG_TR:
|
|
{
|
|
// step 2.1-2
|
|
myhtml_tree_node_t* last_template = myhtml_tree_node_find_parent_by_tag_id(target, MyHTML_TAG_TEMPLATE);
|
|
myhtml_tree_node_t* last_table = myhtml_tree_node_find_parent_by_tag_id(target, MyHTML_TAG_TABLE);
|
|
myhtml_tree_node_t* last_table_in_template = NULL;
|
|
|
|
if(last_template) {
|
|
last_table_in_template = myhtml_tree_node_find_parent_by_tag_id(last_template, MyHTML_TAG_TABLE);
|
|
}
|
|
|
|
// step 2.3
|
|
if(last_template && (last_table == NULL || last_table != last_table_in_template))
|
|
{
|
|
*mode = MyHTML_TREE_INSERTION_MODE_DEFAULT;
|
|
adjusted_location = last_template;
|
|
break;
|
|
}
|
|
|
|
// step 2.4
|
|
else if(last_table == NULL)
|
|
{
|
|
adjusted_location = target;
|
|
break;
|
|
}
|
|
|
|
// step 2.5
|
|
else if(last_table->parent)
|
|
{
|
|
//adjusted_location = last_table->parent;
|
|
|
|
if(last_table->prev) {
|
|
adjusted_location = last_table->prev;
|
|
*mode = MyHTML_TREE_INSERTION_MODE_AFTER;
|
|
}
|
|
else {
|
|
adjusted_location = last_table;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(idx_table == 0) {
|
|
MyCORE_DEBUG_ERROR("Appropriate place inserting; Step 2.5; idx_table is 0");
|
|
}
|
|
#endif
|
|
|
|
// step 2.6-7
|
|
adjusted_location = target;
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
*mode = MyHTML_TREE_INSERTION_MODE_DEFAULT;
|
|
adjusted_location = target;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
#ifdef DEBUG_MODE
|
|
if(target == NULL) {
|
|
MyCORE_DEBUG_ERROR("Appropriate place inserting; Step 3-5; target is NULL in return value! This IS very bad");
|
|
}
|
|
#endif
|
|
|
|
*mode = MyHTML_TREE_INSERTION_MODE_DEFAULT;
|
|
// step 3-4
|
|
return target;
|
|
}
|
|
|
|
// step 3-4
|
|
return adjusted_location;
|
|
}
|
|
|
|
|
|
// stack of template insertion modes
|
|
myhtml_tree_insertion_list_t * myhtml_tree_template_insertion_init(myhtml_tree_t* tree)
|
|
{
|
|
myhtml_tree_insertion_list_t* list = mycore_malloc(sizeof(myhtml_tree_insertion_list_t));
|
|
|
|
list->length = 0;
|
|
list->size = 1024;
|
|
list->list = (enum myhtml_insertion_mode*)mycore_malloc(sizeof(enum myhtml_insertion_mode) * list->size);
|
|
|
|
tree->template_insertion = list;
|
|
|
|
return list;
|
|
}
|
|
|
|
void myhtml_tree_template_insertion_clean(myhtml_tree_t* tree)
|
|
{
|
|
tree->template_insertion->length = 0;
|
|
}
|
|
|
|
myhtml_tree_insertion_list_t * myhtml_tree_template_insertion_destroy(myhtml_tree_t* tree)
|
|
{
|
|
if(tree->template_insertion == NULL)
|
|
return NULL;
|
|
|
|
if(tree->template_insertion->list)
|
|
mycore_free(tree->template_insertion->list);
|
|
|
|
if(tree->template_insertion)
|
|
mycore_free(tree->template_insertion);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void myhtml_tree_template_insertion_append(myhtml_tree_t* tree, enum myhtml_insertion_mode insert_mode)
|
|
{
|
|
myhtml_tree_insertion_list_t* list = tree->template_insertion;
|
|
|
|
if(list->length >= list->size) {
|
|
list->size <<= 1;
|
|
|
|
enum myhtml_insertion_mode* tmp = (enum myhtml_insertion_mode*)mycore_realloc(list->list,
|
|
sizeof(enum myhtml_insertion_mode) * list->size);
|
|
|
|
if(tmp)
|
|
list->list = tmp;
|
|
}
|
|
|
|
list->list[list->length] = insert_mode;
|
|
list->length++;
|
|
}
|
|
|
|
void myhtml_tree_template_insertion_pop(myhtml_tree_t* tree)
|
|
{
|
|
if(tree->template_insertion->length)
|
|
tree->template_insertion->length--;
|
|
|
|
#ifdef DEBUG_MODE
|
|
if(tree->template_insertion->length == 0) {
|
|
MyCORE_DEBUG("Pop template insertion; length is 0");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
size_t myhtml_tree_template_insertion_length(myhtml_tree_t* tree)
|
|
{
|
|
return tree->template_insertion->length;
|
|
}
|
|
|
|
// token list
|
|
myhtml_tree_token_list_t * myhtml_tree_token_list_init(void)
|
|
{
|
|
myhtml_tree_token_list_t* list = mycore_malloc(sizeof(myhtml_tree_token_list_t));
|
|
|
|
list->length = 0;
|
|
list->size = 4096;
|
|
list->list = (myhtml_token_node_t**)mycore_malloc(sizeof(myhtml_token_node_t*) * list->size);
|
|
|
|
return list;
|
|
}
|
|
|
|
void myhtml_tree_token_list_clean(myhtml_tree_token_list_t* list)
|
|
{
|
|
list->length = 0;
|
|
}
|
|
|
|
myhtml_tree_token_list_t * myhtml_tree_token_list_destroy(myhtml_tree_token_list_t* list, bool destroy_self)
|
|
{
|
|
if(list == NULL)
|
|
return NULL;
|
|
|
|
if(list->list)
|
|
mycore_free(list->list);
|
|
|
|
if(destroy_self && list) {
|
|
mycore_free(list);
|
|
return NULL;
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
void myhtml_tree_token_list_append(myhtml_tree_token_list_t* list, myhtml_token_node_t* token)
|
|
{
|
|
if(list->length >= list->size) {
|
|
list->size <<= 1;
|
|
|
|
myhtml_token_node_t** tmp = (myhtml_token_node_t**)mycore_realloc(list->list, sizeof(myhtml_token_node_t*) * list->size);
|
|
|
|
if(tmp)
|
|
list->list = tmp;
|
|
}
|
|
|
|
list->list[list->length] = token;
|
|
list->length++;
|
|
}
|
|
|
|
void myhtml_tree_token_list_append_after_index(myhtml_tree_token_list_t* list, myhtml_token_node_t* token, size_t index)
|
|
{
|
|
if(list->length >= list->size) {
|
|
list->size <<= 1;
|
|
|
|
myhtml_token_node_t** tmp = (myhtml_token_node_t**)mycore_realloc(list->list, sizeof(myhtml_token_node_t*) * list->size);
|
|
|
|
if(tmp)
|
|
list->list = tmp;
|
|
}
|
|
|
|
myhtml_token_node_t** node_list = list->list;
|
|
size_t el_idx = index;
|
|
|
|
while(el_idx > list->length) {
|
|
node_list[(el_idx + 1)] = node_list[el_idx];
|
|
el_idx++;
|
|
}
|
|
|
|
list->list[(index + 1)] = token;
|
|
list->length++;
|
|
}
|
|
|
|
myhtml_token_node_t * myhtml_tree_token_list_current_node(myhtml_tree_token_list_t* list)
|
|
{
|
|
if(list->length == 0) {
|
|
MyCORE_DEBUG("Token list current node; length is 0");
|
|
return NULL;
|
|
}
|
|
|
|
return list->list[ list->length - 1 ];
|
|
}
|
|
|
|
// other
|
|
void myhtml_tree_tags_close_p(myhtml_tree_t* tree, myhtml_token_node_t* token)
|
|
{
|
|
myhtml_tree_generate_implied_end_tags(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML);
|
|
|
|
myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
|
|
if(myhtml_is_html_node(current_node, MyHTML_TAG_P) == false) {
|
|
// parse error
|
|
/* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
|
|
/* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:MyHTML_TAG_P NEED_NS:MyHTML_NAMESPACE_HTML */
|
|
}
|
|
|
|
myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML, false);
|
|
}
|
|
|
|
myhtml_tree_node_t * myhtml_tree_generic_raw_text_element_parsing_algorithm(myhtml_tree_t* tree, myhtml_token_node_t* token_node)
|
|
{
|
|
myhtml_tree_node_t* node = myhtml_tree_node_insert_by_token(tree, token_node, MyHTML_NAMESPACE_HTML);
|
|
|
|
tree->orig_insert_mode = tree->insert_mode;
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_TEXT;
|
|
|
|
return node;
|
|
}
|
|
|
|
void myhtml_tree_clear_stack_back_table_context(myhtml_tree_t* tree)
|
|
{
|
|
myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
|
|
|
|
while(!((current_node->tag_id == MyHTML_TAG_TABLE ||
|
|
current_node->tag_id == MyHTML_TAG_TEMPLATE ||
|
|
current_node->tag_id == MyHTML_TAG_HTML) &&
|
|
current_node->ns == MyHTML_NAMESPACE_HTML))
|
|
{
|
|
myhtml_tree_open_elements_pop(tree);
|
|
current_node = myhtml_tree_current_node(tree);
|
|
}
|
|
}
|
|
|
|
void myhtml_tree_clear_stack_back_table_body_context(myhtml_tree_t* tree)
|
|
{
|
|
myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
|
|
|
|
while(!((current_node->tag_id == MyHTML_TAG_TBODY ||
|
|
current_node->tag_id == MyHTML_TAG_TFOOT ||
|
|
current_node->tag_id == MyHTML_TAG_THEAD ||
|
|
current_node->tag_id == MyHTML_TAG_TEMPLATE ||
|
|
current_node->tag_id == MyHTML_TAG_HTML) &&
|
|
current_node->ns == MyHTML_NAMESPACE_HTML))
|
|
{
|
|
myhtml_tree_open_elements_pop(tree);
|
|
current_node = myhtml_tree_current_node(tree);
|
|
}
|
|
}
|
|
|
|
void myhtml_tree_clear_stack_back_table_row_context(myhtml_tree_t* tree)
|
|
{
|
|
myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
|
|
|
|
while(!((current_node->tag_id == MyHTML_TAG_TR ||
|
|
current_node->tag_id == MyHTML_TAG_TEMPLATE ||
|
|
current_node->tag_id == MyHTML_TAG_HTML) &&
|
|
current_node->ns == MyHTML_NAMESPACE_HTML))
|
|
{
|
|
myhtml_tree_open_elements_pop(tree);
|
|
current_node = myhtml_tree_current_node(tree);
|
|
}
|
|
}
|
|
|
|
void myhtml_tree_close_cell(myhtml_tree_t* tree, myhtml_tree_node_t* tr_or_th_node, myhtml_token_node_t* token)
|
|
{
|
|
// step 1
|
|
myhtml_tree_generate_implied_end_tags(tree, 0, MyHTML_NAMESPACE_UNDEF);
|
|
|
|
// step 2
|
|
myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
|
|
if(!((current_node->tag_id == MyHTML_TAG_TD ||
|
|
current_node->tag_id == MyHTML_TAG_TH) &&
|
|
current_node->ns == MyHTML_NAMESPACE_HTML))
|
|
{
|
|
// parse error
|
|
/* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_OPEN_NOT_FOUND LEVEL:ERROR */
|
|
/* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:NULL NEED:NULL HAVE_TAG_ID:MyHTML_TAG__UNDEF HAVE_NS:MyHTML_NAMESPACE_UNDEF NEED_TAG_ID:MyHTML_TAG_TD NEED_NS:MyHTML_NAMESPACE_HTML */
|
|
/* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:NULL NEED:NULL HAVE_TAG_ID:MyHTML_TAG__UNDEF HAVE_NS:MyHTML_NAMESPACE_UNDEF NEED_TAG_ID:MyHTML_TAG_TH NEED_NS:MyHTML_NAMESPACE_HTML */
|
|
}
|
|
|
|
// step 3
|
|
myhtml_tree_open_elements_pop_until(tree, tr_or_th_node->tag_id, tr_or_th_node->ns, false);
|
|
|
|
// step 4
|
|
myhtml_tree_active_formatting_up_to_last_marker(tree);
|
|
|
|
// step 5
|
|
tree->insert_mode = MyHTML_INSERTION_MODE_IN_ROW;
|
|
}
|
|
|
|
bool myhtml_tree_is_mathml_integration_point(myhtml_tree_t* tree, myhtml_tree_node_t* node)
|
|
{
|
|
if(node->ns == MyHTML_NAMESPACE_MATHML &&
|
|
(node->tag_id == MyHTML_TAG_MI ||
|
|
node->tag_id == MyHTML_TAG_MO ||
|
|
node->tag_id == MyHTML_TAG_MN ||
|
|
node->tag_id == MyHTML_TAG_MS ||
|
|
node->tag_id == MyHTML_TAG_MTEXT)
|
|
)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool myhtml_tree_is_html_integration_point(myhtml_tree_t* tree, myhtml_tree_node_t* node)
|
|
{
|
|
if(node->ns == MyHTML_NAMESPACE_SVG &&
|
|
(node->tag_id == MyHTML_TAG_FOREIGNOBJECT ||
|
|
node->tag_id == MyHTML_TAG_DESC ||
|
|
node->tag_id == MyHTML_TAG_TITLE)
|
|
)
|
|
return true;
|
|
|
|
if(node->ns == MyHTML_NAMESPACE_MATHML &&
|
|
node->tag_id == MyHTML_TAG_ANNOTATION_XML && node->token &&
|
|
(node->token->type & MyHTML_TOKEN_TYPE_CLOSE) == 0)
|
|
{
|
|
myhtml_token_node_wait_for_done(tree->token, node->token);
|
|
|
|
myhtml_token_attr_t* attr = myhtml_token_attr_match_case(tree->token, node->token,
|
|
"encoding", 8, "text/html", 9);
|
|
if(attr)
|
|
return true;
|
|
|
|
attr = myhtml_token_attr_match_case(tree->token, node->token,
|
|
"encoding", 8, "application/xhtml+xml", 21);
|
|
if(attr)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// temp tag name
|
|
mystatus_t myhtml_tree_temp_tag_name_init(myhtml_tree_temp_tag_name_t* temp_tag_name)
|
|
{
|
|
temp_tag_name->size = 1024;
|
|
temp_tag_name->length = 0;
|
|
temp_tag_name->data = (char *)mycore_malloc(temp_tag_name->size * sizeof(char));
|
|
|
|
if(temp_tag_name->data == NULL)
|
|
return MyHTML_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
|
|
return MyHTML_STATUS_OK;
|
|
}
|
|
|
|
void myhtml_tree_temp_tag_name_clean(myhtml_tree_temp_tag_name_t* temp_tag_name)
|
|
{
|
|
temp_tag_name->length = 0;
|
|
}
|
|
|
|
myhtml_tree_temp_tag_name_t * myhtml_tree_temp_tag_name_destroy(myhtml_tree_temp_tag_name_t* temp_tag_name, bool self_destroy)
|
|
{
|
|
if(temp_tag_name == NULL)
|
|
return NULL;
|
|
|
|
if(temp_tag_name->data) {
|
|
mycore_free(temp_tag_name->data);
|
|
temp_tag_name->data = NULL;
|
|
}
|
|
|
|
if(self_destroy) {
|
|
mycore_free(temp_tag_name);
|
|
return NULL;
|
|
}
|
|
|
|
return temp_tag_name;
|
|
}
|
|
|
|
mystatus_t myhtml_tree_temp_tag_name_append_one(myhtml_tree_temp_tag_name_t* temp_tag_name, const char name)
|
|
{
|
|
if(temp_tag_name->length >= temp_tag_name->size) {
|
|
size_t nsize = temp_tag_name->size << 1;
|
|
char *tmp = (char *)mycore_realloc(temp_tag_name->data, nsize * sizeof(char));
|
|
|
|
if(tmp) {
|
|
temp_tag_name->size = nsize;
|
|
temp_tag_name->data = tmp;
|
|
}
|
|
else
|
|
return MyHTML_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
}
|
|
|
|
temp_tag_name->data[temp_tag_name->length] = name;
|
|
|
|
temp_tag_name->length++;
|
|
return MyHTML_STATUS_OK;
|
|
}
|
|
|
|
mystatus_t myhtml_tree_temp_tag_name_append(myhtml_tree_temp_tag_name_t* temp_tag_name, const char* name, size_t name_len)
|
|
{
|
|
if(temp_tag_name->data == NULL || name_len == 0)
|
|
return MyHTML_STATUS_OK;
|
|
|
|
if((temp_tag_name->length + name_len) >= temp_tag_name->size) {
|
|
size_t nsize = (temp_tag_name->size << 1) + name_len;
|
|
char *tmp = (char *)mycore_realloc(temp_tag_name->data, nsize * sizeof(char));
|
|
|
|
if(tmp) {
|
|
temp_tag_name->size = nsize;
|
|
temp_tag_name->data = tmp;
|
|
}
|
|
else
|
|
return MyHTML_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
}
|
|
|
|
memcpy(&temp_tag_name->data[temp_tag_name->length], name, name_len);
|
|
|
|
temp_tag_name->length += name_len;
|
|
return MyHTML_STATUS_OK;
|
|
}
|
|
|
|
void myhtml_tree_wait_for_last_done_token(myhtml_tree_t* tree, myhtml_token_node_t* token_for_wait)
|
|
{
|
|
#ifndef MyCORE_BUILD_WITHOUT_THREADS
|
|
|
|
while(tree->token_last_done != token_for_wait) {mythread_nanosleep_sleep(tree->myhtml->thread_stream->timespec);}
|
|
|
|
#endif
|
|
}
|
|
|
|
/* special tonek list */
|
|
mystatus_t myhtml_tree_special_list_init(myhtml_tree_special_token_list_t* special)
|
|
{
|
|
special->size = 1024;
|
|
special->length = 0;
|
|
special->list = (myhtml_tree_special_token_t *)mycore_malloc(special->size * sizeof(myhtml_tree_special_token_t));
|
|
|
|
if(special->list == NULL)
|
|
return MyHTML_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
|
|
return MyHTML_STATUS_OK;
|
|
}
|
|
|
|
void myhtml_tree_special_list_clean(myhtml_tree_temp_tag_name_t* temp_tag_name)
|
|
{
|
|
temp_tag_name->length = 0;
|
|
}
|
|
|
|
myhtml_tree_special_token_list_t * myhtml_tree_special_list_destroy(myhtml_tree_special_token_list_t* special, bool self_destroy)
|
|
{
|
|
if(special == NULL)
|
|
return NULL;
|
|
|
|
if(special->list) {
|
|
mycore_free(special->list);
|
|
special->list = NULL;
|
|
}
|
|
|
|
if(self_destroy) {
|
|
mycore_free(special);
|
|
return NULL;
|
|
}
|
|
|
|
return special;
|
|
}
|
|
|
|
mystatus_t myhtml_tree_special_list_append(myhtml_tree_special_token_list_t* special, myhtml_token_node_t *token, myhtml_namespace_t ns)
|
|
{
|
|
if(special->length >= special->size) {
|
|
size_t nsize = special->size << 1;
|
|
myhtml_tree_special_token_t *tmp = (myhtml_tree_special_token_t *)mycore_realloc(special->list, nsize * sizeof(myhtml_tree_special_token_t));
|
|
|
|
if(tmp) {
|
|
special->size = nsize;
|
|
special->list = tmp;
|
|
}
|
|
else
|
|
return MyHTML_STATUS_ERROR_MEMORY_ALLOCATION;
|
|
}
|
|
|
|
special->list[special->length].ns = ns;
|
|
special->list[special->length].token = token;
|
|
|
|
special->length++;
|
|
return MyHTML_STATUS_OK;
|
|
}
|
|
|
|
size_t myhtml_tree_special_list_length(myhtml_tree_special_token_list_t* special)
|
|
{
|
|
return special->length;
|
|
}
|
|
|
|
size_t myhtml_tree_special_list_pop(myhtml_tree_special_token_list_t* special)
|
|
{
|
|
if(special->length)
|
|
special->length--;
|
|
|
|
return special->length;
|
|
}
|
|
|
|
|
|
myhtml_tree_special_token_t * myhtml_tree_special_list_get_last(myhtml_tree_special_token_list_t* special)
|
|
{
|
|
if(special->length == 0)
|
|
return NULL;
|
|
|
|
return &special->list[special->length];
|
|
}
|
|
|
|
/* incoming buffer */
|
|
mycore_incoming_buffer_t * myhtml_tree_incoming_buffer_first(myhtml_tree_t *tree)
|
|
{
|
|
return tree->incoming_buf_first;
|
|
}
|
|
|
|
const char * myhtml_tree_incomming_buffer_make_data(myhtml_tree_t *tree, size_t begin, size_t length)
|
|
{
|
|
mycore_incoming_buffer_t *buffer = mycore_incoming_buffer_find_by_position(tree->incoming_buf_first, begin);
|
|
size_t relative_begin = begin - buffer->offset;
|
|
|
|
if((relative_begin + length) <= buffer->size) {
|
|
return &buffer->data[ relative_begin ];
|
|
}
|
|
|
|
if(tree->temp_tag_name.data == NULL)
|
|
myhtml_tree_temp_tag_name_init(&tree->temp_tag_name);
|
|
else
|
|
myhtml_tree_temp_tag_name_clean(&tree->temp_tag_name);
|
|
|
|
while(buffer) {
|
|
if((relative_begin + length) > buffer->size)
|
|
{
|
|
size_t relative_end = (buffer->size - relative_begin);
|
|
length -= relative_end;
|
|
|
|
myhtml_tree_temp_tag_name_append(&tree->temp_tag_name, &buffer->data[relative_begin], relative_end);
|
|
|
|
relative_begin = 0;
|
|
buffer = buffer->next;
|
|
}
|
|
else {
|
|
myhtml_tree_temp_tag_name_append(&tree->temp_tag_name, &buffer->data[relative_begin], length);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return tree->temp_tag_name.data;
|
|
}
|
|
|
|
|