nokogiri 1.10.10 → 1.14.3-aarch64-linux

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of nokogiri might be problematic. Click here for more details.

Files changed (251) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +44 -0
  3. data/LICENSE-DEPENDENCIES.md +1632 -1022
  4. data/LICENSE.md +1 -1
  5. data/README.md +185 -96
  6. data/bin/nokogiri +63 -50
  7. data/dependencies.yml +33 -66
  8. data/ext/nokogiri/depend +38 -358
  9. data/ext/nokogiri/extconf.rb +819 -421
  10. data/ext/nokogiri/gumbo.c +594 -0
  11. data/ext/nokogiri/html4_document.c +166 -0
  12. data/ext/nokogiri/html4_element_description.c +294 -0
  13. data/ext/nokogiri/html4_entity_lookup.c +37 -0
  14. data/ext/nokogiri/html4_sax_parser_context.c +114 -0
  15. data/ext/nokogiri/html4_sax_push_parser.c +95 -0
  16. data/ext/nokogiri/include/libexslt/exslt.h +108 -0
  17. data/ext/nokogiri/include/libexslt/exsltconfig.h +70 -0
  18. data/ext/nokogiri/include/libexslt/exsltexports.h +63 -0
  19. data/ext/nokogiri/include/libxml2/libxml/HTMLparser.h +306 -0
  20. data/ext/nokogiri/include/libxml2/libxml/HTMLtree.h +147 -0
  21. data/ext/nokogiri/include/libxml2/libxml/SAX.h +204 -0
  22. data/ext/nokogiri/include/libxml2/libxml/SAX2.h +172 -0
  23. data/ext/nokogiri/include/libxml2/libxml/c14n.h +128 -0
  24. data/ext/nokogiri/include/libxml2/libxml/catalog.h +182 -0
  25. data/ext/nokogiri/include/libxml2/libxml/chvalid.h +230 -0
  26. data/ext/nokogiri/include/libxml2/libxml/debugXML.h +217 -0
  27. data/ext/nokogiri/include/libxml2/libxml/dict.h +81 -0
  28. data/ext/nokogiri/include/libxml2/libxml/encoding.h +232 -0
  29. data/ext/nokogiri/include/libxml2/libxml/entities.h +153 -0
  30. data/ext/nokogiri/include/libxml2/libxml/globals.h +499 -0
  31. data/ext/nokogiri/include/libxml2/libxml/hash.h +236 -0
  32. data/ext/nokogiri/include/libxml2/libxml/list.h +137 -0
  33. data/ext/nokogiri/include/libxml2/libxml/nanoftp.h +186 -0
  34. data/ext/nokogiri/include/libxml2/libxml/nanohttp.h +81 -0
  35. data/ext/nokogiri/include/libxml2/libxml/parser.h +1244 -0
  36. data/ext/nokogiri/include/libxml2/libxml/parserInternals.h +656 -0
  37. data/ext/nokogiri/include/libxml2/libxml/pattern.h +100 -0
  38. data/ext/nokogiri/include/libxml2/libxml/relaxng.h +218 -0
  39. data/ext/nokogiri/include/libxml2/libxml/schemasInternals.h +958 -0
  40. data/ext/nokogiri/include/libxml2/libxml/schematron.h +142 -0
  41. data/ext/nokogiri/include/libxml2/libxml/threads.h +91 -0
  42. data/ext/nokogiri/include/libxml2/libxml/tree.h +1312 -0
  43. data/ext/nokogiri/include/libxml2/libxml/uri.h +94 -0
  44. data/ext/nokogiri/include/libxml2/libxml/valid.h +463 -0
  45. data/ext/nokogiri/include/libxml2/libxml/xinclude.h +129 -0
  46. data/ext/nokogiri/include/libxml2/libxml/xlink.h +189 -0
  47. data/ext/nokogiri/include/libxml2/libxml/xmlIO.h +368 -0
  48. data/ext/nokogiri/include/libxml2/libxml/xmlautomata.h +146 -0
  49. data/ext/nokogiri/include/libxml2/libxml/xmlerror.h +947 -0
  50. data/ext/nokogiri/include/libxml2/libxml/xmlexports.h +77 -0
  51. data/ext/nokogiri/include/libxml2/libxml/xmlmemory.h +226 -0
  52. data/ext/nokogiri/include/libxml2/libxml/xmlmodule.h +57 -0
  53. data/ext/nokogiri/include/libxml2/libxml/xmlreader.h +428 -0
  54. data/ext/nokogiri/include/libxml2/libxml/xmlregexp.h +222 -0
  55. data/ext/nokogiri/include/libxml2/libxml/xmlsave.h +88 -0
  56. data/ext/nokogiri/include/libxml2/libxml/xmlschemas.h +246 -0
  57. data/ext/nokogiri/include/libxml2/libxml/xmlschemastypes.h +152 -0
  58. data/ext/nokogiri/include/libxml2/libxml/xmlstring.h +140 -0
  59. data/ext/nokogiri/include/libxml2/libxml/xmlunicode.h +202 -0
  60. data/ext/nokogiri/include/libxml2/libxml/xmlversion.h +503 -0
  61. data/ext/nokogiri/include/libxml2/libxml/xmlwriter.h +488 -0
  62. data/ext/nokogiri/include/libxml2/libxml/xpath.h +575 -0
  63. data/ext/nokogiri/include/libxml2/libxml/xpathInternals.h +632 -0
  64. data/ext/nokogiri/include/libxml2/libxml/xpointer.h +137 -0
  65. data/ext/nokogiri/include/libxslt/attributes.h +38 -0
  66. data/ext/nokogiri/include/libxslt/documents.h +93 -0
  67. data/ext/nokogiri/include/libxslt/extensions.h +262 -0
  68. data/ext/nokogiri/include/libxslt/extra.h +72 -0
  69. data/ext/nokogiri/include/libxslt/functions.h +78 -0
  70. data/ext/nokogiri/include/libxslt/imports.h +75 -0
  71. data/ext/nokogiri/include/libxslt/keys.h +53 -0
  72. data/ext/nokogiri/include/libxslt/namespaces.h +68 -0
  73. data/ext/nokogiri/include/libxslt/numbersInternals.h +73 -0
  74. data/ext/nokogiri/include/libxslt/pattern.h +84 -0
  75. data/ext/nokogiri/include/libxslt/preproc.h +43 -0
  76. data/ext/nokogiri/include/libxslt/security.h +104 -0
  77. data/ext/nokogiri/include/libxslt/templates.h +77 -0
  78. data/ext/nokogiri/include/libxslt/transform.h +207 -0
  79. data/ext/nokogiri/include/libxslt/variables.h +118 -0
  80. data/ext/nokogiri/include/libxslt/xslt.h +110 -0
  81. data/ext/nokogiri/include/libxslt/xsltInternals.h +1982 -0
  82. data/ext/nokogiri/include/libxslt/xsltconfig.h +179 -0
  83. data/ext/nokogiri/include/libxslt/xsltexports.h +64 -0
  84. data/ext/nokogiri/include/libxslt/xsltlocale.h +76 -0
  85. data/ext/nokogiri/include/libxslt/xsltutils.h +310 -0
  86. data/ext/nokogiri/libxml2_backwards_compat.c +121 -0
  87. data/ext/nokogiri/nokogiri.c +228 -104
  88. data/ext/nokogiri/nokogiri.h +204 -90
  89. data/ext/nokogiri/test_global_handlers.c +40 -0
  90. data/ext/nokogiri/xml_attr.c +17 -17
  91. data/ext/nokogiri/xml_attribute_decl.c +22 -22
  92. data/ext/nokogiri/xml_cdata.c +15 -20
  93. data/ext/nokogiri/xml_comment.c +19 -26
  94. data/ext/nokogiri/xml_document.c +306 -225
  95. data/ext/nokogiri/xml_document_fragment.c +12 -16
  96. data/ext/nokogiri/xml_dtd.c +64 -58
  97. data/ext/nokogiri/xml_element_content.c +33 -28
  98. data/ext/nokogiri/xml_element_decl.c +26 -26
  99. data/ext/nokogiri/xml_encoding_handler.c +45 -20
  100. data/ext/nokogiri/xml_entity_decl.c +37 -35
  101. data/ext/nokogiri/xml_entity_reference.c +16 -18
  102. data/ext/nokogiri/xml_namespace.c +136 -61
  103. data/ext/nokogiri/xml_node.c +1344 -672
  104. data/ext/nokogiri/xml_node_set.c +178 -168
  105. data/ext/nokogiri/xml_processing_instruction.c +17 -19
  106. data/ext/nokogiri/xml_reader.c +316 -190
  107. data/ext/nokogiri/xml_relax_ng.c +52 -30
  108. data/ext/nokogiri/xml_sax_parser.c +130 -124
  109. data/ext/nokogiri/xml_sax_parser_context.c +110 -89
  110. data/ext/nokogiri/xml_sax_push_parser.c +36 -29
  111. data/ext/nokogiri/xml_schema.c +98 -50
  112. data/ext/nokogiri/xml_syntax_error.c +42 -21
  113. data/ext/nokogiri/xml_text.c +14 -18
  114. data/ext/nokogiri/xml_xpath_context.c +263 -148
  115. data/ext/nokogiri/xslt_stylesheet.c +271 -178
  116. data/gumbo-parser/CHANGES.md +63 -0
  117. data/gumbo-parser/Makefile +111 -0
  118. data/gumbo-parser/THANKS +27 -0
  119. data/lib/nokogiri/2.7/nokogiri.so +0 -0
  120. data/lib/nokogiri/3.0/nokogiri.so +0 -0
  121. data/lib/nokogiri/3.1/nokogiri.so +0 -0
  122. data/lib/nokogiri/3.2/nokogiri.so +0 -0
  123. data/lib/nokogiri/class_resolver.rb +67 -0
  124. data/lib/nokogiri/css/node.rb +10 -8
  125. data/lib/nokogiri/css/parser.rb +397 -377
  126. data/lib/nokogiri/css/parser.y +250 -245
  127. data/lib/nokogiri/css/parser_extras.rb +54 -49
  128. data/lib/nokogiri/css/syntax_error.rb +3 -1
  129. data/lib/nokogiri/css/tokenizer.rb +5 -3
  130. data/lib/nokogiri/css/tokenizer.rex +3 -2
  131. data/lib/nokogiri/css/xpath_visitor.rb +223 -94
  132. data/lib/nokogiri/css.rb +56 -17
  133. data/lib/nokogiri/decorators/slop.rb +9 -7
  134. data/lib/nokogiri/encoding_handler.rb +57 -0
  135. data/lib/nokogiri/extension.rb +32 -0
  136. data/lib/nokogiri/gumbo.rb +15 -0
  137. data/lib/nokogiri/html.rb +38 -27
  138. data/lib/nokogiri/{html → html4}/builder.rb +4 -2
  139. data/lib/nokogiri/html4/document.rb +214 -0
  140. data/lib/nokogiri/html4/document_fragment.rb +54 -0
  141. data/lib/nokogiri/{html → html4}/element_description.rb +3 -1
  142. data/lib/nokogiri/html4/element_description_defaults.rb +572 -0
  143. data/lib/nokogiri/html4/encoding_reader.rb +121 -0
  144. data/lib/nokogiri/{html → html4}/entity_lookup.rb +4 -2
  145. data/lib/nokogiri/{html → html4}/sax/parser.rb +17 -16
  146. data/lib/nokogiri/html4/sax/parser_context.rb +20 -0
  147. data/lib/nokogiri/{html → html4}/sax/push_parser.rb +12 -11
  148. data/lib/nokogiri/html4.rb +47 -0
  149. data/lib/nokogiri/html5/document.rb +168 -0
  150. data/lib/nokogiri/html5/document_fragment.rb +90 -0
  151. data/lib/nokogiri/html5/node.rb +98 -0
  152. data/lib/nokogiri/html5.rb +389 -0
  153. data/lib/nokogiri/jruby/dependencies.rb +3 -0
  154. data/lib/nokogiri/jruby/nokogiri_jars.rb +43 -0
  155. data/lib/nokogiri/syntax_error.rb +2 -0
  156. data/lib/nokogiri/version/constant.rb +6 -0
  157. data/lib/nokogiri/version/info.rb +223 -0
  158. data/lib/nokogiri/version.rb +3 -108
  159. data/lib/nokogiri/xml/attr.rb +55 -3
  160. data/lib/nokogiri/xml/attribute_decl.rb +3 -1
  161. data/lib/nokogiri/xml/builder.rb +75 -34
  162. data/lib/nokogiri/xml/cdata.rb +3 -1
  163. data/lib/nokogiri/xml/character_data.rb +2 -0
  164. data/lib/nokogiri/xml/document.rb +312 -126
  165. data/lib/nokogiri/xml/document_fragment.rb +93 -48
  166. data/lib/nokogiri/xml/dtd.rb +4 -2
  167. data/lib/nokogiri/xml/element_content.rb +2 -0
  168. data/lib/nokogiri/xml/element_decl.rb +3 -1
  169. data/lib/nokogiri/xml/entity_decl.rb +4 -2
  170. data/lib/nokogiri/xml/entity_reference.rb +2 -0
  171. data/lib/nokogiri/xml/namespace.rb +45 -0
  172. data/lib/nokogiri/xml/node/save_options.rb +15 -8
  173. data/lib/nokogiri/xml/node.rb +1067 -406
  174. data/lib/nokogiri/xml/node_set.rb +135 -59
  175. data/lib/nokogiri/xml/notation.rb +13 -0
  176. data/lib/nokogiri/xml/parse_options.rb +145 -52
  177. data/lib/nokogiri/xml/pp/character_data.rb +9 -6
  178. data/lib/nokogiri/xml/pp/node.rb +27 -26
  179. data/lib/nokogiri/xml/pp.rb +4 -2
  180. data/lib/nokogiri/xml/processing_instruction.rb +4 -1
  181. data/lib/nokogiri/xml/reader.rb +21 -28
  182. data/lib/nokogiri/xml/relax_ng.rb +8 -2
  183. data/lib/nokogiri/xml/sax/document.rb +45 -49
  184. data/lib/nokogiri/xml/sax/parser.rb +39 -36
  185. data/lib/nokogiri/xml/sax/parser_context.rb +8 -3
  186. data/lib/nokogiri/xml/sax/push_parser.rb +6 -5
  187. data/lib/nokogiri/xml/sax.rb +6 -4
  188. data/lib/nokogiri/xml/schema.rb +19 -9
  189. data/lib/nokogiri/xml/searchable.rb +112 -72
  190. data/lib/nokogiri/xml/syntax_error.rb +6 -4
  191. data/lib/nokogiri/xml/text.rb +2 -0
  192. data/lib/nokogiri/xml/xpath/syntax_error.rb +4 -2
  193. data/lib/nokogiri/xml/xpath.rb +15 -4
  194. data/lib/nokogiri/xml/xpath_context.rb +3 -3
  195. data/lib/nokogiri/xml.rb +38 -37
  196. data/lib/nokogiri/xslt/stylesheet.rb +3 -1
  197. data/lib/nokogiri/xslt.rb +29 -20
  198. data/lib/nokogiri.rb +48 -72
  199. data/lib/xsd/xmlparser/nokogiri.rb +29 -25
  200. metadata +146 -307
  201. data/ext/nokogiri/html_document.c +0 -170
  202. data/ext/nokogiri/html_document.h +0 -10
  203. data/ext/nokogiri/html_element_description.c +0 -279
  204. data/ext/nokogiri/html_element_description.h +0 -10
  205. data/ext/nokogiri/html_entity_lookup.c +0 -32
  206. data/ext/nokogiri/html_entity_lookup.h +0 -8
  207. data/ext/nokogiri/html_sax_parser_context.c +0 -116
  208. data/ext/nokogiri/html_sax_parser_context.h +0 -11
  209. data/ext/nokogiri/html_sax_push_parser.c +0 -87
  210. data/ext/nokogiri/html_sax_push_parser.h +0 -9
  211. data/ext/nokogiri/xml_attr.h +0 -9
  212. data/ext/nokogiri/xml_attribute_decl.h +0 -9
  213. data/ext/nokogiri/xml_cdata.h +0 -9
  214. data/ext/nokogiri/xml_comment.h +0 -9
  215. data/ext/nokogiri/xml_document.h +0 -23
  216. data/ext/nokogiri/xml_document_fragment.h +0 -10
  217. data/ext/nokogiri/xml_dtd.h +0 -10
  218. data/ext/nokogiri/xml_element_content.h +0 -10
  219. data/ext/nokogiri/xml_element_decl.h +0 -9
  220. data/ext/nokogiri/xml_encoding_handler.h +0 -8
  221. data/ext/nokogiri/xml_entity_decl.h +0 -10
  222. data/ext/nokogiri/xml_entity_reference.h +0 -9
  223. data/ext/nokogiri/xml_io.c +0 -61
  224. data/ext/nokogiri/xml_io.h +0 -11
  225. data/ext/nokogiri/xml_libxml2_hacks.c +0 -112
  226. data/ext/nokogiri/xml_libxml2_hacks.h +0 -12
  227. data/ext/nokogiri/xml_namespace.h +0 -14
  228. data/ext/nokogiri/xml_node.h +0 -13
  229. data/ext/nokogiri/xml_node_set.h +0 -12
  230. data/ext/nokogiri/xml_processing_instruction.h +0 -9
  231. data/ext/nokogiri/xml_reader.h +0 -10
  232. data/ext/nokogiri/xml_relax_ng.h +0 -9
  233. data/ext/nokogiri/xml_sax_parser.h +0 -39
  234. data/ext/nokogiri/xml_sax_parser_context.h +0 -10
  235. data/ext/nokogiri/xml_sax_push_parser.h +0 -9
  236. data/ext/nokogiri/xml_schema.h +0 -9
  237. data/ext/nokogiri/xml_syntax_error.h +0 -13
  238. data/ext/nokogiri/xml_text.h +0 -9
  239. data/ext/nokogiri/xml_xpath_context.h +0 -10
  240. data/ext/nokogiri/xslt_stylesheet.h +0 -14
  241. data/lib/nokogiri/html/document.rb +0 -335
  242. data/lib/nokogiri/html/document_fragment.rb +0 -49
  243. data/lib/nokogiri/html/element_description_defaults.rb +0 -671
  244. data/lib/nokogiri/html/sax/parser_context.rb +0 -16
  245. data/patches/libxml2/0001-Revert-Do-not-URI-escape-in-server-side-includes.patch +0 -78
  246. data/patches/libxml2/0002-Remove-script-macro-support.patch +0 -40
  247. data/patches/libxml2/0003-Update-entities-to-remove-handling-of-ssi.patch +0 -44
  248. data/patches/libxml2/0004-libxml2.la-is-in-top_builddir.patch +0 -25
  249. data/patches/libxml2/0005-Fix-infinite-loop-in-xmlStringLenDecodeEntities.patch +0 -32
  250. data/ports/archives/libxml2-2.9.10.tar.gz +0 -0
  251. data/ports/archives/libxslt-1.1.34.tar.gz +0 -0
