nokogiri 1.6.0 → 1.13.2

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 (340) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -19
  3. data/LICENSE-DEPENDENCIES.md +1903 -0
  4. data/LICENSE.md +9 -0
  5. data/README.md +280 -0
  6. data/bin/nokogiri +84 -31
  7. data/dependencies.yml +23 -4
  8. data/ext/nokogiri/depend +38 -358
  9. data/ext/nokogiri/extconf.rb +952 -132
  10. data/ext/nokogiri/gumbo.c +584 -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 +120 -0
  15. data/ext/nokogiri/html4_sax_push_parser.c +95 -0
  16. data/ext/nokogiri/libxml2_backwards_compat.c +121 -0
  17. data/ext/nokogiri/nokogiri.c +231 -96
  18. data/ext/nokogiri/nokogiri.h +188 -129
  19. data/ext/nokogiri/test_global_handlers.c +40 -0
  20. data/ext/nokogiri/xml_attr.c +49 -40
  21. data/ext/nokogiri/xml_attribute_decl.c +18 -18
  22. data/ext/nokogiri/xml_cdata.c +24 -23
  23. data/ext/nokogiri/xml_comment.c +29 -21
  24. data/ext/nokogiri/xml_document.c +327 -223
  25. data/ext/nokogiri/xml_document_fragment.c +12 -16
  26. data/ext/nokogiri/xml_dtd.c +56 -50
  27. data/ext/nokogiri/xml_element_content.c +31 -26
  28. data/ext/nokogiri/xml_element_decl.c +22 -22
  29. data/ext/nokogiri/xml_encoding_handler.c +45 -20
  30. data/ext/nokogiri/xml_entity_decl.c +32 -30
  31. data/ext/nokogiri/xml_entity_reference.c +16 -18
  32. data/ext/nokogiri/xml_namespace.c +74 -32
  33. data/ext/nokogiri/xml_node.c +1290 -680
  34. data/ext/nokogiri/xml_node_set.c +239 -208
  35. data/ext/nokogiri/xml_processing_instruction.c +17 -19
  36. data/ext/nokogiri/xml_reader.c +227 -189
  37. data/ext/nokogiri/xml_relax_ng.c +52 -28
  38. data/ext/nokogiri/xml_sax_parser.c +123 -125
  39. data/ext/nokogiri/xml_sax_parser_context.c +138 -79
  40. data/ext/nokogiri/xml_sax_push_parser.c +88 -35
  41. data/ext/nokogiri/xml_schema.c +112 -33
  42. data/ext/nokogiri/xml_syntax_error.c +50 -23
  43. data/ext/nokogiri/xml_text.c +14 -18
  44. data/ext/nokogiri/xml_xpath_context.c +227 -140
  45. data/ext/nokogiri/xslt_stylesheet.c +269 -177
  46. data/gumbo-parser/CHANGES.md +63 -0
  47. data/gumbo-parser/Makefile +101 -0
  48. data/gumbo-parser/THANKS +27 -0
  49. data/gumbo-parser/src/Makefile +34 -0
  50. data/gumbo-parser/src/README.md +41 -0
  51. data/gumbo-parser/src/ascii.c +75 -0
  52. data/gumbo-parser/src/ascii.h +115 -0
  53. data/gumbo-parser/src/attribute.c +42 -0
  54. data/gumbo-parser/src/attribute.h +17 -0
  55. data/gumbo-parser/src/char_ref.c +22225 -0
  56. data/gumbo-parser/src/char_ref.h +29 -0
  57. data/gumbo-parser/src/char_ref.rl +2154 -0
  58. data/gumbo-parser/src/error.c +626 -0
  59. data/gumbo-parser/src/error.h +148 -0
  60. data/gumbo-parser/src/foreign_attrs.c +104 -0
  61. data/gumbo-parser/src/foreign_attrs.gperf +27 -0
  62. data/gumbo-parser/src/gumbo.h +943 -0
  63. data/gumbo-parser/src/insertion_mode.h +33 -0
  64. data/gumbo-parser/src/macros.h +91 -0
  65. data/gumbo-parser/src/parser.c +4875 -0
  66. data/gumbo-parser/src/parser.h +41 -0
  67. data/gumbo-parser/src/replacement.h +33 -0
  68. data/gumbo-parser/src/string_buffer.c +103 -0
  69. data/gumbo-parser/src/string_buffer.h +68 -0
  70. data/gumbo-parser/src/string_piece.c +48 -0
  71. data/gumbo-parser/src/svg_attrs.c +174 -0
  72. data/gumbo-parser/src/svg_attrs.gperf +77 -0
  73. data/gumbo-parser/src/svg_tags.c +137 -0
  74. data/gumbo-parser/src/svg_tags.gperf +55 -0
  75. data/gumbo-parser/src/tag.c +222 -0
  76. data/gumbo-parser/src/tag_lookup.c +382 -0
  77. data/gumbo-parser/src/tag_lookup.gperf +169 -0
  78. data/gumbo-parser/src/tag_lookup.h +13 -0
  79. data/gumbo-parser/src/token_buffer.c +79 -0
  80. data/gumbo-parser/src/token_buffer.h +71 -0
  81. data/gumbo-parser/src/token_type.h +17 -0
  82. data/gumbo-parser/src/tokenizer.c +3463 -0
  83. data/gumbo-parser/src/tokenizer.h +112 -0
  84. data/gumbo-parser/src/tokenizer_states.h +339 -0
  85. data/gumbo-parser/src/utf8.c +245 -0
  86. data/gumbo-parser/src/utf8.h +164 -0
  87. data/gumbo-parser/src/util.c +68 -0
  88. data/gumbo-parser/src/util.h +30 -0
  89. data/gumbo-parser/src/vector.c +111 -0
  90. data/gumbo-parser/src/vector.h +45 -0
  91. data/lib/nokogiri/class_resolver.rb +67 -0
  92. data/lib/nokogiri/css/node.rb +10 -58
  93. data/lib/nokogiri/css/parser.rb +407 -357
  94. data/lib/nokogiri/css/parser.y +265 -246
  95. data/lib/nokogiri/css/parser_extras.rb +52 -49
  96. data/lib/nokogiri/css/syntax_error.rb +3 -1
  97. data/lib/nokogiri/css/tokenizer.rb +107 -104
  98. data/lib/nokogiri/css/tokenizer.rex +8 -7
  99. data/lib/nokogiri/css/xpath_visitor.rb +266 -80
  100. data/lib/nokogiri/css.rb +50 -17
  101. data/lib/nokogiri/decorators/slop.rb +17 -8
  102. data/lib/nokogiri/extension.rb +31 -0
  103. data/lib/nokogiri/gumbo.rb +15 -0
  104. data/lib/nokogiri/html.rb +38 -27
  105. data/lib/nokogiri/{html → html4}/builder.rb +4 -2
  106. data/lib/nokogiri/html4/document.rb +331 -0
  107. data/lib/nokogiri/html4/document_fragment.rb +54 -0
  108. data/lib/nokogiri/{html → html4}/element_description.rb +3 -1
  109. data/lib/nokogiri/html4/element_description_defaults.rb +578 -0
  110. data/lib/nokogiri/{html → html4}/entity_lookup.rb +4 -2
  111. data/lib/nokogiri/{html → html4}/sax/parser.rb +24 -15
  112. data/lib/nokogiri/html4/sax/parser_context.rb +20 -0
  113. data/lib/nokogiri/html4/sax/push_parser.rb +37 -0
  114. data/lib/nokogiri/html4.rb +46 -0
  115. data/lib/nokogiri/html5/document.rb +88 -0
  116. data/lib/nokogiri/html5/document_fragment.rb +83 -0
  117. data/lib/nokogiri/html5/node.rb +96 -0
  118. data/lib/nokogiri/html5.rb +477 -0
  119. data/lib/nokogiri/jruby/dependencies.rb +21 -0
  120. data/lib/nokogiri/syntax_error.rb +2 -0
  121. data/lib/nokogiri/version/constant.rb +6 -0
  122. data/lib/nokogiri/version/info.rb +221 -0
  123. data/lib/nokogiri/version.rb +3 -105
  124. data/lib/nokogiri/xml/attr.rb +6 -3
  125. data/lib/nokogiri/xml/attribute_decl.rb +3 -1
  126. data/lib/nokogiri/xml/builder.rb +96 -54
  127. data/lib/nokogiri/xml/cdata.rb +3 -1
  128. data/lib/nokogiri/xml/character_data.rb +2 -0
  129. data/lib/nokogiri/xml/document.rb +234 -95
  130. data/lib/nokogiri/xml/document_fragment.rb +86 -36
  131. data/lib/nokogiri/xml/dtd.rb +16 -4
  132. data/lib/nokogiri/xml/element_content.rb +2 -0
  133. data/lib/nokogiri/xml/element_decl.rb +3 -1
  134. data/lib/nokogiri/xml/entity_decl.rb +4 -2
  135. data/lib/nokogiri/xml/entity_reference.rb +20 -0
  136. data/lib/nokogiri/xml/namespace.rb +3 -0
  137. data/lib/nokogiri/xml/node/save_options.rb +8 -4
  138. data/lib/nokogiri/xml/node.rb +947 -502
  139. data/lib/nokogiri/xml/node_set.rb +168 -159
  140. data/lib/nokogiri/xml/notation.rb +13 -0
  141. data/lib/nokogiri/xml/parse_options.rb +40 -5
  142. data/lib/nokogiri/xml/pp/character_data.rb +9 -6
  143. data/lib/nokogiri/xml/pp/node.rb +25 -26
  144. data/lib/nokogiri/xml/pp.rb +4 -2
  145. data/lib/nokogiri/xml/processing_instruction.rb +3 -1
  146. data/lib/nokogiri/xml/reader.rb +23 -28
  147. data/lib/nokogiri/xml/relax_ng.rb +8 -2
  148. data/lib/nokogiri/xml/sax/document.rb +45 -49
  149. data/lib/nokogiri/xml/sax/parser.rb +43 -41
  150. data/lib/nokogiri/xml/sax/parser_context.rb +8 -3
  151. data/lib/nokogiri/xml/sax/push_parser.rb +6 -5
  152. data/lib/nokogiri/xml/sax.rb +6 -4
  153. data/lib/nokogiri/xml/schema.rb +19 -9
  154. data/lib/nokogiri/xml/searchable.rb +270 -0
  155. data/lib/nokogiri/xml/syntax_error.rb +25 -1
  156. data/lib/nokogiri/xml/text.rb +2 -0
  157. data/lib/nokogiri/xml/xpath/syntax_error.rb +4 -2
  158. data/lib/nokogiri/xml/xpath.rb +15 -4
  159. data/lib/nokogiri/xml/xpath_context.rb +3 -3
  160. data/lib/nokogiri/xml.rb +38 -36
  161. data/lib/nokogiri/xslt/stylesheet.rb +3 -1
  162. data/lib/nokogiri/xslt.rb +29 -20
  163. data/lib/nokogiri.rb +69 -69
  164. data/lib/xsd/xmlparser/nokogiri.rb +26 -24
  165. data/patches/libxml2/0001-Remove-script-macro-support.patch +40 -0
  166. data/patches/libxml2/0002-Update-entities-to-remove-handling-of-ssi.patch +44 -0
  167. data/patches/libxml2/0003-libxml2.la-is-in-top_builddir.patch +25 -0
  168. data/patches/libxml2/0004-use-glibc-strlen.patch +53 -0
  169. data/patches/libxml2/0005-avoid-isnan-isinf.patch +81 -0
  170. data/patches/libxml2/0006-update-automake-files-for-arm64.patch +3040 -0
  171. data/patches/libxml2/0008-htmlParseComment-handle-abruptly-closed-comments.patch +61 -0
  172. data/patches/libxml2/0009-allow-wildcard-namespaces.patch +77 -0
  173. data/patches/libxslt/0001-update-automake-files-for-arm64.patch +3037 -0
  174. data/ports/archives/libxml2-2.9.13.tar.xz +0 -0
  175. data/ports/archives/libxslt-1.1.35.tar.xz +0 -0
  176. metadata +278 -362
  177. data/.autotest +0 -26
  178. data/.gemtest +0 -0
  179. data/.travis.yml +0 -27
  180. data/CHANGELOG.ja.rdoc +0 -819
  181. data/CHANGELOG.rdoc +0 -819
  182. data/C_CODING_STYLE.rdoc +0 -33
  183. data/Manifest.txt +0 -315
  184. data/README.ja.rdoc +0 -106
  185. data/README.rdoc +0 -175
  186. data/ROADMAP.md +0 -90
  187. data/Rakefile +0 -246
  188. data/STANDARD_RESPONSES.md +0 -47
  189. data/Y_U_NO_GEMSPEC.md +0 -155
  190. data/build_all +0 -105
  191. data/ext/nokogiri/html_document.c +0 -170
  192. data/ext/nokogiri/html_document.h +0 -10
  193. data/ext/nokogiri/html_element_description.c +0 -279
  194. data/ext/nokogiri/html_element_description.h +0 -10
  195. data/ext/nokogiri/html_entity_lookup.c +0 -32
  196. data/ext/nokogiri/html_entity_lookup.h +0 -8
  197. data/ext/nokogiri/html_sax_parser_context.c +0 -116
  198. data/ext/nokogiri/html_sax_parser_context.h +0 -11
  199. data/ext/nokogiri/html_sax_push_parser.c +0 -87
  200. data/ext/nokogiri/html_sax_push_parser.h +0 -9
  201. data/ext/nokogiri/xml_attr.h +0 -9
  202. data/ext/nokogiri/xml_attribute_decl.h +0 -9
  203. data/ext/nokogiri/xml_cdata.h +0 -9
  204. data/ext/nokogiri/xml_comment.h +0 -9
  205. data/ext/nokogiri/xml_document.h +0 -23
  206. data/ext/nokogiri/xml_document_fragment.h +0 -10
  207. data/ext/nokogiri/xml_dtd.h +0 -10
  208. data/ext/nokogiri/xml_element_content.h +0 -10
  209. data/ext/nokogiri/xml_element_decl.h +0 -9
  210. data/ext/nokogiri/xml_encoding_handler.h +0 -8
  211. data/ext/nokogiri/xml_entity_decl.h +0 -10
  212. data/ext/nokogiri/xml_entity_reference.h +0 -9
  213. data/ext/nokogiri/xml_io.c +0 -56
  214. data/ext/nokogiri/xml_io.h +0 -11
  215. data/ext/nokogiri/xml_libxml2_hacks.c +0 -112
  216. data/ext/nokogiri/xml_libxml2_hacks.h +0 -12
  217. data/ext/nokogiri/xml_namespace.h +0 -13
  218. data/ext/nokogiri/xml_node.h +0 -13
  219. data/ext/nokogiri/xml_node_set.h +0 -14
  220. data/ext/nokogiri/xml_processing_instruction.h +0 -9
  221. data/ext/nokogiri/xml_reader.h +0 -10
  222. data/ext/nokogiri/xml_relax_ng.h +0 -9
  223. data/ext/nokogiri/xml_sax_parser.h +0 -39
  224. data/ext/nokogiri/xml_sax_parser_context.h +0 -10
  225. data/ext/nokogiri/xml_sax_push_parser.h +0 -9
  226. data/ext/nokogiri/xml_schema.h +0 -9
  227. data/ext/nokogiri/xml_syntax_error.h +0 -13
  228. data/ext/nokogiri/xml_text.h +0 -9
  229. data/ext/nokogiri/xml_xpath_context.h +0 -10
  230. data/ext/nokogiri/xslt_stylesheet.h +0 -14
  231. data/lib/nokogiri/html/document.rb +0 -254
  232. data/lib/nokogiri/html/document_fragment.rb +0 -41
  233. data/lib/nokogiri/html/element_description_defaults.rb +0 -671
  234. data/lib/nokogiri/html/sax/parser_context.rb +0 -16
  235. data/lib/nokogiri/html/sax/push_parser.rb +0 -16
  236. data/ports/archives/libxml2-2.8.0.tar.gz +0 -0
  237. data/ports/archives/libxslt-1.1.26.tar.gz +0 -0
  238. data/tasks/cross_compile.rb +0 -132
  239. data/tasks/nokogiri.org.rb +0 -24
  240. data/tasks/test.rb +0 -95
  241. data/test/css/test_nthiness.rb +0 -159
  242. data/test/css/test_parser.rb +0 -341
  243. data/test/css/test_tokenizer.rb +0 -198
  244. data/test/css/test_xpath_visitor.rb +0 -91
  245. data/test/decorators/test_slop.rb +0 -16
  246. data/test/files/2ch.html +0 -108
  247. data/test/files/address_book.rlx +0 -12
  248. data/test/files/address_book.xml +0 -10
  249. data/test/files/bar/bar.xsd +0 -4
  250. data/test/files/bogus.xml +0 -0
  251. data/test/files/dont_hurt_em_why.xml +0 -422
  252. data/test/files/encoding.html +0 -82
  253. data/test/files/encoding.xhtml +0 -84
  254. data/test/files/exslt.xml +0 -8
  255. data/test/files/exslt.xslt +0 -35
  256. data/test/files/foo/foo.xsd +0 -4
  257. data/test/files/metacharset.html +0 -10
  258. data/test/files/noencoding.html +0 -47
  259. data/test/files/po.xml +0 -32
  260. data/test/files/po.xsd +0 -66
  261. data/test/files/saml/saml20assertion_schema.xsd +0 -283
  262. data/test/files/saml/saml20protocol_schema.xsd +0 -302
  263. data/test/files/saml/xenc_schema.xsd +0 -146
  264. data/test/files/saml/xmldsig_schema.xsd +0 -318
  265. data/test/files/shift_jis.html +0 -10
  266. data/test/files/shift_jis.xml +0 -5
  267. data/test/files/snuggles.xml +0 -3
  268. data/test/files/staff.dtd +0 -10
  269. data/test/files/staff.xml +0 -59
  270. data/test/files/staff.xslt +0 -32
  271. data/test/files/test_document_url/bar.xml +0 -2
  272. data/test/files/test_document_url/document.dtd +0 -4
  273. data/test/files/test_document_url/document.xml +0 -6
  274. data/test/files/tlm.html +0 -850
  275. data/test/files/to_be_xincluded.xml +0 -2
  276. data/test/files/valid_bar.xml +0 -2
  277. data/test/files/xinclude.xml +0 -4
  278. data/test/helper.rb +0 -154
  279. data/test/html/sax/test_parser.rb +0 -141
  280. data/test/html/sax/test_parser_context.rb +0 -46
  281. data/test/html/test_builder.rb +0 -164
  282. data/test/html/test_document.rb +0 -552
  283. data/test/html/test_document_encoding.rb +0 -138
  284. data/test/html/test_document_fragment.rb +0 -261
  285. data/test/html/test_element_description.rb +0 -105
  286. data/test/html/test_named_characters.rb +0 -14
  287. data/test/html/test_node.rb +0 -196
  288. data/test/html/test_node_encoding.rb +0 -27
  289. data/test/namespaces/test_additional_namespaces_in_builder_doc.rb +0 -14
  290. data/test/namespaces/test_namespaces_in_builder_doc.rb +0 -75
  291. data/test/namespaces/test_namespaces_in_created_doc.rb +0 -75
  292. data/test/namespaces/test_namespaces_in_parsed_doc.rb +0 -66
  293. data/test/test_convert_xpath.rb +0 -135
  294. data/test/test_css_cache.rb +0 -45
  295. data/test/test_encoding_handler.rb +0 -46
  296. data/test/test_memory_leak.rb +0 -156
  297. data/test/test_nokogiri.rb +0 -132
  298. data/test/test_reader.rb +0 -555
  299. data/test/test_soap4r_sax.rb +0 -52
  300. data/test/test_xslt_transforms.rb +0 -254
  301. data/test/xml/node/test_save_options.rb +0 -28
  302. data/test/xml/node/test_subclass.rb +0 -44
  303. data/test/xml/sax/test_parser.rb +0 -366
  304. data/test/xml/sax/test_parser_context.rb +0 -106
  305. data/test/xml/sax/test_push_parser.rb +0 -157
  306. data/test/xml/test_attr.rb +0 -64
  307. data/test/xml/test_attribute_decl.rb +0 -86
  308. data/test/xml/test_builder.rb +0 -306
  309. data/test/xml/test_c14n.rb +0 -151
  310. data/test/xml/test_cdata.rb +0 -48
  311. data/test/xml/test_comment.rb +0 -29
  312. data/test/xml/test_document.rb +0 -828
  313. data/test/xml/test_document_encoding.rb +0 -28
  314. data/test/xml/test_document_fragment.rb +0 -223
  315. data/test/xml/test_dtd.rb +0 -103
  316. data/test/xml/test_dtd_encoding.rb +0 -33
  317. data/test/xml/test_element_content.rb +0 -56
  318. data/test/xml/test_element_decl.rb +0 -73
  319. data/test/xml/test_entity_decl.rb +0 -122
  320. data/test/xml/test_entity_reference.rb +0 -245
  321. data/test/xml/test_namespace.rb +0 -95
  322. data/test/xml/test_node.rb +0 -1137
  323. data/test/xml/test_node_attributes.rb +0 -96
  324. data/test/xml/test_node_encoding.rb +0 -107
  325. data/test/xml/test_node_inheritance.rb +0 -32
  326. data/test/xml/test_node_reparenting.rb +0 -374
  327. data/test/xml/test_node_set.rb +0 -755
  328. data/test/xml/test_parse_options.rb +0 -64
  329. data/test/xml/test_processing_instruction.rb +0 -30
  330. data/test/xml/test_reader_encoding.rb +0 -142
  331. data/test/xml/test_relax_ng.rb +0 -60
  332. data/test/xml/test_schema.rb +0 -103
  333. data/test/xml/test_syntax_error.rb +0 -12
  334. data/test/xml/test_text.rb +0 -45
  335. data/test/xml/test_unparented_node.rb +0 -422
  336. data/test/xml/test_xinclude.rb +0 -83
  337. data/test/xml/test_xpath.rb +0 -295
  338. data/test/xslt/test_custom_functions.rb +0 -133
  339. data/test/xslt/test_exception_handling.rb +0 -37
  340. data/test_all +0 -81
