/** * Reconstruct the list of active formatting elements * * \param treebuilder Treebuilder instance containing list * \return HUBBUB_OK on success, appropriate error otherwise. */ hubbub_error reconstruct_active_formatting_list(hubbub_treebuilder *treebuilder) { hubbub_error error = HUBBUB_OK; formatting_list_entry *entry, *initial_entry; uint32_t sp = treebuilder->context.current_node; if (treebuilder->context.formatting_list == NULL) return HUBBUB_OK; entry = treebuilder->context.formatting_list_end; /* Assumption: HTML and TABLE elements are not inserted into the list */ if (is_scoping_element(entry->details.type) || entry->stack_index != 0) return HUBBUB_OK; while (entry->prev != NULL) { entry = entry->prev; if (is_scoping_element(entry->details.type) || entry->stack_index != 0) { entry = entry->next; break; } } /* Save initial entry for later */ initial_entry = entry; /* Process formatting list entries, cloning nodes and * inserting them into the DOM and element stack */ while (entry != NULL) { void *clone, *appended; bool foster; element_type type = current_node(treebuilder); error = treebuilder->tree_handler->clone_node( treebuilder->tree_handler->ctx, entry->details.node, false, &clone); if (error != HUBBUB_OK) goto cleanup; foster = treebuilder->context.in_table_foster && (type == TABLE || type == TBODY || type == TFOOT || type == THEAD || type == TR); if (foster) { error = aa_insert_into_foster_parent(treebuilder, clone, &appended); } else { error = treebuilder->tree_handler->append_child( treebuilder->tree_handler->ctx, treebuilder->context.element_stack[ treebuilder->context.current_node].node, clone, &appended); } if (error != HUBBUB_OK) goto cleanup; error = element_stack_push(treebuilder, entry->details.ns, entry->details.type, appended); if (error != HUBBUB_OK) { remove_node_from_dom(treebuilder, appended); goto cleanup; } entry = entry->next; } /* Now, replace the formatting list entries */ for (entry = initial_entry; entry != NULL; entry = entry->next) { void *node; hubbub_ns prev_ns; element_type prev_type; void *prev_node; uint32_t prev_stack_index; node = treebuilder->context.element_stack[++sp].node; error = formatting_list_replace(treebuilder, entry, entry->details.ns, entry->details.type, node, sp, &prev_ns, &prev_type, &prev_node, &prev_stack_index); /* Cannot fail. Ensure this. */ assert(error == HUBBUB_OK); } return HUBBUB_OK; cleanup: /* An error occurred while cloning nodes and inserting them. * We must restore the state on entry here. */ while (treebuilder->context.current_node > sp) { hubbub_ns ns; element_type type; void *node; element_stack_pop(treebuilder, &ns, &type, &node); remove_node_from_dom(treebuilder, node); } return error; }
/** * Create element and insert it into the DOM, * potentially pushing it on the stack * * \param treebuilder The treebuilder instance * \param tag The element to insert * \param push Whether to push the element onto the stack * \return HUBBUB_OK on success, appropriate error otherwise. */ hubbub_error insert_element(hubbub_treebuilder *treebuilder, const hubbub_tag *tag, bool push) { element_type type = current_node(treebuilder); hubbub_error error; void *appended; if (treebuilder->context.in_table_foster && (type == TABLE || type == TBODY || type == TFOOT || type == THEAD || type == TR)) { error = aa_insert_into_foster_parent_new(treebuilder, HUBBUB_TOKEN_START_TAG, &appended); } else { error = treebuilder->tree_handler->append_child_new( treebuilder->tree_handler->ctx, treebuilder->context.element_stack[ treebuilder->context.current_node].node, HUBBUB_TOKEN_START_TAG, &appended); } if (error != HUBBUB_OK) return error; error = treebuilder->tree_handler->set_name( treebuilder->tree_handler->ctx, appended, &tag->name); if (error != HUBBUB_OK) return error; error = treebuilder->tree_handler->add_attributes( treebuilder->tree_handler->ctx, appended, tag->attributes, tag->n_attributes); if (error != HUBBUB_OK) return error; type = element_type_from_name(treebuilder, &tag->name); if (treebuilder->context.form_element != NULL && is_form_associated(type)) { /* Consideration of @form is left to the client */ error = treebuilder->tree_handler->form_associate( treebuilder->tree_handler->ctx, treebuilder->context.form_element, appended); if (error != HUBBUB_OK) { remove_node_from_dom(treebuilder, appended); return error; } } if (push) { error = element_stack_push(treebuilder, tag->ns, type, appended); if (error != HUBBUB_OK) { remove_node_from_dom(treebuilder, appended); return error; } } return HUBBUB_OK; }
/** * Handle tokens in "in row" insertion mode * * \param treebuilder The treebuilder instance * \param token The token to process * \return True to reprocess the token, false otherwise */ hubbub_error handle_in_row(hubbub_treebuilder *treebuilder, const hubbub_token *token) { hubbub_error err = HUBBUB_OK; switch (token->type) { case HUBBUB_TOKEN_START_TAG: { element_type type = element_type_from_name(treebuilder, &token->data.tag.name); if (type == TH || type == TD) { table_clear_stack(treebuilder); err = insert_element(treebuilder, &token->data.tag, true); if (err != HUBBUB_OK) return err; treebuilder->context.mode = IN_CELL; /* ref node for formatting list */ treebuilder->tree_handler->ref_node( treebuilder->tree_handler->ctx, treebuilder->context.element_stack[ treebuilder->context.current_node].node); err = formatting_list_append(treebuilder, token->data.tag.ns, type, treebuilder->context.element_stack[ treebuilder->context.current_node].node, treebuilder->context.current_node); if (err != HUBBUB_OK) { hubbub_ns ns; element_type type; void *node; /* Revert changes */ remove_node_from_dom(treebuilder, treebuilder->context.element_stack[ treebuilder->context.current_node].node); element_stack_pop(treebuilder, &ns, &type, &node); return err; } } else if (type == CAPTION || type == COL || type == COLGROUP || type == TBODY || type == TFOOT || type == THEAD || type == TR) { err = act_as_if_end_tag_tr(treebuilder); } else { err = handle_in_table(treebuilder, token); } } break; case HUBBUB_TOKEN_END_TAG: { element_type type = element_type_from_name(treebuilder, &token->data.tag.name); if (type == TR) { /* We're done with this token, but act_as_if_end_tag_tr * will return HUBBUB_REPROCESS. Therefore, ignore the * return value. */ (void) act_as_if_end_tag_tr(treebuilder); } else if (type == TABLE) { err = act_as_if_end_tag_tr(treebuilder); } else if (type == BODY || type == CAPTION || type == COL || type == COLGROUP || type == HTML || type == TD || type == TH) { /** \todo parse error */ /* Ignore the token */ } else { err = handle_in_table(treebuilder, token); } } break; case HUBBUB_TOKEN_CHARACTER: case HUBBUB_TOKEN_COMMENT: case HUBBUB_TOKEN_DOCTYPE: case HUBBUB_TOKEN_EOF: err = handle_in_table(treebuilder, token); break; } return err; }