@@ -1,36 +1,61 @@
1
- #include <xml_node.h>
1
+ #include <nokogiri.h>
2
2
 
3
- static ID decorate, decorate_bang;
3
+ #include <stdbool.h>
4
4
 
5
- #ifdef DEBUG
6
- static void debug_node_dealloc(xmlNodePtr x)
7
- {
8
- NOKOGIRI_DEBUG_START(x)
9
- NOKOGIRI_DEBUG_END(x)
10
- }
11
- #else
12
- # define debug_node_dealloc 0
13
- #endif
5
+ // :stopdoc:
14
6
 
15
- static void mark(xmlNodePtr node)
7
+ VALUE cNokogiriXmlNode ;
8
+ static ID id_decorate, id_decorate_bang;
9
+
10
+ typedef xmlNodePtr(*pivot_reparentee_func)(xmlNodePtr, xmlNodePtr);
11
+
12
+ static void
13
+ _xml_node_mark(void *ptr)
16
14
  {
15
+ xmlNodePtr node = ptr;
16
+
17
+ if (!DOC_RUBY_OBJECT_TEST(node->doc)) {
18
+ return;
19
+ }
20
+
17
21
  xmlDocPtr doc = node->doc;
18
- if(doc->type == XML_DOCUMENT_NODE || doc->type == XML_HTML_DOCUMENT_NODE) {
19
- if(DOC_RUBY_OBJECT_TEST(doc)) {
22
+ if (doc->type == XML_DOCUMENT_NODE || doc->type == XML_HTML_DOCUMENT_NODE) {
23
+ if (DOC_RUBY_OBJECT_TEST(doc)) {
20
24
  rb_gc_mark(DOC_RUBY_OBJECT(doc));
21
25
  }
22
- } else if(node->doc->_private) {
26
+ } else if (node->doc->_private) {
23
27
  rb_gc_mark((VALUE)doc->_private);
24
28
  }
25
29
  }
26
30
 
27
- /* :nodoc: */
28
- typedef xmlNodePtr (*pivot_reparentee_func)(xmlNodePtr, xmlNodePtr);
31
+ #ifdef HAVE_RB_GC_LOCATION
32
+ static void
33
+ _xml_node_update_references(void *ptr)
34
+ {
35
+ xmlNodePtr node = ptr;
29
36
 
30
- /* :nodoc: */
31
- static void relink_namespace(xmlNodePtr reparented)
37
+ if (node->_private) {
38
+ node->_private = (void *)rb_gc_location((VALUE)node->_private);
39
+ }
40
+ }
41
+ #else
42
+ # define _xml_node_update_references 0
43
+ #endif
44
+
45
+ static const rb_data_type_t nokogiri_node_type = {
46
+ "Nokogiri/XMLNode",
47
+ {_xml_node_mark, 0, 0, _xml_node_update_references},
48
+ 0, 0,
49
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
50
+ RUBY_TYPED_FREE_IMMEDIATELY,
51
+ #endif
52
+ };
53
+
54
+ static void
55
+ relink_namespace(xmlNodePtr reparented)
32
56
  {
33
57
  xmlNodePtr child;
58
+ xmlAttrPtr attr;
34
59
 
35
60
  if (reparented->type != XML_ATTRIBUTE_NODE &&
36
61
  reparented->type != XML_ELEMENT_NODE) { return; }
@@ -42,7 +67,7 @@ static void relink_namespace(xmlNodePtr reparented)
42
67
  name = xmlSplitQName2(reparented->name, &prefix);
43
68
 
44
69
  if (reparented->type == XML_ATTRIBUTE_NODE) {
45
- if (prefix == NULL || strcmp((char*)prefix, XMLNS_PREFIX) == 0) {
70
+ if (prefix == NULL || strcmp((char *)prefix, XMLNS_PREFIX) == 0) {
46
71
  xmlFree(name);
47
72
  xmlFree(prefix);
48
73
  return;
@@ -64,7 +89,9 @@ static void relink_namespace(xmlNodePtr reparented)
64
89
  if (reparented->type != XML_ELEMENT_NODE || !reparented->parent) { return; }
65
90
 
66
91
  /* Make sure that our reparented node has the correct namespaces */
67
- if (!reparented->ns && reparented->doc != (xmlDocPtr)reparented->parent) {
92
+ if (!reparented->ns &&
93
+ (reparented->doc != (xmlDocPtr)reparented->parent) &&
94
+ (rb_iv_get(DOC_RUBY_OBJECT(reparented->doc), "@namespace_inheritance") == Qtrue)) {
68
95
  xmlSetNs(reparented, reparented->parent->ns);
69
96
  }
70
97
 
@@ -87,7 +114,7 @@ static void relink_namespace(xmlNodePtr reparented)
87
114
  } else {
88
115
  reparented->nsDef = curr->next;
89
116
  }
90
- nokogiri_root_nsdef(curr, reparented->doc);
117
+ noko_xml_document_pin_namespace(curr, reparented->doc);
91
118
  } else {
92
119
  prev = curr;
93
120
  }
@@ -127,16 +154,19 @@ static void relink_namespace(xmlNodePtr reparented)
127
154
  }
128
155
 
129
156
  if (reparented->type == XML_ELEMENT_NODE) {
130
- child = (xmlNodePtr)((xmlElementPtr)reparented)->attributes;
131
- while(NULL != child) {
132
- relink_namespace(child);
133
- child = child->next;
157
+ attr = reparented->properties;
158
+ while (NULL != attr) {
159
+ relink_namespace((xmlNodePtr)attr);
160
+ attr = attr->next;
134
161
  }
135
162
  }
136
163
  }
137
164
 
138
- /* :nodoc: */
139
- static xmlNodePtr xmlReplaceNodeWrapper(xmlNodePtr pivot, xmlNodePtr new_node)
165
+
166
+ /* internal function meant to wrap xmlReplaceNode
167
+ and fix some issues we have with libxml2 merging nodes */
168
+ static xmlNodePtr
169
+ xmlReplaceNodeWrapper(xmlNodePtr pivot, xmlNodePtr new_node)
140
170
  {
141
171
  xmlNodePtr retval ;
142
172
 
@@ -159,22 +189,34 @@ static xmlNodePtr xmlReplaceNodeWrapper(xmlNodePtr pivot, xmlNodePtr new_node)
159
189
  return retval ;
160
190
  }
161
191
 
162
- /* :nodoc: */
163
- static VALUE reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_reparentee_func prf)
192
+
193
+ static void
194
+ raise_if_ancestor_of_self(xmlNodePtr self)
195
+ {
196
+ for (xmlNodePtr ancestor = self->parent ; ancestor ; ancestor = ancestor->parent) {
197
+ if (self == ancestor) {
198
+ rb_raise(rb_eRuntimeError, "cycle detected: node '%s' is an ancestor of itself", self->name);
199
+ }
200
+ }
201
+ }
202
+
203
+
204
+ static VALUE
205
+ reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_reparentee_func prf)
164
206
  {
165
207
  VALUE reparented_obj ;
166
- xmlNodePtr reparentee, pivot, reparented, next_text, new_next_text, parent ;
208
+ xmlNodePtr reparentee, original_reparentee, pivot, reparented, next_text, new_next_text, parent ;
167
209
  int original_ns_prefix_is_default = 0 ;
168
210
 
169
- if(!rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlNode)) {
211
+ if (!rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlNode)) {
170
212
  rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
171
213
  }
172
- if(rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlDocument)) {
214
+ if (rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlDocument)) {
173
215
  rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
174
216
  }
175
217
 
176
- Data_Get_Struct(reparentee_obj, xmlNode, reparentee);
177
- Data_Get_Struct(pivot_obj, xmlNode, pivot);
218
+ Noko_Node_Get_Struct(reparentee_obj, xmlNode, reparentee);
219
+ Noko_Node_Get_Struct(pivot_obj, xmlNode, pivot);
178
220
 
179
221
  /*
180
222
  * Check if nodes given are appropriate to have a parent-child
@@ -190,66 +232,66 @@ static VALUE reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_rep
190
232
 
191
233
  if (parent) {
192
234
  switch (parent->type) {
193
- case XML_DOCUMENT_NODE:
194
- case XML_HTML_DOCUMENT_NODE:
195
- switch (reparentee->type) {
196
- case XML_ELEMENT_NODE:
197
- case XML_PI_NODE:
198
- case XML_COMMENT_NODE:
199
- case XML_DOCUMENT_TYPE_NODE:
200
- /*
201
- * The DOM specification says no to adding text-like nodes
202
- * directly to a document, but we allow it for compatibility.
203
- */
204
- case XML_TEXT_NODE:
205
- case XML_CDATA_SECTION_NODE:
206
- case XML_ENTITY_REF_NODE:
207
- goto ok;
208
- default:
235
+ case XML_DOCUMENT_NODE:
236
+ case XML_HTML_DOCUMENT_NODE:
237
+ switch (reparentee->type) {
238
+ case XML_ELEMENT_NODE:
239
+ case XML_PI_NODE:
240
+ case XML_COMMENT_NODE:
241
+ case XML_DOCUMENT_TYPE_NODE:
242
+ /*
243
+ * The DOM specification says no to adding text-like nodes
244
+ * directly to a document, but we allow it for compatibility.
245
+ */
246
+ case XML_TEXT_NODE:
247
+ case XML_CDATA_SECTION_NODE:
248
+ case XML_ENTITY_REF_NODE:
249
+ goto ok;
250
+ default:
251
+ break;
252
+ }
209
253
  break;
210
- }
211
- break;
212
- case XML_DOCUMENT_FRAG_NODE:
213
- case XML_ENTITY_REF_NODE:
214
- case XML_ELEMENT_NODE:
215
- switch (reparentee->type) {
216
- case XML_ELEMENT_NODE:
217
- case XML_PI_NODE:
218
- case XML_COMMENT_NODE:
219
- case XML_TEXT_NODE:
220
- case XML_CDATA_SECTION_NODE:
254
+ case XML_DOCUMENT_FRAG_NODE:
221
255
  case XML_ENTITY_REF_NODE:
222
- goto ok;
223
- default:
256
+ case XML_ELEMENT_NODE:
257
+ switch (reparentee->type) {
258
+ case XML_ELEMENT_NODE:
259
+ case XML_PI_NODE:
260
+ case XML_COMMENT_NODE:
261
+ case XML_TEXT_NODE:
262
+ case XML_CDATA_SECTION_NODE:
263
+ case XML_ENTITY_REF_NODE:
264
+ goto ok;
265
+ default:
266
+ break;
267
+ }
268
+ break;
269
+ case XML_ATTRIBUTE_NODE:
270
+ switch (reparentee->type) {
271
+ case XML_TEXT_NODE:
272
+ case XML_ENTITY_REF_NODE:
273
+ goto ok;
274
+ default:
275
+ break;
276
+ }
224
277
  break;
225
- }
226
- break;
227
- case XML_ATTRIBUTE_NODE:
228
- switch (reparentee->type) {
229
278
  case XML_TEXT_NODE:
230
- case XML_ENTITY_REF_NODE:
231
- goto ok;
279
+ /*
280
+ * xmlAddChild() breaks the DOM specification in that it allows
281
+ * adding a text node to another, in which case text nodes are
282
+ * coalesced, but since our JRuby version does not support such
283
+ * operation, we should inhibit it.
284
+ */
285
+ break;
232
286
  default:
233
287
  break;
234
- }
235
- break;
236
- case XML_TEXT_NODE:
237
- /*
238
- * xmlAddChild() breaks the DOM specification in that it allows
239
- * adding a text node to another, in which case text nodes are
240
- * coalesced, but since our JRuby version does not support such
241
- * operation, we should inhibit it.
242
- */
243
- break;
244
- default:
245
- break;
246
288
  }
247
289
 
248
290
  rb_raise(rb_eArgError, "cannot reparent %s there", rb_obj_classname(reparentee_obj));
249
291
  }
250
292
 
251
293
  ok:
252
- xmlUnlinkNode(reparentee);
294
+ original_reparentee = reparentee;
253
295
 
254
296
  if (reparentee->doc != pivot->doc || reparentee->type == XML_TEXT_NODE) {
255
297
  /*
@@ -290,7 +332,7 @@ ok:
290
332
  original_ns_prefix_is_default = 1;
291
333
  }
292
334
 
293
- nokogiri_root_node(reparentee);
335
+ noko_xml_document_pin_node(reparentee);
294
336
 
295
337
  if (!(reparentee = xmlDocCopyNode(reparentee, pivot->doc, 1))) {
296
338
  rb_raise(rb_eRuntimeError, "Could not reparent node (xmlDocCopyNode)");
@@ -301,11 +343,13 @@ ok:
301
343
  * issue #391, where new node's prefix may become the string "default"
302
344
  * see libxml2 tree.c xmlNewReconciliedNs which implements this behavior.
303
345
  */
304
- xmlFree(reparentee->ns->prefix);
346
+ xmlFree(DISCARD_CONST_QUAL_XMLCHAR(reparentee->ns->prefix));
305
347
  reparentee->ns->prefix = NULL;
306
348
  }
307
349
  }
308
350
 
351
+ xmlUnlinkNode(original_reparentee);
352
+
309
353
  if (prf != xmlAddPrevSibling && prf != xmlAddNextSibling
310
354
  && reparentee->type == XML_TEXT_NODE && pivot->next && pivot->next->type == XML_TEXT_NODE) {
311
355
  /*
@@ -330,12 +374,12 @@ ok:
330
374
  new_next_text = xmlDocCopyNode(next_text, pivot->doc, 1) ;
331
375
 
332
376
  xmlUnlinkNode(next_text);
333
- nokogiri_root_node(next_text);
377
+ noko_xml_document_pin_node(next_text);
334
378
 
335
379
  xmlAddNextSibling(pivot, new_next_text);
336
380
  }
337
381
 
338
- if(!(reparented = (*prf)(pivot, reparentee))) {
382
+ if (!(reparented = (*prf)(pivot, reparentee))) {
339
383
  rb_raise(rb_eRuntimeError, "Could not reparent node");
340
384
  }
341
385
 
@@ -345,57 +389,432 @@ ok:
345
389
  * adjacent text nodes.
346
390
  */
347
391
  DATA_PTR(reparentee_obj) = reparented ;
392
+ reparented_obj = noko_xml_node_wrap(Qnil, reparented);
348
393
 
349
- relink_namespace(reparented);
394
+ rb_funcall(reparented_obj, id_decorate_bang, 0);
350
395
 
351
- reparented_obj = Nokogiri_wrap_xml_node(Qnil, reparented);
396
+ /* if we've created a cycle, raise an exception */
397
+ raise_if_ancestor_of_self(reparented);
352
398
 
353
- rb_funcall(reparented_obj, decorate_bang, 0);
399
+ relink_namespace(reparented);
354
400
 
355
401
  return reparented_obj ;
356
402
  }
357
403
 
404
+ // :startdoc:
358
405
 