@@ -1,81 +1,103 @@
1
- #include <xml_node.h>
1
+ #include <nokogiri.h>
2
+
3
+ // :stopdoc:
4
+
5
+ VALUE cNokogiriXmlNode ;
6
+ static ID id_decorate, id_decorate_bang;
7
+
8
+ typedef xmlNodePtr(*pivot_reparentee_func)(xmlNodePtr, xmlNodePtr);
2
9
 
3
- static ID decorate, decorate_bang;
4
10
 
5
11
  #ifdef DEBUG
6
- static void debug_node_dealloc(xmlNodePtr x)
12
+ static void
13
+ _xml_node_dealloc(xmlNodePtr x)
7
14
  {
8
15
  NOKOGIRI_DEBUG_START(x)
9
16
  NOKOGIRI_DEBUG_END(x)
10
17
  }
11
18
  #else
12
- # define debug_node_dealloc 0
19
+ # define _xml_node_dealloc 0
13
20
  #endif
14
21
 
15
- static void mark(xmlNodePtr node)
22
+
23
+ static void
24
+ _xml_node_mark(xmlNodePtr node)
16
25
  {
17
- rb_gc_mark(DOC_RUBY_OBJECT(node->doc));
26
+ xmlDocPtr doc = node->doc;
27
+ if (doc->type == XML_DOCUMENT_NODE || doc->type == XML_HTML_DOCUMENT_NODE) {
28
+ if (DOC_RUBY_OBJECT_TEST(doc)) {
29
+ rb_gc_mark(DOC_RUBY_OBJECT(doc));
30
+ }
31
+ } else if (node->doc->_private) {
32
+ rb_gc_mark((VALUE)doc->_private);
33
+ }
18
34
  }
19
35
 
20
- /* :nodoc: */
21
- typedef xmlNodePtr (*pivot_reparentee_func)(xmlNodePtr, xmlNodePtr);
22
36
 
23
- /* :nodoc: */
24
- static void relink_namespace(xmlNodePtr reparented)
37
+ static void
38
+ relink_namespace(xmlNodePtr reparented)
25
39
  {
26
- xmlChar *name, *prefix;
27
40
  xmlNodePtr child;
28
- xmlNsPtr ns;
41
+ xmlAttrPtr attr;
29
42
 
30
43
  if (reparented->type != XML_ATTRIBUTE_NODE &&
31
- reparented->type != XML_ELEMENT_NODE) return;
44
+ reparented->type != XML_ELEMENT_NODE) { return; }
32
45
 
33
46
  if (reparented->ns == NULL || reparented->ns->prefix == NULL) {
47
+ xmlNsPtr ns = NULL;
48
+ xmlChar *name = NULL, *prefix = NULL;
49
+
34
50
  name = xmlSplitQName2(reparented->name, &prefix);
35
51
 
36
- if(reparented->type == XML_ATTRIBUTE_NODE) {
37
- if (prefix == NULL || strcmp((char*)prefix, XMLNS_PREFIX) == 0) return;
52
+ if (reparented->type == XML_ATTRIBUTE_NODE) {
53
+ if (prefix == NULL || strcmp((char *)prefix, XMLNS_PREFIX) == 0) {
54
+ xmlFree(name);
55
+ xmlFree(prefix);
56
+ return;
57
+ }
38
58
  }
39
59
 
40
60
  ns = xmlSearchNs(reparented->doc, reparented, prefix);
41
61
 
42
- if (ns == NULL && reparented->parent) {
43
- ns = xmlSearchNs(reparented->doc, reparented->parent, prefix);
44
- }
45
-
46
62
  if (ns != NULL) {
47
63
  xmlNodeSetName(reparented, name);
48
64
  xmlSetNs(reparented, ns);
49
65
  }
66
+
67
+ xmlFree(name);
68
+ xmlFree(prefix);
50
69
  }
51
70
 
52
71
  /* Avoid segv when relinking against unlinked nodes. */
53
- if (reparented->type != XML_ELEMENT_NODE || !reparented->parent) return;
72
+ if (reparented->type != XML_ELEMENT_NODE || !reparented->parent) { return; }
54
73
 
55
74
  /* Make sure that our reparented node has the correct namespaces */
56
- if(!reparented->ns && reparented->doc != (xmlDocPtr)reparented->parent)
75
+ if (!reparented->ns &&
76
+ (reparented->doc != (xmlDocPtr)reparented->parent) &&
77
+ (rb_iv_get(DOC_RUBY_OBJECT(reparented->doc), "@namespace_inheritance") == Qtrue)) {
57
78
  xmlSetNs(reparented, reparented->parent->ns);
79
+ }
58
80
 
59
81
  /* Search our parents for an existing definition */
60
- if(reparented->nsDef) {
82
+ if (reparented->nsDef) {
61
83
  xmlNsPtr curr = reparented->nsDef;
62
84
  xmlNsPtr prev = NULL;
63
85
 
64
- while(curr) {
86
+ while (curr) {
65
87
  xmlNsPtr ns = xmlSearchNsByHref(
66
- reparented->doc,
67
- reparented->parent,
68
- curr->href
69
- );
88
+ reparented->doc,
89
+ reparented->parent,
90
+ curr->href
91
+ );
70
92
  /* If we find the namespace is already declared, remove it from this
71
93
  * definition list. */
72
- if(ns && ns != curr) {
94
+ if (ns && ns != curr && xmlStrEqual(ns->prefix, curr->prefix)) {
73
95
  if (prev) {
74
96
  prev->next = curr->next;
75
97
  } else {
76
98
  reparented->nsDef = curr->next;
77
99
  }
78
- nokogiri_root_nsdef(curr, reparented->doc);
100
+ noko_xml_document_pin_namespace(curr, reparented->doc);
79
101
  } else {
80
102
  prev = curr;
81
103
  }
@@ -83,29 +105,51 @@ static void relink_namespace(xmlNodePtr reparented)
83
105
  }
84
106
  }
85
107
 
108
+ /*
109
+ * Search our parents for an existing definition of current namespace,
110
+ * because the definition it's pointing to may have just been removed nsDef.
111
+ *
112
+ * And although that would technically probably be OK, I'd feel better if we
113
+ * referred to a namespace that's still present in a node's nsDef somewhere
114
+ * in the doc.
115
+ */
116
+ if (reparented->ns) {
117
+ xmlNsPtr ns = xmlSearchNs(reparented->doc, reparented, reparented->ns->prefix);
118
+ if (ns
119
+ && ns != reparented->ns
120
+ && xmlStrEqual(ns->prefix, reparented->ns->prefix)
121
+ && xmlStrEqual(ns->href, reparented->ns->href)
122
+ ) {
123
+ xmlSetNs(reparented, ns);
124
+ }
125
+ }
126
+
86
127
  /* Only walk all children if there actually is a namespace we need to */
87
128
  /* reparent. */
88
- if(NULL == reparented->ns) return;
129
+ if (NULL == reparented->ns) { return; }
89
130
 
90
131
  /* When a node gets reparented, walk it's children to make sure that */
91
132
  /* their namespaces are reparented as well. */
92
133
  child = reparented->children;
93
- while(NULL != child) {
134
+ while (NULL != child) {
94
135
  relink_namespace(child);
95
136
  child = child->next;
96
137
  }
97
138
 
98
139
  if (reparented->type == XML_ELEMENT_NODE) {
99
- child = (xmlNodePtr)((xmlElementPtr)reparented)->attributes;
100
- while(NULL != child) {
101
- relink_namespace(child);
102
- child = child->next;
140
+ attr = reparented->properties;
141
+ while (NULL != attr) {
142
+ relink_namespace((xmlNodePtr)attr);
143
+ attr = attr->next;
103
144
  }
104
145
  }
105
146
  }
106
147
 
107
- /* :nodoc: */
108
- static xmlNodePtr xmlReplaceNodeWrapper(xmlNodePtr pivot, xmlNodePtr new_node)
148
+
149
+ /* internal function meant to wrap xmlReplaceNode
150
+ and fix some issues we have with libxml2 merging nodes */
151
+ static xmlNodePtr
152
+ xmlReplaceNodeWrapper(xmlNodePtr pivot, xmlNodePtr new_node)
109
153
  {
110
154
  xmlNodePtr retval ;
111
155
 
@@ -128,24 +172,109 @@ static xmlNodePtr xmlReplaceNodeWrapper(xmlNodePtr pivot, xmlNodePtr new_node)
128
172
  return retval ;
129
173
  }
130
174
 
131
- /* :nodoc: */
132
- static VALUE reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_reparentee_func prf)
175
+
176
+ static void
177
+ raise_if_ancestor_of_self(xmlNodePtr self)
178
+ {
179
+ for (xmlNodePtr ancestor = self->parent ; ancestor ; ancestor = ancestor->parent) {
180
+ if (self == ancestor) {
181
+ rb_raise(rb_eRuntimeError, "cycle detected: node '%s' is an ancestor of itself", self->name);
182
+ }
183
+ }
184
+ }
185
+
186
+
187
+ static VALUE
188
+ reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_reparentee_func prf)
133
189
  {
134
190
  VALUE reparented_obj ;
135
- xmlNodePtr reparentee, pivot, reparented, next_text, new_next_text ;
191
+ xmlNodePtr reparentee, original_reparentee, pivot, reparented, next_text, new_next_text, parent ;
192
+ int original_ns_prefix_is_default = 0 ;
136
193
 
137
- if(!rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlNode))
194
+ if (!rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlNode)) {
138
195
  rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
139
- if(rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlDocument))
196
+ }
197
+ if (rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlDocument)) {
140
198
  rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
199
+ }
141
200
 
142
201
  Data_Get_Struct(reparentee_obj, xmlNode, reparentee);
143
202
  Data_Get_Struct(pivot_obj, xmlNode, pivot);
144
203
 
145
- if(XML_DOCUMENT_NODE == reparentee->type || XML_HTML_DOCUMENT_NODE == reparentee->type)
146
- rb_raise(rb_eArgError, "cannot reparent a document node");
204
+ /*
205
+ * Check if nodes given are appropriate to have a parent-child
206
+ * relationship, based on the DOM specification.
207
+ *
208
+ * cf. http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-1590626202
209
+ */
210
+ if (prf == xmlAddChild) {
211
+ parent = pivot;
212
+ } else {
213
+ parent = pivot->parent;
214
+ }
215
+
216
+ if (parent) {
217
+ switch (parent->type) {
218
+ case XML_DOCUMENT_NODE:
219
+ case XML_HTML_DOCUMENT_NODE:
220
+ switch (reparentee->type) {
221
+ case XML_ELEMENT_NODE:
222
+ case XML_PI_NODE:
223
+ case XML_COMMENT_NODE:
224
+ case XML_DOCUMENT_TYPE_NODE:
225
+ /*
226
+ * The DOM specification says no to adding text-like nodes
227
+ * directly to a document, but we allow it for compatibility.
228
+ */
229
+ case XML_TEXT_NODE:
230
+ case XML_CDATA_SECTION_NODE:
231
+ case XML_ENTITY_REF_NODE:
232
+ goto ok;
233
+ default:
234
+ break;
235
+ }
236
+ break;
237
+ case XML_DOCUMENT_FRAG_NODE:
238
+ case XML_ENTITY_REF_NODE:
239
+ case XML_ELEMENT_NODE:
240
+ switch (reparentee->type) {
241
+ case XML_ELEMENT_NODE:
242
+ case XML_PI_NODE:
243
+ case XML_COMMENT_NODE:
244
+ case XML_TEXT_NODE:
245
+ case XML_CDATA_SECTION_NODE:
246
+ case XML_ENTITY_REF_NODE:
247
+ goto ok;
248
+ default:
249
+ break;
250
+ }
251
+ break;
252
+ case XML_ATTRIBUTE_NODE:
253
+ switch (reparentee->type) {
254
+ case XML_TEXT_NODE:
255
+ case XML_ENTITY_REF_NODE:
256
+ goto ok;
257
+ default:
258
+ break;
259
+ }
260
+ break;
261
+ case XML_TEXT_NODE:
262
+ /*
263
+ * xmlAddChild() breaks the DOM specification in that it allows
264
+ * adding a text node to another, in which case text nodes are
265
+ * coalesced, but since our JRuby version does not support such
266
+ * operation, we should inhibit it.
267
+ */
268
+ break;
269
+ default:
270
+ break;
271
+ }
272
+
273
+ rb_raise(rb_eArgError, "cannot reparent %s there", rb_obj_classname(reparentee_obj));
274
+ }
147
275
 
148
- xmlUnlinkNode(reparentee);
276
+ ok:
277
+ original_reparentee = reparentee;
149
278
 
