Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * This file is part of libdom.
  3.  * Licensed under the MIT License,
  4.  *                http://www.opensource.org/licenses/mit-license.php
  5.  * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
  6.  * Copyright 2009 Bo Yang <struggleyb.nku@gmail.com>
  7.  */
  8.  
  9. #include <assert.h>
  10. #include <stdlib.h>
  11.  
  12. #include <dom/core/string.h>
  13. #include <dom/core/text.h>
  14.  
  15. #include <libwapcaplet/libwapcaplet.h>
  16.  
  17. #include "core/characterdata.h"
  18. #include "core/document.h"
  19. #include "core/text.h"
  20. #include "utils/utils.h"
  21.  
  22. /* The virtual table for dom_text */
  23. struct dom_text_vtable text_vtable = {
  24.         {
  25.                 {
  26.                         {
  27.                                 DOM_NODE_EVENT_TARGET_VTABLE,
  28.                         },
  29.                         DOM_NODE_VTABLE_CHARACTERDATA
  30.                 },
  31.                 DOM_CHARACTERDATA_VTABLE
  32.         },
  33.         DOM_TEXT_VTABLE
  34. };
  35.  
  36. static struct dom_node_protect_vtable text_protect_vtable = {
  37.         DOM_TEXT_PROTECT_VTABLE
  38. };
  39.  
  40. /* Following comes helper functions */
  41. typedef enum walk_operation {
  42.         COLLECT,
  43.         DELETE
  44. } walk_operation;
  45. typedef enum walk_order {
  46.         LEFT,
  47.         RIGHT
  48. } walk_order;
  49.  
  50. /* Walk the logic-adjacent text in document order */
  51. static dom_exception walk_logic_adjacent_text_in_order(
  52.                 dom_node_internal *node, walk_operation opt,
  53.                 walk_order order, dom_string **ret, bool *cont);
  54. /* Walk the logic-adjacent text */
  55. static dom_exception walk_logic_adjacent_text(dom_text *text,
  56.                 walk_operation opt, dom_string **ret);
  57.        
  58. /*----------------------------------------------------------------------*/
  59. /* Constructor and Destructor */
  60.  
  61. /**
  62.  * Create a text node
  63.  *
  64.  * \param doc     The owning document
  65.  * \param name    The name of the node to create
  66.  * \param value   The text content of the node
  67.  * \param result  Pointer to location to receive created node
  68.  * \return DOM_NO_ERR                on success,
  69.  *         DOM_NO_MEM_ERR            on memory exhaustion.
  70.  *
  71.  * ::doc, ::name and ::value will have their reference counts increased.
  72.  *
  73.  * The returned node will already be referenced.
  74.  */
  75. dom_exception _dom_text_create(dom_document *doc,
  76.                 dom_string *name, dom_string *value,
  77.                 dom_text **result)
  78. {
  79.         dom_text *t;
  80.         dom_exception err;
  81.  
  82.         /* Allocate the text node */
  83.         t = malloc(sizeof(dom_text));
  84.         if (t == NULL)
  85.                 return DOM_NO_MEM_ERR;
  86.  
  87.         /* And initialise the node */
  88.         err = _dom_text_initialise(t, doc, DOM_TEXT_NODE, name, value);
  89.         if (err != DOM_NO_ERR) {
  90.                 free(t);
  91.                 return err;
  92.         }
  93.  
  94.         /* Compose the vtable */
  95.         ((dom_node *) t)->vtable = &text_vtable;
  96.         ((dom_node_internal *) t)->vtable = &text_protect_vtable;
  97.  
  98.         *result = t;
  99.  
  100.         return DOM_NO_ERR;
  101. }
  102.  
  103. /**
  104.  * Destroy a text node
  105.  *
  106.  * \param doc   The owning document
  107.  * \param text  The text node to destroy
  108.  *
  109.  * The contents of ::text will be destroyed and ::text will be freed.
  110.  */
  111. void _dom_text_destroy(dom_text *text)
  112. {
  113.         /* Finalise node */
  114.         _dom_text_finalise(text);
  115.  
  116.         /* Free node */
  117.         free(text);
  118. }
  119.  
  120. /**
  121.  * Initialise a text node
  122.  *
  123.  * \param text   The node to initialise
  124.  * \param doc    The owning document
  125.  * \param type   The type of the node
  126.  * \param name   The name of the node to create
  127.  * \param value  The text content of the node
  128.  * \return DOM_NO_ERR on success.
  129.  *
  130.  * ::doc, ::name and ::value will have their reference counts increased.
  131.  */
  132. dom_exception _dom_text_initialise(dom_text *text,
  133.                 dom_document *doc, dom_node_type type,
  134.                 dom_string *name, dom_string *value)
  135. {
  136.         dom_exception err;
  137.  
  138.         /* Initialise the base class */
  139.         err = _dom_characterdata_initialise(&text->base, doc, type,
  140.                         name, value);
  141.         if (err != DOM_NO_ERR)
  142.                 return err;
  143.  
  144.         /* Perform our type-specific initialisation */
  145.         text->element_content_whitespace = false;
  146.  
  147.         return DOM_NO_ERR;
  148. }
  149.  
  150. /**
  151.  * Finalise a text node
  152.  *
  153.  * \param doc   The owning document
  154.  * \param text  The text node to finalise
  155.  *
  156.  * The contents of ::text will be cleaned up. ::text will not be freed.
  157.  */
  158. void _dom_text_finalise(dom_text *text)
  159. {
  160.         _dom_characterdata_finalise(&text->base);
  161. }
  162.  
  163. /*----------------------------------------------------------------------*/
  164. /* The public virtual functions */
  165.  
  166. /**
  167.  * Split a text node at a given character offset
  168.  *
  169.  * \param text  The node to split
  170.  * \param offset  Character offset to split at
  171.  * \param result  Pointer to location to receive new node
  172.  * \return DOM_NO_ERR                      on success,
  173.  *         DOM_INDEX_SIZE_ERR              if ::offset is greater than the
  174.  *                                         number of characters in ::text,
  175.  *         DOM_NO_MODIFICATION_ALLOWED_ERR if ::text is readonly.
  176.  *
  177.  * The returned node will be referenced. The client should unref the node
  178.  * once it has finished with it.
  179.  */
  180. dom_exception _dom_text_split_text(dom_text *text,
  181.                 uint32_t offset, dom_text **result)
  182. {
  183.         dom_node_internal *t = (dom_node_internal *) text;
  184.         dom_string *value;
  185.         dom_text *res;
  186.         uint32_t len;
  187.         dom_exception err;
  188.  
  189.         if (_dom_node_readonly(t)) {
  190.                 return DOM_NO_MODIFICATION_ALLOWED_ERR;
  191.         }
  192.  
  193.         err = dom_characterdata_get_length(&text->base, &len);
  194.         if (err != DOM_NO_ERR) {
  195.                 return err;
  196.         }
  197.  
  198.         if (offset >= len) {
  199.                 return DOM_INDEX_SIZE_ERR;
  200.         }
  201.  
  202.         /* Retrieve the data after the split point --
  203.          * this will be the new node's value. */
  204.         err = dom_characterdata_substring_data(&text->base, offset,
  205.                         len - offset, &value);
  206.         if (err != DOM_NO_ERR) {
  207.                 return err;
  208.         }
  209.  
  210.         /* Create new node */
  211.         err = _dom_text_create(t->owner, t->name, value, &res);
  212.         if (err != DOM_NO_ERR) {
  213.                 dom_string_unref(value);
  214.                 return err;
  215.         }
  216.  
  217.         /* Release our reference on the value (the new node has its own) */
  218.         dom_string_unref(value);
  219.  
  220.         /* Delete the data after the split point */
  221.         err = dom_characterdata_delete_data(&text->base, offset, len - offset);
  222.         if (err != DOM_NO_ERR) {
  223.                 dom_node_unref(res);
  224.                 return err;
  225.         }
  226.  
  227.         *result = res;
  228.  
  229.         return DOM_NO_ERR;
  230. }
  231.  
  232. /**
  233.  * Determine if a text node contains element content whitespace
  234.  *
  235.  * \param text    The node to consider
  236.  * \param result  Pointer to location to receive result
  237.  * \return DOM_NO_ERR.
  238.  */
  239. dom_exception _dom_text_get_is_element_content_whitespace(
  240.                 dom_text *text, bool *result)
  241. {
  242.         *result = text->element_content_whitespace;
  243.  
  244.         return DOM_NO_ERR;
  245. }
  246.  
  247. /**
  248.  * Retrieve all text in Text nodes logically adjacent to a Text node
  249.  *
  250.  * \param text    Text node to consider
  251.  * \param result  Pointer to location to receive result
  252.  * \return DOM_NO_ERR.
  253.  */
  254. dom_exception _dom_text_get_whole_text(dom_text *text,
  255.                 dom_string **result)
  256. {
  257.         return walk_logic_adjacent_text(text, COLLECT, result);
  258. }
  259.  
  260. /**
  261.  * Replace the text of a Text node and all logically adjacent Text nodes
  262.  *
  263.  * \param text     Text node to consider
  264.  * \param content  Replacement content
  265.  * \param result   Pointer to location to receive Text node
  266.  * \return DOM_NO_ERR                      on success,
  267.  *         DOM_NO_MODIFICATION_ALLOWED_ERR if one of the Text nodes being
  268.  *                                         replaced is readonly.
  269.  *
  270.  * The returned node will be referenced. The client should unref the node
  271.  * once it has finished with it.
  272.  */
  273. dom_exception _dom_text_replace_whole_text(dom_text *text,
  274.                 dom_string *content, dom_text **result)
  275. {
  276.         dom_exception err;
  277.         dom_string *ret;
  278.  
  279.         err = walk_logic_adjacent_text(text, DELETE, &ret);
  280.         if (err != DOM_NO_ERR)
  281.                 return err;
  282.        
  283.         err = dom_characterdata_set_data(text, content);
  284.         if (err != DOM_NO_ERR)
  285.                 return err;
  286.        
  287.         *result = text;
  288.         dom_node_ref(text);
  289.  
  290.         return DOM_NO_ERR;
  291. }
  292.  
  293. /*-----------------------------------------------------------------------*/
  294. /* The protected virtual functions */
  295.  
  296. /* The destroy function of this class */
  297. void __dom_text_destroy(dom_node_internal *node)
  298. {
  299.         _dom_text_destroy((dom_text *) node);
  300. }
  301.  
  302. /* The copy constructor of this class */
  303. dom_exception _dom_text_copy(dom_node_internal *old, dom_node_internal **copy)
  304. {
  305.         dom_text *new_text;
  306.         dom_exception err;
  307.  
  308.         new_text = malloc(sizeof(dom_text));
  309.         if (new_text == NULL)
  310.                 return DOM_NO_MEM_ERR;
  311.  
  312.         err = dom_text_copy_internal(old, new_text);
  313.         if (err != DOM_NO_ERR) {
  314.                 free(new_text);
  315.                 return err;
  316.         }
  317.  
  318.         *copy = (dom_node_internal *) new_text;
  319.  
  320.         return DOM_NO_ERR;
  321. }
  322.  
  323. dom_exception _dom_text_copy_internal(dom_text *old, dom_text *new)
  324. {
  325.         dom_exception err;
  326.  
  327.         err = dom_characterdata_copy_internal(old, new);
  328.         if (err != DOM_NO_ERR)
  329.                 return err;
  330.  
  331.         new->element_content_whitespace = old->element_content_whitespace;
  332.  
  333.         return DOM_NO_ERR;
  334. }
  335.  
  336. /*----------------------------------------------------------------------*/
  337. /* Helper functions */
  338.  
  339. /**
  340.  * Walk the logic adjacent text in certain order
  341.  *
  342.  * \param node   The start Text node
  343.  * \param opt    The operation on each Text Node
  344.  * \param order  The order
  345.  * \param ret    The string of the logic adjacent text
  346.  * \param cont   Whether the logic adjacent text is interrupt here
  347.  * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
  348.  */
  349. dom_exception walk_logic_adjacent_text_in_order(
  350.                 dom_node_internal *node, walk_operation opt,
  351.                 walk_order order, dom_string **ret, bool *cont)
  352. {
  353.         dom_exception err;
  354.         dom_string *data, *tmp;
  355.         dom_node_internal *parent;
  356.  
  357.         /* If we reach the leaf of the DOM tree, just return to continue
  358.          * to next sibling of our parent */
  359.         if (node == NULL) {
  360.                 *cont = true;
  361.                 return DOM_NO_ERR;
  362.         }
  363.  
  364.         parent = dom_node_get_parent(node);
  365.  
  366.         while (node != NULL) {
  367.                 dom_node_internal *p;
  368.  
  369.                 /* If we reach the boundary of logical-adjacent text, we stop */
  370.                 if (node->type == DOM_ELEMENT_NODE ||
  371.                                 node->type == DOM_COMMENT_NODE ||
  372.                                 node->type ==
  373.                                 DOM_PROCESSING_INSTRUCTION_NODE) {
  374.                         *cont = false;
  375.                         return DOM_NO_ERR;
  376.                 }
  377.  
  378.                 if (node->type == DOM_TEXT_NODE) {
  379.                         /* According the DOM spec, text node never have child */
  380.                         assert(node->first_child == NULL);
  381.                         assert(node->last_child == NULL);
  382.                         if (opt == COLLECT) {
  383.                                 err = dom_characterdata_get_data(node, &data);
  384.                                 if (err == DOM_NO_ERR)
  385.                                         return err;
  386.  
  387.                                 tmp = *ret;
  388.                                 if (order == LEFT) {
  389.                                         err = dom_string_concat(data, tmp, ret);
  390.                                         if (err == DOM_NO_ERR)
  391.                                                 return err;
  392.                                 } else if (order == RIGHT) {
  393.                                         err = dom_string_concat(tmp, data, ret);
  394.                                         if (err == DOM_NO_ERR)
  395.                                                 return err;
  396.                                 }
  397.  
  398.                                 dom_string_unref(tmp);
  399.                                 dom_string_unref(data);
  400.  
  401.                                 *cont = true;
  402.                                 return DOM_NO_ERR;
  403.                         }
  404.  
  405.                         if (opt == DELETE) {
  406.                                 dom_node_internal *tn;
  407.                                 err = dom_node_remove_child(node->parent,
  408.                                                 node, (void *) &tn);
  409.                                 if (err != DOM_NO_ERR)
  410.                                         return err;
  411.  
  412.                                 *cont = true;
  413.                                 dom_node_unref(tn);
  414.                                 return DOM_NO_ERR;
  415.                         }
  416.                 }
  417.  
  418.                 p = dom_node_get_parent(node);
  419.                 if (order == LEFT) {
  420.                         if (node->last_child != NULL) {
  421.                                 node = node->last_child;
  422.                         } else if (node->previous != NULL) {
  423.                                 node = node->previous;
  424.                         } else {
  425.                                 while (p != parent && node == p->last_child) {
  426.                                         node = p;
  427.                                         p = dom_node_get_parent(p);
  428.                                 }
  429.  
  430.                                 node = node->previous;
  431.                         }
  432.                 } else {
  433.                         if (node->first_child != NULL) {
  434.                                 node = node->first_child;
  435.                         } else if (node->next != NULL) {
  436.                                 node = node->next;
  437.                         } else {
  438.                                 while (p != parent && node == p->first_child) {
  439.                                         node = p;
  440.                                         p = dom_node_get_parent(p);
  441.                                 }
  442.  
  443.                                 node = node->next;
  444.                         }
  445.                 }
  446.         }
  447.  
  448.         return DOM_NO_ERR;
  449. }
  450.  
  451. /**
  452.  * Traverse the logic adjacent text.
  453.  *
  454.  * \param text  The Text Node from which we start traversal
  455.  * \param opt   The operation code
  456.  * \param ret   The returned string if the opt is COLLECT
  457.  * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
  458.  */
  459. dom_exception walk_logic_adjacent_text(dom_text *text,
  460.                 walk_operation opt, dom_string **ret)
  461. {
  462.         dom_node_internal *node = (dom_node_internal *) text;
  463.         dom_node_internal *parent = node->parent;
  464.         dom_node_internal *left = node->previous;
  465.         dom_node_internal *right = node->next;
  466.         dom_exception err;
  467.         bool cont;
  468.        
  469.         if (parent->type == DOM_ENTITY_NODE) {
  470.                 return DOM_NOT_SUPPORTED_ERR;
  471.         }
  472.  
  473.         *ret = NULL;
  474.  
  475.         /* Firstly, we look our left */
  476.         err = walk_logic_adjacent_text_in_order(left, opt, LEFT, ret, &cont);
  477.         if (err != DOM_NO_ERR) {
  478.                 if (opt == COLLECT) {
  479.                         dom_string_unref(*ret);
  480.                         *ret = NULL;
  481.                 }
  482.                 return err;
  483.         }
  484.  
  485.         /* Ourself */
  486.         if (opt == COLLECT) {
  487.                 dom_string *data = NULL, *tmp = NULL;
  488.                 err = dom_characterdata_get_data(text, &data);
  489.                 if (err == DOM_NO_ERR) {
  490.                         dom_string_unref(*ret);
  491.                         return err;
  492.                 }
  493.  
  494.                 err = dom_string_concat(*ret, data, &tmp);
  495.                 if (err == DOM_NO_ERR) {
  496.                         dom_string_unref(*ret);
  497.                         return err;
  498.                 }
  499.  
  500.                 dom_string_unref(*ret);
  501.                 dom_string_unref(data);
  502.                 *ret = tmp;
  503.         } else {
  504.                         dom_node_internal *tn;
  505.                         err = dom_node_remove_child(node->parent, node,
  506.                                         (void *) &tn);
  507.                         if (err != DOM_NO_ERR)
  508.                                 return err;
  509.                         dom_node_unref(tn);
  510.         }
  511.  
  512.         /* Now, look right */
  513.         err = walk_logic_adjacent_text_in_order(right, opt, RIGHT, ret, &cont);
  514.         if (err != DOM_NO_ERR) {
  515.                 if (opt == COLLECT) {
  516.                         dom_string_unref(*ret);
  517.                         *ret = NULL;
  518.                 }
  519.                 return err;
  520.         }
  521.  
  522.         return DOM_NO_ERR;
  523. }
  524.  
  525.