359
406
  /*
360
- * call-seq:
361
- * document
407
+ * :call-seq:
408
+ * add_namespace_definition(prefix, href) → Nokogiri::XML::Namespace
409
+ * add_namespace(prefix, href) → Nokogiri::XML::Namespace
410
+ *
411
+ * :category: Manipulating Document Structure
412
+ *
413
+ * Adds a namespace definition to this node with +prefix+ using +href+ value, as if this node had
414
+ * included an attribute "xmlns:prefix=href".
415
+ *
416
+ * A default namespace definition for this node can be added by passing +nil+ for +prefix+.
417
+ *
418
+ * [Parameters]
419
+ * - +prefix+ (String, +nil+) An {XML Name}[https://www.w3.org/TR/xml-names/#ns-decl]
420
+ * - +href+ (String) The {URI reference}[https://www.w3.org/TR/xml-names/#sec-namespaces]
421
+ *
422
+ * [Returns] The new Nokogiri::XML::Namespace
423
+ *
424
+ * *Example:* adding a non-default namespace definition
425
+ *
426
+ * doc = Nokogiri::XML("<store><inventory></inventory></store>")
427
+ * inventory = doc.at_css("inventory")
428
+ * inventory.add_namespace_definition("automobile", "http://alices-autos.com/")
429
+ * inventory.add_namespace_definition("bicycle", "http://bobs-bikes.com/")
430
+ * inventory.add_child("<automobile:tire>Michelin model XGV, size 75R</automobile:tire>")
431
+ * doc.to_xml
432
+ * # => "<?xml version=\"1.0\"?>\n" +
433
+ * # "<store>\n" +
434
+ * # " <inventory xmlns:automobile=\"http://alices-autos.com/\" xmlns:bicycle=\"http://bobs-bikes.com/\">\n" +
435
+ * # " <automobile:tire>Michelin model XGV, size 75R</automobile:tire>\n" +
436
+ * # " </inventory>\n" +
437
+ * # "</store>\n"
438
+ *
439
+ * *Example:* adding a default namespace definition
440
+ *
441
+ * doc = Nokogiri::XML("<store><inventory><tire>Michelin model XGV, size 75R</tire></inventory></store>")
442
+ * doc.at_css("tire").add_namespace_definition(nil, "http://bobs-bikes.com/")
443
+ * doc.to_xml
444
+ * # => "<?xml version=\"1.0\"?>\n" +
445
+ * # "<store>\n" +
446
+ * # " <inventory>\n" +
447
+ * # " <tire xmlns=\"http://bobs-bikes.com/\">Michelin model XGV, size 75R</tire>\n" +
448
+ * # " </inventory>\n" +
449
+ * # "</store>\n"
450
+ *
451
+ */
452
+ static VALUE
453
+ rb_xml_node_add_namespace_definition(VALUE rb_node, VALUE rb_prefix, VALUE rb_href)
454
+ {
455
+ xmlNodePtr c_node, element;
456
+ xmlNsPtr c_namespace;
457
+ const xmlChar *c_prefix = (const xmlChar *)(NIL_P(rb_prefix) ? NULL : StringValueCStr(rb_prefix));
458
+
459
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
460
+ element = c_node ;
461
+
462
+ c_namespace = xmlSearchNs(c_node->doc, c_node, c_prefix);
463
+
464
+ if (!c_namespace) {
465
+ if (c_node->type != XML_ELEMENT_NODE) {
466
+ element = c_node->parent;
467
+ }
468
+ c_namespace = xmlNewNs(element, (const xmlChar *)StringValueCStr(rb_href), c_prefix);
469
+ }
470
+
471
+ if (!c_namespace) {
472
+ return Qnil ;
473
+ }
474
+
475
+ if (NIL_P(rb_prefix) || c_node != element) {
476
+ xmlSetNs(c_node, c_namespace);
477
+ }
478
+
479
+ return noko_xml_namespace_wrap(c_namespace, c_node->doc);
480
+ }
481
+
482
+
483
+ /*
484
+ * :call-seq: attribute(name) → Nokogiri::XML::Attr
485
+ *
486
+ * :category: Working With Node Attributes
487
+ *
488
+ * [Returns] Attribute (Nokogiri::XML::Attr) belonging to this node with name +name+.
489
+ *
490
+ * ⚠ Note that attribute namespaces are ignored and only the simple (non-namespace-prefixed) name is
491
+ * used to find a matching attribute. In case of a simple name collision, only one of the matching
492
+ * attributes will be returned. In this case, you will need to use #attribute_with_ns.
493
+ *
494
+ * *Example:*
495
+ *
496
+ * doc = Nokogiri::XML("<root><child size='large' class='big wide tall'/></root>")
497
+ * child = doc.at_css("child")
498
+ * child.attribute("size") # => #<Nokogiri::XML::Attr:0x550 name="size" value="large">
499
+ * child.attribute("class") # => #<Nokogiri::XML::Attr:0x564 name="class" value="big wide tall">
500
+ *
501
+ * *Example* showing that namespaced attributes will not be returned:
502
+ *
503
+ * ⚠ Note that only one of the two matching attributes is returned.
504
+ *
505
+ * doc = Nokogiri::XML(<<~EOF)
506
+ * <root xmlns:width='http://example.com/widths'
507
+ * xmlns:height='http://example.com/heights'>
508
+ * <child width:size='broad' height:size='tall'/>
509
+ * </root>
510
+ * EOF
511
+ * doc.at_css("child").attribute("size")
512
+ * # => #(Attr:0x550 {
513
+ * # name = "size",
514
+ * # namespace = #(Namespace:0x564 {
515
+ * # prefix = "width",
516
+ * # href = "http://example.com/widths"
517
+ * # }),
518
+ * # value = "broad"
519
+ * # })
520
+ */
521
+ static VALUE
522
+ rb_xml_node_attribute(VALUE self, VALUE name)
523
+ {
524
+ xmlNodePtr node;
525
+ xmlAttrPtr prop;
526
+ Noko_Node_Get_Struct(self, xmlNode, node);
527
+ prop = xmlHasProp(node, (xmlChar *)StringValueCStr(name));
528
+
529
+ if (! prop) { return Qnil; }
530
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)prop);
531
+ }
532
+
533
+
534
+ /*
535
+ * :call-seq: attribute_nodes() → Array<Nokogiri::XML::Attr>
536
+ *
537
+ * :category: Working With Node Attributes
538
+ *
539
+ * [Returns] Attributes (an Array of Nokogiri::XML::Attr) belonging to this node.
540
+ *
541
+ * Note that this is the preferred alternative to #attributes when the simple
542
+ * (non-namespace-prefixed) attribute names may collide.
543
+ *
544
+ * *Example:*
545
+ *
546
+ * Contrast this with the colliding-name example from #attributes.
547
+ *
548
+ * doc = Nokogiri::XML(<<~EOF)
549
+ * <root xmlns:width='http://example.com/widths'
550
+ * xmlns:height='http://example.com/heights'>
551
+ * <child width:size='broad' height:size='tall'/>
552
+ * </root>
553
+ * EOF
554
+ * doc.at_css("child").attribute_nodes
555
+ * # => [#(Attr:0x550 {
556
+ * # name = "size",
557
+ * # namespace = #(Namespace:0x564 {
558
+ * # prefix = "width",
559
+ * # href = "http://example.com/widths"
560
+ * # }),
561
+ * # value = "broad"
562
+ * # }),
563
+ * # #(Attr:0x578 {
564
+ * # name = "size",
565
+ * # namespace = #(Namespace:0x58c {
566
+ * # prefix = "height",
567
+ * # href = "http://example.com/heights"
568
+ * # }),
569
+ * # value = "tall"
570
+ * # })]
571
+ */
572
+ static VALUE
573
+ rb_xml_node_attribute_nodes(VALUE rb_node)
574
+ {
575
+ xmlNodePtr c_node;
576
+
577
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
578
+
579
+ return noko_xml_node_attrs(c_node);
580
+ }
581
+
582
+
583
+ /*
584
+ * :call-seq: attribute_with_ns(name, namespace) → Nokogiri::XML::Attr
585
+ *
586
+ * :category: Working With Node Attributes
587
+ *
588
+ * [Returns]
589
+ * Attribute (Nokogiri::XML::Attr) belonging to this node with matching +name+ and +namespace+.
590
+ *
591
+ * [Parameters]
592
+ * - +name+ (String): the simple (non-namespace-prefixed) name of the attribute
593
+ * - +namespace+ (String): the URI of the attribute's namespace
594
+ *
595
+ * See related: #attribute
596
+ *
597
+ * *Example:*
598
+ *
599
+ * doc = Nokogiri::XML(<<~EOF)
600
+ * <root xmlns:width='http://example.com/widths'
601
+ * xmlns:height='http://example.com/heights'>
602
+ * <child width:size='broad' height:size='tall'/>
603
+ * </root>
604
+ * EOF
605
+ * doc.at_css("child").attribute_with_ns("size", "http://example.com/widths")
606
+ * # => #(Attr:0x550 {
607
+ * # name = "size",
608
+ * # namespace = #(Namespace:0x564 {
609
+ * # prefix = "width",
610
+ * # href = "http://example.com/widths"
611
+ * # }),
612
+ * # value = "broad"
613
+ * # })
614
+ * doc.at_css("child").attribute_with_ns("size", "http://example.com/heights")
615
+ * # => #(Attr:0x578 {
616
+ * # name = "size",
617
+ * # namespace = #(Namespace:0x58c {
618
+ * # prefix = "height",
619
+ * # href = "http://example.com/heights"
620
+ * # }),
621
+ * # value = "tall"
622
+ * # })
623
+ */
624
+ static VALUE
625
+ rb_xml_node_attribute_with_ns(VALUE self, VALUE name, VALUE namespace)
626
+ {
627
+ xmlNodePtr node;
628
+ xmlAttrPtr prop;
629
+ Noko_Node_Get_Struct(self, xmlNode, node);
630
+ prop = xmlHasNsProp(node, (xmlChar *)StringValueCStr(name),
631
+ NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace));
632
+
633
+ if (! prop) { return Qnil; }
634
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)prop);
635
+ }
636
+
637
+
638
+
639
+ /*
640
+ * call-seq: blank? → Boolean
641
+ *
642
+ * [Returns] +true+ if the node is an empty or whitespace-only text or cdata node, else +false+.
643
+ *
644
+ * *Example:*
645
+ *
646
+ * Nokogiri("<root><child/></root>").root.child.blank? # => false
647
+ * Nokogiri("<root>\t \n</root>").root.child.blank? # => true
648
+ * Nokogiri("<root><![CDATA[\t \n]]></root>").root.child.blank? # => true
649
+ * Nokogiri("<root>not-blank</root>").root.child
650
+ * .tap { |n| n.content = "" }.blank # => true
651
+ */
652
+ static VALUE
653
+ rb_xml_node_blank_eh(VALUE self)
654
+ {
655
+ xmlNodePtr node;
656
+ Noko_Node_Get_Struct(self, xmlNode, node);
657
+ return (1 == xmlIsBlankNode(node)) ? Qtrue : Qfalse ;
658
+ }
659
+
660
+
661
+ /*
662
+ * :call-seq: child() → Nokogiri::XML::Node
663
+ *
664
+ * :category: Traversing Document Structure
665
+ *
666
+ * [Returns] First of this node's children, or +nil+ if there are no children
667
+ *
668
+ * This is a convenience method and is equivalent to:
669
+ *
670
+ * node.children.first
671
+ *
672
+ * See related: #children
673
+ */
674
+ static VALUE
675
+ rb_xml_node_child(VALUE self)
676
+ {
677
+ xmlNodePtr node, child;
678
+ Noko_Node_Get_Struct(self, xmlNode, node);
679
+
680
+ child = node->children;
681
+ if (!child) { return Qnil; }
682
+
683
+ return noko_xml_node_wrap(Qnil, child);
684
+ }
685
+
686
+
687
+ /*
688
+ * :call-seq: children() → Nokogiri::XML::NodeSet
689
+ *
690
+ * :category: Traversing Document Structure
691
+ *
692
+ * [Returns] Nokogiri::XML::NodeSet containing this node's children.
693
+ */
694
+ static VALUE
695
+ rb_xml_node_children(VALUE self)
696
+ {
697
+ xmlNodePtr node;
698
+ xmlNodePtr child;
699
+ xmlNodeSetPtr set;
700
+ VALUE document;
701
+ VALUE node_set;
702
+
703
+ Noko_Node_Get_Struct(self, xmlNode, node);
704
+
705
+ child = node->children;
706
+ set = xmlXPathNodeSetCreate(child);
707
+
708
+ document = DOC_RUBY_OBJECT(node->doc);
709
+
710
+ if (!child) { return noko_xml_node_set_wrap(set, document); }
711
+
712
+ child = child->next;
713
+ while (NULL != child) {
714
+ xmlXPathNodeSetAddUnique(set, child);
715
+ child = child->next;
716
+ }
717
+
718
+ node_set = noko_xml_node_set_wrap(set, document);
719
+
720
+ return node_set;
721
+ }
722
+
723
+
724
+ /*
725
+ * :call-seq:
726
+ * content() → String
727
+ * inner_text() → String
728
+ * text() → String
729
+ * to_str() → String
730
+ *
731
+ * [Returns]
732
+ * Contents of all the text nodes in this node's subtree, concatenated together into a single
733
+ * String.
734
+ *
735
+ * ⚠ Note that entities will _always_ be expanded in the returned String.
736
+ *
737
+ * See related: #inner_html
738
+ *
739
+ * *Example* of how entities are handled:
740
+ *
741
+ * Note that <tt>&lt;</tt> becomes <tt><</tt> in the returned String.
742
+ *
743
+ * doc = Nokogiri::XML.fragment("<child>a &lt; b</child>")
744
+ * doc.at_css("child").content
745
+ * # => "a < b"
746
+ *
747
+ * *Example* of how a subtree is handled:
748
+ *
749
+ * Note that the <tt><span></tt> tags are omitted and only the text node contents are returned,
750
+ * concatenated into a single string.
751
+ *
752
+ * doc = Nokogiri::XML.fragment("<child><span>first</span> <span>second</span></child>")
753
+ * doc.at_css("child").content
754
+ * # => "first second"
755
+ */
756
+ static VALUE
757
+ rb_xml_node_content(VALUE self)
758
+ {
759
+ xmlNodePtr node;
760
+ xmlChar *content;
761
+
762
+ Noko_Node_Get_Struct(self, xmlNode, node);
763
+
764
+ content = xmlNodeGetContent(node);
765
+ if (content) {
766
+ VALUE rval = NOKOGIRI_STR_NEW2(content);
767
+ xmlFree(content);
768
+ return rval;
769
+ }
770
+ return Qnil;
771
+ }
772
+
773
+
774
+ /*
775
+ * :call-seq: document() → Nokogiri::XML::Document
776
+ *
777
+ * :category: Traversing Document Structure
362
778
  *
363
- * Get the document for this Node
779
+ * [Returns] Parent Nokogiri::XML::Document for this node
364
780
  */
365
- static VALUE document(VALUE self)
781
+ static VALUE
782
+ rb_xml_node_document(VALUE self)
366
783
  {
367
784
  xmlNodePtr node;
368
- Data_Get_Struct(self, xmlNode, node);
785
+ Noko_Node_Get_Struct(self, xmlNode, node);
369
786
  return DOC_RUBY_OBJECT(node->doc);
370
787
  }
371
788
 
372
789
  /*
373
- * call-seq:
374
- * pointer_id
790
+ * :call-seq: pointer_id() → Integer
375
791
  *
376
- * Get the internal pointer number
792
+ * [Returns]
793
+ * A unique id for this node based on the internal memory structures. This method is used by #==
794
+ * to determine node identity.
377
795
  */
378
- static VALUE pointer_id(VALUE self)
796
+ static VALUE
797
+ rb_xml_node_pointer_id(VALUE self)
379
798
  {
380
799
  xmlNodePtr node;
381
- Data_Get_Struct(self, xmlNode, node);
800
+ Noko_Node_Get_Struct(self, xmlNode, node);
382
801
 
383
- return INT2NUM((long)(node));
802
+ return rb_uint2inum((uintptr_t)(node));
384
803
  }
385
804
 
386
805
  /*
387
- * call-seq:
388
- * encode_special_chars(string)
806
+ * :call-seq: encode_special_chars(string) → String
389
807
  *
390
808
  * Encode any special characters in +string+
391
809
  */