150
279
  if (reparentee->doc != pivot->doc || reparentee->type == XML_TEXT_NODE) {
151
280
  /*
@@ -164,12 +293,46 @@ static VALUE reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_rep
164
293
  * uninteresting libxml2 implementation detail). as a result, we cannot
165
294
  * reparent the actual reparentee, so we reparent a duplicate.
166
295
  */
167
- nokogiri_root_node(reparentee);
296
+ if (reparentee->type == XML_TEXT_NODE && reparentee->_private) {
297
+ /*
298
+ * additionally, since we know this C struct isn't going to be related to
299
+ * a Ruby object anymore, let's break the relationship on this end as
300
+ * well.
301
+ *
302
+ * this is not absolutely necessary unless libxml-ruby is also in effect,
303
+ * in which case its global callback `rxml_node_deregisterNode` will try
304
+ * to do things to our data.
305
+ *
306
+ * for more details on this particular (and particularly nasty) edge
307
+ * case, see:
308
+ *
309
+ * https://github.com/sparklemotion/nokogiri/issues/1426
310
+ */
311
+ reparentee->_private = NULL ;
312
+ }
313
+
314
+ if (reparentee->ns != NULL && reparentee->ns->prefix == NULL) {
315
+ original_ns_prefix_is_default = 1;
316
+ }
317
+
318
+ noko_xml_document_pin_node(reparentee);
319
+
168
320
  if (!(reparentee = xmlDocCopyNode(reparentee, pivot->doc, 1))) {
169
321
  rb_raise(rb_eRuntimeError, "Could not reparent node (xmlDocCopyNode)");
170
322
  }
323
+
324
+ if (original_ns_prefix_is_default && reparentee->ns != NULL && reparentee->ns->prefix != NULL) {
325
+ /*
326
+ * issue #391, where new node's prefix may become the string "default"
327
+ * see libxml2 tree.c xmlNewReconciliedNs which implements this behavior.
328
+ */
329
+ xmlFree(DISCARD_CONST_QUAL_XMLCHAR(reparentee->ns->prefix));
330
+ reparentee->ns->prefix = NULL;
331
+ }
171
332
  }
172
333
 
334
+ xmlUnlinkNode(original_reparentee);
335
+
173
336
  if (prf != xmlAddPrevSibling && prf != xmlAddNextSibling
174
337
  && reparentee->type == XML_TEXT_NODE && pivot->next && pivot->next->type == XML_TEXT_NODE) {
175
338
  /*
@@ -194,12 +357,12 @@ static VALUE reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_rep
194
357
  new_next_text = xmlDocCopyNode(next_text, pivot->doc, 1) ;
195
358
 
196
359
  xmlUnlinkNode(next_text);
197
- nokogiri_root_node(next_text);
360
+ noko_xml_document_pin_node(next_text);
198
361
 
199
362
  xmlAddNextSibling(pivot, new_next_text);
200
363
  }
201
364
 
202
- if(!(reparented = (*prf)(pivot, reparentee))) {
365
+ if (!(reparented = (*prf)(pivot, reparentee))) {
203
366
  rb_raise(rb_eRuntimeError, "Could not reparent node");
204
367
  }
205
368
 
@@ -209,24 +372,397 @@ static VALUE reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_rep
209
372
  * adjacent text nodes.
210
373
  */
211
374
  DATA_PTR(reparentee_obj) = reparented ;
375
+ reparented_obj = noko_xml_node_wrap(Qnil, reparented);
212
376
 
213
- relink_namespace(reparented);
377
+ rb_funcall(reparented_obj, id_decorate_bang, 0);
214
378
 
215
- reparented_obj = Nokogiri_wrap_xml_node(Qnil, reparented);
379
+ /* if we've created a cycle, raise an exception */
380
+ raise_if_ancestor_of_self(reparented);
216
381
 
217
- rb_funcall(reparented_obj, decorate_bang, 0);
382
+ relink_namespace(reparented);
218
383
 
219
384
  return reparented_obj ;
220
385
  }
221
386
 
387
+ // :startdoc:
222
388
 
223
389
  /*
224
- * call-seq:
225
- * document
390
+ * :call-seq:
391
+ * add_namespace_definition(prefix, href) → Nokogiri::XML::Namespace
392
+ * add_namespace(prefix, href) → Nokogiri::XML::Namespace
393
+ *
394
+ * :category: Manipulating Document Structure
395
+ *
396
+ * Adds a namespace definition to this node with +prefix+ using +href+ value, as if this node had
397
+ * included an attribute "xmlns:prefix=href".
398
+ *
399
+ * A default namespace definition for this node can be added by passing +nil+ for +prefix+.
400
+ *
401
+ * [Parameters]
402
+ * - +prefix+ (String, +nil+) An {XML Name}[https://www.w3.org/TR/xml-names/#ns-decl]
403
+ * - +href+ (String) The {URI reference}[https://www.w3.org/TR/xml-names/#sec-namespaces]
404
+ *
405
+ * [Returns] The new Nokogiri::XML::Namespace
406
+ *
407
+ * *Example:* adding a non-default namespace definition
408
+ *
409
+ * doc = Nokogiri::XML("<store><inventory></inventory></store>")
410
+ * inventory = doc.at_css("inventory")
411
+ * inventory.add_namespace_definition("automobile", "http://alices-autos.com/")
412
+ * inventory.add_namespace_definition("bicycle", "http://bobs-bikes.com/")
413
+ * inventory.add_child("<automobile:tire>Michelin model XGV, size 75R</automobile:tire>")
414
+ * doc.to_xml
415
+ * # => "<?xml version=\"1.0\"?>\n" +
416
+ * # "<store>\n" +
417
+ * # " <inventory xmlns:automobile=\"http://alices-autos.com/\" xmlns:bicycle=\"http://bobs-bikes.com/\">\n" +
418
+ * # " <automobile:tire>Michelin model XGV, size 75R</automobile:tire>\n" +
419
+ * # " </inventory>\n" +
420
+ * # "</store>\n"
421
+ *
422
+ * *Example:* adding a default namespace definition
423
+ *
424
+ * doc = Nokogiri::XML("<store><inventory><tire>Michelin model XGV, size 75R</tire></inventory></store>")
425
+ * doc.at_css("tire").add_namespace_definition(nil, "http://bobs-bikes.com/")
426
+ * doc.to_xml
427
+ * # => "<?xml version=\"1.0\"?>\n" +
428
+ * # "<store>\n" +
429
+ * # " <inventory>\n" +
430
+ * # " <tire xmlns=\"http://bobs-bikes.com/\">Michelin model XGV, size 75R</tire>\n" +
431
+ * # " </inventory>\n" +
432
+ * # "</store>\n"
433
+ *
434
+ */
435
+ static VALUE
436
+ rb_xml_node_add_namespace_definition(VALUE rb_node, VALUE rb_prefix, VALUE rb_href)
437
+ {
438
+ xmlNodePtr c_node, element;
439
+ xmlNsPtr c_namespace;
440
+ const xmlChar *c_prefix = (const xmlChar *)(NIL_P(rb_prefix) ? NULL : StringValueCStr(rb_prefix));
441
+
442
+ Data_Get_Struct(rb_node, xmlNode, c_node);
443
+ element = c_node ;
444
+
445
+ c_namespace = xmlSearchNs(c_node->doc, c_node, c_prefix);
446
+
447
+ if (!c_namespace) {
448
+ if (c_node->type != XML_ELEMENT_NODE) {
449
+ element = c_node->parent;
450
+ }
451
+ c_namespace = xmlNewNs(element, (const xmlChar *)StringValueCStr(rb_href), c_prefix);
452
+ }
453
+
454
+ if (!c_namespace) {
455
+ return Qnil ;
456
+ }
457
+
458
+ if (NIL_P(rb_prefix) || c_node != element) {
459
+ xmlSetNs(c_node, c_namespace);
460
+ }
461
+
462
+ return noko_xml_namespace_wrap(c_namespace, c_node->doc);
463
+ }
464
+
465
+
466
+ /*
467
+ * :call-seq: attribute(name) → Nokogiri::XML::Attr
468
+ *
469
+ * :category: Working With Node Attributes
470
+ *
471
+ * [Returns] Attribute (Nokogiri::XML::Attr) belonging to this node with name +name+.
472
+ *
473
+ * ⚠ Note that attribute namespaces are ignored and only the simple (non-namespace-prefixed) name is
474
+ * used to find a matching attribute. In case of a simple name collision, only one of the matching
475
+ * attributes will be returned. In this case, you will need to use #attribute_with_ns.
476
+ *
477
+ * *Example:*
478
+ *
479
+ * doc = Nokogiri::XML("<root><child size='large' class='big wide tall'/></root>")
480
+ * child = doc.at_css("child")
481
+ * child.attribute("size") # => #<Nokogiri::XML::Attr:0x550 name="size" value="large">
482
+ * child.attribute("class") # => #<Nokogiri::XML::Attr:0x564 name="class" value="big wide tall">
483
+ *
484
+ * *Example* showing that namespaced attributes will not be returned:
485
+ *
486
+ * ⚠ Note that only one of the two matching attributes is returned.
487
+ *
488
+ * doc = Nokogiri::XML(<<~EOF)
489
+ * <root xmlns:width='http://example.com/widths'
490
+ * xmlns:height='http://example.com/heights'>
491
+ * <child width:size='broad' height:size='tall'/>
492
+ * </root>
493
+ * EOF
494
+ * doc.at_css("child").attribute("size")
495
+ * # => #(Attr:0x550 {
496
+ * # name = "size",
497
+ * # namespace = #(Namespace:0x564 {
498
+ * # prefix = "width",
499
+ * # href = "http://example.com/widths"
500
+ * # }),
501
+ * # value = "broad"
502
+ * # })
503
+ */
504
+ static VALUE
505
+ rb_xml_node_attribute(VALUE self, VALUE name)
506
+ {
507
+ xmlNodePtr node;
508
+ xmlAttrPtr prop;
509
+ Data_Get_Struct(self, xmlNode, node);
510
+ prop = xmlHasProp(node, (xmlChar *)StringValueCStr(name));
511
+
512
+ if (! prop) { return Qnil; }
513
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)prop);
514
+ }
515
+
516
+
517
+ /*
518
+ * :call-seq: attribute_nodes() → Array<Nokogiri::XML::Attr>
519
+ *
520
+ * :category: Working With Node Attributes
521
+ *
522
+ * [Returns] Attributes (an Array of Nokogiri::XML::Attr) belonging to this node.
523
+ *
524
+ * Note that this is the preferred alternative to #attributes when the simple
525
+ * (non-namespace-prefixed) attribute names may collide.
526
+ *
527
+ * *Example:*
528
+ *
529
+ * Contrast this with the colliding-name example from #attributes.
530
+ *
531
+ * doc = Nokogiri::XML(<<~EOF)
532
+ * <root xmlns:width='http://example.com/widths'
533
+ * xmlns:height='http://example.com/heights'>
534
+ * <child width:size='broad' height:size='tall'/>
535
+ * </root>
536
+ * EOF
537
+ * doc.at_css("child").attribute_nodes
538
+ * # => [#(Attr:0x550 {
539
+ * # name = "size",
540
+ * # namespace = #(Namespace:0x564 {
541
+ * # prefix = "width",
542
+ * # href = "http://example.com/widths"
543
+ * # }),
544
+ * # value = "broad"
545
+ * # }),
546
+ * # #(Attr:0x578 {
547
+ * # name = "size",
548
+ * # namespace = #(Namespace:0x58c {
549
+ * # prefix = "height",
550
+ * # href = "http://example.com/heights"
551
+ * # }),
552
+ * # value = "tall"
553
+ * # })]
554
+ */
555
+ static VALUE
556
+ rb_xml_node_attribute_nodes(VALUE rb_node)
557
+ {
558
+ xmlNodePtr c_node;
559
+
560
+ Data_Get_Struct(rb_node, xmlNode, c_node);
561
+
562
+ return noko_xml_node_attrs(c_node);
563
+ }
564
+
565
+
566
+ /*
567
+ * :call-seq: attribute_with_ns(name, namespace) → Nokogiri::XML::Attr
568
+ *
569
+ * :category: Working With Node Attributes
570
+ *
571
+ * [Returns]
572
+ * Attribute (Nokogiri::XML::Attr) belonging to this node with matching +name+ and +namespace+.
573
+ *
574
+ * [Parameters]
575
+ * - +name+ (String): the simple (non-namespace-prefixed) name of the attribute
576
+ * - +namespace+ (String): the URI of the attribute's namespace
577
+ *
578
+ * See related: #attribute
579
+ *
580
+ * *Example:*
581
+ *
582
+ * doc = Nokogiri::XML(<<~EOF)
583
+ * <root xmlns:width='http://example.com/widths'
584
+ * xmlns:height='http://example.com/heights'>
585
+ * <child width:size='broad' height:size='tall'/>
586
+ * </root>
587
+ * EOF
588
+ * doc.at_css("child").attribute_with_ns("size", "http://example.com/widths")
589
+ * # => #(Attr:0x550 {
590
+ * # name = "size",
591
+ * # namespace = #(Namespace:0x564 {
592
+ * # prefix = "width",
593
+ * # href = "http://example.com/widths"
594
+ * # }),
595
+ * # value = "broad"
596
+ * # })
597
+ * doc.at_css("child").attribute_with_ns("size", "http://example.com/heights")
598
+ * # => #(Attr:0x578 {
599
+ * # name = "size",
600
+ * # namespace = #(Namespace:0x58c {
601
+ * # prefix = "height",
602
+ * # href = "http://example.com/heights"
603
+ * # }),
604
+ * # value = "tall"
605
+ * # })
606
+ */
607
+ static VALUE
608
+ rb_xml_node_attribute_with_ns(VALUE self, VALUE name, VALUE namespace)
609
+ {
610
+ xmlNodePtr node;
611
+ xmlAttrPtr prop;
612
+ Data_Get_Struct(self, xmlNode, node);
613
+ prop = xmlHasNsProp(node, (xmlChar *)StringValueCStr(name),
614
+ NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace));
615
+
616
+ if (! prop) { return Qnil; }
617
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)prop);
618
+ }
619
+
620
+
621
+
622
+ /*
623
+ * call-seq: blank? → Boolean
624
+ *
625
+ * [Returns] +true+ if the node is an empty or whitespace-only text or cdata node, else +false+.
626
+ *
627
+ * *Example:*
628
+ *
629
+ * Nokogiri("<root><child/></root>").root.child.blank? # => false
630
+ * Nokogiri("<root>\t \n</root>").root.child.blank? # => true
631
+ * Nokogiri("<root><![CDATA[\t \n]]></root>").root.child.blank? # => true
632
+ * Nokogiri("<root>not-blank</root>").root.child
633
+ * .tap { |n| n.content = "" }.blank # => true
634
+ */
635
+ static VALUE
636
+ rb_xml_node_blank_eh(VALUE self)
637
+ {
638
+ xmlNodePtr node;
639
+ Data_Get_Struct(self, xmlNode, node);
640
+ return (1 == xmlIsBlankNode(node)) ? Qtrue : Qfalse ;
641
+ }
642
+
643
+
644
+ /*
645
+ * :call-seq: child() → Nokogiri::XML::Node
646
+ *
647
+ * :category: Traversing Document Structure
648
+ *
649
+ * [Returns] First of this node's children, or +nil+ if there are no children
650
+ *
651
+ * This is a convenience method and is equivalent to:
652
+ *
653
+ * node.children.first
654
+ *
655
+ * See related: #children
656
+ */
657
+ static VALUE
658
+ rb_xml_node_child(VALUE self)
659
+ {
660
+ xmlNodePtr node, child;
661
+ Data_Get_Struct(self, xmlNode, node);
662
+
663
+ child = node->children;
664
+ if (!child) { return Qnil; }
665
+
666
+ return noko_xml_node_wrap(Qnil, child);
667
+ }
668
+
669
+
670
+ /*
671
+ * :call-seq: children() → Nokogiri::XML::NodeSet
672
+ *
673
+ * :category: Traversing Document Structure
674
+ *
675
+ * [Returns] Nokogiri::XML::NodeSet containing this node's children.
676
+ */
677
+ static VALUE
678
+ rb_xml_node_children(VALUE self)
679
+ {
680
+ xmlNodePtr node;
681
+ xmlNodePtr child;
682
+ xmlNodeSetPtr set;
683
+ VALUE document;
684
+ VALUE node_set;
685
+
686
+ Data_Get_Struct(self, xmlNode, node);
687
+
688
+ child = node->children;
689
+ set = xmlXPathNodeSetCreate(child);
690
+
691
+ document = DOC_RUBY_OBJECT(node->doc);
692
+
693
+ if (!child) { return noko_xml_node_set_wrap(set, document); }
694
+
695
+ child = child->next;
696
+ while (NULL != child) {
697
+ xmlXPathNodeSetAddUnique(set, child);
698
+ child = child->next;
699
+ }
700
+
701
+ node_set = noko_xml_node_set_wrap(set, document);
702
+
703
+ return node_set;
704
+ }
705
+
706
+
707
+ /*
708
+ * :call-seq:
709
+ * content() → String
710
+ * inner_text() → String
711
+ * text() → String
712
+ * to_str() → String
713
+ *
714
+ * [Returns]
715
+ * Contents of all the text nodes in this node's subtree, concatenated together into a single
716
+ * String.
717
+ *
718
+ * ⚠ Note that entities will _always_ be expanded in the returned String.
719
+ *
720
+ * See related: #inner_html
226
721
  *
227
- * Get the document for this Node
722
+ * *Example* of how entities are handled:
723
+ *
724
+ * Note that <tt>&lt;</tt> becomes <tt><</tt> in the returned String.
725
+ *
726
+ * doc = Nokogiri::XML.fragment("<child>a &lt; b</child>")
727
+ * doc.at_css("child").content
728
+ * # => "a < b"
729
+ *
730
+ * *Example* of how a subtree is handled:
731
+ *
732
+ * Note that the <tt><span></tt> tags are omitted and only the text node contents are returned,
733
+ * concatenated into a single string.
734
+ *
735
+ * doc = Nokogiri::XML.fragment("<child><span>first</span> <span>second</span></child>")
736
+ * doc.at_css("child").content
737
+ * # => "first second"
738
+ */
739
+ static VALUE
740
+ rb_xml_node_content(VALUE self)
741
+ {
742
+ xmlNodePtr node;
743
+ xmlChar *content;
744
+
745
+ Data_Get_Struct(self, xmlNode, node);
746
+
747
+ content = xmlNodeGetContent(node);
748
+ if (content) {
749
+ VALUE rval = NOKOGIRI_STR_NEW2(content);
750
+ xmlFree(content);
751
+ return rval;
752
+ }
753
+ return Qnil;
754
+ }
755
+
756
+
757
+ /*
758
+ * :call-seq: document() → Nokogiri::XML::Document
759
+ *
760
+ * :category: Traversing Document Structure
761
+ *
762
+ * [Returns] Parent Nokogiri::XML::Document for this node
228
763
  */
229
- static VALUE document(VALUE self)
764
+ static VALUE
765
+ rb_xml_node_document(VALUE self)
230
766
  {
231
767
  xmlNodePtr node;
232
768
  Data_Get_Struct(self, xmlNode, node);
@@ -234,12 +770,14 @@ static VALUE document(VALUE self)
234
770
  }
235
771
 
236
772
  /*
237
- * call-seq:
238
- * pointer_id
773
+ * :call-seq: pointer_id() → Integer
239
774
  *
240
- * Get the internal pointer number
775
+ * [Returns]
776
+ * A unique id for this node based on the internal memory structures. This method is used by #==
777
+ * to determine node identity.
241
778
  */
242
- static VALUE pointer_id(VALUE self)
779
+ static VALUE
780
+ rb_xml_node_pointer_id(VALUE self)
243
781
  {
244
782
  xmlNodePtr node;
245
783
  Data_Get_Struct(self, xmlNode, node);
@@ -248,12 +786,12 @@ static VALUE pointer_id(VALUE self)
248
786
  }
249
787
 
250
788
  /*
251
- * call-seq:
252
- * encode_special_chars(string)
789
+ * :call-seq: encode_special_chars(string) → String
253
790
  *
254
791
  * Encode any special characters in +string+
255
792
  */
256
- static VALUE encode_special_chars(VALUE self, VALUE string)
793
+ static VALUE
794
+ encode_special_chars(VALUE self, VALUE string)
257
795
  {
258
796
  xmlNodePtr node;
259
797
  xmlChar *encoded;
@@ -261,9 +799,9 @@ static VALUE encode_special_chars(VALUE self, VALUE string)
261
799
 
262
800
  Data_Get_Struct(self, xmlNode, node);
263
801
  encoded = xmlEncodeSpecialChars(
264
- node->doc,
265
- (const xmlChar *)StringValuePtr(string)
266
- );
802
+ node->doc,
803
+ (const xmlChar *)StringValueCStr(string)
804
+ );
267
805
 
268
806
  encoded_str = NOKOGIRI_STR_NEW2(encoded);
269
807
  xmlFree(encoded);
@@ -272,8 +810,8 @@ static VALUE encode_special_chars(VALUE self, VALUE string)
272
810
  }
273
811
 
274
812
  /*
275
- * call-seq:
276
- * create_internal_subset(name, external_id, system_id)
813
+ * :call-seq:
814
+ * create_internal_subset(name, external_id, system_id)
277
815
  *
278
816
  * Create the internal subset of a document.
279
817
  *
@@ -283,7 +821,8 @@ static VALUE encode_special_chars(VALUE self, VALUE string)
283
821
  * doc.create_internal_subset("chapter", nil, "chapter.dtd")
284
822
  * # => <!DOCTYPE chapter SYSTEM "chapter.dtd">
285
823
  */
286
- static VALUE create_internal_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
824
+ static VALUE
825
+ create_internal_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
287
826
  {
288
827
  xmlNodePtr node;
289
828
  xmlDocPtr doc;
@@ -293,28 +832,30 @@ static VALUE create_internal_subset(VALUE self, VALUE name, VALUE external_id, V
293
832
 
294
833
  doc = node->doc;
295
834
 
296
- if(xmlGetIntSubset(doc))
835
+ if (xmlGetIntSubset(doc)) {
297
836
  rb_raise(rb_eRuntimeError, "Document already has an internal subset");
837
+ }
298
838
 
299
839
  dtd = xmlCreateIntSubset(
300
- doc,
301
- NIL_P(name) ? NULL : (const xmlChar *)StringValuePtr(name),
302
- NIL_P(external_id) ? NULL : (const xmlChar *)StringValuePtr(external_id),
303
- NIL_P(system_id) ? NULL : (const xmlChar *)StringValuePtr(system_id)
304
- );
840
+ doc,
841
+ NIL_P(name) ? NULL : (const xmlChar *)StringValueCStr(name),
842
+ NIL_P(external_id) ? NULL : (const xmlChar *)StringValueCStr(external_id),
843
+ NIL_P(system_id) ? NULL : (const xmlChar *)StringValueCStr(system_id)
844
+ );
305
845
 
306
- if(!dtd) return Qnil;
846
+ if (!dtd) { return Qnil; }
307
847
 
308
- return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
848
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
309
849
  }
310
850
 
311
851
  /*
312
- * call-seq:
313
- * create_external_subset(name, external_id, system_id)
852
+ * :call-seq:
853
+ * create_external_subset(name, external_id, system_id)
314
854
  *
315
855
  * Create an external subset
316
856
  */
317
- static VALUE create_external_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
857
+ static VALUE
858
+ create_external_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
318
859
  {
319
860
  xmlNodePtr node;
320
861
  xmlDocPtr doc;
@@ -324,28 +865,30 @@ static VALUE create_external_subset(VALUE self, VALUE name, VALUE external_id, V
324
865
 
325
866
  doc = node->doc;
326
867
 
327
- if(doc->extSubset)
868
+ if (doc->extSubset) {
328
869
  rb_raise(rb_eRuntimeError, "Document already has an external subset");
870
+ }
329
871
 
330
872
  dtd = xmlNewDtd(
331
- doc,
332
- NIL_P(name) ? NULL : (const xmlChar *)StringValuePtr(name),
333
- NIL_P(external_id) ? NULL : (const xmlChar *)StringValuePtr(external_id),
334
- NIL_P(system_id) ? NULL : (const xmlChar *)StringValuePtr(system_id)
335
- );
873
+ doc,
874
+ NIL_P(name) ? NULL : (const xmlChar *)StringValueCStr(name),
875
+ NIL_P(external_id) ? NULL : (const xmlChar *)StringValueCStr(external_id),
876
+ NIL_P(system_id) ? NULL : (const xmlChar *)StringValueCStr(system_id)
877
+ );
336
878
 
337
- if(!dtd) return Qnil;
879
+ if (!dtd) { return Qnil; }
338
880
 
339
- return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
881
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
340
882
  }
341
883
 
342
884
  /*
343
- * call-seq:
344
- * external_subset
885
+ * :call-seq:
886
+ * external_subset()
345
887
  *
346
888
  * Get the external subset
347
889
  */
348
- static VALUE external_subset(VALUE self)
890
+ static VALUE
891
+ external_subset(VALUE self)
349
892
  {
350
893
  xmlNodePtr node;
351
894
  xmlDocPtr doc;
@@ -353,23 +896,24 @@ static VALUE external_subset(VALUE self)
353
896
 
354
897
  Data_Get_Struct(self, xmlNode, node);
355
898
 
356
- if(!node->doc) return Qnil;
899
+ if (!node->doc) { return Qnil; }
357
900
 
358
901
  doc = node->doc;
359
902
  dtd = doc->extSubset;
360
903
 
361
- if(!dtd) return Qnil;
904
+ if (!dtd) { return Qnil; }
362
905
 
363
- return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
906
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
364
907
  }
365
908
 
366
909
  /*
367
- * call-seq:
368
- * internal_subset
910
+ * :call-seq:
911
+ * internal_subset()
369
912
  *
370
913
  * Get the internal subset
371
914
  */
372
- static VALUE internal_subset(VALUE self)
915
+ static VALUE
916
+ internal_subset(VALUE self)
373
917
  {
374
918
  xmlNodePtr node;
375
919
  xmlDocPtr doc;
@@ -377,68 +921,79 @@ static VALUE internal_subset(VALUE self)
377
921
 
378
922
  Data_Get_Struct(self, xmlNode, node);
379
923
 
380
- if(!node->doc) return Qnil;
924
+ if (!node->doc) { return Qnil; }
381
925
 
382
926
  doc = node->doc;
383
927
  dtd = xmlGetIntSubset(doc);
384
928
 
385
- if(!dtd) return Qnil;
929
+ if (!dtd) { return Qnil; }
386
930
 
387
- return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
931
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
388
932
  }
389
933
 
390
934
  /*
391
- * call-seq:
392
- * dup
935
+ * :call-seq:
936
+ * dup → Nokogiri::XML::Node
937
+ * dup(depth) → Nokogiri::XML::Node
938
+ * dup(depth, new_parent_doc) → Nokogiri::XML::Node
393
939
  *
394
- * Copy this node. An optional depth may be passed in, but it defaults
395
- * to a deep copy. 0 is a shallow copy, 1 is a deep copy.
940
+ * Copy this node.
941
+ *
942
+ * [Parameters]
943
+ * - +depth+ 0 is a shallow copy, 1 (the default) is a deep copy.
944
+ * - +new_parent_doc+
945
+ * The new node's parent Document. Defaults to the this node's document.
946
+ *
947
+ * [Returns] The new Nokgiri::XML::Node
396
948
  */
397
- static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
949
+ static VALUE
950
+ duplicate_node(int argc, VALUE *argv, VALUE self)
398
951
  {
399
- VALUE level;
952
+ VALUE r_level, r_new_parent_doc;
953
+ int level;
954
+ int n_args;
955
+ xmlDocPtr new_parent_doc;
400
956
  xmlNodePtr node, dup;
401
957
 
402
- if(rb_scan_args(argc, argv, "01", &level) == 0)
403
- level = INT2NUM((long)1);
404
-
405
958
  Data_Get_Struct(self, xmlNode, node);
406
959
 
407
- dup = xmlDocCopyNode(node, node->doc, (int)NUM2INT(level));
408
- if(dup == NULL) return Qnil;
960
+ n_args = rb_scan_args(argc, argv, "02", &r_level, &r_new_parent_doc);
409
961
 
410
- nokogiri_root_node(dup);
962
+ if (n_args < 1) {
963
+ r_level = INT2NUM((long)1);
964
+ }
965
+ level = (int)NUM2INT(r_level);
966
+
967
+ if (n_args < 2) {
968
+ new_parent_doc = node->doc;
969
+ } else {
970
+ Data_Get_Struct(r_new_parent_doc, xmlDoc, new_parent_doc);
971
+ }
411
972
 
412
- return Nokogiri_wrap_xml_node(rb_obj_class(self), dup);
973
+ dup = xmlDocCopyNode(node, new_parent_doc, level);
974
+ if (dup == NULL) { return Qnil; }
975
+
976
+ noko_xml_document_pin_node(dup);
977
+
978
+ return noko_xml_node_wrap(rb_obj_class(self), dup);
413
979
  }
414
980
 
415
981
  /*
416
- * call-seq:
417
- * unlink
982
+ * :call-seq:
983
+ * unlink() → self
418
984
  *
419
985
  * Unlink this node from its current context.
420
986
  */
421
- static VALUE unlink_node(VALUE self)
987
+ static VALUE
988
+ unlink_node(VALUE self)
422
989
  {
423
990
  xmlNodePtr node;
424
991
  Data_Get_Struct(self, xmlNode, node);
425
992
  xmlUnlinkNode(node);
426
- nokogiri_root_node(node);
993
+ noko_xml_document_pin_node(node);
427
994
  return self;
428
995
  }
429
996
 
430
- /*
431
- * call-seq:
432
- * blank?
433
- *
434
- * Is this node blank?
435
- */
436
- static VALUE blank_eh(VALUE self)
437
- {
438
- xmlNodePtr node;
439
- Data_Get_Struct(self, xmlNode, node);
440
- return (1 == xmlIsBlankNode(node)) ? Qtrue : Qfalse ;
441
- }
442
997
 
443
998
  /*
444
999
  * call-seq:
@@ -446,15 +1001,16 @@ static VALUE blank_eh(VALUE self)
446
1001
  *
447
1002
  * Returns the next sibling node
448
1003
  */
449
- static VALUE next_sibling(VALUE self)
1004
+ static VALUE
1005
+ next_sibling(VALUE self)
450
1006
  {
451
1007
  xmlNodePtr node, sibling;
452
1008
  Data_Get_Struct(self, xmlNode, node);
453
1009
 
454
1010
  sibling = node->next;
455
- if(!sibling) return Qnil;
1011
+ if (!sibling) { return Qnil; }
456
1012
 
457
- return Nokogiri_wrap_xml_node(Qnil, sibling) ;
1013
+ return noko_xml_node_wrap(Qnil, sibling) ;
458
1014
  }
459
1015
 
460
1016
  /*
@@ -463,15 +1019,16 @@ static VALUE next_sibling(VALUE self)
463
1019
  *
464
1020
  * Returns the previous sibling node
465
1021
  */
466
- static VALUE previous_sibling(VALUE self)
1022
+ static VALUE
1023
+ previous_sibling(VALUE self)
467
1024
  {
468
1025
  xmlNodePtr node, sibling;
469
1026
  Data_Get_Struct(self, xmlNode, node);
470
1027
 
471
1028
  sibling = node->prev;
472
- if(!sibling) return Qnil;
1029
+ if (!sibling) { return Qnil; }
473
1030
 
474
- return Nokogiri_wrap_xml_node(Qnil, sibling);
1031
+ return noko_xml_node_wrap(Qnil, sibling);
475
1032
  }
476
1033
 
477
1034
  /*
@@ -480,15 +1037,16 @@ static VALUE previous_sibling(VALUE self)
480
1037
  *
481
1038
  * Returns the next Nokogiri::XML::Element type sibling node.
482
1039
  */
483
- static VALUE next_element(VALUE self)
1040
+ static VALUE
1041
+ next_element(VALUE self)
484
1042
  {
485
1043
  xmlNodePtr node, sibling;
486
1044
  Data_Get_Struct(self, xmlNode, node);
487
1045
 
488
1046
  sibling = xmlNextElementSibling(node);
489
- if(!sibling) return Qnil;
1047
+ if (!sibling) { return Qnil; }
490
1048
 
491
- return Nokogiri_wrap_xml_node(Qnil, sibling);
1049
+ return noko_xml_node_wrap(Qnil, sibling);
492
1050
  }
493
1051
 
494
1052
  /*
@@ -497,7 +1055,8 @@ static VALUE next_element(VALUE self)
497
1055
  *
498
1056
  * Returns the previous Nokogiri::XML::Element type sibling node.
499
1057
  */
500
- static VALUE previous_element(VALUE self)
1058
+ static VALUE
1059
+ previous_element(VALUE self)
501
1060
  {
502
1061
  xmlNodePtr node, sibling;
503
1062
  Data_Get_Struct(self, xmlNode, node);
@@ -506,72 +1065,50 @@ static VALUE previous_element(VALUE self)
506
1065
  * note that we don't use xmlPreviousElementSibling here because it's buggy pre-2.7.7.
507
1066
  */
508
1067
  sibling = node->prev;
509
- if(!sibling) return Qnil;
1068
+ if (!sibling) { return Qnil; }
510
1069
 
511
- while(sibling && sibling->type != XML_ELEMENT_NODE)
1070
+ while (sibling && sibling->type != XML_ELEMENT_NODE) {
512
1071
  sibling = sibling->prev;
1072
+ }
513
1073
 
514
- return sibling ? Nokogiri_wrap_xml_node(Qnil, sibling) : Qnil ;
1074
+ return sibling ? noko_xml_node_wrap(Qnil, sibling) : Qnil ;
515
1075
  }
516
1076
 
517
1077
  /* :nodoc: */
518
- static VALUE replace(VALUE self, VALUE new_node)
1078
+ static VALUE
1079
+ replace(VALUE self, VALUE new_node)
519
1080
  {
520
- VALUE reparent = reparent_node_with(self, new_node, xmlReplaceNodeWrapper);
1081
+ VALUE reparent = reparent_node_with(self, new_node, xmlReplaceNodeWrapper);
521
1082
 
522
- xmlNodePtr pivot;
523
- Data_Get_Struct(self, xmlNode, pivot);
524
- nokogiri_root_node(pivot);
1083
+ xmlNodePtr pivot;
1084
+ Data_Get_Struct(self, xmlNode, pivot);
1085
+ noko_xml_document_pin_node(pivot);
525
1086
 
526
- return reparent;
1087
+ return reparent;
527
1088
  }
528
1089
 