392
- static VALUE encode_special_chars(VALUE self, VALUE string)
810
+ static VALUE
811
+ encode_special_chars(VALUE self, VALUE string)
393
812
  {
394
813
  xmlNodePtr node;
395
814
  xmlChar *encoded;
396
815
  VALUE encoded_str;
397
816
 
398
- Data_Get_Struct(self, xmlNode, node);
817
+ Noko_Node_Get_Struct(self, xmlNode, node);
399
818
  encoded = xmlEncodeSpecialChars(
400
819
  node->doc,
401
820
  (const xmlChar *)StringValueCStr(string)
@@ -408,8 +827,8 @@ static VALUE encode_special_chars(VALUE self, VALUE string)
408
827
  }
409
828
 
410
829
  /*
411
- * call-seq:
412
- * create_internal_subset(name, external_id, system_id)
830
+ * :call-seq:
831
+ * create_internal_subset(name, external_id, system_id)
413
832
  *
414
833
  * Create the internal subset of a document.
415
834
  *
@@ -419,17 +838,18 @@ static VALUE encode_special_chars(VALUE self, VALUE string)
419
838
  * doc.create_internal_subset("chapter", nil, "chapter.dtd")
420
839
  * # => <!DOCTYPE chapter SYSTEM "chapter.dtd">
421
840
  */
422
- static VALUE create_internal_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
841
+ static VALUE
842
+ create_internal_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
423
843
  {
424
844
  xmlNodePtr node;
425
845
  xmlDocPtr doc;
426
846
  xmlDtdPtr dtd;
427
847
 
428
- Data_Get_Struct(self, xmlNode, node);
848
+ Noko_Node_Get_Struct(self, xmlNode, node);
429
849
 
430
850
  doc = node->doc;
431
851
 
432
- if(xmlGetIntSubset(doc)) {
852
+ if (xmlGetIntSubset(doc)) {
433
853
  rb_raise(rb_eRuntimeError, "Document already has an internal subset");
434
854
  }
435
855
 
@@ -440,28 +860,29 @@ static VALUE create_internal_subset(VALUE self, VALUE name, VALUE external_id, V
440
860
  NIL_P(system_id) ? NULL : (const xmlChar *)StringValueCStr(system_id)
441
861
  );
442
862
 
443
- if(!dtd) { return Qnil; }
863
+ if (!dtd) { return Qnil; }
444
864
 
445
- return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
865
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
446
866
  }
447
867
 
448
868
  /*
449
- * call-seq:
450
- * create_external_subset(name, external_id, system_id)
869
+ * :call-seq:
870
+ * create_external_subset(name, external_id, system_id)
451
871
  *
452
872
  * Create an external subset
453
873
  */
454
- static VALUE create_external_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
874
+ static VALUE
875
+ create_external_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
455
876
  {
456
877
  xmlNodePtr node;
457
878
  xmlDocPtr doc;
458
879
  xmlDtdPtr dtd;
459
880
 
460
- Data_Get_Struct(self, xmlNode, node);
881
+ Noko_Node_Get_Struct(self, xmlNode, node);
461
882
 
462
883
  doc = node->doc;
463
884
 
464
- if(doc->extSubset) {
885
+ if (doc->extSubset) {
465
886
  rb_raise(rb_eRuntimeError, "Document already has an external subset");
466
887
  }
467
888
 
@@ -472,72 +893,78 @@ static VALUE create_external_subset(VALUE self, VALUE name, VALUE external_id, V
472
893
  NIL_P(system_id) ? NULL : (const xmlChar *)StringValueCStr(system_id)
473
894
  );
474
895
 
475
- if(!dtd) { return Qnil; }
896
+ if (!dtd) { return Qnil; }
476
897
 
477
- return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
898
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
478
899
  }
479
900
 
480
901
  /*
481
- * call-seq:
482
- * external_subset
902
+ * :call-seq:
903
+ * external_subset()
483
904
  *
484
905
  * Get the external subset
485
906
  */
486
- static VALUE external_subset(VALUE self)
907
+ static VALUE
908
+ external_subset(VALUE self)
487
909
  {
488
910
  xmlNodePtr node;
489
911
  xmlDocPtr doc;
490
912
  xmlDtdPtr dtd;
491
913
 
492
- Data_Get_Struct(self, xmlNode, node);
914
+ Noko_Node_Get_Struct(self, xmlNode, node);
493
915
 
494
- if(!node->doc) { return Qnil; }
916
+ if (!node->doc) { return Qnil; }
495
917
 
496
918
  doc = node->doc;
497
919
  dtd = doc->extSubset;
498
920
 
499
- if(!dtd) { return Qnil; }
921
+ if (!dtd) { return Qnil; }
500
922
 
501
- return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
923
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
502
924
  }
503
925
 
504
926
  /*
505
- * call-seq:
506
- * internal_subset
927
+ * :call-seq:
928
+ * internal_subset()
507
929
  *
508
930
  * Get the internal subset
509
931
  */
510
- static VALUE internal_subset(VALUE self)
932
+ static VALUE
933
+ internal_subset(VALUE self)
511
934
  {
512
935
  xmlNodePtr node;
513
936
  xmlDocPtr doc;
514
937
  xmlDtdPtr dtd;
515
938
 
516
- Data_Get_Struct(self, xmlNode, node);
939
+ Noko_Node_Get_Struct(self, xmlNode, node);
517
940
 
518
- if(!node->doc) { return Qnil; }
941
+ if (!node->doc) { return Qnil; }
519
942
 
520
943
  doc = node->doc;
521
944
  dtd = xmlGetIntSubset(doc);
522
945
 
523
- if(!dtd) { return Qnil; }
946
+ if (!dtd) { return Qnil; }
524
947
 
525
- return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
948
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
526
949
  }
527
950
 
528
951
  /*
529
- * call-seq:
530
- * dup
531
- * dup(depth)
532
- * dup(depth, new_parent_doc)
952
+ * :call-seq:
953
+ * dup → Nokogiri::XML::Node
954
+ * dup(depth) → Nokogiri::XML::Node
955
+ * dup(depth, new_parent_doc) → Nokogiri::XML::Node
533
956
  *
534
957
  * Copy this node.
535
- * An optional depth may be passed in. 0 is a shallow copy, 1 (the default) is a deep copy.
536
- * An optional new_parent_doc may also be passed in, which will be the new
537
- * node's parent document. Defaults to the current node's document.
538
- * current document.
958
+ *
959
+ * [Parameters]
960
+ * - +depth+ 0 is a shallow copy, 1 (the default) is a deep copy.
961
+ * - +new_parent_doc+
962
+ * The new node's parent Document. Defaults to the this node's document.
963
+ *
964
+ * [Returns] The new Nokgiri::XML::Node
539
965
  */
540
- static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
966
+ static VALUE
967
+ duplicate_node(int argc, VALUE *argv, VALUE self)
541
968
  {
542
969
  VALUE r_level, r_new_parent_doc;
543
970
  int level;
@@ -545,7 +972,7 @@ static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
545
972
  xmlDocPtr new_parent_doc;
546
973
  xmlNodePtr node, dup;
547
974
 
548
- Data_Get_Struct(self, xmlNode, node);
975
+ Noko_Node_Get_Struct(self, xmlNode, node);
549
976
 
550
977
  n_args = rb_scan_args(argc, argv, "02", &r_level, &r_new_parent_doc);
551
978
 
@@ -561,40 +988,29 @@ static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
561
988
  }
562
989
 
563
990
  dup = xmlDocCopyNode(node, new_parent_doc, level);
564
- if(dup == NULL) { return Qnil; }
991
+ if (dup == NULL) { return Qnil; }
565
992
 
566
- nokogiri_root_node(dup);
993
+ noko_xml_document_pin_node(dup);
567
994
 
568
- return Nokogiri_wrap_xml_node(rb_obj_class(self), dup);
995
+ return noko_xml_node_wrap(rb_obj_class(self), dup);
569
996
  }
570
997
 
571
998
  /*
572
- * call-seq:
573
- * unlink
999
+ * :call-seq:
1000
+ * unlink() → self
574
1001
  *
575
1002
  * Unlink this node from its current context.
576
1003
  */
577
- static VALUE unlink_node(VALUE self)
1004
+ static VALUE
1005
+ unlink_node(VALUE self)
578
1006
  {
579
1007
  xmlNodePtr node;
580
- Data_Get_Struct(self, xmlNode, node);
1008
+ Noko_Node_Get_Struct(self, xmlNode, node);
581
1009
  xmlUnlinkNode(node);
582
- nokogiri_root_node(node);
1010
+ noko_xml_document_pin_node(node);
583
1011
  return self;
584
1012
  }
585
1013
 
586
- /*
587
- * call-seq:
588
- * blank?
589
- *
590
- * Is this node blank?
591
- */
592
- static VALUE blank_eh(VALUE self)
593
- {
594
- xmlNodePtr node;
595
- Data_Get_Struct(self, xmlNode, node);
596
- return (1 == xmlIsBlankNode(node)) ? Qtrue : Qfalse ;
597
- }
598
1014
 
599
1015
  /*
600
1016
  * call-seq:
@@ -602,15 +1018,16 @@ static VALUE blank_eh(VALUE self)
602
1018
  *
603
1019
  * Returns the next sibling node
604
1020
  */
605
- static VALUE next_sibling(VALUE self)
1021
+ static VALUE
1022
+ next_sibling(VALUE self)
606
1023
  {
607
1024
  xmlNodePtr node, sibling;
608
- Data_Get_Struct(self, xmlNode, node);
1025
+ Noko_Node_Get_Struct(self, xmlNode, node);
609
1026
 
610
1027
  sibling = node->next;
611
- if(!sibling) { return Qnil; }
1028
+ if (!sibling) { return Qnil; }
612
1029
 
613
- return Nokogiri_wrap_xml_node(Qnil, sibling) ;
1030
+ return noko_xml_node_wrap(Qnil, sibling) ;
614
1031
  }
615
1032
 
616
1033
  /*
@@ -619,15 +1036,16 @@ static VALUE next_sibling(VALUE self)
619
1036
  *
620
1037
  * Returns the previous sibling node
621
1038
  */
622
- static VALUE previous_sibling(VALUE self)
1039
+ static VALUE
1040
+ previous_sibling(VALUE self)
623
1041
  {
624
1042
  xmlNodePtr node, sibling;
625
- Data_Get_Struct(self, xmlNode, node);
1043
+ Noko_Node_Get_Struct(self, xmlNode, node);
626
1044
 
627
1045
  sibling = node->prev;
628
- if(!sibling) { return Qnil; }
1046
+ if (!sibling) { return Qnil; }
629
1047
 
630
- return Nokogiri_wrap_xml_node(Qnil, sibling);
1048
+ return noko_xml_node_wrap(Qnil, sibling);
631
1049
  }
632
1050
 
633
1051
  /*
@@ -636,15 +1054,16 @@ static VALUE previous_sibling(VALUE self)
636
1054
  *
637
1055
  * Returns the next Nokogiri::XML::Element type sibling node.
638
1056
  */
639
- static VALUE next_element(VALUE self)
1057
+ static VALUE
1058
+ next_element(VALUE self)
640
1059
  {
641
1060
  xmlNodePtr node, sibling;
642
- Data_Get_Struct(self, xmlNode, node);
1061
+ Noko_Node_Get_Struct(self, xmlNode, node);
643
1062
 
644
1063
  sibling = xmlNextElementSibling(node);
645
- if(!sibling) { return Qnil; }
1064
+ if (!sibling) { return Qnil; }
646
1065
 
647
- return Nokogiri_wrap_xml_node(Qnil, sibling);
1066
+ return noko_xml_node_wrap(Qnil, sibling);
648
1067
  }
649
1068
 
650
1069
  /*
@@ -653,82 +1072,60 @@ static VALUE next_element(VALUE self)
653
1072
  *
654
1073
  * Returns the previous Nokogiri::XML::Element type sibling node.
655
1074
  */
656
- static VALUE previous_element(VALUE self)
1075
+ static VALUE
1076
+ previous_element(VALUE self)
657
1077
  {
658
1078
  xmlNodePtr node, sibling;
659
- Data_Get_Struct(self, xmlNode, node);
1079
+ Noko_Node_Get_Struct(self, xmlNode, node);
660
1080
 
661
1081
  /*
662
1082
  * note that we don't use xmlPreviousElementSibling here because it's buggy pre-2.7.7.
663
1083
  */
664
1084
  sibling = node->prev;
665
- if(!sibling) { return Qnil; }
1085
+ if (!sibling) { return Qnil; }
666
1086
 
667
- while(sibling && sibling->type != XML_ELEMENT_NODE) {
1087
+ while (sibling && sibling->type != XML_ELEMENT_NODE) {
668
1088
  sibling = sibling->prev;
669
1089
  }
670
1090
 
671
- return sibling ? Nokogiri_wrap_xml_node(Qnil, sibling) : Qnil ;
1091
+ return sibling ? noko_xml_node_wrap(Qnil, sibling) : Qnil ;
672
1092
  }
673
1093
 
674
1094
  /* :nodoc: */
675
- static VALUE replace(VALUE self, VALUE new_node)
1095
+ static VALUE
1096
+ replace(VALUE self, VALUE new_node)
676
1097
  {
677
1098
  VALUE reparent = reparent_node_with(self, new_node, xmlReplaceNodeWrapper);
678
1099
 
679
1100
  xmlNodePtr pivot;
680
- Data_Get_Struct(self, xmlNode, pivot);
681
- nokogiri_root_node(pivot);
1101
+ Noko_Node_Get_Struct(self, xmlNode, pivot);
1102
+ noko_xml_document_pin_node(pivot);
682
1103
 
683
1104
  return reparent;
684
1105
  }
685
1106
 
686
1107
  /*
687
- * call-seq:
688
- * children
1108
+ * :call-seq:
1109
+ * element_children() → NodeSet
1110
+ * elements() → NodeSet
689
1111
  *
690
- * Get the list of children for this node as a NodeSet
691
- */
692
- static VALUE children(VALUE self)
693
- {
694
- xmlNodePtr node;
695
- xmlNodePtr child;
696
- xmlNodeSetPtr set;
697
- VALUE document;
698
- VALUE node_set;
699
-
700
- Data_Get_Struct(self, xmlNode, node);
701
-
702
- child = node->children;
703
- set = xmlXPathNodeSetCreate(child);
704
-
705
- document = DOC_RUBY_OBJECT(node->doc);
706
-
707
- if(!child) { return Nokogiri_wrap_xml_node_set(set, document); }
708
-
709
- child = child->next;
710
- while(NULL != child) {
711
- xmlXPathNodeSetAddUnique(set, child);
712
- child = child->next;
713
- }
714
-
715
- node_set = Nokogiri_wrap_xml_node_set(set, document);
716
-
717
- return node_set;
718
- }
719
-
720
- /*
721
- * call-seq:
722
- * element_children
1112
+ * [Returns]
1113
+ * The node's child elements as a NodeSet. Only children that are elements will be returned, which
1114
+ * notably excludes Text nodes.
723
1115
  *
724
- * Get the list of children for this node as a NodeSet. All nodes will be
725
- * element nodes.
1116
+ * *Example:*
726
1117
  *
727
- * Example:
1118
+ * Note that #children returns the Text node "hello" while #element_children does not.
728
1119
  *
729
- * @doc.root.element_children.all? { |x| x.element? } # => true
1120
+ * div = Nokogiri::HTML5("<div>hello<span>world</span>").at_css("div")
1121
+ * div.element_children
1122
+ * # => [#<Nokogiri::XML::Element:0x50 name="span" children=[#<Nokogiri::XML::Text:0x3c "world">]>]
1123
+ * div.children
1124
+ * # => [#<Nokogiri::XML::Text:0x64 "hello">,
1125
+ * # #<Nokogiri::XML::Element:0x50 name="span" children=[#<Nokogiri::XML::Text:0x3c "world">]>]
730
1126
  */
731
- static VALUE element_children(VALUE self)
1127
+ static VALUE
1128
+ rb_xml_node_element_children(VALUE self)
732
1129
  {
733
1130
  xmlNodePtr node;
734
1131
  xmlNodePtr child;
@@ -736,83 +1133,78 @@ static VALUE element_children(VALUE self)
736
1133
  VALUE document;
737
1134
  VALUE node_set;
738
1135
 
739
- Data_Get_Struct(self, xmlNode, node);
1136
+ Noko_Node_Get_Struct(self, xmlNode, node);
740
1137
 
741
1138
  child = xmlFirstElementChild(node);
742
1139
  set = xmlXPathNodeSetCreate(child);
743
1140
 
744
1141
  document = DOC_RUBY_OBJECT(node->doc);
745
1142
 
746
- if(!child) { return Nokogiri_wrap_xml_node_set(set, document); }
747
-
748
- child = xmlNextElementSibling(child);
749
- while(NULL != child) {
750
- xmlXPathNodeSetAddUnique(set, child);
751
- child = xmlNextElementSibling(child);
752
- }
753
-
754
- node_set = Nokogiri_wrap_xml_node_set(set, document);
1143
+ if (!child) { return noko_xml_node_set_wrap(set, document); }
755
1144
 
756
- return node_set;
757
- }
758
-
759
- /*
760
- * call-seq:
761
- * child
762
- *
763
- * Returns the child node
764
- */
765
- static VALUE child(VALUE self)
766
- {
767
- xmlNodePtr node, child;
768
- Data_Get_Struct(self, xmlNode, node);
1145
+ child = xmlNextElementSibling(child);
1146
+ while (NULL != child) {
1147
+ xmlXPathNodeSetAddUnique(set, child);
1148
+ child = xmlNextElementSibling(child);
1149
+ }
769
1150
 
770
- child = node->children;
771
- if(!child) { return Qnil; }
1151
+ node_set = noko_xml_node_set_wrap(set, document);
772
1152
 
773
- return Nokogiri_wrap_xml_node(Qnil, child);
1153
+ return node_set;
774
1154
  }
775
1155
 
776
1156
  /*
777
- * call-seq:
778
- * first_element_child
1157
+ * :call-seq:
1158
+ * first_element_child() → Node
779
1159
  *
780
- * Returns the first child node of this node that is an element.
1160
+ * [Returns] The first child Node that is an element.
781
1161
  *
782
- * Example:
1162
+ * *Example:*
783
1163
  *
784
- * @doc.root.first_element_child.element? # => true
1164
+ * Note that the "hello" child, which is a Text node, is skipped and the <tt><span></tt> element is
1165
+ * returned.
1166
+ *
1167
+ * div = Nokogiri::HTML5("<div>hello<span>world</span>").at_css("div")
1168
+ * div.first_element_child
1169
+ * # => #(Element:0x3c { name = "span", children = [ #(Text "world")] })
785
1170
  */
786
- static VALUE first_element_child(VALUE self)
1171
+ static VALUE
1172
+ rb_xml_node_first_element_child(VALUE self)
787
1173
  {
788
1174
  xmlNodePtr node, child;
789
- Data_Get_Struct(self, xmlNode, node);
1175
+ Noko_Node_Get_Struct(self, xmlNode, node);
790
1176
 
791
1177
  child = xmlFirstElementChild(node);
792
- if(!child) { return Qnil; }
1178
+ if (!child) { return Qnil; }
793
1179
 
794
- return Nokogiri_wrap_xml_node(Qnil, child);
1180
+ return noko_xml_node_wrap(Qnil, child);
795
1181
  }
796
1182
 
797
1183
  /*
798
- * call-seq:
799
- * last_element_child
1184
+ * :call-seq:
1185
+ * last_element_child() → Node
800
1186
  *
801
- * Returns the last child node of this node that is an element.
1187
+ * [Returns] The last child Node that is an element.
802
1188
  *
803
- * Example:
1189
+ * *Example:*
804
1190
  *
805
- * @doc.root.last_element_child.element? # => true
1191
+ * Note that the "hello" child, which is a Text node, is skipped and the <tt><span>yes</span></tt>
1192
+ * element is returned.
1193
+ *
1194
+ * div = Nokogiri::HTML5("<div><span>no</span><span>yes</span>skip</div>").at_css("div")
1195
+ * div.last_element_child
1196
+ * # => #(Element:0x3c { name = "span", children = [ #(Text "yes")] })
806
1197
  */
807
- static VALUE last_element_child(VALUE self)
1198
+ static VALUE
1199
+ rb_xml_node_last_element_child(VALUE self)
808
1200
  {
809
1201
  xmlNodePtr node, child;
810
- Data_Get_Struct(self, xmlNode, node);
1202
+ Noko_Node_Get_Struct(self, xmlNode, node);
811
1203
 
812
1204
  child = xmlLastElementChild(node);
813
- if(!child) { return Qnil; }
1205
+ if (!child) { return Qnil; }
814
1206
 
815
- return Nokogiri_wrap_xml_node(Qnil, child);
1207
+ return noko_xml_node_wrap(Qnil, child);
816
1208
  }
817
1209
 
818
1210
  /*
@@ -821,11 +1213,12 @@ static VALUE last_element_child(VALUE self)
821
1213
  *
822
1214
  * Returns true if +attribute+ is set
823
1215
  */
824
- static VALUE key_eh(VALUE self, VALUE attribute)
1216
+ static VALUE
1217
+ key_eh(VALUE self, VALUE attribute)
825
1218
  {
826
1219
  xmlNodePtr node;
827
- Data_Get_Struct(self, xmlNode, node);
828
- if(xmlHasProp(node, (xmlChar *)StringValueCStr(attribute))) {
1220
+ Noko_Node_Get_Struct(self, xmlNode, node);
1221
+ if (xmlHasProp(node, (xmlChar *)StringValueCStr(attribute))) {
829
1222
  return Qtrue;
830
1223
  }
831
1224
  return Qfalse;
@@ -837,12 +1230,13 @@ static VALUE key_eh(VALUE self, VALUE attribute)
837
1230
  *
838
1231
  * Returns true if +attribute+ is set with +namespace+
839
1232
  */
840
- static VALUE namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
1233
+ static VALUE
1234
+ namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
841
1235
  {
842
1236
  xmlNodePtr node;
843
- Data_Get_Struct(self, xmlNode, node);
844
- if(xmlHasNsProp(node, (xmlChar *)StringValueCStr(attribute),
845
- NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace))) {
1237
+ Noko_Node_Get_Struct(self, xmlNode, node);
1238
+ if (xmlHasNsProp(node, (xmlChar *)StringValueCStr(attribute),
1239
+ NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace))) {
846
1240
  return Qtrue;
847
1241
  }
848
1242
  return Qfalse;
@@ -854,11 +1248,12 @@ static VALUE namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
854
1248
  *
855
1249
  * Set the +property+ to +value+
856
1250
  */
857
- static VALUE set(VALUE self, VALUE property, VALUE value)
1251
+ static VALUE
1252
+ set(VALUE self, VALUE property, VALUE value)
858
1253
  {
859
1254
  xmlNodePtr node, cur;
860
1255
  xmlAttrPtr prop;
861
- Data_Get_Struct(self, xmlNode, node);
1256
+ Noko_Node_Get_Struct(self, xmlNode, node);
862
1257
 
863
1258
  /* If a matching attribute node already exists, then xmlSetProp will destroy
864
1259
  * the existing node's children. However, if Nokogiri has a node object
@@ -867,13 +1262,13 @@ static VALUE set(VALUE self, VALUE property, VALUE value)
867
1262
  * We can avoid this by unlinking these nodes first.
868
1263
  */
869
1264
  if (node->type != XML_ELEMENT_NODE) {
870
- return(Qnil);
1265
+ return (Qnil);
871
1266
  }
872
1267
  prop = xmlHasProp(node, (xmlChar *)StringValueCStr(property));
873
1268
  if (prop && prop->children) {
874
1269
  for (cur = prop->children; cur; cur = cur->next) {
875
1270
  if (cur->_private) {
876
- nokogiri_root_node(cur);
1271
+ noko_xml_document_pin_node(cur);
877
1272
  xmlUnlinkNode(cur);
878
1273
  }
879
1274
  }
@@ -891,7 +1286,8 @@ static VALUE set(VALUE self, VALUE property, VALUE value)
891
1286
  *
892
1287
  * Get the value for +attribute+
893
1288
  */
894
- static VALUE get(VALUE self, VALUE rattribute)
1289
+ static VALUE
1290
+ get(VALUE self, VALUE rattribute)
895
1291
  {
896
1292
  xmlNodePtr node;
897
1293
  xmlChar *value = 0;
@@ -902,10 +1298,10 @@ static VALUE get(VALUE self, VALUE rattribute)
902
1298
 
903
1299
  if (NIL_P(rattribute)) { return Qnil; }
904
1300
 
905
- Data_Get_Struct(self, xmlNode, node);
1301
+ Noko_Node_Get_Struct(self, xmlNode, node);
906
1302
  attribute = xmlCharStrdup(StringValueCStr(rattribute));
907
1303
 
908
- colon = (xmlChar *)(uintptr_t)xmlStrchr(attribute, (const xmlChar)':');
1304
+ colon = DISCARD_CONST_QUAL_XMLCHAR(xmlStrchr(attribute, (const xmlChar)':'));
909
1305
  if (colon) {
910
1306
  /* split the attribute string into separate prefix and name by
911
1307
  * null-terminating the prefix at the colon */
@@ -917,7 +1313,7 @@ static VALUE get(VALUE self, VALUE rattribute)
917
1313
  if (ns) {
918
1314
  value = xmlGetNsProp(node, attr_name, ns->href);
919
1315
  } else {
920
- value = xmlGetProp(node, (xmlChar*)StringValueCStr(rattribute));
1316
+ value = xmlGetProp(node, (xmlChar *)StringValueCStr(rattribute));
921
1317
  }
922
1318
  } else {
923
1319
  value = xmlGetNoNsProp(node, attribute);
@@ -938,15 +1334,16 @@ static VALUE get(VALUE self, VALUE rattribute)
938
1334
  *
939
1335
  * Set the namespace to +namespace+
940
1336
  */
941
- static VALUE set_namespace(VALUE self, VALUE namespace)
1337
+ static VALUE
1338
+ set_namespace(VALUE self, VALUE namespace)
942
1339
  {
943
1340
  xmlNodePtr node;
944
1341
  xmlNsPtr ns = NULL;
945
1342
 
946
- Data_Get_Struct(self, xmlNode, node);
1343
+ Noko_Node_Get_Struct(self, xmlNode, node);
947
1344
 
948
- if(!NIL_P(namespace)) {
949
- Data_Get_Struct(namespace, xmlNs, ns);
1345
+ if (!NIL_P(namespace)) {
1346
+ Noko_Namespace_Get_Struct(namespace, xmlNs, ns);
950
1347
  }
951
1348
 
952
1349
  xmlSetNs(node, ns);
@@ -955,138 +1352,140 @@ static VALUE set_namespace(VALUE self, VALUE namespace)
955
1352
  }
956
1353
 
957
1354
  /*
958
- * call-seq:
959
- * attribute(name)
1355
+ * :call-seq:
1356
+ * namespace() → Namespace
960
1357
  *
961
- * Get the attribute node with +name+
962
- */
963
- static VALUE attr(VALUE self, VALUE name)
964
- {
965
- xmlNodePtr node;
966
- xmlAttrPtr prop;
967
- Data_Get_Struct(self, xmlNode, node);
968
- prop = xmlHasProp(node, (xmlChar *)StringValueCStr(name));
969
-
970
- if(! prop) { return Qnil; }
971
- return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)prop);
972
- }
973
-
974
- /*
975
- * call-seq:
976
- * attribute_with_ns(name, namespace)
1358
+ * [Returns] The Namespace of the element or attribute node, or +nil+ if there is no namespace.
977
1359
  *
978
- * Get the attribute node with +name+ and +namespace+
979
- */
980
- static VALUE attribute_with_ns(VALUE self, VALUE name, VALUE namespace)
981
- {
982
- xmlNodePtr node;
983
- xmlAttrPtr prop;
984
- Data_Get_Struct(self, xmlNode, node);
985
- prop = xmlHasNsProp(node, (xmlChar *)StringValueCStr(name),
986
- NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace));
987
-
988
- if(! prop) { return Qnil; }
989
- return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)prop);
990
- }
991
-
992
- /*
993
- * call-seq:
994
- * attribute_nodes()
1360
+ * *Example:*
995
1361
  *
996
- * returns a list containing the Node attributes.
1362
+ * doc = Nokogiri::XML(<<~EOF)
1363
+ * <root>
1364
+ * <first/>
1365
+ * <second xmlns="http://example.com/child"/>
1366
+ * <foo:third xmlns:foo="http://example.com/foo"/>
1367
+ * </root>
1368
+ * EOF
1369
+ * doc.at_xpath("//first").namespace
1370
+ * # => nil
1371
+ * doc.at_xpath("//xmlns:second", "xmlns" => "http://example.com/child").namespace
1372
+ * # => #(Namespace:0x3c { href = "http://example.com/child" })
1373
+ * doc.at_xpath("//foo:third", "foo" => "http://example.com/foo").namespace
1374
+ * # => #(Namespace:0x50 { prefix = "foo", href = "http://example.com/foo" })
997
1375
  */
998
- static VALUE attribute_nodes(VALUE self)
1376
+ static VALUE
1377
+ rb_xml_node_namespace(VALUE rb_node)
999
1378
  {
1000
- /* this code in the mode of xmlHasProp() */
1001
- xmlNodePtr node;
1002
- VALUE attr;
1379
+ xmlNodePtr c_node ;
1380
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1003
1381
 
1004
- Data_Get_Struct(self, xmlNode, node);
1005
-
1006
- attr = rb_ary_new();
1007
- Nokogiri_xml_node_properties(node, attr);
1382
+ if (c_node->ns) {
1383
+ return noko_xml_namespace_wrap(c_node->ns, c_node->doc);
1384
+ }
1008
1385
 
1009
- return attr ;
1386
+ return Qnil ;
1010
1387
  }
1011
1388
 
1012
-
1013
1389
  /*
1014
- * call-seq:
1015
- * namespace()
1390
+ * :call-seq:
1391
+ * namespace_definitions() → Array<Nokogiri::XML::Namespace>
1016
1392
  *
1017
- * returns the namespace of the element or attribute node as a Namespace
1018
- * object, or nil if there is no namespace for the element or attribute.
1019
- */
1020
- static VALUE namespace(VALUE self)
1021
- {
1022
- xmlNodePtr node ;
1023
- Data_Get_Struct(self, xmlNode, node);
1024
-
1025
- if (node->ns) {
1026
- return Nokogiri_wrap_xml_namespace(node->doc, node->ns);
1027
- }
1028
-
1029
- return Qnil ;
1030
- }
1031
-
1032
- /*
1033
- * call-seq:
1034
- * namespace_definitions()
1393
+ * [Returns]
1394
+ * Namespaces that are defined directly on this node, as an Array of Namespace objects. The array
1395
+ * will be empty if no namespaces are defined on this node.
1035
1396
  *
1036
- * returns namespaces defined on self element directly, as an array of Namespace objects. Includes both a default namespace (as in"xmlns="), and prefixed namespaces (as in "xmlns:prefix=").
1397
+ * *Example:*
1398
+ *
1399
+ * doc = Nokogiri::XML(<<~EOF)
1400
+ * <root xmlns="http://example.com/root">
1401
+ * <first/>
1402
+ * <second xmlns="http://example.com/child" xmlns:unused="http://example.com/unused"/>
1403
+ * <foo:third xmlns:foo="http://example.com/foo"/>
1404
+ * </root>
1405
+ * EOF
1406
+ * doc.at_xpath("//root:first", "root" => "http://example.com/root").namespace_definitions
1407
+ * # => []
1408
+ * doc.at_xpath("//xmlns:second", "xmlns" => "http://example.com/child").namespace_definitions
1409
+ * # => [#(Namespace:0x3c { href = "http://example.com/child" }),
1410
+ * # #(Namespace:0x50 {
1411
+ * # prefix = "unused",
1412
+ * # href = "http://example.com/unused"
1413
+ * # })]
1414
+ * doc.at_xpath("//foo:third", "foo" => "http://example.com/foo").namespace_definitions
1415
+ * # => [#(Namespace:0x64 { prefix = "foo", href = "http://example.com/foo" })]
1037
1416
  */
1038
- static VALUE namespace_definitions(VALUE self)
1417
+ static VALUE
1418
+ namespace_definitions(VALUE rb_node)
1039
1419
  {
1040
1420
  /* this code in the mode of xmlHasProp() */
1041
- xmlNodePtr node ;
1042
- VALUE list;
1043
- xmlNsPtr ns;
1044
-
1045
- Data_Get_Struct(self, xmlNode, node);
1421
+ xmlNodePtr c_node ;
1422
+ xmlNsPtr c_namespace;
1423
+ VALUE definitions = rb_ary_new();
1046
1424
 
1047
- list = rb_ary_new();
1425
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1048
1426
 
1049
- ns = node->nsDef;
1050
-
1051
- if(!ns) { return list; }
1427
+ c_namespace = c_node->nsDef;
1428
+ if (!c_namespace) {
1429
+ return definitions;
1430
+ }
1052
1431
 
1053
- while(NULL != ns) {
1054
- rb_ary_push(list, Nokogiri_wrap_xml_namespace(node->doc, ns));
1055
- ns = ns->next;
1432
+ while (c_namespace != NULL) {
1433
+ rb_ary_push(definitions, noko_xml_namespace_wrap(c_namespace, c_node->doc));
1434
+ c_namespace = c_namespace->next;
1056
1435
  }
1057
1436
 
1058
- return list;
1437
+ return definitions;
1059
1438
  }
1060
1439
 
1061
1440
  /*
1062
- * call-seq:
1063
- * namespace_scopes()
1441
+ * :call-seq:
1442
+ * namespace_scopes() → Array<Nokogiri::XML::Namespace>
1064
1443
  *
1065
- * returns namespaces in scope for self -- those defined on self element
1066
- * directly or any ancestor node -- as an array of Namespace objects. Default
1067
- * namespaces ("xmlns=" style) for self are included in this array; Default
1068
- * namespaces for ancestors, however, are not. See also #namespaces
1444
+ * [Returns] Array of all the Namespaces on this node and its ancestors.
1445
+ *
1446
+ * See also #namespaces
1447
+ *
1448
+ * *Example:*
1449
+ *
1450
+ * doc = Nokogiri::XML(<<~EOF)
1451
+ * <root xmlns="http://example.com/root" xmlns:bar="http://example.com/bar">
1452
+ * <first/>
1453
+ * <second xmlns="http://example.com/child"/>
1454
+ * <third xmlns:foo="http://example.com/foo"/>
1455
+ * </root>
1456
+ * EOF
1457
+ * doc.at_xpath("//root:first", "root" => "http://example.com/root").namespace_scopes
1458
+ * # => [#(Namespace:0x3c { href = "http://example.com/root" }),
1459
+ * # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
1460
+ * doc.at_xpath("//child:second", "child" => "http://example.com/child").namespace_scopes
1461
+ * # => [#(Namespace:0x64 { href = "http://example.com/child" }),
1462
+ * # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
1463
+ * doc.at_xpath("//root:third", "root" => "http://example.com/root").namespace_scopes
1464
+ * # => [#(Namespace:0x78 { prefix = "foo", href = "http://example.com/foo" }),
1465
+ * # #(Namespace:0x3c { href = "http://example.com/root" }),
1466
+ * # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
1069
1467
  */
1070
- static VALUE namespace_scopes(VALUE self)
1468
+ static VALUE
1469
+ rb_xml_node_namespace_scopes(VALUE rb_node)
1071
1470
  {
1072
- xmlNodePtr node ;
1073
- VALUE list;
1074
- xmlNsPtr *ns_list;
1471
+ xmlNodePtr c_node ;
1472
+ xmlNsPtr *namespaces;
1473
+ VALUE scopes = rb_ary_new();
1075
1474
  int j;
1076
1475
 
1077
- Data_Get_Struct(self, xmlNode, node);
1476
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1078
1477
 
1079
- list = rb_ary_new();
1080
- ns_list = xmlGetNsList(node->doc, node);
1081
-
1082
- if(!ns_list) { return list; }
1478
+ namespaces = xmlGetNsList(c_node->doc, c_node);
1479
+ if (!namespaces) {
1480
+ return scopes;
1481
+ }
1083
1482
 
1084
- for (j = 0 ; ns_list[j] != NULL ; ++j) {
1085
- rb_ary_push(list, Nokogiri_wrap_xml_namespace(node->doc, ns_list[j]));
1483
+ for (j = 0 ; namespaces[j] != NULL ; ++j) {
1484
+ rb_ary_push(scopes, noko_xml_namespace_wrap(namespaces[j], c_node->doc));
1086
1485
  }
1087
1486
 
1088
- xmlFree(ns_list);
1089
- return list;
1487
+ xmlFree(namespaces);
1488
+ return scopes;
1090
1489
  }
1091
1490
 
1092
1491
  /*
@@ -1095,11 +1494,12 @@ static VALUE namespace_scopes(VALUE self)
1095
1494
  *
1096
1495
  * Get the type for this Node
1097
1496
  */
1098
- static VALUE node_type(VALUE self)
1497
+ static VALUE
1498
+ node_type(VALUE self)
1099
1499
  {
1100
1500
  xmlNodePtr node;
1101
- Data_Get_Struct(self, xmlNode, node);
1102
- return INT2NUM((long)node->type);
1501
+ Noko_Node_Get_Struct(self, xmlNode, node);
1502
+ return INT2NUM(node->type);
1103
1503
  }
1104
1504
 
1105
1505
  /*
@@ -1108,16 +1508,17 @@ static VALUE node_type(VALUE self)
1108
1508
  *
1109
1509
  * Set the content for this Node
1110
1510
  */
1111
- static VALUE set_native_content(VALUE self, VALUE content)
1511
+ static VALUE
1512
+ set_native_content(VALUE self, VALUE content)
1112
1513
  {
1113
1514
  xmlNodePtr node, child, next ;
1114
- Data_Get_Struct(self, xmlNode, node);
1515
+ Noko_Node_Get_Struct(self, xmlNode, node);
1115
1516
 
1116
1517
  child = node->children;
1117
1518
  while (NULL != child) {
1118
1519
  next = child->next ;
1119
1520
  xmlUnlinkNode(child) ;
1120
- nokogiri_root_node(child);
1521
+ noko_xml_document_pin_node(child);
1121
1522
  child = next ;
1122
1523
  }
1123
1524
 
@@ -1125,42 +1526,20 @@ static VALUE set_native_content(VALUE self, VALUE content)
1125
1526
  return content;
1126
1527
  }
1127
1528
 
1128
- /*
1129
- * call-seq:
1130
- * content
1131
- *
1132
- * Returns the plaintext content for this Node. Note that entities will always
1133
- * be expanded in the returned string.
1134
- */
1135
- static VALUE get_native_content(VALUE self)
1136
- {
1137
- xmlNodePtr node;
1138
- xmlChar * content;
1139
-
1140
- Data_Get_Struct(self, xmlNode, node);
1141
-
1142
- content = xmlNodeGetContent(node);
1143
- if(content) {
1144
- VALUE rval = NOKOGIRI_STR_NEW2(content);
1145
- xmlFree(content);
1146
- return rval;
1147
- }
1148
- return Qnil;
1149
- }
1150
-
1151
1529
  /*
1152
1530
  * call-seq:
1153
1531
  * lang=
1154
1532
  *
1155
1533
  * Set the language of a node, i.e. the values of the xml:lang attribute.
1156
1534
  */
1157
- static VALUE set_lang(VALUE self_rb, VALUE lang_rb)
1535
+ static VALUE
1536
+ set_lang(VALUE self_rb, VALUE lang_rb)
1158
1537
  {
1159
1538
  xmlNodePtr self ;
1160
- xmlChar* lang ;
1539
+ xmlChar *lang ;
1161
1540
 
1162
- Data_Get_Struct(self_rb, xmlNode, self);
1163
- lang = (xmlChar*)StringValueCStr(lang_rb);
1541
+ Noko_Node_Get_Struct(self_rb, xmlNode, self);
1542
+ lang = (xmlChar *)StringValueCStr(lang_rb);
1164
1543
 
1165
1544
  xmlNodeSetLang(self, lang);
1166
1545
 
@@ -1174,13 +1553,14 @@ static VALUE set_lang(VALUE self_rb, VALUE lang_rb)
1174
1553
  * Searches the language of a node, i.e. the values of the xml:lang attribute or
1175
1554
  * the one carried by the nearest ancestor.
1176
1555
  */
1177
- static VALUE get_lang(VALUE self_rb)
1556
+ static VALUE
1557
+ get_lang(VALUE self_rb)
1178
1558
  {
1179
1559
  xmlNodePtr self ;
1180
- xmlChar* lang ;
1560
+ xmlChar *lang ;
1181
1561
  VALUE lang_rb ;
1182
1562
 
1183
- Data_Get_Struct(self_rb, xmlNode, self);
1563
+ Noko_Node_Get_Struct(self_rb, xmlNode, self);
1184
1564
 
1185
1565
  lang = xmlNodeGetLang(self);
1186
1566
  if (lang) {
@@ -1193,7 +1573,8 @@ static VALUE get_lang(VALUE self_rb)
1193
1573
  }
1194
1574
 
1195
1575
  /* :nodoc: */
1196
- static VALUE add_child(VALUE self, VALUE new_child)
1576
+ static VALUE
1577
+ add_child(VALUE self, VALUE new_child)
1197
1578
  {
1198
1579
  return reparent_node_with(self, new_child, xmlAddChild);
1199
1580
  }
@@ -1204,15 +1585,16 @@ static VALUE add_child(VALUE self, VALUE new_child)
1204
1585
  *
1205
1586
  * Get the parent Node for this Node
1206
1587
  */
1207
- static VALUE get_parent(VALUE self)
1588
+ static VALUE
1589
+ get_parent(VALUE self)
1208
1590
  {
1209
1591
  xmlNodePtr node, parent;
1210
- Data_Get_Struct(self, xmlNode, node);
1592
+ Noko_Node_Get_Struct(self, xmlNode, node);
1211
1593
 
1212
1594
  parent = node->parent;
1213
- if(!parent) { return Qnil; }
1595
+ if (!parent) { return Qnil; }
1214
1596
 
1215
- return Nokogiri_wrap_xml_node(Qnil, parent) ;
1597
+ return noko_xml_node_wrap(Qnil, parent) ;
1216
1598
  }
1217
1599
 
1218
1600
  /*
@@ -1221,11 +1603,12 @@ static VALUE get_parent(VALUE self)
1221
1603
  *
1222
1604
  * Set the name for this Node
1223
1605
  */
1224
- static VALUE set_name(VALUE self, VALUE new_name)
1606
+ static VALUE
1607
+ set_name(VALUE self, VALUE new_name)
1225
1608
  {
1226
1609
  xmlNodePtr node;
1227
- Data_Get_Struct(self, xmlNode, node);
1228
- xmlNodeSetName(node, (xmlChar*)StringValueCStr(new_name));
1610
+ Noko_Node_Get_Struct(self, xmlNode, node);
1611
+ xmlNodeSetName(node, (xmlChar *)StringValueCStr(new_name));
1229
1612
  return new_name;
1230
1613
  }
1231
1614
 
@@ -1235,11 +1618,12 @@ static VALUE set_name(VALUE self, VALUE new_name)
1235
1618
  *
1236
1619
  * Returns the name for this Node
1237
1620
  */
1238
- static VALUE get_name(VALUE self)
1621
+ static VALUE
1622
+ get_name(VALUE self)
1239
1623
  {
1240
1624
  xmlNodePtr node;
1241
- Data_Get_Struct(self, xmlNode, node);
1242
- if(node->name) {
1625
+ Noko_Node_Get_Struct(self, xmlNode, node);
1626
+ if (node->name) {
1243
1627
  return NOKOGIRI_STR_NEW2(node->name);
1244
1628
  }
1245
1629
  return Qnil;
@@ -1251,28 +1635,39 @@ static VALUE get_name(VALUE self)
1251
1635
  *
1252
1636
  * Returns the path associated with this Node
1253
1637
  */
1254
- static VALUE path(VALUE self)
1638
+ static VALUE
1639
+ rb_xml_node_path(VALUE rb_node)
1255
1640
  {
1256
- xmlNodePtr node;
1257
- xmlChar *path ;
1641
+ xmlNodePtr c_node;
1642
+ xmlChar *c_path ;
1258
1643
  VALUE rval;
1259
1644
 
1260
- Data_Get_Struct(self, xmlNode, node);
1645
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1646
+
1647
+ c_path = xmlGetNodePath(c_node);
1648
+ if (c_path == NULL) {
1649
+ // see https://github.com/sparklemotion/nokogiri/issues/2250
1650
+ // this behavior is clearly undesirable, but is what libxml <= 2.9.10 returned, and so we
1651
+ // do this for now to preserve the behavior across libxml2 versions.
1652
+ rval = NOKOGIRI_STR_NEW2("?");
1653
+ } else {
1654
+ rval = NOKOGIRI_STR_NEW2(c_path);
1655
+ xmlFree(c_path);
1656
+ }
1261
1657
 
1262
- path = xmlGetNodePath(node);
1263
- rval = NOKOGIRI_STR_NEW2(path);
1264
- xmlFree(path);
1265
1658
  return rval ;
1266
1659
  }
1267
1660
 
1268
1661
  /* :nodoc: */
1269
- static VALUE add_next_sibling(VALUE self, VALUE new_sibling)
1662
+ static VALUE
1663
+ add_next_sibling(VALUE self, VALUE new_sibling)
1270
1664
  {
1271
1665
  return reparent_node_with(self, new_sibling, xmlAddNextSibling) ;
1272
1666
  }
1273
1667
 
1274
1668
  /* :nodoc: */
1275
- static VALUE add_previous_sibling(VALUE self, VALUE new_sibling)
1669
+ static VALUE
1670
+ add_previous_sibling(VALUE self, VALUE new_sibling)
1276
1671
  {
1277
1672
  return reparent_node_with(self, new_sibling, xmlAddPrevSibling) ;
1278
1673
  }
@@ -1283,7 +1678,8 @@ static VALUE add_previous_sibling(VALUE self, VALUE new_sibling)
1283
1678
  *
1284
1679
  * Write this Node to +io+ with +encoding+ and +options+
1285
1680
  */
1286
- static VALUE native_write_to(
1681
+ static VALUE
1682
+ native_write_to(
1287
1683
  VALUE self,
1288
1684
  VALUE io,
1289
1685
  VALUE encoding,
@@ -1292,10 +1688,10 @@ static VALUE native_write_to(
1292
1688
  )
1293
1689
  {
1294
1690
  xmlNodePtr node;
1295
- const char * before_indent;
1691
+ const char *before_indent;
1296
1692
  xmlSaveCtxtPtr savectx;
1297
1693
 
1298
- Data_Get_Struct(self, xmlNode, node);
1694
+ Noko_Node_Get_Struct(self, xmlNode, node);
1299
1695
 
1300
1696
  xmlIndentTreeOutput = 1;
1301
1697
 
@@ -1304,8 +1700,8 @@ static VALUE native_write_to(
1304
1700
  xmlTreeIndentString = StringValueCStr(indent_string);
1305
1701
 
1306
1702
  savectx = xmlSaveToIO(
1307
- (xmlOutputWriteCallback)io_write_callback,
1308
- (xmlOutputCloseCallback)io_close_callback,
1703
+ (xmlOutputWriteCallback)noko_io_write,
1704
+ (xmlOutputCloseCallback)noko_io_close,
1309
1705
  (void *)io,
1310
1706
  RTEST(encoding) ? StringValueCStr(encoding) : NULL,
1311
1707
  (int)NUM2INT(options)
@@ -1318,93 +1714,366 @@ static VALUE native_write_to(
1318
1714
  return io;
1319
1715
  }
1320
1716
 
1321
- /*
1322
- * call-seq:
1323
- * line
1324
- *
1325
- * Returns the line for this Node
1326
- */
1327
- static VALUE line(VALUE self)
1717
+
1718
+ static inline void
1719
+ output_partial_string(VALUE out, char const *str, size_t length)
1328
1720
  {
1329
- xmlNodePtr node;
1330
- Data_Get_Struct(self, xmlNode, node);
1721
+ if (length) {
1722
+ rb_enc_str_buf_cat(out, str, (long)length, rb_utf8_encoding());
1723
+ }
1724
+ }
1331
1725
 
1332
- return INT2NUM(xmlGetLineNo(node));
1726
+ static inline void
1727
+ output_char(VALUE out, char ch)
1728
+ {
1729
+ output_partial_string(out, &ch, 1);
1333
1730
  }
1334
1731
 
1335
- /*
1336
- * call-seq:
1337
- * add_namespace_definition(prefix, href)
1338
- *
1339
- * Adds a namespace definition with +prefix+ using +href+ value. The result is
1340
- * as if parsed XML for this node had included an attribute
1341
- * 'xmlns:prefix=value'. A default namespace for this node ("xmlns=") can be
1342
- * added by passing 'nil' for prefix. Namespaces added this way will not
1343
- * show up in #attributes, but they will be included as an xmlns attribute
1344
- * when the node is serialized to XML.
1345
- */
1346
- static VALUE add_namespace_definition(VALUE self, VALUE prefix, VALUE href)
1732
+ static inline void
1733
+ output_string(VALUE out, char const *str)
1347
1734
  {
1348
- xmlNodePtr node, namespace;
1349
- xmlNsPtr ns;
1735
+ output_partial_string(out, str, strlen(str));
1736
+ }
1737
+
1738
+ static inline void
1739
+ output_tagname(VALUE out, xmlNodePtr elem)
1740
+ {
1741
+ // Elements in the HTML, MathML, and SVG namespaces do not use a namespace
1742
+ // prefix in the HTML syntax.
1743
+ char const *name = (char const *)elem->name;
1744
+ xmlNsPtr ns = elem->ns;
1745
+ if (ns && ns->href && ns->prefix
1746
+ && strcmp((char const *)ns->href, "http://www.w3.org/1999/xhtml")
1747
+ && strcmp((char const *)ns->href, "http://www.w3.org/1998/Math/MathML")
1748
+ && strcmp((char const *)ns->href, "http://www.w3.org/2000/svg")) {
1749
+ output_string(out, (char const *)elem->ns->prefix);
1750
+ output_char(out, ':');
1751
+ char const *colon = strchr(name, ':');
1752
+ if (colon) {
1753
+ name = colon + 1;
1754
+ }
1755
+ }
1756
+ output_string(out, name);
1757
+ }
1758
+
1759
+ static inline void
1760
+ output_attr_name(VALUE out, xmlAttrPtr attr)
1761
+ {
1762
+ xmlNsPtr ns = attr->ns;
1763
+ char const *name = (char const *)attr->name;
1764
+ if (ns && ns->href) {
1765
+ char const *uri = (char const *)ns->href;
1766
+ char const *localname = strchr(name, ':');
1767
+ if (localname) {
1768
+ ++localname;
1769
+ } else {
1770
+ localname = name;
1771
+ }
1772
+
1773
+ if (!strcmp(uri, "http://www.w3.org/XML/1998/namespace")) {
1774
+ output_string(out, "xml:");
1775
+ name = localname;
1776
+ } else if (!strcmp(uri, "http://www.w3.org/2000/xmlns/")) {
1777
+ // xmlns:xmlns -> xmlns
1778
+ // xmlns:foo -> xmlns:foo
1779
+ if (strcmp(localname, "xmlns")) {
1780
+ output_string(out, "xmlns:");
1781
+ }
1782
+ name = localname;
1783
+ } else if (!strcmp(uri, "http://www.w3.org/1999/xlink")) {
1784
+ output_string(out, "xlink:");
1785
+ name = localname;
1786
+ } else if (ns->prefix) {
1787
+ output_string(out, (char const *)ns->prefix);
1788
+ output_char(out, ':');
1789
+ name = localname;
1790
+ }
1791
+ }
1792
+ output_string(out, name);
1793
+ }
1794
+
1795
+ static void
1796
+ output_escaped_string(VALUE out, xmlChar const *start, bool attr)
1797
+ {
1798
+ xmlChar const *next = start;
1799
+ int ch;
1800
+
1801
+ while ((ch = *next) != 0) {
1802
+ char const *replacement = NULL;
1803
+ size_t replaced_bytes = 1;
1804
+ if (ch == '&') {
1805
+ replacement = "&amp;";
1806
+ } else if (ch == 0xC2 && next[1] == 0xA0) {
1807
+ // U+00A0 NO-BREAK SPACE has the UTF-8 encoding C2 A0.
1808
+ replacement = "&nbsp;";
1809
+ replaced_bytes = 2;
1810
+ } else if (attr && ch == '"') {
1811
+ replacement = "&quot;";
1812
+ } else if (!attr && ch == '<') {
1813
+ replacement = "&lt;";
1814
+ } else if (!attr && ch == '>') {
1815
+ replacement = "&gt;";
1816
+ } else {
1817
+ ++next;
1818
+ continue;
1819
+ }
1820
+ output_partial_string(out, (char const *)start, next - start);
1821
+ output_string(out, replacement);
1822
+ next += replaced_bytes;
1823
+ start = next;
1824
+ }
1825
+ output_partial_string(out, (char const *)start, next - start);
1826
+ }
1350
1827
 
1351
- Data_Get_Struct(self, xmlNode, node);
1352
- namespace = node ;
1828
+ static bool
1829
+ should_prepend_newline(xmlNodePtr node)
1830
+ {
1831
+ char const *name = (char const *)node->name;
1832
+ xmlNodePtr child = node->children;
1833
+
1834
+ if (!name || !child || (strcmp(name, "pre") && strcmp(name, "textarea") && strcmp(name, "listing"))) {
1835
+ return false;
1836
+ }
1837
+
1838
+ return child->type == XML_TEXT_NODE && child->content && child->content[0] == '\n';
1839
+ }
1353
1840
 
1354
- ns = xmlSearchNs(
1355
- node->doc,
1356
- node,
1357
- (const xmlChar *)(NIL_P(prefix) ? NULL : StringValueCStr(prefix))
1358
- );
1841
+ static VALUE
1842
+ rb_prepend_newline(VALUE self)
1843
+ {
1844
+ xmlNodePtr node;
1845
+ Noko_Node_Get_Struct(self, xmlNode, node);
1846
+ return should_prepend_newline(node) ? Qtrue : Qfalse;
1847
+ }
1359
1848
 
1360
- if(!ns) {
1361
- if (node->type != XML_ELEMENT_NODE) {
1362
- namespace = node->parent;
1849
+ static bool
1850
+ is_one_of(xmlNodePtr node, char const *const *tagnames, size_t num_tagnames)
1851
+ {
1852
+ char const *name = (char const *)node->name;
1853
+ if (name == NULL) { // fragments don't have a name
1854
+ return false;
1855
+ }
1856
+ for (size_t idx = 0; idx < num_tagnames; ++idx) {
1857
+ if (!strcmp(name, tagnames[idx])) {
1858
+ return true;
1363
1859
  }
1364
- ns = xmlNewNs(
1365
- namespace,
1366
- (const xmlChar *)StringValueCStr(href),
1367
- (const xmlChar *)(NIL_P(prefix) ? NULL : StringValueCStr(prefix))
1368
- );
1369
1860
  }
1861
+ return false;
1862
+
1863
+ }
1864
+
1865
+ static void
1866
+ output_node(
1867
+ VALUE out,
1868
+ xmlNodePtr node,
1869
+ bool preserve_newline
1870
+ )
1871
+ {
1872
+ static char const *const VOID_ELEMENTS[] = {
1873
+ "area", "base", "basefont", "bgsound", "br", "col", "embed", "frame", "hr",
1874
+ "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr",
1875
+ };
1876
+
1877
+ static char const *const UNESCAPED_TEXT_ELEMENTS[] = {
1878
+ "style", "script", "xmp", "iframe", "noembed", "noframes", "plaintext", "noscript",
1879
+ };
1880
+
1881
+ switch (node->type) {
1882
+ case XML_ELEMENT_NODE:
1883
+ // Serialize the start tag.
1884
+ output_char(out, '<');
1885
+ output_tagname(out, node);
1886
+
1887
+ // Add attributes.
1888
+ for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) {
1889
+ output_char(out, ' ');
1890
+ output_attr_name(out, attr);
1891
+ if (attr->children) {
1892
+ output_string(out, "=\"");
1893
+ xmlChar *value = xmlNodeListGetString(attr->doc, attr->children, 1);
1894
+ output_escaped_string(out, value, true);
1895
+ xmlFree(value);
1896
+ output_char(out, '"');
1897
+ } else {
1898
+ // Output name=""
1899
+ output_string(out, "=\"\"");
1900
+ }
1901
+ }
1902
+ output_char(out, '>');
1903
+
1904
+ // Add children and end tag if element is not void.
1905
+ if (!is_one_of(node, VOID_ELEMENTS, sizeof VOID_ELEMENTS / sizeof VOID_ELEMENTS[0])) {
1906
+ if (preserve_newline && should_prepend_newline(node)) {
1907
+ output_char(out, '\n');
1908
+ }
1909
+ for (xmlNodePtr child = node->children; child; child = child->next) {
1910
+ output_node(out, child, preserve_newline);
1911
+ }
1912
+ output_string(out, "</");
1913
+ output_tagname(out, node);
1914
+ output_char(out, '>');
1915
+ }
1916
+ break;
1917
+
1918
+ case XML_TEXT_NODE:
1919
+ if (node->parent
1920
+ && is_one_of(node->parent, UNESCAPED_TEXT_ELEMENTS,
1921
+ sizeof UNESCAPED_TEXT_ELEMENTS / sizeof UNESCAPED_TEXT_ELEMENTS[0])) {
1922
+ output_string(out, (char const *)node->content);
1923
+ } else {
1924
+ output_escaped_string(out, node->content, false);
1925
+ }
1926
+ break;
1927
+
1928
+ case XML_CDATA_SECTION_NODE:
1929
+ output_string(out, "<![CDATA[");
1930
+ output_string(out, (char const *)node->content);
1931
+ output_string(out, "]]>");
1932
+ break;
1933
+
1934
+ case XML_COMMENT_NODE:
1935
+ output_string(out, "<!--");
1936
+ output_string(out, (char const *)node->content);
1937
+ output_string(out, "-->");
1938
+ break;
1939
+
1940
+ case XML_PI_NODE:
1941
+ output_string(out, "<?");
1942
+ output_string(out, (char const *)node->content);
1943
+ output_char(out, '>');
1944
+ break;
1945
+
1946
+ case XML_DOCUMENT_TYPE_NODE:
1947
+ case XML_DTD_NODE:
1948
+ output_string(out, "<!DOCTYPE ");
1949
+ output_string(out, (char const *)node->name);
1950
+ output_string(out, ">");
1951
+ break;
1952
+
1953
+ case XML_DOCUMENT_NODE:
1954
+ case XML_DOCUMENT_FRAG_NODE:
1955
+ case XML_HTML_DOCUMENT_NODE:
1956
+ for (xmlNodePtr child = node->children; child; child = child->next) {
1957
+ output_node(out, child, preserve_newline);
1958
+ }
1959
+ break;
1960
+
1961
+ default:
1962
+ rb_raise(rb_eRuntimeError, "Unsupported document node (%d); this is a bug in Nokogiri", node->type);
1963
+ break;
1964
+ }
1965
+ }
1370
1966
 
1371
- if (!ns) { return Qnil ; }
1967
+ static VALUE
1968
+ html_standard_serialize(
1969
+ VALUE self,
1970
+ VALUE preserve_newline
1971
+ )
1972
+ {
1973
+ xmlNodePtr node;
1974
+ Noko_Node_Get_Struct(self, xmlNode, node);
1975
+ VALUE output = rb_str_buf_new(4096);
1976
+ output_node(output, node, RTEST(preserve_newline));
1977
+ return output;
1978
+ }
1372
1979
 
1373
- if(NIL_P(prefix) || node != namespace) { xmlSetNs(node, ns); }
1980
+ /*
1981
+ * :call-seq:
1982
+ * line() → Integer
1983
+ *
1984
+ * [Returns] The line number of this Node.
1985
+ *
1986
+ * ---
1987
+ *
1988
+ * <b> ⚠ The CRuby and JRuby implementations differ in important ways! </b>
1989
+ *
1990
+ * Semantic differences:
1991
+ * - The CRuby method reflects the node's line number <i>in the parsed string</i>
1992
+ * - The JRuby method reflects the node's line number <i>in the final DOM structure</i> after
1993
+ * corrections have been applied
1994
+ *
1995
+ * Performance differences:
1996
+ * - The CRuby method is {O(1)}[https://en.wikipedia.org/wiki/Time_complexity#Constant_time]
1997
+ * (constant time)
1998
+ * - The JRuby method is {O(n)}[https://en.wikipedia.org/wiki/Time_complexity#Linear_time] (linear
1999
+ * time, where n is the number of nodes before/above the element in the DOM)
2000
+ *
2001
+ * If you'd like to help improve the JRuby implementation, please review these issues and reach out
2002
+ * to the maintainers:
2003
+ * - https://github.com/sparklemotion/nokogiri/issues/1223
2004
+ * - https://github.com/sparklemotion/nokogiri/pull/2177
2005
+ * - https://github.com/sparklemotion/nokogiri/issues/2380
2006
+ */
2007
+ static VALUE
2008
+ rb_xml_node_line(VALUE rb_node)
2009
+ {
2010
+ xmlNodePtr c_node;
2011
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1374
2012
 
1375
- return Nokogiri_wrap_xml_namespace(node->doc, ns);
2013
+ return LONG2NUM(xmlGetLineNo(c_node));
1376
2014
  }
1377
2015
 
1378
2016
  /*
1379
2017
  * call-seq:
1380
- * new(name, document)
2018
+ * line=(num)
1381
2019
  *
1382
- * Create a new node with +name+ sharing GC lifecycle with +document+
2020
+ * Sets the line for this Node. num must be less than 65535.
1383
2021
  */
1384
- static VALUE new(int argc, VALUE *argv, VALUE klass)
2022
+ static VALUE
2023
+ rb_xml_node_line_set(VALUE rb_node, VALUE rb_line_number)
1385
2024
  {
1386
- xmlDocPtr doc;
1387
- xmlNodePtr node;
1388
- VALUE name;
1389
- VALUE document;
2025
+ xmlNodePtr c_node;
2026
+ int line_number = NUM2INT(rb_line_number);
2027
+
2028
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
2029
+
2030
+ // libxml2 optionally uses xmlNode.psvi to store longer line numbers, but only for text nodes.
2031
+ // search for "psvi" in SAX2.c and tree.c to learn more.
2032
+ if (line_number < 65535) {
2033
+ c_node->line = (short) line_number;
2034
+ } else {
2035
+ c_node->line = 65535;
2036
+ if (c_node->type == XML_TEXT_NODE) {
2037
+ c_node->psvi = (void *)(ptrdiff_t) line_number;
2038
+ }
2039
+ }
2040
+
2041
+ return rb_line_number;
2042
+ }
2043
+
2044
+ /* :nodoc: documented in lib/nokogiri/xml/node.rb */
2045
+ static VALUE
2046
+ rb_xml_node_new(int argc, VALUE *argv, VALUE klass)
2047
+ {
2048
+ xmlNodePtr c_document_node;
2049
+ xmlNodePtr c_node;
2050
+ VALUE rb_name;
2051
+ VALUE rb_document_node;
1390
2052
  VALUE rest;
1391
2053
  VALUE rb_node;
1392
2054
 
1393
- rb_scan_args(argc, argv, "2*", &name, &document, &rest);
2055
+ rb_scan_args(argc, argv, "2*", &rb_name, &rb_document_node, &rest);
1394
2056
 
1395
- Data_Get_Struct(document, xmlDoc, doc);
2057
+ if (!rb_obj_is_kind_of(rb_document_node, cNokogiriXmlNode)) {
2058
+ rb_raise(rb_eArgError, "document must be a Nokogiri::XML::Node");
2059
+ }
2060
+ if (!rb_obj_is_kind_of(rb_document_node, cNokogiriXmlDocument)) {
2061
+ // TODO: deprecate allowing Node
2062
+ NOKO_WARN_DEPRECATION("Passing a Node as the second parameter to Node.new is deprecated. Please pass a Document instead, or prefer an alternative constructor like Node#add_child. This will become an error in a future release of Nokogiri.");
2063
+ }
2064
+ Noko_Node_Get_Struct(rb_document_node, xmlNode, c_document_node);
1396
2065
 
1397
- node = xmlNewNode(NULL, (xmlChar *)StringValueCStr(name));
1398
- node->doc = doc->doc;
1399
- nokogiri_root_node(node);
2066
+ c_node = xmlNewNode(NULL, (xmlChar *)StringValueCStr(rb_name));
2067
+ c_node->doc = c_document_node->doc;
2068
+ noko_xml_document_pin_node(c_node);
1400
2069
 
1401
- rb_node = Nokogiri_wrap_xml_node(
2070
+ rb_node = noko_xml_node_wrap(
1402
2071
  klass == cNokogiriXmlNode ? (VALUE)NULL : klass,
1403
- node
2072
+ c_node
1404
2073
  );
1405
2074
  rb_obj_call_init(rb_node, argc, argv);
1406
2075
 
1407
- if(rb_block_given_p()) { rb_yield(rb_node); }
2076
+ if (rb_block_given_p()) { rb_yield(rb_node); }
1408
2077
 
1409
2078
  return rb_node;
1410
2079
  }
@@ -1415,13 +2084,14 @@ static VALUE new(int argc, VALUE *argv, VALUE klass)
1415
2084
  *
1416
2085
  * Returns the Node as html.
1417
2086
  */
1418
- static VALUE dump_html(VALUE self)
2087
+ static VALUE
2088
+ dump_html(VALUE self)
1419
2089
  {
1420
2090
  xmlBufferPtr buf ;
1421
2091
  xmlNodePtr node ;
1422
2092
  VALUE html;
1423
2093
 
1424
- Data_Get_Struct(self, xmlNode, node);
2094
+ Noko_Node_Get_Struct(self, xmlNode, node);
1425
2095
 
1426
2096
  buf = xmlBufferCreate() ;
1427
2097
  htmlNodeDump(buf, node->doc, node);
@@ -1436,13 +2106,14 @@ static VALUE dump_html(VALUE self)
1436
2106
  *
1437
2107
  * Compare this Node to +other+ with respect to their Document
1438
2108
  */
1439
- static VALUE compare(VALUE self, VALUE _other)
2109
+ static VALUE
2110
+ compare(VALUE self, VALUE _other)
1440
2111
  {
1441
2112
  xmlNodePtr node, other;
1442
- Data_Get_Struct(self, xmlNode, node);
1443
- Data_Get_Struct(_other, xmlNode, other);
2113
+ Noko_Node_Get_Struct(self, xmlNode, node);
2114
+ Noko_Node_Get_Struct(_other, xmlNode, other);
1444
2115
 
1445
- return INT2NUM((long)xmlXPathCmpNodes(other, node));
2116
+ return INT2NUM(xmlXPathCmpNodes(other, node));
1446
2117
  }
1447
2118
 
1448
2119
 
@@ -1453,13 +2124,14 @@ static VALUE compare(VALUE self, VALUE _other)
1453
2124
  * Loads and substitutes all xinclude elements below the node. The
1454
2125
  * parser context will be initialized with +options+.
1455
2126
  */
1456
- static VALUE process_xincludes(VALUE self, VALUE options)
2127
+ static VALUE
2128
+ process_xincludes(VALUE self, VALUE options)
1457
2129
  {
1458
2130
  int rcode ;
1459
2131
  xmlNodePtr node;
1460
2132
  VALUE error_list = rb_ary_new();
1461
2133
 
1462
- Data_Get_Struct(self, xmlNode, node);
2134
+ Noko_Node_Get_Struct(self, xmlNode, node);
1463
2135
 
1464
2136
  xmlSetStructuredErrorFunc((void *)error_list, Nokogiri_error_array_pusher);
1465
2137
  rcode = xmlXIncludeProcessTreeFlags(node, (int)NUM2INT(options));
@@ -1469,7 +2141,7 @@ static VALUE process_xincludes(VALUE self, VALUE options)
1469
2141
  xmlErrorPtr error;
1470
2142
 
1471
2143
  error = xmlGetLastError();
1472
- if(error) {
2144
+ if (error) {
1473
2145
  rb_exc_raise(Nokogiri_wrap_xml_syntax_error(error));
1474
2146
  } else {
1475
2147
  rb_raise(rb_eRuntimeError, "Could not perform xinclude substitution");
@@ -1481,7 +2153,8 @@ static VALUE process_xincludes(VALUE self, VALUE options)
1481
2153
 
1482
2154
 
1483
2155
  /* TODO: DOCUMENT ME */
1484
- static VALUE in_context(VALUE self, VALUE _str, VALUE _options)
2156
+ static VALUE
2157
+ in_context(VALUE self, VALUE _str, VALUE _options)
1485
2158
  {
1486
2159
  xmlNodePtr node, list = 0, tmp, child_iter, node_children, doc_children;
1487
2160
  xmlNodeSetPtr set;
@@ -1489,7 +2162,7 @@ static VALUE in_context(VALUE self, VALUE _str, VALUE _options)
1489
2162
  VALUE doc, err;
1490
2163
  int doc_is_empty;
1491
2164
 
1492
- Data_Get_Struct(self, xmlNode, node);
2165
+ Noko_Node_Get_Struct(self, xmlNode, node);
1493
2166
 
1494
2167
  doc = DOC_RUBY_OBJECT(node->doc);
1495
2168
  err = rb_iv_get(doc, "@errors");
@@ -1530,9 +2203,7 @@ static VALUE in_context(VALUE self, VALUE _str, VALUE _options)
1530
2203
  */
1531
2204
  child_iter = node->doc->children ;
1532
2205
  while (child_iter) {
1533
- if (child_iter->parent != (xmlNodePtr)node->doc) {
1534
- child_iter->parent = (xmlNodePtr)node->doc;
1535
- }
2206
+ child_iter->parent = (xmlNodePtr)node->doc;
1536
2207
  child_iter = child_iter->next;
1537
2208
  }
1538
2209
 
@@ -1542,12 +2213,17 @@ static VALUE in_context(VALUE self, VALUE _str, VALUE _options)
1542
2213
 
1543
2214
  xmlSetStructuredErrorFunc(NULL, NULL);
1544
2215
 
1545
- /* Workaround for a libxml2 bug where a parsing error may leave a broken
2216
+ /*
2217
+ * Workaround for a libxml2 bug where a parsing error may leave a broken
1546
2218
  * node reference in node->doc->children.
2219
+ *
2220
+ * https://bugzilla.gnome.org/show_bug.cgi?id=668155
2221
+ *
1547
2222
  * This workaround is limited to when a parse error occurs, the document
1548
2223
  * went from having no children to having children, and the context node is
1549
2224
  * part of a document fragment.
1550
- * https://bugzilla.gnome.org/show_bug.cgi?id=668155
2225
+ *
2226
+ * TODO: This was fixed in libxml 2.8.0 by 71a243d
1551
2227
  */
1552
2228
  if (error != XML_ERR_OK && doc_is_empty && node->doc->children != NULL) {
1553
2229
  child_iter = node;
@@ -1562,12 +2238,12 @@ static VALUE in_context(VALUE self, VALUE _str, VALUE _options)
1562
2238
 
1563
2239
  /* FIXME: This probably needs to handle more constants... */
1564
2240
  switch (error) {
1565
- case XML_ERR_INTERNAL_ERROR:
1566
- case XML_ERR_NO_MEMORY:
1567
- rb_raise(rb_eRuntimeError, "error parsing fragment (%d)", error);
1568
- break;
1569
- default:
1570
- break;
2241
+ case XML_ERR_INTERNAL_ERROR:
2242
+ case XML_ERR_NO_MEMORY:
2243
+ rb_raise(rb_eRuntimeError, "error parsing fragment (%d)", error);
2244
+ break;
2245
+ default:
2246
+ break;
1571
2247
  }
1572
2248
 
1573
2249
  set = xmlXPathNodeSetCreate(NULL);
@@ -1576,178 +2252,174 @@ static VALUE in_context(VALUE self, VALUE _str, VALUE _options)
1576
2252
  tmp = list->next;
1577
2253
  list->next = NULL;
1578
2254
  xmlXPathNodeSetAddUnique(set, list);
1579
- nokogiri_root_node(list);
2255
+ noko_xml_document_pin_node(list);
1580
2256
  list = tmp;
1581
2257
  }
1582
2258
 
1583
- return Nokogiri_wrap_xml_node_set(set, doc);
2259
+ return noko_xml_node_set_wrap(set, doc);
1584
2260
  }
1585
2261
 
1586
-
1587
- VALUE Nokogiri_wrap_xml_node(VALUE klass, xmlNodePtr node)
2262
+ VALUE
2263
+ noko_xml_node_wrap(VALUE rb_class, xmlNodePtr c_node)
1588
2264
  {
1589
- VALUE document = Qnil ;
1590
- VALUE node_cache = Qnil ;
1591
- VALUE rb_node = Qnil ;
2265
+ VALUE rb_document, rb_node_cache, rb_node;
1592
2266
  nokogiriTuplePtr node_has_a_document;
1593
- xmlDocPtr doc;
1594
- void (*mark_method)(xmlNodePtr) = NULL ;
2267
+ xmlDocPtr c_doc;
1595
2268
 
1596
- assert(node);
2269
+ assert(c_node);
1597
2270
 
1598
- if(node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE) {
1599
- return DOC_RUBY_OBJECT(node->doc);
2271
+ if (c_node->type == XML_DOCUMENT_NODE || c_node->type == XML_HTML_DOCUMENT_NODE) {
2272
+ return DOC_RUBY_OBJECT(c_node->doc);
1600
2273
  }
1601
2274
 
1602
- /* It's OK if the node doesn't have a fully-realized document (as in XML::Reader). */
1603
- /* see https://github.com/sparklemotion/nokogiri/issues/95 */
1604
- /* and https://github.com/sparklemotion/nokogiri/issues/439 */
1605
- doc = node->doc;
1606
- if (doc->type == XML_DOCUMENT_FRAG_NODE) { doc = doc->doc; }
1607
- node_has_a_document = DOC_RUBY_OBJECT_TEST(doc);
2275
+ c_doc = c_node->doc;
2276
+
2277
+ // Nodes yielded from XML::Reader don't have a fully-realized Document
2278
+ node_has_a_document = DOC_RUBY_OBJECT_TEST(c_doc);
1608
2279
 
1609
- if(node->_private && node_has_a_document) {
1610
- return (VALUE)node->_private;
2280
+ if (c_node->_private && node_has_a_document) {
2281
+ return (VALUE)c_node->_private;
1611
2282
  }
1612
2283
 
1613
- if(!RTEST(klass)) {
1614
- switch(node->type) {
1615
- case XML_ELEMENT_NODE:
1616
- klass = cNokogiriXmlElement;
1617
- break;
1618
- case XML_TEXT_NODE:
1619
- klass = cNokogiriXmlText;
1620
- break;
1621
- case XML_ATTRIBUTE_NODE:
1622
- klass = cNokogiriXmlAttr;
1623
- break;
1624
- case XML_ENTITY_REF_NODE:
1625
- klass = cNokogiriXmlEntityReference;
1626
- break;
1627
- case XML_COMMENT_NODE:
1628
- klass = cNokogiriXmlComment;
1629
- break;
1630
- case XML_DOCUMENT_FRAG_NODE:
1631
- klass = cNokogiriXmlDocumentFragment;
1632
- break;
1633
- case XML_PI_NODE:
1634
- klass = cNokogiriXmlProcessingInstruction;
1635
- break;
1636
- case XML_ENTITY_DECL:
1637
- klass = cNokogiriXmlEntityDecl;
1638
- break;
1639
- case XML_CDATA_SECTION_NODE:
1640
- klass = cNokogiriXmlCData;
1641
- break;
1642
- case XML_DTD_NODE:
1643
- klass = cNokogiriXmlDtd;
1644
- break;
1645
- case XML_ATTRIBUTE_DECL:
1646
- klass = cNokogiriXmlAttributeDecl;
1647
- break;
1648
- case XML_ELEMENT_DECL:
1649
- klass = cNokogiriXmlElementDecl;
1650
- break;
1651
- default:
1652
- klass = cNokogiriXmlNode;
2284
+ if (!RTEST(rb_class)) {
2285
+ switch (c_node->type) {
2286
+ case XML_ELEMENT_NODE:
2287
+ rb_class = cNokogiriXmlElement;
2288
+ break;
2289
+ case XML_TEXT_NODE:
2290
+ rb_class = cNokogiriXmlText;
2291
+ break;
2292
+ case XML_ATTRIBUTE_NODE:
2293
+ rb_class = cNokogiriXmlAttr;
2294
+ break;
2295
+ case XML_ENTITY_REF_NODE:
2296
+ rb_class = cNokogiriXmlEntityReference;
2297
+ break;
2298
+ case XML_COMMENT_NODE:
2299
+ rb_class = cNokogiriXmlComment;
2300
+ break;
2301
+ case XML_DOCUMENT_FRAG_NODE:
2302
+ rb_class = cNokogiriXmlDocumentFragment;
2303
+ break;
2304
+ case XML_PI_NODE:
2305
+ rb_class = cNokogiriXmlProcessingInstruction;
2306
+ break;
2307
+ case XML_ENTITY_DECL:
2308
+ rb_class = cNokogiriXmlEntityDecl;
2309
+ break;
2310
+ case XML_CDATA_SECTION_NODE:
2311
+ rb_class = cNokogiriXmlCData;
2312
+ break;
2313
+ case XML_DTD_NODE:
2314
+ rb_class = cNokogiriXmlDtd;
2315
+ break;
2316
+ case XML_ATTRIBUTE_DECL:
2317
+ rb_class = cNokogiriXmlAttributeDecl;
2318
+ break;
2319
+ case XML_ELEMENT_DECL:
2320
+ rb_class = cNokogiriXmlElementDecl;
2321
+ break;
2322
+ default:
2323
+ rb_class = cNokogiriXmlNode;
1653
2324
  }
1654
2325
  }
1655
2326
 
1656
- mark_method = node_has_a_document ? mark : NULL ;
1657
-
1658
- rb_node = Data_Wrap_Struct(klass, mark_method, debug_node_dealloc, node) ;
1659
- node->_private = (void *)rb_node;
2327
+ rb_node = TypedData_Wrap_Struct(rb_class, &nokogiri_node_type, c_node) ;
2328
+ c_node->_private = (void *)rb_node;
1660
2329
 
1661
2330
  if (node_has_a_document) {
1662
- document = DOC_RUBY_OBJECT(doc);
1663
- node_cache = DOC_NODE_CACHE(doc);
1664
- rb_ary_push(node_cache, rb_node);
1665
- rb_funcall(document, decorate, 1, rb_node);
2331
+ rb_document = DOC_RUBY_OBJECT(c_doc);
2332
+ rb_node_cache = DOC_NODE_CACHE(c_doc);
2333
+ rb_ary_push(rb_node_cache, rb_node);
2334
+ rb_funcall(rb_document, id_decorate, 1, rb_node);
1666
2335
  }
1667
2336
 
1668
2337
  return rb_node ;
1669
2338
  }
1670
2339
 
1671
2340
 
1672
- void Nokogiri_xml_node_properties(xmlNodePtr node, VALUE attr_list)
2341
+ /*
2342
+ * return Array<Nokogiri::XML::Attr> containing the node's attributes
2343
+ */
2344
+ VALUE
2345
+ noko_xml_node_attrs(xmlNodePtr c_node)
1673
2346
  {
1674
- xmlAttrPtr prop;
1675
- prop = node->properties ;
1676
- while (prop != NULL) {
1677
- rb_ary_push(attr_list, Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)prop));
1678
- prop = prop->next ;
2347
+ VALUE rb_properties = rb_ary_new();
2348
+ xmlAttrPtr c_property;
2349
+
2350
+ c_property = c_node->properties ;
2351
+ while (c_property != NULL) {
2352
+ rb_ary_push(rb_properties, noko_xml_node_wrap(Qnil, (xmlNodePtr)c_property));
2353
+ c_property = c_property->next ;
1679
2354
  }
2355
+
2356
+ return rb_properties;
1680
2357
  }
1681
2358
 
1682
- VALUE cNokogiriXmlNode ;
1683
- VALUE cNokogiriXmlElement ;
1684
-
1685
- void init_xml_node()
1686
- {
1687
- VALUE nokogiri = rb_define_module("Nokogiri");
1688
- VALUE xml = rb_define_module_under(nokogiri, "XML");
1689
- VALUE klass = rb_define_class_under(xml, "Node", rb_cObject);
1690
-
1691
- cNokogiriXmlNode = klass;
1692
-
1693
- cNokogiriXmlElement = rb_define_class_under(xml, "Element", klass);
1694
-
1695
- rb_define_singleton_method(klass, "new", new, -1);
1696
-
1697
- rb_define_method(klass, "add_namespace_definition", add_namespace_definition, 2);
1698
- rb_define_method(klass, "node_name", get_name, 0);
1699
- rb_define_method(klass, "document", document, 0);
1700
- rb_define_method(klass, "node_name=", set_name, 1);
1701
- rb_define_method(klass, "parent", get_parent, 0);
1702
- rb_define_method(klass, "child", child, 0);
1703
- rb_define_method(klass, "first_element_child", first_element_child, 0);
1704
- rb_define_method(klass, "last_element_child", last_element_child, 0);
1705
- rb_define_method(klass, "children", children, 0);
1706
- rb_define_method(klass, "element_children", element_children, 0);
1707
- rb_define_method(klass, "next_sibling", next_sibling, 0);
1708
- rb_define_method(klass, "previous_sibling", previous_sibling, 0);
1709
- rb_define_method(klass, "next_element", next_element, 0);
1710
- rb_define_method(klass, "previous_element", previous_element, 0);
1711
- rb_define_method(klass, "node_type", node_type, 0);
1712
- rb_define_method(klass, "path", path, 0);
1713
- rb_define_method(klass, "key?", key_eh, 1);
1714
- rb_define_method(klass, "namespaced_key?", namespaced_key_eh, 2);
1715
- rb_define_method(klass, "blank?", blank_eh, 0);
1716
- rb_define_method(klass, "attribute_nodes", attribute_nodes, 0);
1717
- rb_define_method(klass, "attribute", attr, 1);
1718
- rb_define_method(klass, "attribute_with_ns", attribute_with_ns, 2);
1719
- rb_define_method(klass, "namespace", namespace, 0);
1720
- rb_define_method(klass, "namespace_definitions", namespace_definitions, 0);
1721
- rb_define_method(klass, "namespace_scopes", namespace_scopes, 0);
1722
- rb_define_method(klass, "encode_special_chars", encode_special_chars, 1);
1723
- rb_define_method(klass, "dup", duplicate_node, -1);
1724
- rb_define_method(klass, "unlink", unlink_node, 0);
1725
- rb_define_method(klass, "internal_subset", internal_subset, 0);
1726
- rb_define_method(klass, "external_subset", external_subset, 0);
1727
- rb_define_method(klass, "create_internal_subset", create_internal_subset, 3);
1728
- rb_define_method(klass, "create_external_subset", create_external_subset, 3);
1729
- rb_define_method(klass, "pointer_id", pointer_id, 0);
1730
- rb_define_method(klass, "line", line, 0);
1731
- rb_define_method(klass, "content", get_native_content, 0);
1732
- rb_define_method(klass, "native_content=", set_native_content, 1);
1733
- rb_define_method(klass, "lang", get_lang, 0);
1734
- rb_define_method(klass, "lang=", set_lang, 1);
1735
-
1736
- rb_define_private_method(klass, "process_xincludes", process_xincludes, 1);
1737
- rb_define_private_method(klass, "in_context", in_context, 2);
1738
- rb_define_private_method(klass, "add_child_node", add_child, 1);
1739
- rb_define_private_method(klass, "add_previous_sibling_node", add_previous_sibling, 1);
1740
- rb_define_private_method(klass, "add_next_sibling_node", add_next_sibling, 1);
1741
- rb_define_private_method(klass, "replace_node", replace, 1);
1742
- rb_define_private_method(klass, "dump_html", dump_html, 0);
1743
- rb_define_private_method(klass, "native_write_to", native_write_to, 4);
1744
- rb_define_private_method(klass, "get", get, 1);
1745
- rb_define_private_method(klass, "set", set, 2);
1746
- rb_define_private_method(klass, "set_namespace", set_namespace, 1);
1747
- rb_define_private_method(klass, "compare", compare, 1);
1748
-
1749
- decorate = rb_intern("decorate");
1750
- decorate_bang = rb_intern("decorate!");
1751
- }
1752
-
1753
- /* vim: set noet sw=4 sws=4 */
2359
+ void
2360
+ noko_init_xml_node(void)
2361
+ {
2362
+ cNokogiriXmlNode = rb_define_class_under(mNokogiriXml, "Node", rb_cObject);
2363
+
2364
+ rb_undef_alloc_func(cNokogiriXmlNode);
2365
+
2366
+ rb_define_singleton_method(cNokogiriXmlNode, "new", rb_xml_node_new, -1);
2367
+
2368
+ rb_define_method(cNokogiriXmlNode, "add_namespace_definition", rb_xml_node_add_namespace_definition, 2);
2369
+ rb_define_method(cNokogiriXmlNode, "attribute", rb_xml_node_attribute, 1);
2370
+ rb_define_method(cNokogiriXmlNode, "attribute_nodes", rb_xml_node_attribute_nodes, 0);
2371
+ rb_define_method(cNokogiriXmlNode, "attribute_with_ns", rb_xml_node_attribute_with_ns, 2);
2372
+ rb_define_method(cNokogiriXmlNode, "blank?", rb_xml_node_blank_eh, 0);
2373
+ rb_define_method(cNokogiriXmlNode, "child", rb_xml_node_child, 0);
2374
+ rb_define_method(cNokogiriXmlNode, "children", rb_xml_node_children, 0);
2375
+ rb_define_method(cNokogiriXmlNode, "content", rb_xml_node_content, 0);
2376
+ rb_define_method(cNokogiriXmlNode, "create_external_subset", create_external_subset, 3);
2377
+ rb_define_method(cNokogiriXmlNode, "create_internal_subset", create_internal_subset, 3);
2378
+ rb_define_method(cNokogiriXmlNode, "document", rb_xml_node_document, 0);
2379
+ rb_define_method(cNokogiriXmlNode, "dup", duplicate_node, -1);
2380
+ rb_define_method(cNokogiriXmlNode, "element_children", rb_xml_node_element_children, 0);
2381
+ rb_define_method(cNokogiriXmlNode, "encode_special_chars", encode_special_chars, 1);
2382
+ rb_define_method(cNokogiriXmlNode, "external_subset", external_subset, 0);
2383
+ rb_define_method(cNokogiriXmlNode, "first_element_child", rb_xml_node_first_element_child, 0);
2384
+ rb_define_method(cNokogiriXmlNode, "internal_subset", internal_subset, 0);
2385
+ rb_define_method(cNokogiriXmlNode, "key?", key_eh, 1);
2386
+ rb_define_method(cNokogiriXmlNode, "lang", get_lang, 0);
2387
+ rb_define_method(cNokogiriXmlNode, "lang=", set_lang, 1);
2388
+ rb_define_method(cNokogiriXmlNode, "last_element_child", rb_xml_node_last_element_child, 0);
2389
+ rb_define_method(cNokogiriXmlNode, "line", rb_xml_node_line, 0);
2390
+ rb_define_method(cNokogiriXmlNode, "line=", rb_xml_node_line_set, 1);
2391
+ rb_define_method(cNokogiriXmlNode, "namespace", rb_xml_node_namespace, 0);
2392
+ rb_define_method(cNokogiriXmlNode, "namespace_definitions", namespace_definitions, 0);
2393
+ rb_define_method(cNokogiriXmlNode, "namespace_scopes", rb_xml_node_namespace_scopes, 0);
2394
+ rb_define_method(cNokogiriXmlNode, "namespaced_key?", namespaced_key_eh, 2);
2395
+ rb_define_method(cNokogiriXmlNode, "native_content=", set_native_content, 1);
2396
+ rb_define_method(cNokogiriXmlNode, "next_element", next_element, 0);
2397
+ rb_define_method(cNokogiriXmlNode, "next_sibling", next_sibling, 0);
2398
+ rb_define_method(cNokogiriXmlNode, "node_name", get_name, 0);
2399
+ rb_define_method(cNokogiriXmlNode, "node_name=", set_name, 1);
2400
+ rb_define_method(cNokogiriXmlNode, "node_type", node_type, 0);
2401
+ rb_define_method(cNokogiriXmlNode, "parent", get_parent, 0);
2402
+ rb_define_method(cNokogiriXmlNode, "path", rb_xml_node_path, 0);
2403
+ rb_define_method(cNokogiriXmlNode, "pointer_id", rb_xml_node_pointer_id, 0);
2404
+ rb_define_method(cNokogiriXmlNode, "previous_element", previous_element, 0);
2405
+ rb_define_method(cNokogiriXmlNode, "previous_sibling", previous_sibling, 0);
2406
+ rb_define_method(cNokogiriXmlNode, "unlink", unlink_node, 0);
2407
+
2408
+ rb_define_private_method(cNokogiriXmlNode, "add_child_node", add_child, 1);
2409
+ rb_define_private_method(cNokogiriXmlNode, "add_next_sibling_node", add_next_sibling, 1);
2410
+ rb_define_private_method(cNokogiriXmlNode, "add_previous_sibling_node", add_previous_sibling, 1);
2411
+ rb_define_private_method(cNokogiriXmlNode, "compare", compare, 1);
2412
+ rb_define_private_method(cNokogiriXmlNode, "dump_html", dump_html, 0);
2413
+ rb_define_private_method(cNokogiriXmlNode, "get", get, 1);
2414
+ rb_define_private_method(cNokogiriXmlNode, "in_context", in_context, 2);
2415
+ rb_define_private_method(cNokogiriXmlNode, "native_write_to", native_write_to, 4);
2416
+ rb_define_private_method(cNokogiriXmlNode, "prepend_newline?", rb_prepend_newline, 0);
2417
+ rb_define_private_method(cNokogiriXmlNode, "html_standard_serialize", html_standard_serialize, 1);
2418
+ rb_define_private_method(cNokogiriXmlNode, "process_xincludes", process_xincludes, 1);
2419
+ rb_define_private_method(cNokogiriXmlNode, "replace_node", replace, 1);
2420
+ rb_define_private_method(cNokogiriXmlNode, "set", set, 2);
2421
+ rb_define_private_method(cNokogiriXmlNode, "set_namespace", set_namespace, 1);
2422
+
2423
+ id_decorate = rb_intern("decorate");
2424
+ id_decorate_bang = rb_intern("decorate!");
2425
+ }