529
1090
  /*
530
- * call-seq:
531
- * children
1091
+ * :call-seq:
1092
+ * element_children() → NodeSet
1093
+ * elements() → NodeSet
532
1094
  *
533
- * Get the list of children for this node as a NodeSet
534
- */
535
- static VALUE children(VALUE self)
536
- {
537
- xmlNodePtr node;
538
- xmlNodePtr child;
539
- xmlNodeSetPtr set;
540
- VALUE document;
541
- VALUE node_set;
542
-
543
- Data_Get_Struct(self, xmlNode, node);
544
-
545
- child = node->children;
546
- set = xmlXPathNodeSetCreate(child);
547
-
548
- document = DOC_RUBY_OBJECT(node->doc);
549
-
550
- if(!child) return Nokogiri_wrap_xml_node_set(set, document);
551
-
552
- child = child->next;
553
- while(NULL != child) {
554
- xmlXPathNodeSetAddUnique(set, child);
555
- child = child->next;
556
- }
557
-
558
- node_set = Nokogiri_wrap_xml_node_set(set, document);
559
-
560
- return node_set;
561
- }
562
-
563
- /*
564
- * call-seq:
565
- * element_children
1095
+ * [Returns]
1096
+ * The node's child elements as a NodeSet. Only children that are elements will be returned, which
1097
+ * notably excludes Text nodes.
566
1098
  *
567
- * Get the list of children for this node as a NodeSet. All nodes will be
568
- * element nodes.
1099
+ * *Example:*
569
1100
  *
570
- * Example:
1101
+ * Note that #children returns the Text node "hello" while #element_children does not.
571
1102
  *
572
- * @doc.root.element_children.all? { |x| x.element? } # => true
1103
+ * div = Nokogiri::HTML5("<div>hello<span>world</span>").at_css("div")
1104
+ * div.element_children
1105
+ * # => [#<Nokogiri::XML::Element:0x50 name="span" children=[#<Nokogiri::XML::Text:0x3c "world">]>]
1106
+ * div.children
1107
+ * # => [#<Nokogiri::XML::Text:0x64 "hello">,
1108
+ * # #<Nokogiri::XML::Element:0x50 name="span" children=[#<Nokogiri::XML::Text:0x3c "world">]>]
573
1109
  */
574
- static VALUE element_children(VALUE self)
1110
+ static VALUE
1111
+ rb_xml_node_element_children(VALUE self)
575
1112
  {
576
1113
  xmlNodePtr node;
577
1114
  xmlNodePtr child;
@@ -586,76 +1123,71 @@ static VALUE element_children(VALUE self)
586
1123
 
587
1124
  document = DOC_RUBY_OBJECT(node->doc);
588
1125
 
589
- if(!child) return Nokogiri_wrap_xml_node_set(set, document);
1126
+ if (!child) { return noko_xml_node_set_wrap(set, document); }
590
1127
 
591
1128
  child = xmlNextElementSibling(child);
592
- while(NULL != child) {
1129
+ while (NULL != child) {
593
1130
  xmlXPathNodeSetAddUnique(set, child);
594
1131
  child = xmlNextElementSibling(child);
595
1132
  }
596
1133
 
597
- node_set = Nokogiri_wrap_xml_node_set(set, document);
1134
+ node_set = noko_xml_node_set_wrap(set, document);
598
1135
 
599
1136
  return node_set;
600
1137
  }
601
1138
 
602
1139
  /*
603
- * call-seq:
604
- * child
1140
+ * :call-seq:
1141
+ * first_element_child() → Node
605
1142
  *
606
- * Returns the child node
607
- */
608
- static VALUE child(VALUE self)
609
- {
610
- xmlNodePtr node, child;
611
- Data_Get_Struct(self, xmlNode, node);
612
-
613
- child = node->children;
614
- if(!child) return Qnil;
615
-
616
- return Nokogiri_wrap_xml_node(Qnil, child);
617
- }
618
-
619
- /*
620
- * call-seq:
621
- * first_element_child
1143
+ * [Returns] The first child Node that is an element.
622
1144
  *
623
- * Returns the first child node of this node that is an element.
1145
+ * *Example:*
624
1146
  *
625
- * Example:
1147
+ * Note that the "hello" child, which is a Text node, is skipped and the <tt><span></tt> element is
1148
+ * returned.
626
1149
  *
627
- * @doc.root.first_element_child.element? # => true
1150
+ * div = Nokogiri::HTML5("<div>hello<span>world</span>").at_css("div")
1151
+ * div.first_element_child
1152
+ * # => #(Element:0x3c { name = "span", children = [ #(Text "world")] })
628
1153
  */
629
- static VALUE first_element_child(VALUE self)
1154
+ static VALUE
1155
+ rb_xml_node_first_element_child(VALUE self)
630
1156
  {
631
1157
  xmlNodePtr node, child;
632
1158
  Data_Get_Struct(self, xmlNode, node);
633
1159
 
634
1160
  child = xmlFirstElementChild(node);
635
- if(!child) return Qnil;
1161
+ if (!child) { return Qnil; }
636
1162
 
637
- return Nokogiri_wrap_xml_node(Qnil, child);
1163
+ return noko_xml_node_wrap(Qnil, child);
638
1164
  }
639
1165
 
640
1166
  /*
641
- * call-seq:
642
- * last_element_child
1167
+ * :call-seq:
1168
+ * last_element_child() → Node
1169
+ *
1170
+ * [Returns] The last child Node that is an element.
643
1171
  *
644
- * Returns the last child node of this node that is an element.
1172
+ * *Example:*
645
1173
  *
646
- * Example:
1174
+ * Note that the "hello" child, which is a Text node, is skipped and the <tt><span>yes</span></tt>
1175
+ * element is returned.
647
1176
  *
648
- * @doc.root.last_element_child.element? # => true
1177
+ * div = Nokogiri::HTML5("<div><span>no</span><span>yes</span>skip</div>").at_css("div")
1178
+ * div.last_element_child
1179
+ * # => #(Element:0x3c { name = "span", children = [ #(Text "yes")] })
649
1180
  */
650
- static VALUE last_element_child(VALUE self)
1181
+ static VALUE
1182
+ rb_xml_node_last_element_child(VALUE self)
651
1183
  {
652
1184
  xmlNodePtr node, child;
653
1185
  Data_Get_Struct(self, xmlNode, node);
654
1186
 
655
1187
  child = xmlLastElementChild(node);
656
- if(!child) return Qnil;
1188
+ if (!child) { return Qnil; }
657
1189
 
658
- return Nokogiri_wrap_xml_node(Qnil, child);
1190
+ return noko_xml_node_wrap(Qnil, child);
659
1191
  }
660
1192
 
661
1193
  /*
@@ -664,12 +1196,14 @@ static VALUE last_element_child(VALUE self)
664
1196
  *
665
1197
  * Returns true if +attribute+ is set
666
1198
  */
667
- static VALUE key_eh(VALUE self, VALUE attribute)
1199
+ static VALUE
1200
+ key_eh(VALUE self, VALUE attribute)
668
1201
  {
669
1202
  xmlNodePtr node;
670
1203
  Data_Get_Struct(self, xmlNode, node);
671
- if(xmlHasProp(node, (xmlChar *)StringValuePtr(attribute)))
1204
+ if (xmlHasProp(node, (xmlChar *)StringValueCStr(attribute))) {
672
1205
  return Qtrue;
1206
+ }
673
1207
  return Qfalse;
674
1208
  }
675
1209
 
@@ -679,13 +1213,15 @@ static VALUE key_eh(VALUE self, VALUE attribute)
679
1213
  *
680
1214
  * Returns true if +attribute+ is set with +namespace+
681
1215
  */
682
- static VALUE namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
1216
+ static VALUE
1217
+ namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
683
1218
  {
684
1219
  xmlNodePtr node;
685
1220
  Data_Get_Struct(self, xmlNode, node);
686
- if(xmlHasNsProp(node, (xmlChar *)StringValuePtr(attribute),
687
- NIL_P(namespace) ? NULL : (xmlChar *)StringValuePtr(namespace)))
1221
+ if (xmlHasNsProp(node, (xmlChar *)StringValueCStr(attribute),
1222
+ NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace))) {
688
1223
  return Qtrue;
1224
+ }
689
1225
  return Qfalse;
690
1226
  }
691
1227
 
@@ -695,7 +1231,8 @@ static VALUE namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
695
1231
  *
696
1232
  * Set the +property+ to +value+
697
1233
  */
698
- static VALUE set(VALUE self, VALUE property, VALUE value)
1234
+ static VALUE
1235
+ set(VALUE self, VALUE property, VALUE value)
699
1236
  {
700
1237
  xmlNodePtr node, cur;
701
1238
  xmlAttrPtr prop;
@@ -707,20 +1244,21 @@ static VALUE set(VALUE self, VALUE property, VALUE value)
707
1244
  *
708
1245
  * We can avoid this by unlinking these nodes first.
709
1246
  */
710
- if (node->type != XML_ELEMENT_NODE)
711
- return(Qnil);
712
- prop = xmlHasProp(node, (xmlChar *)StringValuePtr(property));
1247
+ if (node->type != XML_ELEMENT_NODE) {
1248
+ return (Qnil);
1249
+ }
1250
+ prop = xmlHasProp(node, (xmlChar *)StringValueCStr(property));
713
1251
  if (prop && prop->children) {
714
1252
  for (cur = prop->children; cur; cur = cur->next) {
715
1253
  if (cur->_private) {
716
- nokogiri_root_node(cur);
1254
+ noko_xml_document_pin_node(cur);
717
1255
  xmlUnlinkNode(cur);
718
1256
  }
719
1257
  }
720
1258
  }
721
1259
 
722
- xmlSetProp(node, (xmlChar *)StringValuePtr(property),
723
- (xmlChar *)StringValuePtr(value));
1260
+ xmlSetProp(node, (xmlChar *)StringValueCStr(property),
1261
+ (xmlChar *)StringValueCStr(value));
724
1262
 
725
1263
  return value;
726
1264
  }
@@ -731,40 +1269,44 @@ static VALUE set(VALUE self, VALUE property, VALUE value)
731
1269
  *
732
1270
  * Get the value for +attribute+
733
1271
  */
734
- static VALUE get(VALUE self, VALUE rattribute)
1272
+ static VALUE
1273
+ get(VALUE self, VALUE rattribute)
735
1274
  {
736
1275
  xmlNodePtr node;
737
- xmlChar* value = 0;
738
- VALUE rvalue ;
739
- char* attribute = 0;
740
- char *colon = 0, *attr_name = 0, *prefix = 0;
1276
+ xmlChar *value = 0;
1277
+ VALUE rvalue;
1278
+ xmlChar *colon;
1279
+ xmlChar *attribute, *attr_name, *prefix;
741
1280
  xmlNsPtr ns;
742
1281
 
743
- if (NIL_P(rattribute)) return Qnil;
1282
+ if (NIL_P(rattribute)) { return Qnil; }
744
1283
 
745
1284
  Data_Get_Struct(self, xmlNode, node);
746
- attribute = strdup(StringValuePtr(rattribute));
1285
+ attribute = xmlCharStrdup(StringValueCStr(rattribute));
747
1286
 
748
- colon = strchr(attribute, ':');
1287
+ colon = DISCARD_CONST_QUAL_XMLCHAR(xmlStrchr(attribute, (const xmlChar)':'));
749
1288
  if (colon) {
750
- (*colon) = 0 ; /* create two null-terminated strings of the prefix and attribute name */
751
- prefix = attribute ;
752
- attr_name = colon + 1 ;
753
- ns = xmlSearchNs(node->doc, node, (const xmlChar *)(prefix));
1289
+ /* split the attribute string into separate prefix and name by
1290
+ * null-terminating the prefix at the colon */
1291
+ prefix = attribute;
1292
+ attr_name = colon + 1;
1293
+ (*colon) = 0;
1294
+
1295
+ ns = xmlSearchNs(node->doc, node, prefix);
754
1296
  if (ns) {
755
- value = xmlGetNsProp(node, (xmlChar*)(attr_name), ns->href);
1297
+ value = xmlGetNsProp(node, attr_name, ns->href);
756
1298
  } else {
757
- value = xmlGetProp(node, (xmlChar*)StringValuePtr(rattribute));
1299
+ value = xmlGetProp(node, (xmlChar *)StringValueCStr(rattribute));
758
1300
  }
759
1301
  } else {
760
- value = xmlGetNoNsProp(node, (xmlChar*)attribute);
1302
+ value = xmlGetNoNsProp(node, attribute);
761
1303
  }
762
1304
 
763
- free(attribute);
764
- if (!value) return Qnil;
1305
+ xmlFree((void *)attribute);
1306
+ if (!value) { return Qnil; }
765
1307
 
766
1308
  rvalue = NOKOGIRI_STR_NEW2(value);
767
- xmlFree(value);
1309
+ xmlFree((void *)value);
768
1310
 
769
1311
  return rvalue ;
770
1312
  }
@@ -775,15 +1317,17 @@ static VALUE get(VALUE self, VALUE rattribute)
775
1317
  *
776
1318
  * Set the namespace to +namespace+
777
1319
  */
778
- static VALUE set_namespace(VALUE self, VALUE namespace)
1320
+ static VALUE
1321
+ set_namespace(VALUE self, VALUE namespace)
779
1322
  {
780
1323
  xmlNodePtr node;
781
1324
  xmlNsPtr ns = NULL;
782
1325
 
783
1326
  Data_Get_Struct(self, xmlNode, node);
784
1327
 
785
- if(!NIL_P(namespace))
1328
+ if (!NIL_P(namespace)) {
786
1329
  Data_Get_Struct(namespace, xmlNs, ns);
1330
+ }
787
1331
 
788
1332
  xmlSetNs(node, ns);
789
1333
 
@@ -791,137 +1335,140 @@ static VALUE set_namespace(VALUE self, VALUE namespace)
791
1335
  }
792
1336
 
793
1337
  /*
794
- * call-seq:
795
- * attribute(name)
796
- *
797
- * Get the attribute node with +name+
798
- */
799
- static VALUE attr(VALUE self, VALUE name)
800
- {
801
- xmlNodePtr node;
802
- xmlAttrPtr prop;
803
- Data_Get_Struct(self, xmlNode, node);
804
- prop = xmlHasProp(node, (xmlChar *)StringValuePtr(name));
805
-
806
- if(! prop) return Qnil;
807
- return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)prop);
808
- }
809
-
810
- /*
811
- * call-seq:
812
- * attribute_with_ns(name, namespace)
1338
+ * :call-seq:
1339
+ * namespace() → Namespace
813
1340
  *
814
- * Get the attribute node with +name+ and +namespace+
815
- */
816
- static VALUE attribute_with_ns(VALUE self, VALUE name, VALUE namespace)
817
- {
818
- xmlNodePtr node;
819
- xmlAttrPtr prop;
820
- Data_Get_Struct(self, xmlNode, node);
821
- prop = xmlHasNsProp(node, (xmlChar *)StringValuePtr(name),
822
- NIL_P(namespace) ? NULL : (xmlChar *)StringValuePtr(namespace));
823
-
824
- if(! prop) return Qnil;
825
- return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)prop);
826
- }
827
-
828
- /*
829
- * call-seq:
830
- * attribute_nodes()
1341
+ * [Returns] The Namespace of the element or attribute node, or +nil+ if there is no namespace.
831
1342
  *
832
- * returns a list containing the Node attributes.
833
- */
834
- static VALUE attribute_nodes(VALUE self)
835
- {
836
- /* this code in the mode of xmlHasProp() */
837
- xmlNodePtr node;
838
- VALUE attr;
839
-
840
- Data_Get_Struct(self, xmlNode, node);
841
-
842
- attr = rb_ary_new();
843
- Nokogiri_xml_node_properties(node, attr);
844
-
845
- return attr ;
846
- }
847
-
848
-
849
- /*
850
- * call-seq:
851
- * namespace()
1343
+ * *Example:*
852
1344
  *
853
- * returns the default namespace set on this node (as with an "xmlns="
854
- * attribute), as a Namespace object.
1345
+ * doc = Nokogiri::XML(<<~EOF)
1346
+ * <root>
1347
+ * <first/>
1348
+ * <second xmlns="http://example.com/child"/>
1349
+ * <foo:third xmlns:foo="http://example.com/foo"/>
1350
+ * </root>
1351
+ * EOF
1352
+ * doc.at_xpath("//first").namespace
1353
+ * # => nil
1354
+ * doc.at_xpath("//xmlns:second", "xmlns" => "http://example.com/child").namespace
1355
+ * # => #(Namespace:0x3c { href = "http://example.com/child" })
1356
+ * doc.at_xpath("//foo:third", "foo" => "http://example.com/foo").namespace
1357
+ * # => #(Namespace:0x50 { prefix = "foo", href = "http://example.com/foo" })
855
1358
  */
856
- static VALUE namespace(VALUE self)
1359
+ static VALUE
1360
+ rb_xml_node_namespace(VALUE rb_node)
857
1361
  {
858
- xmlNodePtr node ;
859
- Data_Get_Struct(self, xmlNode, node);
1362
+ xmlNodePtr c_node ;
1363
+ Data_Get_Struct(rb_node, xmlNode, c_node);
860
1364
 
861
- if (node->ns)
862
- return Nokogiri_wrap_xml_namespace(node->doc, node->ns);
1365
+ if (c_node->ns) {
1366
+ return noko_xml_namespace_wrap(c_node->ns, c_node->doc);
1367
+ }
863
1368
 
864
1369
  return Qnil ;
865
1370
  }
866
1371
 
867
1372
  /*
868
- * call-seq:
869
- * namespace_definitions()
1373
+ * :call-seq:
1374
+ * namespace_definitions() → Array<Nokogiri::XML::Namespace>
870
1375
  *
871
- * 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=").
1376
+ * [Returns]
1377
+ * Namespaces that are defined directly on this node, as an Array of Namespace objects. The array
1378
+ * will be empty if no namespaces are defined on this node.
1379
+ *
1380
+ * *Example:*
1381
+ *
1382
+ * doc = Nokogiri::XML(<<~EOF)
1383
+ * <root xmlns="http://example.com/root">
1384
+ * <first/>
1385
+ * <second xmlns="http://example.com/child" xmlns:unused="http://example.com/unused"/>
1386
+ * <foo:third xmlns:foo="http://example.com/foo"/>
1387
+ * </root>
1388
+ * EOF
1389
+ * doc.at_xpath("//root:first", "root" => "http://example.com/root").namespace_definitions
1390
+ * # => []
1391
+ * doc.at_xpath("//xmlns:second", "xmlns" => "http://example.com/child").namespace_definitions
1392
+ * # => [#(Namespace:0x3c { href = "http://example.com/child" }),
1393
+ * # #(Namespace:0x50 {
1394
+ * # prefix = "unused",
1395
+ * # href = "http://example.com/unused"
1396
+ * # })]
1397
+ * doc.at_xpath("//foo:third", "foo" => "http://example.com/foo").namespace_definitions
1398
+ * # => [#(Namespace:0x64 { prefix = "foo", href = "http://example.com/foo" })]
872
1399
  */
873
- static VALUE namespace_definitions(VALUE self)
1400
+ static VALUE
1401
+ namespace_definitions(VALUE rb_node)
874
1402
  {
875
1403
  /* this code in the mode of xmlHasProp() */
876
- xmlNodePtr node ;
877
- VALUE list;
878
- xmlNsPtr ns;
879
-
880
- Data_Get_Struct(self, xmlNode, node);
881
-
882
- list = rb_ary_new();
1404
+ xmlNodePtr c_node ;
1405
+ xmlNsPtr c_namespace;
1406
+ VALUE definitions = rb_ary_new();
883
1407
 
884
- ns = node->nsDef;
1408
+ Data_Get_Struct(rb_node, xmlNode, c_node);
885
1409
 
886
- if(!ns) return list;
1410
+ c_namespace = c_node->nsDef;
1411
+ if (!c_namespace) {
1412
+ return definitions;
1413
+ }
887
1414
 
888
- while(NULL != ns) {
889
- rb_ary_push(list, Nokogiri_wrap_xml_namespace(node->doc, ns));
890
- ns = ns->next;
1415
+ while (c_namespace != NULL) {
1416
+ rb_ary_push(definitions, noko_xml_namespace_wrap(c_namespace, c_node->doc));
1417
+ c_namespace = c_namespace->next;
891
1418
  }
892
1419
 
893
- return list;
1420
+ return definitions;
894
1421
  }
895
1422
 
896
1423
  /*
897
- * call-seq:
898
- * namespace_scopes()
1424
+ * :call-seq:
1425
+ * namespace_scopes() → Array<Nokogiri::XML::Namespace>
1426
+ *
1427
+ * [Returns] Array of all the Namespaces on this node and its ancestors.
1428
+ *
1429
+ * See also #namespaces
1430
+ *
1431
+ * *Example:*
899
1432
  *
900
- * returns namespaces in scope for self -- those defined on self element
901
- * directly or any ancestor node -- as an array of Namespace objects. Default
902
- * namespaces ("xmlns=" style) for self are included in this array; Default
903
- * namespaces for ancestors, however, are not. See also #namespaces
1433
+ * doc = Nokogiri::XML(<<~EOF)
1434
+ * <root xmlns="http://example.com/root" xmlns:bar="http://example.com/bar">
1435
+ * <first/>
1436
+ * <second xmlns="http://example.com/child"/>
1437
+ * <third xmlns:foo="http://example.com/foo"/>
1438
+ * </root>
1439
+ * EOF
1440
+ * doc.at_xpath("//root:first", "root" => "http://example.com/root").namespace_scopes
1441
+ * # => [#(Namespace:0x3c { href = "http://example.com/root" }),
1442
+ * # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
1443
+ * doc.at_xpath("//child:second", "child" => "http://example.com/child").namespace_scopes
1444
+ * # => [#(Namespace:0x64 { href = "http://example.com/child" }),
1445
+ * # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
1446
+ * doc.at_xpath("//root:third", "root" => "http://example.com/root").namespace_scopes
1447
+ * # => [#(Namespace:0x78 { prefix = "foo", href = "http://example.com/foo" }),
1448
+ * # #(Namespace:0x3c { href = "http://example.com/root" }),
1449
+ * # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
904
1450
  */
905
- static VALUE namespace_scopes(VALUE self)
1451
+ static VALUE
1452
+ rb_xml_node_namespace_scopes(VALUE rb_node)
906
1453
  {
907
- xmlNodePtr node ;
908
- VALUE list;
909
- xmlNsPtr *ns_list;
1454
+ xmlNodePtr c_node ;
1455
+ xmlNsPtr *namespaces;
1456
+ VALUE scopes = rb_ary_new();
910
1457
  int j;
911
1458
 
912
- Data_Get_Struct(self, xmlNode, node);
913
-
914
- list = rb_ary_new();
915
- ns_list = xmlGetNsList(node->doc, node);
1459
+ Data_Get_Struct(rb_node, xmlNode, c_node);
916
1460
 
917
- if(!ns_list) return list;
1461
+ namespaces = xmlGetNsList(c_node->doc, c_node);
1462
+ if (!namespaces) {
1463
+ return scopes;
1464
+ }
918
1465
 
919
- for (j = 0 ; ns_list[j] != NULL ; ++j) {
920
- rb_ary_push(list, Nokogiri_wrap_xml_namespace(node->doc, ns_list[j]));
1466
+ for (j = 0 ; namespaces[j] != NULL ; ++j) {
1467
+ rb_ary_push(scopes, noko_xml_namespace_wrap(namespaces[j], c_node->doc));
921
1468
  }
922
1469
 
923
- xmlFree(ns_list);
924
- return list;
1470
+ xmlFree(namespaces);
1471
+ return scopes;
925
1472
  }
926
1473
 
927
1474
  /*
@@ -930,7 +1477,8 @@ static VALUE namespace_scopes(VALUE self)
930
1477
  *
931
1478
  * Get the type for this Node
932
1479
  */
933
- static VALUE node_type(VALUE self)
1480
+ static VALUE
1481
+ node_type(VALUE self)
934
1482
  {
935
1483
  xmlNodePtr node;
936
1484
  Data_Get_Struct(self, xmlNode, node);
@@ -943,7 +1491,8 @@ static VALUE node_type(VALUE self)
943
1491
  *
944
1492
  * Set the content for this Node
945
1493
  */
946
- static VALUE native_content(VALUE self, VALUE content)
1494
+ static VALUE
1495
+ set_native_content(VALUE self, VALUE content)
947
1496
  {
948
1497
  xmlNodePtr node, child, next ;
949
1498
  Data_Get_Struct(self, xmlNode, node);
@@ -952,38 +1501,63 @@ static VALUE native_content(VALUE self, VALUE content)
952
1501
  while (NULL != child) {
953
1502
  next = child->next ;
954
1503
  xmlUnlinkNode(child) ;
955
- nokogiri_root_node(child);
1504
+ noko_xml_document_pin_node(child);
956
1505
  child = next ;
957
1506
  }
958
1507
 
959
- xmlNodeSetContent(node, (xmlChar *)StringValuePtr(content));
1508
+ xmlNodeSetContent(node, (xmlChar *)StringValueCStr(content));
960
1509
  return content;
961
1510
  }
962
1511
 
963
1512
  /*
964
1513
  * call-seq:
965
- * content
1514
+ * lang=
966
1515
  *
967
- * Returns the content for this Node
1516
+ * Set the language of a node, i.e. the values of the xml:lang attribute.
968
1517
  */
969
- static VALUE get_content(VALUE self)
1518
+ static VALUE
1519
+ set_lang(VALUE self_rb, VALUE lang_rb)
970
1520
  {
971
- xmlNodePtr node;
972
- xmlChar * content;
1521
+ xmlNodePtr self ;
1522
+ xmlChar *lang ;
973
1523
 
974
- Data_Get_Struct(self, xmlNode, node);
1524
+ Data_Get_Struct(self_rb, xmlNode, self);
1525
+ lang = (xmlChar *)StringValueCStr(lang_rb);
975
1526
 
976
- content = xmlNodeGetContent(node);
977
- if(content) {
978
- VALUE rval = NOKOGIRI_STR_NEW2(content);
979
- xmlFree(content);
980
- return rval;
1527
+ xmlNodeSetLang(self, lang);
1528
+
1529
+ return Qnil ;
1530
+ }
1531
+
1532
+ /*
1533
+ * call-seq:
1534
+ * lang
1535
+ *
1536
+ * Searches the language of a node, i.e. the values of the xml:lang attribute or
1537
+ * the one carried by the nearest ancestor.
1538
+ */
1539
+ static VALUE
1540
+ get_lang(VALUE self_rb)
1541
+ {
1542
+ xmlNodePtr self ;
1543
+ xmlChar *lang ;
1544
+ VALUE lang_rb ;
1545
+
1546
+ Data_Get_Struct(self_rb, xmlNode, self);
1547
+
1548
+ lang = xmlNodeGetLang(self);
1549
+ if (lang) {
1550
+ lang_rb = NOKOGIRI_STR_NEW2(lang);
1551
+ xmlFree(lang);
1552
+ return lang_rb ;
981
1553
  }
982
- return Qnil;
1554
+
1555
+ return Qnil ;
983
1556
  }
984
1557
 
985
1558
  /* :nodoc: */
986
- static VALUE add_child(VALUE self, VALUE new_child)
1559
+ static VALUE
1560
+ add_child(VALUE self, VALUE new_child)
987
1561
  {
988
1562
  return reparent_node_with(self, new_child, xmlAddChild);
989
1563
  }
@@ -994,15 +1568,16 @@ static VALUE add_child(VALUE self, VALUE new_child)
994
1568
  *
995
1569
  * Get the parent Node for this Node
996
1570
  */
997
- static VALUE get_parent(VALUE self)
1571
+ static VALUE
1572
+ get_parent(VALUE self)
998
1573
  {
999
1574
  xmlNodePtr node, parent;
1000
1575
  Data_Get_Struct(self, xmlNode, node);
1001
1576
 
1002
1577
  parent = node->parent;
1003
- if(!parent) return Qnil;
1578
+ if (!parent) { return Qnil; }
1004
1579
 
1005
- return Nokogiri_wrap_xml_node(Qnil, parent) ;
1580
+ return noko_xml_node_wrap(Qnil, parent) ;
1006
1581
  }
1007
1582
 
1008
1583
  /*
@@ -1011,11 +1586,12 @@ static VALUE get_parent(VALUE self)
1011
1586
  *
1012
1587
  * Set the name for this Node
1013
1588
  */
1014
- static VALUE set_name(VALUE self, VALUE new_name)
1589
+ static VALUE
1590
+ set_name(VALUE self, VALUE new_name)
1015
1591
  {
1016
1592
  xmlNodePtr node;
1017
1593
  Data_Get_Struct(self, xmlNode, node);
1018
- xmlNodeSetName(node, (xmlChar*)StringValuePtr(new_name));
1594
+ xmlNodeSetName(node, (xmlChar *)StringValueCStr(new_name));
1019
1595
  return new_name;
1020
1596
  }
1021
1597
 
@@ -1025,12 +1601,14 @@ static VALUE set_name(VALUE self, VALUE new_name)
1025
1601
  *
1026
1602
  * Returns the name for this Node
1027
1603
  */
1028
- static VALUE get_name(VALUE self)
1604
+ static VALUE
1605
+ get_name(VALUE self)
1029
1606
  {
1030
1607
  xmlNodePtr node;
1031
1608
  Data_Get_Struct(self, xmlNode, node);
1032
- if(node->name)
1609
+ if (node->name) {
1033
1610
  return NOKOGIRI_STR_NEW2(node->name);
1611
+ }
1034
1612
  return Qnil;
1035
1613
  }
1036
1614
 
@@ -1040,28 +1618,39 @@ static VALUE get_name(VALUE self)
1040
1618
  *
1041
1619
  * Returns the path associated with this Node
1042
1620
  */
1043
- static VALUE path(VALUE self)
1621
+ static VALUE
1622
+ rb_xml_node_path(VALUE rb_node)
1044
1623
  {
1045
- xmlNodePtr node;
1046
- xmlChar *path ;
1624
+ xmlNodePtr c_node;
1625
+ xmlChar *c_path ;
1047
1626
  VALUE rval;
1048
1627
 
1049
- Data_Get_Struct(self, xmlNode, node);
1628
+ Data_Get_Struct(rb_node, xmlNode, c_node);
1629
+
1630
+ c_path = xmlGetNodePath(c_node);
1631
+ if (c_path == NULL) {
1632
+ // see https://github.com/sparklemotion/nokogiri/issues/2250
1633
+ // this behavior is clearly undesirable, but is what libxml <= 2.9.10 returned, and so we
1634
+ // do this for now to preserve the behavior across libxml2 versions.
1635
+ rval = NOKOGIRI_STR_NEW2("?");
1636
+ } else {
1637
+ rval = NOKOGIRI_STR_NEW2(c_path);
1638
+ xmlFree(c_path);
1639
+ }
1050
1640
 
1051
- path = xmlGetNodePath(node);
1052
- rval = NOKOGIRI_STR_NEW2(path);
1053
- xmlFree(path);
1054
1641
  return rval ;
1055
1642
  }
1056
1643
 
1057
1644
  /* :nodoc: */
1058
- static VALUE add_next_sibling(VALUE self, VALUE new_sibling)
1645
+ static VALUE
1646
+ add_next_sibling(VALUE self, VALUE new_sibling)
1059
1647
  {
1060
1648
  return reparent_node_with(self, new_sibling, xmlAddNextSibling) ;
1061
1649
  }
1062
1650
 
1063
1651
  /* :nodoc: */
1064
- static VALUE add_previous_sibling(VALUE self, VALUE new_sibling)
1652
+ static VALUE
1653
+ add_previous_sibling(VALUE self, VALUE new_sibling)
1065
1654
  {
1066
1655
  return reparent_node_with(self, new_sibling, xmlAddPrevSibling) ;
1067
1656
  }
@@ -1072,15 +1661,17 @@ static VALUE add_previous_sibling(VALUE self, VALUE new_sibling)
1072
1661
  *
1073
1662
  * Write this Node to +io+ with +encoding+ and +options+
1074
1663
  */
1075
- static VALUE native_write_to(
1076
- VALUE self,
1077
- VALUE io,
1078
- VALUE encoding,
1079
- VALUE indent_string,
1080
- VALUE options
1081
- ) {
1664
+ static VALUE
1665
+ native_write_to(
1666
+ VALUE self,
1667
+ VALUE io,
1668
+ VALUE encoding,
1669
+ VALUE indent_string,
1670
+ VALUE options
1671
+ )
1672
+ {
1082
1673
  xmlNodePtr node;
1083
- const char * before_indent;
1674
+ const char *before_indent;
1084
1675
  xmlSaveCtxtPtr savectx;
1085
1676
 
1086
1677
  Data_Get_Struct(self, xmlNode, node);
@@ -1089,15 +1680,15 @@ static VALUE native_write_to(
1089
1680
 
1090
1681
  before_indent = xmlTreeIndentString;
1091
1682
 
1092
- xmlTreeIndentString = StringValuePtr(indent_string);
1683
+ xmlTreeIndentString = StringValueCStr(indent_string);
1093
1684
 
1094
1685
  savectx = xmlSaveToIO(
1095
- (xmlOutputWriteCallback)io_write_callback,
1096
- (xmlOutputCloseCallback)io_close_callback,
1097
- (void *)io,
1098
- RTEST(encoding) ? StringValuePtr(encoding) : NULL,
1099
- (int)NUM2INT(options)
1100
- );
1686
+ (xmlOutputWriteCallback)noko_io_write,
1687
+ (xmlOutputCloseCallback)noko_io_close,
1688
+ (void *)io,
1689
+ RTEST(encoding) ? StringValueCStr(encoding) : NULL,
1690
+ (int)NUM2INT(options)
1691
+ );
1101
1692
 
1102
1693
  xmlSaveTree(savectx, node);
1103
1694
  xmlSaveClose(savectx);
@@ -1107,92 +1698,102 @@ static VALUE native_write_to(
1107
1698
  }
1108
1699
 
1109
1700
  /*
1110
- * call-seq:
1111
- * line
1701
+ * :call-seq:
1702
+ * line() → Integer
1703
+ *
1704
+ * [Returns] The line number of this Node.
1705
+ *
1706
+ * ---
1707
+ *
1708
+ * <b> ⚠ The CRuby and JRuby implementations differ in important ways! </b>
1112
1709
  *
1113
- * Returns the line for this Node
1710
+ * Semantic differences:
1711
+ * - The CRuby method reflects the node's line number <i>in the parsed string</i>
1712
+ * - The JRuby method reflects the node's line number <i>in the final DOM structure</i> after
1713
+ * corrections have been applied
1714
+ *
1715
+ * Performance differences:
1716
+ * - The CRuby method is {O(1)}[https://en.wikipedia.org/wiki/Time_complexity#Constant_time]
1717
+ * (constant time)
1718
+ * - The JRuby method is {O(n)}[https://en.wikipedia.org/wiki/Time_complexity#Linear_time] (linear
1719
+ * time, where n is the number of nodes before/above the element in the DOM)
1720
+ *
1721
+ * If you'd like to help improve the JRuby implementation, please review these issues and reach out
1722
+ * to the maintainers:
1723
+ * - https://github.com/sparklemotion/nokogiri/issues/1223
1724
+ * - https://github.com/sparklemotion/nokogiri/pull/2177
1725
+ * - https://github.com/sparklemotion/nokogiri/issues/2380
1114
1726
  */
1115
- static VALUE line(VALUE self)
1727
+ static VALUE
1728
+ rb_xml_node_line(VALUE rb_node)
1116
1729
  {
1117
- xmlNodePtr node;
1118
- Data_Get_Struct(self, xmlNode, node);
1730
+ xmlNodePtr c_node;
1731
+ Data_Get_Struct(rb_node, xmlNode, c_node);
1119
1732
 
1120
- return INT2NUM(xmlGetLineNo(node));
1733
+ return INT2NUM(xmlGetLineNo(c_node));
1121
1734
  }
1122
1735
 
1123
1736
  /*
1124
1737
  * call-seq:
1125
- * add_namespace_definition(prefix, href)
1126
- *
1127
- * Adds a namespace definition with +prefix+ using +href+ value. The result is
1128
- * as if parsed XML for this node had included an attribute
1129
- * 'xmlns:prefix=value'. A default namespace for this node ("xmlns=") can be
1130
- * added by passing 'nil' for prefix. Namespaces added this way will not
1131
- * show up in #attributes, but they will be included as an xmlns attribute
1132
- * when the node is serialized to XML.
1738
+ * line=(num)
1739
+ *
1740
+ * Sets the line for this Node. num must be less than 65535.
1133
1741
  */
1134
- static VALUE add_namespace_definition(VALUE self, VALUE prefix, VALUE href)
1742
+ static VALUE
1743
+ rb_xml_node_line_set(VALUE rb_node, VALUE rb_line_number)
1135
1744
  {
1136
- xmlNodePtr node, namespacee;
1137
- xmlNsPtr ns;
1745
+ xmlNodePtr c_node;
1746
+ int line_number = NUM2INT(rb_line_number);
1138
1747
 
1139
- Data_Get_Struct(self, xmlNode, node);
1140
- namespacee = node ;
1748
+ Data_Get_Struct(rb_node, xmlNode, c_node);
1141
1749
 
1142
- ns = xmlSearchNs(
1143
- node->doc,
1144
- node,
1145
- (const xmlChar *)(NIL_P(prefix) ? NULL : StringValuePtr(prefix))
1146
- );
1147
-
1148
- if(!ns) {
1149
- if (node->type != XML_ELEMENT_NODE) {
1150
- namespacee = node->parent;
1750
+ // libxml2 optionally uses xmlNode.psvi to store longer line numbers, but only for text nodes.
1751
+ // search for "psvi" in SAX2.c and tree.c to learn more.
1752
+ if (line_number < 65535) {
1753
+ c_node->line = (short) line_number;
1754
+ } else {
1755
+ c_node->line = 65535;
1756
+ if (c_node->type == XML_TEXT_NODE) {
1757
+ c_node->psvi = (void *)(ptrdiff_t) line_number;
1151
1758
  }
1152
- ns = xmlNewNs(
1153
- namespacee,
1154
- (const xmlChar *)StringValuePtr(href),
1155
- (const xmlChar *)(NIL_P(prefix) ? NULL : StringValuePtr(prefix))
1156
- );
1157
1759
  }
1158
1760
 
1159
- if (!ns) return Qnil ;
1160
-
1161
- if(NIL_P(prefix) || node != namespacee) xmlSetNs(node, ns);
1162
-
1163
- return Nokogiri_wrap_xml_namespace(node->doc, ns);
1761
+ return rb_line_number;
1164
1762
  }
1165
1763
 
1166
- /*
1167
- * call-seq:
1168
- * new(name, document)
1169
- *
1170
- * Create a new node with +name+ sharing GC lifecycle with +document+
1171
- */
1172
- static VALUE new(int argc, VALUE *argv, VALUE klass)
1764
+ /* :nodoc: documented in lib/nokogiri/xml/node.rb */
1765
+ static VALUE
1766
+ rb_xml_node_new(int argc, VALUE *argv, VALUE klass)
1173
1767
  {
1174
- xmlDocPtr doc;
1175
- xmlNodePtr node;
1176
- VALUE name;
1177
- VALUE document;
1768
+ xmlNodePtr c_document_node;
1769
+ xmlNodePtr c_node;
1770
+ VALUE rb_name;
1771
+ VALUE rb_document_node;
1178
1772
  VALUE rest;
1179
1773
  VALUE rb_node;
1180
1774
 
1181
- rb_scan_args(argc, argv, "2*", &name, &document, &rest);
1775
+ rb_scan_args(argc, argv, "2*", &rb_name, &rb_document_node, &rest);
1182
1776
 
1183
- Data_Get_Struct(document, xmlDoc, doc);
1777
+ if (!rb_obj_is_kind_of(rb_document_node, cNokogiriXmlNode)) {
1778
+ rb_raise(rb_eArgError, "document must be a Nokogiri::XML::Node");
1779
+ }
1780
+ if (!rb_obj_is_kind_of(rb_document_node, cNokogiriXmlDocument)) {
1781
+ // TODO: deprecate allowing Node
1782
+ rb_warn("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.");
1783
+ }
1784
+ Data_Get_Struct(rb_document_node, xmlNode, c_document_node);
1184
1785
 
1185
- node = xmlNewNode(NULL, (xmlChar *)StringValuePtr(name));
1186
- node->doc = doc->doc;
1187
- nokogiri_root_node(node);
1786
+ c_node = xmlNewNode(NULL, (xmlChar *)StringValueCStr(rb_name));
1787
+ c_node->doc = c_document_node->doc;
1788
+ noko_xml_document_pin_node(c_node);
1188
1789
 
1189
- rb_node = Nokogiri_wrap_xml_node(
1190
- klass == cNokogiriXmlNode ? (VALUE)NULL : klass,
1191
- node
1192
- );
1790
+ rb_node = noko_xml_node_wrap(
1791
+ klass == cNokogiriXmlNode ? (VALUE)NULL : klass,
1792
+ c_node
1793
+ );
1193
1794
  rb_obj_call_init(rb_node, argc, argv);
1194
1795
 
1195
- if(rb_block_given_p()) rb_yield(rb_node);
1796
+ if (rb_block_given_p()) { rb_yield(rb_node); }
1196
1797
 
1197
1798
  return rb_node;
1198
1799
  }
@@ -1203,7 +1804,8 @@ static VALUE new(int argc, VALUE *argv, VALUE klass)
1203
1804
  *
1204
1805
  * Returns the Node as html.
1205
1806
  */
1206
- static VALUE dump_html(VALUE self)
1807
+ static VALUE
1808
+ dump_html(VALUE self)
1207
1809
  {
1208
1810
  xmlBufferPtr buf ;
1209
1811
  xmlNodePtr node ;
@@ -1224,7 +1826,8 @@ static VALUE dump_html(VALUE self)
1224
1826
  *
1225
1827
  * Compare this Node to +other+ with respect to their Document
1226
1828
  */
1227
- static VALUE compare(VALUE self, VALUE _other)
1829
+ static VALUE
1830
+ compare(VALUE self, VALUE _other)
1228
1831
  {
1229
1832
  xmlNodePtr node, other;
1230
1833
  Data_Get_Struct(self, xmlNode, node);
@@ -1241,7 +1844,8 @@ static VALUE compare(VALUE self, VALUE _other)
1241
1844
  * Loads and substitutes all xinclude elements below the node. The
1242
1845
  * parser context will be initialized with +options+.
1243
1846
  */
1244
- static VALUE process_xincludes(VALUE self, VALUE options)
1847
+ static VALUE
1848
+ process_xincludes(VALUE self, VALUE options)
1245
1849
  {
1246
1850
  int rcode ;
1247
1851
  xmlNodePtr node;
@@ -1257,10 +1861,11 @@ static VALUE process_xincludes(VALUE self, VALUE options)
1257
1861
  xmlErrorPtr error;
1258
1862
 
1259
1863
  error = xmlGetLastError();
1260
- if(error)
1261
- rb_exc_raise(Nokogiri_wrap_xml_syntax_error((VALUE)NULL, error));
1262
- else
1864
+ if (error) {
1865
+ rb_exc_raise(Nokogiri_wrap_xml_syntax_error(error));
1866
+ } else {
1263
1867
  rb_raise(rb_eRuntimeError, "Could not perform xinclude substitution");
1868
+ }
1264
1869
  }
1265
1870
 
1266
1871
  return self;
@@ -1268,267 +1873,272 @@ static VALUE process_xincludes(VALUE self, VALUE options)
1268
1873
 
1269
1874
 
1270
1875
  /* TODO: DOCUMENT ME */
1271
- static VALUE in_context(VALUE self, VALUE _str, VALUE _options)
1876
+ static VALUE
1877
+ in_context(VALUE self, VALUE _str, VALUE _options)
1272
1878
  {
1273
- xmlNodePtr node, list = 0, tmp, child_iter, node_children, doc_children;
1274
- xmlNodeSetPtr set;
1275
- xmlParserErrors error;
1276
- VALUE doc, err;
1277
- int doc_is_empty;
1879
+ xmlNodePtr node, list = 0, tmp, child_iter, node_children, doc_children;
1880
+ xmlNodeSetPtr set;
1881
+ xmlParserErrors error;
1882
+ VALUE doc, err;
1883
+ int doc_is_empty;
1278
1884
 
1279
- Data_Get_Struct(self, xmlNode, node);
1885
+ Data_Get_Struct(self, xmlNode, node);
1280
1886
 
1281
- doc = DOC_RUBY_OBJECT(node->doc);
1282
- err = rb_iv_get(doc, "@errors");
1283
- doc_is_empty = (node->doc->children == NULL) ? 1 : 0;
1284
- node_children = node->children;
1285
- doc_children = node->doc->children;
1887
+ doc = DOC_RUBY_OBJECT(node->doc);
1888
+ err = rb_iv_get(doc, "@errors");
1889
+ doc_is_empty = (node->doc->children == NULL) ? 1 : 0;
1890
+ node_children = node->children;
1891
+ doc_children = node->doc->children;
1286
1892
 
1287
- xmlSetStructuredErrorFunc((void *)err, Nokogiri_error_array_pusher);
1893
+ xmlSetStructuredErrorFunc((void *)err, Nokogiri_error_array_pusher);
1288
1894
 
1289
- /* Twiddle global variable because of a bug in libxml2.
1290
- * http://git.gnome.org/browse/libxml2/commit/?id=e20fb5a72c83cbfc8e4a8aa3943c6be8febadab7
1291
- */
1895
+ /* Twiddle global variable because of a bug in libxml2.
1896
+ * http://git.gnome.org/browse/libxml2/commit/?id=e20fb5a72c83cbfc8e4a8aa3943c6be8febadab7
1897
+ */
1292
1898
  #ifndef HTML_PARSE_NOIMPLIED
1293
- htmlHandleOmittedElem(0);
1899
+ htmlHandleOmittedElem(0);
1294
1900
  #endif
1295
1901
 
1296
- /* This function adds a fake node to the child of +node+. If the parser
1297
- * does not exit cleanly with XML_ERR_OK, the list is freed. This can
1298
- * leave the child pointers in a bad state if they were originally empty.
1299
- *
1300
- * http://git.gnome.org/browse/libxml2/tree/parser.c#n13177
1301
- * */
1302
- error = xmlParseInNodeContext(node, StringValuePtr(_str),
1303
- (int)RSTRING_LEN(_str),
1304
- (int)NUM2INT(_options), &list);
1305
-
1306
- /* xmlParseInNodeContext should not mutate the original document or node,
1307
- * so reassigning these pointers should be OK. The reason we're reassigning
1308
- * is because if there were errors, it's possible for the child pointers
1309
- * to be manipulated. */
1310
- if (error != XML_ERR_OK) {
1311
- node->doc->children = doc_children;
1312
- node->children = node_children;
1313
- }
1902
+ /* This function adds a fake node to the child of +node+. If the parser
1903
+ * does not exit cleanly with XML_ERR_OK, the list is freed. This can
1904
+ * leave the child pointers in a bad state if they were originally empty.
1905
+ *
1906
+ * http://git.gnome.org/browse/libxml2/tree/parser.c#n13177
1907
+ * */
1908
+ error = xmlParseInNodeContext(node, StringValuePtr(_str),
1909
+ (int)RSTRING_LEN(_str),
1910
+ (int)NUM2INT(_options), &list);
1911
+
1912
+ /* xmlParseInNodeContext should not mutate the original document or node,
1913
+ * so reassigning these pointers should be OK. The reason we're reassigning
1914
+ * is because if there were errors, it's possible for the child pointers
1915
+ * to be manipulated. */
1916
+ if (error != XML_ERR_OK) {
1917
+ node->doc->children = doc_children;
1918
+ node->children = node_children;
1919
+ }
1314
1920
 
1315
- /* make sure parent/child pointers are coherent so an unlink will work
1316
- * properly (#331)
1317
- */
1318
- child_iter = node->doc->children ;
1319
- while (child_iter) {
1320
- if (child_iter->parent != (xmlNodePtr)node->doc)
1321
- child_iter->parent = (xmlNodePtr)node->doc;
1322
- child_iter = child_iter->next;
1323
- }
1921
+ /* make sure parent/child pointers are coherent so an unlink will work
1922
+ * properly (#331)
1923
+ */
1924
+ child_iter = node->doc->children ;
1925
+ while (child_iter) {
1926
+ child_iter->parent = (xmlNodePtr)node->doc;
1927
+ child_iter = child_iter->next;
1928
+ }
1324
1929
 
1325
1930
  #ifndef HTML_PARSE_NOIMPLIED
1326
- htmlHandleOmittedElem(1);
1931
+ htmlHandleOmittedElem(1);
1327
1932
  #endif
1328
1933
 
1329
- xmlSetStructuredErrorFunc(NULL, NULL);
1330
-
1331
- /* Workaround for a libxml2 bug where a parsing error may leave a broken
1332
- * node reference in node->doc->children.
1333
- * This workaround is limited to when a parse error occurs, the document
1334
- * went from having no children to having children, and the context node is
1335
- * part of a document fragment.
1336
- * https://bugzilla.gnome.org/show_bug.cgi?id=668155
1337
- */
1338
- if (error != XML_ERR_OK && doc_is_empty && node->doc->children != NULL) {
1339
- child_iter = node;
1340
- while (child_iter->parent)
1341
- child_iter = child_iter->parent;
1934
+ xmlSetStructuredErrorFunc(NULL, NULL);
1342
1935
 
1343
- if (child_iter->type == XML_DOCUMENT_FRAG_NODE)
1344
- node->doc->children = NULL;
1936
+ /* Workaround for a libxml2 bug where a parsing error may leave a broken
1937
+ * node reference in node->doc->children.
1938
+ * This workaround is limited to when a parse error occurs, the document
1939
+ * went from having no children to having children, and the context node is
1940
+ * part of a document fragment.
1941
+ * https://bugzilla.gnome.org/show_bug.cgi?id=668155
1942
+ */
1943
+ if (error != XML_ERR_OK && doc_is_empty && node->doc->children != NULL) {
1944
+ child_iter = node;
1945
+ while (child_iter->parent) {
1946
+ child_iter = child_iter->parent;
1345
1947
  }
1346
1948
 
1347
- /* FIXME: This probably needs to handle more constants... */
1348
- switch (error) {
1349
- case XML_ERR_INTERNAL_ERROR:
1350
- case XML_ERR_NO_MEMORY:
1351
- rb_raise(rb_eRuntimeError, "error parsing fragment (%d)", error);
1352
- break;
1353
- default:
1354
- break;
1949
+ if (child_iter->type == XML_DOCUMENT_FRAG_NODE) {
1950
+ node->doc->children = NULL;
1355
1951
  }
1952
+ }
1356
1953
 
1357
- set = xmlXPathNodeSetCreate(NULL);
1954
+ /* FIXME: This probably needs to handle more constants... */
1955
+ switch (error) {
1956
+ case XML_ERR_INTERNAL_ERROR:
1957
+ case XML_ERR_NO_MEMORY:
1958
+ rb_raise(rb_eRuntimeError, "error parsing fragment (%d)", error);
1959
+ break;
1960
+ default:
1961
+ break;
1962
+ }
1358
1963
 
1359
- while (list) {
1360
- tmp = list->next;
1361
- list->next = NULL;
1362
- xmlXPathNodeSetAddUnique(set, list);
1363
- nokogiri_root_node(list);
1364
- list = tmp;
1365
- }
1964
+ set = xmlXPathNodeSetCreate(NULL);
1366
1965
 
1367
- return Nokogiri_wrap_xml_node_set(set, doc);
1966
+ while (list) {
1967
+ tmp = list->next;
1968
+ list->next = NULL;
1969
+ xmlXPathNodeSetAddUnique(set, list);
1970
+ noko_xml_document_pin_node(list);
1971
+ list = tmp;
1972
+ }
1973
+
1974
+ return noko_xml_node_set_wrap(set, doc);
1368
1975
  }
1369
1976
 
1370
1977
 
1371
- VALUE Nokogiri_wrap_xml_node(VALUE klass, xmlNodePtr node)
1978
+ VALUE
1979
+ noko_xml_node_wrap(VALUE rb_class, xmlNodePtr c_node)
1372
1980
  {
1373
- VALUE document = Qnil ;
1374
- VALUE node_cache = Qnil ;
1375
- VALUE rb_node = Qnil ;
1981
+ VALUE rb_document, rb_node_cache, rb_node;
1376
1982
  nokogiriTuplePtr node_has_a_document;
1377
- xmlDocPtr doc;
1378
- void (*mark_method)(xmlNodePtr) = NULL ;
1983
+ xmlDocPtr c_doc;
1984
+ void (*f_mark)(xmlNodePtr) = NULL ;
1379
1985
 
1380
- assert(node);
1986
+ assert(c_node);
1381
1987
 
1382
- if(node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE)
1383
- return DOC_RUBY_OBJECT(node->doc);
1988
+ if (c_node->type == XML_DOCUMENT_NODE || c_node->type == XML_HTML_DOCUMENT_NODE) {
1989
+ return DOC_RUBY_OBJECT(c_node->doc);
1990
+ }
1384
1991
 
1385
1992
  /* It's OK if the node doesn't have a fully-realized document (as in XML::Reader). */
1386
1993
  /* see https://github.com/sparklemotion/nokogiri/issues/95 */
1387
1994
  /* and https://github.com/sparklemotion/nokogiri/issues/439 */
1388
- doc = node->doc;
1389
- if (doc->type == XML_DOCUMENT_FRAG_NODE) doc = doc->doc;
1390
- node_has_a_document = DOC_RUBY_OBJECT_TEST(doc);
1995
+ c_doc = c_node->doc;
1996
+ if (c_doc->type == XML_DOCUMENT_FRAG_NODE) { c_doc = c_doc->doc; }
1997
+ node_has_a_document = DOC_RUBY_OBJECT_TEST(c_doc);
1391
1998
 
1392
- if(node->_private && node_has_a_document)
1393
- return (VALUE)node->_private;
1999
+ if (c_node->_private && node_has_a_document) {
2000
+ return (VALUE)c_node->_private;
2001
+ }
1394
2002
 
1395
- if(!RTEST(klass)) {
1396
- switch(node->type)
1397
- {
1398
- case XML_ELEMENT_NODE:
1399
- klass = cNokogiriXmlElement;
1400
- break;
1401
- case XML_TEXT_NODE:
1402
- klass = cNokogiriXmlText;
1403
- break;
1404
- case XML_ATTRIBUTE_NODE:
1405
- klass = cNokogiriXmlAttr;
1406
- break;
1407
- case XML_ENTITY_REF_NODE:
1408
- klass = cNokogiriXmlEntityReference;
1409
- break;
1410
- case XML_COMMENT_NODE:
1411
- klass = cNokogiriXmlComment;
1412
- break;
1413
- case XML_DOCUMENT_FRAG_NODE:
1414
- klass = cNokogiriXmlDocumentFragment;
1415
- break;
1416
- case XML_PI_NODE:
1417
- klass = cNokogiriXmlProcessingInstruction;
1418
- break;
1419
- case XML_ENTITY_DECL:
1420
- klass = cNokogiriXmlEntityDecl;
1421
- break;
1422
- case XML_CDATA_SECTION_NODE:
1423
- klass = cNokogiriXmlCData;
1424
- break;
1425
- case XML_DTD_NODE:
1426
- klass = cNokogiriXmlDtd;
1427
- break;
1428
- case XML_ATTRIBUTE_DECL:
1429
- klass = cNokogiriXmlAttributeDecl;
1430
- break;
1431
- case XML_ELEMENT_DECL:
1432
- klass = cNokogiriXmlElementDecl;
1433
- break;
1434
- default:
1435
- klass = cNokogiriXmlNode;
2003
+ if (!RTEST(rb_class)) {
2004
+ switch (c_node->type) {
2005
+ case XML_ELEMENT_NODE:
2006
+ rb_class = cNokogiriXmlElement;
2007
+ break;
2008
+ case XML_TEXT_NODE:
2009
+ rb_class = cNokogiriXmlText;
2010
+ break;
2011
+ case XML_ATTRIBUTE_NODE:
2012
+ rb_class = cNokogiriXmlAttr;
2013
+ break;
2014
+ case XML_ENTITY_REF_NODE:
2015
+ rb_class = cNokogiriXmlEntityReference;
2016
+ break;
2017
+ case XML_COMMENT_NODE:
2018
+ rb_class = cNokogiriXmlComment;
2019
+ break;
2020
+ case XML_DOCUMENT_FRAG_NODE:
2021
+ rb_class = cNokogiriXmlDocumentFragment;
2022
+ break;
2023
+ case XML_PI_NODE:
2024
+ rb_class = cNokogiriXmlProcessingInstruction;
2025
+ break;
2026
+ case XML_ENTITY_DECL:
2027
+ rb_class = cNokogiriXmlEntityDecl;
2028
+ break;
2029
+ case XML_CDATA_SECTION_NODE:
2030
+ rb_class = cNokogiriXmlCData;
2031
+ break;
2032
+ case XML_DTD_NODE:
2033
+ rb_class = cNokogiriXmlDtd;
2034
+ break;
2035
+ case XML_ATTRIBUTE_DECL:
2036
+ rb_class = cNokogiriXmlAttributeDecl;
2037
+ break;
2038
+ case XML_ELEMENT_DECL:
2039
+ rb_class = cNokogiriXmlElementDecl;
2040
+ break;
2041
+ default:
2042
+ rb_class = cNokogiriXmlNode;
1436
2043
  }
1437
2044
  }
1438
2045
 
1439
- mark_method = node_has_a_document ? mark : NULL ;
2046
+ f_mark = node_has_a_document ? _xml_node_mark : NULL ;
1440
2047
 
1441
- rb_node = Data_Wrap_Struct(klass, mark_method, debug_node_dealloc, node) ;
1442
- node->_private = (void *)rb_node;
2048
+ rb_node = Data_Wrap_Struct(rb_class, f_mark, _xml_node_dealloc, c_node) ;
2049
+ c_node->_private = (void *)rb_node;
1443
2050
 
1444
2051
  if (node_has_a_document) {
1445
- document = DOC_RUBY_OBJECT(doc);
1446
- node_cache = DOC_NODE_CACHE(doc);
1447
- rb_ary_push(node_cache, rb_node);
1448
- rb_funcall(document, decorate, 1, rb_node);
2052
+ rb_document = DOC_RUBY_OBJECT(c_doc);
2053
+ rb_node_cache = DOC_NODE_CACHE(c_doc);
2054
+ rb_ary_push(rb_node_cache, rb_node);
2055
+ rb_funcall(rb_document, id_decorate, 1, rb_node);
1449
2056
  }
1450
2057
 
1451
2058
  return rb_node ;
1452
2059
  }
1453
2060
 
1454
2061
 
1455
- void Nokogiri_xml_node_properties(xmlNodePtr node, VALUE attr_list)
2062
+ /*
2063
+ * return Array<Nokogiri::XML::Attr> containing the node's attributes
2064
+ */
2065
+ VALUE
2066
+ noko_xml_node_attrs(xmlNodePtr c_node)
1456
2067
  {
1457
- xmlAttrPtr prop;
1458
- prop = node->properties ;
1459
- while (prop != NULL) {
1460
- rb_ary_push(attr_list, Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)prop));
1461
- prop = prop->next ;
2068
+ VALUE rb_properties = rb_ary_new();
2069
+ xmlAttrPtr c_property;
2070
+
2071
+ c_property = c_node->properties ;
2072
+ while (c_property != NULL) {
2073
+ rb_ary_push(rb_properties, noko_xml_node_wrap(Qnil, (xmlNodePtr)c_property));
2074
+ c_property = c_property->next ;
1462
2075
  }
1463
- }
1464
2076
 
1465
- VALUE cNokogiriXmlNode ;
1466
- VALUE cNokogiriXmlElement ;
2077
+ return rb_properties;
2078
+ }
1467
2079
 
1468
- void init_xml_node()
2080
+ void
2081
+ noko_init_xml_node()
1469
2082
  {
1470
- VALUE nokogiri = rb_define_module("Nokogiri");
1471
- VALUE xml = rb_define_module_under(nokogiri, "XML");
1472
- VALUE klass = rb_define_class_under(xml, "Node", rb_cObject);
1473
-
1474
- cNokogiriXmlNode = klass;
1475
-
1476
- cNokogiriXmlElement = rb_define_class_under(xml, "Element", klass);
1477
-
1478
- rb_define_singleton_method(klass, "new", new, -1);
1479
-
1480
- rb_define_method(klass, "add_namespace_definition", add_namespace_definition, 2);
1481
- rb_define_method(klass, "node_name", get_name, 0);
1482
- rb_define_method(klass, "document", document, 0);
1483
- rb_define_method(klass, "node_name=", set_name, 1);
1484
- rb_define_method(klass, "parent", get_parent, 0);
1485
- rb_define_method(klass, "child", child, 0);
1486
- rb_define_method(klass, "first_element_child", first_element_child, 0);
1487
- rb_define_method(klass, "last_element_child", last_element_child, 0);
1488
- rb_define_method(klass, "children", children, 0);
1489
- rb_define_method(klass, "element_children", element_children, 0);
1490
- rb_define_method(klass, "next_sibling", next_sibling, 0);
1491
- rb_define_method(klass, "previous_sibling", previous_sibling, 0);
1492
- rb_define_method(klass, "next_element", next_element, 0);
1493
- rb_define_method(klass, "previous_element", previous_element, 0);
1494
- rb_define_method(klass, "node_type", node_type, 0);
1495
- rb_define_method(klass, "content", get_content, 0);
1496
- rb_define_method(klass, "path", path, 0);
1497
- rb_define_method(klass, "key?", key_eh, 1);
1498
- rb_define_method(klass, "namespaced_key?", namespaced_key_eh, 2);
1499
- rb_define_method(klass, "blank?", blank_eh, 0);
1500
- rb_define_method(klass, "attribute_nodes", attribute_nodes, 0);
1501
- rb_define_method(klass, "attribute", attr, 1);
1502
- rb_define_method(klass, "attribute_with_ns", attribute_with_ns, 2);
1503
- rb_define_method(klass, "namespace", namespace, 0);
1504
- rb_define_method(klass, "namespace_definitions", namespace_definitions, 0);
1505
- rb_define_method(klass, "namespace_scopes", namespace_scopes, 0);
1506
- rb_define_method(klass, "encode_special_chars", encode_special_chars, 1);
1507
- rb_define_method(klass, "dup", duplicate_node, -1);
1508
- rb_define_method(klass, "unlink", unlink_node, 0);
1509
- rb_define_method(klass, "internal_subset", internal_subset, 0);
1510
- rb_define_method(klass, "external_subset", external_subset, 0);
1511
- rb_define_method(klass, "create_internal_subset", create_internal_subset, 3);
1512
- rb_define_method(klass, "create_external_subset", create_external_subset, 3);
1513
- rb_define_method(klass, "pointer_id", pointer_id, 0);
1514
- rb_define_method(klass, "line", line, 0);
1515
- rb_define_method(klass, "native_content=", native_content, 1);
1516
-
1517
- rb_define_private_method(klass, "process_xincludes", process_xincludes, 1);
1518
- rb_define_private_method(klass, "in_context", in_context, 2);
1519
- rb_define_private_method(klass, "add_child_node", add_child, 1);
1520
- rb_define_private_method(klass, "add_previous_sibling_node", add_previous_sibling, 1);
1521
- rb_define_private_method(klass, "add_next_sibling_node", add_next_sibling, 1);
1522
- rb_define_private_method(klass, "replace_node", replace, 1);
1523
- rb_define_private_method(klass, "dump_html", dump_html, 0);
1524
- rb_define_private_method(klass, "native_write_to", native_write_to, 4);
1525
- rb_define_private_method(klass, "get", get, 1);
1526
- rb_define_private_method(klass, "set", set, 2);
1527
- rb_define_private_method(klass, "set_namespace", set_namespace, 1);
1528
- rb_define_private_method(klass, "compare", compare, 1);
1529
-
1530
- decorate = rb_intern("decorate");
1531
- decorate_bang = rb_intern("decorate!");
2083
+ cNokogiriXmlNode = rb_define_class_under(mNokogiriXml, "Node", rb_cObject);
2084
+
2085
+ rb_undef_alloc_func(cNokogiriXmlNode);
2086
+
2087
+ rb_define_singleton_method(cNokogiriXmlNode, "new", rb_xml_node_new, -1);
2088
+
2089
+ rb_define_method(cNokogiriXmlNode, "add_namespace_definition", rb_xml_node_add_namespace_definition, 2);
2090
+ rb_define_method(cNokogiriXmlNode, "attribute", rb_xml_node_attribute, 1);
2091
+ rb_define_method(cNokogiriXmlNode, "attribute_nodes", rb_xml_node_attribute_nodes, 0);
2092
+ rb_define_method(cNokogiriXmlNode, "attribute_with_ns", rb_xml_node_attribute_with_ns, 2);
2093
+ rb_define_method(cNokogiriXmlNode, "blank?", rb_xml_node_blank_eh, 0);
2094
+ rb_define_method(cNokogiriXmlNode, "child", rb_xml_node_child, 0);
2095
+ rb_define_method(cNokogiriXmlNode, "children", rb_xml_node_children, 0);
2096
+ rb_define_method(cNokogiriXmlNode, "content", rb_xml_node_content, 0);
2097
+ rb_define_method(cNokogiriXmlNode, "create_external_subset", create_external_subset, 3);
2098
+ rb_define_method(cNokogiriXmlNode, "create_internal_subset", create_internal_subset, 3);
2099
+ rb_define_method(cNokogiriXmlNode, "document", rb_xml_node_document, 0);
2100
+ rb_define_method(cNokogiriXmlNode, "dup", duplicate_node, -1);
2101
+ rb_define_method(cNokogiriXmlNode, "element_children", rb_xml_node_element_children, 0);
2102
+ rb_define_method(cNokogiriXmlNode, "encode_special_chars", encode_special_chars, 1);
2103
+ rb_define_method(cNokogiriXmlNode, "external_subset", external_subset, 0);
2104
+ rb_define_method(cNokogiriXmlNode, "first_element_child", rb_xml_node_first_element_child, 0);
2105
+ rb_define_method(cNokogiriXmlNode, "internal_subset", internal_subset, 0);
2106
+ rb_define_method(cNokogiriXmlNode, "key?", key_eh, 1);
2107
+ rb_define_method(cNokogiriXmlNode, "lang", get_lang, 0);
2108
+ rb_define_method(cNokogiriXmlNode, "lang=", set_lang, 1);
2109
+ rb_define_method(cNokogiriXmlNode, "last_element_child", rb_xml_node_last_element_child, 0);
2110
+ rb_define_method(cNokogiriXmlNode, "line", rb_xml_node_line, 0);
2111
+ rb_define_method(cNokogiriXmlNode, "line=", rb_xml_node_line_set, 1);
2112
+ rb_define_method(cNokogiriXmlNode, "namespace", rb_xml_node_namespace, 0);
2113
+ rb_define_method(cNokogiriXmlNode, "namespace_definitions", namespace_definitions, 0);
2114
+ rb_define_method(cNokogiriXmlNode, "namespace_scopes", rb_xml_node_namespace_scopes, 0);
2115
+ rb_define_method(cNokogiriXmlNode, "namespaced_key?", namespaced_key_eh, 2);
2116
+ rb_define_method(cNokogiriXmlNode, "native_content=", set_native_content, 1);
2117
+ rb_define_method(cNokogiriXmlNode, "next_element", next_element, 0);
2118
+ rb_define_method(cNokogiriXmlNode, "next_sibling", next_sibling, 0);
2119
+ rb_define_method(cNokogiriXmlNode, "node_name", get_name, 0);
2120
+ rb_define_method(cNokogiriXmlNode, "node_name=", set_name, 1);
2121
+ rb_define_method(cNokogiriXmlNode, "node_type", node_type, 0);
2122
+ rb_define_method(cNokogiriXmlNode, "parent", get_parent, 0);
2123
+ rb_define_method(cNokogiriXmlNode, "path", rb_xml_node_path, 0);
2124
+ rb_define_method(cNokogiriXmlNode, "pointer_id", rb_xml_node_pointer_id, 0);
2125
+ rb_define_method(cNokogiriXmlNode, "previous_element", previous_element, 0);
2126
+ rb_define_method(cNokogiriXmlNode, "previous_sibling", previous_sibling, 0);
2127
+ rb_define_method(cNokogiriXmlNode, "unlink", unlink_node, 0);
2128
+
2129
+ rb_define_private_method(cNokogiriXmlNode, "add_child_node", add_child, 1);
2130
+ rb_define_private_method(cNokogiriXmlNode, "add_next_sibling_node", add_next_sibling, 1);
2131
+ rb_define_private_method(cNokogiriXmlNode, "add_previous_sibling_node", add_previous_sibling, 1);
2132
+ rb_define_private_method(cNokogiriXmlNode, "compare", compare, 1);
2133
+ rb_define_private_method(cNokogiriXmlNode, "dump_html", dump_html, 0);
2134
+ rb_define_private_method(cNokogiriXmlNode, "get", get, 1);
2135
+ rb_define_private_method(cNokogiriXmlNode, "in_context", in_context, 2);
2136
+ rb_define_private_method(cNokogiriXmlNode, "native_write_to", native_write_to, 4);
2137
+ rb_define_private_method(cNokogiriXmlNode, "process_xincludes", process_xincludes, 1);
2138
+ rb_define_private_method(cNokogiriXmlNode, "replace_node", replace, 1);
2139
+ rb_define_private_method(cNokogiriXmlNode, "set", set, 2);
2140
+ rb_define_private_method(cNokogiriXmlNode, "set_namespace", set_namespace, 1);
2141
+
2142
+ id_decorate = rb_intern("decorate");
2143
+ id_decorate_bang = rb_intern("decorate!");
1532
2144
  }
1533
-
1534
- /* vim: set noet sw=4 sws=4 */