makiri 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +12 -7
  3. data/CHANGELOG.md +93 -14
  4. data/README.md +173 -7
  5. data/Rakefile +103 -7
  6. data/ext/makiri/bridge/bridge.h +28 -0
  7. data/ext/makiri/bridge/ruby_string.c +217 -0
  8. data/ext/makiri/core/mkr_alloc.h +1 -1
  9. data/ext/makiri/core/mkr_buf.c +35 -1
  10. data/ext/makiri/core/mkr_buf.h +37 -3
  11. data/ext/makiri/core/mkr_core.h +1 -1
  12. data/ext/makiri/core/mkr_hash.h +1 -1
  13. data/ext/makiri/core/mkr_text.h +8 -8
  14. data/ext/makiri/extconf.rb +20 -2
  15. data/ext/makiri/glue/glue.h +47 -11
  16. data/ext/makiri/glue/ruby_doc.c +117 -43
  17. data/ext/makiri/glue/ruby_html_css.c +246 -0
  18. data/ext/makiri/glue/{ruby_mutate.c → ruby_html_mutate.c} +242 -51
  19. data/ext/makiri/glue/ruby_html_node.c +888 -0
  20. data/ext/makiri/glue/ruby_html_serialize.c +154 -0
  21. data/ext/makiri/glue/ruby_node.c +54 -748
  22. data/ext/makiri/glue/ruby_node_set.c +167 -32
  23. data/ext/makiri/glue/ruby_xml.c +420 -0
  24. data/ext/makiri/glue/ruby_xml_node.c +1386 -0
  25. data/ext/makiri/glue/ruby_xpath.c +59 -26
  26. data/ext/makiri/glue/ruby_xpath.h +19 -0
  27. data/ext/makiri/lexbor_compat/compat.h +42 -9
  28. data/ext/makiri/lexbor_compat/compat_internal.h +1 -1
  29. data/ext/makiri/lexbor_compat/dom_index.c +2 -2
  30. data/ext/makiri/lexbor_compat/post_parse.c +100 -10
  31. data/ext/makiri/lexbor_compat/source_loc.c +13 -9
  32. data/ext/makiri/lexbor_compat/text_index.c +14 -8
  33. data/ext/makiri/lexbor_compat/utf8_input.c +85 -26
  34. data/ext/makiri/makiri.c +139 -6
  35. data/ext/makiri/makiri.h +43 -2
  36. data/ext/makiri/xml/mkr_xml.h +126 -0
  37. data/ext/makiri/xml/mkr_xml_chars.c +225 -0
  38. data/ext/makiri/xml/mkr_xml_mutate.c +875 -0
  39. data/ext/makiri/xml/mkr_xml_mutate.h +139 -0
  40. data/ext/makiri/xml/mkr_xml_node.c +267 -0
  41. data/ext/makiri/xml/mkr_xml_node.h +119 -0
  42. data/ext/makiri/xml/mkr_xml_tree.c +1479 -0
  43. data/ext/makiri/xpath/mkr_xpath.c +59 -32
  44. data/ext/makiri/xpath/mkr_xpath.h +96 -4
  45. data/ext/makiri/xpath/mkr_xpath_engine_html.c +17 -0
  46. data/ext/makiri/xpath/mkr_xpath_engine_xml.c +12 -0
  47. data/ext/makiri/xpath/{mkr_xpath_eval.c → mkr_xpath_eval_body.h} +202 -175
  48. data/ext/makiri/xpath/{mkr_xpath_funcs.c → mkr_xpath_funcs_body.h} +110 -86
  49. data/ext/makiri/xpath/mkr_xpath_internal.h +91 -200
  50. data/ext/makiri/xpath/mkr_xpath_lex.c +2 -2
  51. data/ext/makiri/xpath/mkr_xpath_node_access_html.h +138 -0
  52. data/ext/makiri/xpath/mkr_xpath_node_access_xml.h +142 -0
  53. data/ext/makiri/xpath/mkr_xpath_parse.c +5 -5
  54. data/ext/makiri/xpath/mkr_xpath_prelude_html.h +30 -0
  55. data/ext/makiri/xpath/mkr_xpath_prelude_xml.h +28 -0
  56. data/ext/makiri/xpath/mkr_xpath_shared.c +593 -0
  57. data/ext/makiri/xpath/{mkr_xpath_value.c → mkr_xpath_value_body.h} +145 -656
  58. data/ext/makiri/xpath/mkr_xpath_xml_selftest.c +76 -0
  59. data/lib/makiri/{attribute.rb → attr.rb} +7 -3
  60. data/lib/makiri/cdata_section.rb +21 -0
  61. data/lib/makiri/comment.rb +12 -0
  62. data/lib/makiri/compat_aliases.rb +30 -0
  63. data/lib/makiri/document.rb +4 -76
  64. data/lib/makiri/document_fragment.rb +14 -9
  65. data/lib/makiri/element.rb +5 -3
  66. data/lib/makiri/html/document.rb +106 -0
  67. data/lib/makiri/html/node_methods.rb +19 -0
  68. data/lib/makiri/html.rb +12 -0
  69. data/lib/makiri/node.rb +58 -15
  70. data/lib/makiri/node_set.rb +8 -0
  71. data/lib/makiri/processing_instruction.rb +12 -0
  72. data/lib/makiri/text.rb +2 -0
  73. data/lib/makiri/version.rb +1 -1
  74. data/lib/makiri/xml/document.rb +24 -0
  75. data/lib/makiri/xml/node_methods.rb +37 -0
  76. data/lib/makiri/xml.rb +10 -0
  77. data/lib/makiri/xpath_context.rb +1 -1
  78. data/lib/makiri.rb +23 -5
  79. data/script/build_native_gem.rb +2 -2
  80. data/script/check_c_safety.rb +32 -0
  81. data/script/check_c_safety_allowlist.yml +83 -0
  82. metadata +35 -9
  83. data/ext/makiri/glue/ruby_css.c +0 -185
  84. data/ext/makiri/glue/ruby_serialize.c +0 -92
  85. data/lib/makiri/cdata.rb +0 -6
@@ -1,6 +1,18 @@
1
1
  #include "glue.h"
2
2
 
3
3
  #include <lexbor/html/parser.h>
4
+ #include <lexbor/ns/ns.h>
5
+
6
+ /* Exported by lexbor but omitted from its public headers. lxb_ns_append interns
7
+ * a namespace URI in the document's ns table; lxb_dom_attr_set_name_ns names an
8
+ * attribute from (namespace, qualified name), splitting prefix/local and
9
+ * interning the namespace. */
10
+ extern const lxb_ns_data_t *
11
+ lxb_ns_append(lexbor_hash_t *hash, const lxb_char_t *link, size_t length);
12
+ extern lxb_status_t
13
+ lxb_dom_attr_set_name_ns(lxb_dom_attr_t *attr, const lxb_char_t *link,
14
+ size_t link_length, const lxb_char_t *name,
15
+ size_t name_length, bool to_lowercase);
4
16
 
5
17
  /*
6
18
  * DOM mutation (v0.2). Thin wrappers over Lexbor's insert/remove/create
@@ -27,13 +39,14 @@ mkr_invalidate_index(VALUE node)
27
39
  mkr_parsed_text_index_invalidate(p);
28
40
  }
29
41
 
42
+ /* An HTML node argument for a tree mutation. Routes through mkr_html_node_unwrap so
43
+ * an XML node is rejected before its mkr_xml_node_t* reaches Lexbor (the
44
+ * same-document/cycle checks below read lxb fields, and the insert hands the
45
+ * pointer to Lexbor). */
30
46
  static lxb_dom_node_t *
31
47
  mkr_arg_node(VALUE v)
32
48
  {
33
- if (!rb_obj_is_kind_of(v, mkr_cNode)) {
34
- rb_raise(rb_eTypeError, "expected a Makiri::Node");
35
- }
36
- return mkr_node_unwrap(v);
49
+ return mkr_html_node_unwrap(v);
37
50
  }
38
51
 
39
52
  /* Validate that `incoming` may be placed relative to `ref` and detach it from
@@ -70,12 +83,22 @@ mkr_is_fragment(const lxb_dom_node_t *n)
70
83
  /* tree mutation */
71
84
  /* ------------------------------------------------------------------ */
72
85
 
86
+ /* Every tree / attribute mutation unwraps `self` through here first: a node the
87
+ * caller has frozen (Ruby's Object#freeze) is immutable, so raise FrozenError
88
+ * rather than silently editing it. Read accessors use mkr_html_node_unwrap (XML rejected at the type boundary). */
89
+ static lxb_dom_node_t *
90
+ mkr_node_unwrap_mutable(VALUE self)
91
+ {
92
+ rb_check_frozen(self);
93
+ return mkr_html_node_unwrap(self);
94
+ }
95
+
73
96
  /* node.add_child(child) -> child. Appends child as the last child. A document
74
97
  * fragment contributes its children rather than itself. */
75
98
  static VALUE
76
99
  mkr_node_add_child(VALUE self, VALUE rb_child)
77
100
  {
78
- lxb_dom_node_t *parent = mkr_node_unwrap(self);
101
+ lxb_dom_node_t *parent = mkr_node_unwrap_mutable(self);
79
102
  lxb_dom_node_t *child = mkr_arg_node(rb_child);
80
103
  mkr_prepare_insert(parent, child);
81
104
  if (mkr_is_fragment(child)) {
@@ -102,7 +125,7 @@ mkr_node_append(VALUE self, VALUE rb_child)
102
125
  static VALUE
103
126
  mkr_node_add_previous_sibling(VALUE self, VALUE rb_node)
104
127
  {
105
- lxb_dom_node_t *ref = mkr_node_unwrap(self);
128
+ lxb_dom_node_t *ref = mkr_node_unwrap_mutable(self);
106
129
  lxb_dom_node_t *node = mkr_arg_node(rb_node);
107
130
  if (ref->parent == NULL) {
108
131
  rb_raise(mkr_eError, "cannot add a sibling to a node with no parent");
@@ -124,7 +147,7 @@ mkr_node_add_previous_sibling(VALUE self, VALUE rb_node)
124
147
  static VALUE
125
148
  mkr_node_add_next_sibling(VALUE self, VALUE rb_node)
126
149
  {
127
- lxb_dom_node_t *ref = mkr_node_unwrap(self);
150
+ lxb_dom_node_t *ref = mkr_node_unwrap_mutable(self);
128
151
  lxb_dom_node_t *node = mkr_arg_node(rb_node);
129
152
  if (ref->parent == NULL) {
130
153
  rb_raise(mkr_eError, "cannot add a sibling to a node with no parent");
@@ -148,7 +171,7 @@ mkr_node_add_next_sibling(VALUE self, VALUE rb_node)
148
171
  static VALUE
149
172
  mkr_node_remove(VALUE self)
150
173
  {
151
- lxb_dom_node_t *node = mkr_node_unwrap(self);
174
+ lxb_dom_node_t *node = mkr_node_unwrap_mutable(self);
152
175
  if (node->type == LXB_DOM_NODE_TYPE_ATTRIBUTE) {
153
176
  rb_raise(mkr_eError, "use delete(name) to remove an attribute");
154
177
  }
@@ -163,7 +186,7 @@ mkr_node_remove(VALUE self)
163
186
  static VALUE
164
187
  mkr_node_replace(VALUE self, VALUE rb_other)
165
188
  {
166
- lxb_dom_node_t *ref = mkr_node_unwrap(self);
189
+ lxb_dom_node_t *ref = mkr_node_unwrap_mutable(self);
167
190
  lxb_dom_node_t *other = mkr_arg_node(rb_other);
168
191
  if (ref->parent == NULL) {
169
192
  rb_raise(mkr_eError, "cannot replace a node with no parent");
@@ -191,7 +214,7 @@ mkr_node_replace(VALUE self, VALUE rb_other)
191
214
  static VALUE
192
215
  mkr_node_aset(VALUE self, VALUE rb_name, VALUE rb_value)
193
216
  {
194
- lxb_dom_node_t *node = mkr_node_unwrap(self);
217
+ lxb_dom_node_t *node = mkr_node_unwrap_mutable(self);
195
218
  if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
196
219
  rb_raise(mkr_eError, "cannot set an attribute on a non-element node");
197
220
  }
@@ -210,13 +233,179 @@ mkr_node_aset(VALUE self, VALUE rb_name, VALUE rb_value)
210
233
  return rb_value;
211
234
  }
212
235
 
236
+ /* An attribute's OWN namespace id: the one recorded by set_attribute_ns (which
237
+ * differs from the owner element's), else the null namespace - a normally-set or
238
+ * parsed attribute inherits the element's ns, which for matching purposes is the
239
+ * null namespace (an unprefixed attribute is namespaceless). */
240
+ static lxb_ns_id_t
241
+ mkr_attr_own_ns(const lxb_dom_attr_t *at)
242
+ {
243
+ if (at->owner != NULL && at->node.ns != at->owner->node.ns) {
244
+ return at->node.ns;
245
+ }
246
+ return LXB_NS__UNDEF;
247
+ }
248
+
249
+ /* Find the attribute on `el` matching (ns_id, local_name) case-sensitively - the
250
+ * DOM keys attributes on (namespace, local name), so two with the same qualified
251
+ * name but different namespaces coexist (unlike Lexbor's by-qualified-name,
252
+ * case-insensitive-for-HTML lookup). */
253
+ static lxb_dom_attr_t *
254
+ mkr_attr_find_ns(lxb_dom_element_t *el, lxb_ns_id_t ns_id,
255
+ const lxb_char_t *local, size_t local_len)
256
+ {
257
+ for (lxb_dom_attr_t *at = el->first_attr; at != NULL; at = at->next) {
258
+ if (mkr_attr_own_ns(at) != ns_id) {
259
+ continue;
260
+ }
261
+ /* Compare the case-preserved local name (the suffix of the qualified
262
+ * name): Lexbor lower-cases the stored local_name even when the
263
+ * qualified name keeps its case, but setAttributeNS is case-sensitive. */
264
+ size_t qlen = 0, llen = 0;
265
+ const lxb_char_t *q = lxb_dom_attr_qualified_name(at, &qlen);
266
+ (void) lxb_dom_attr_local_name(at, &llen);
267
+ if (q != NULL && qlen >= llen
268
+ && llen == local_len
269
+ && memcmp(q + (qlen - llen), local, local_len) == 0) {
270
+ return at;
271
+ }
272
+ }
273
+ return NULL;
274
+ }
275
+
276
+ /* element.set_attribute_ns(namespace_or_nil, qualified_name, value) -> value.
277
+ *
278
+ * Stores the attribute under its qualified name (case-preserved - setAttributeNS
279
+ * is case-sensitive, unlike the HTML setAttribute family) and records its OWN
280
+ * namespace on the attr node, so namespaceURI / getAttributeNS resolve it. The
281
+ * namespace URI is interned in the document's ns table; nil/"" stores the null
282
+ * namespace (LXB_NS__UNDEF). */
283
+ static VALUE
284
+ mkr_node_set_attribute_ns(VALUE self, VALUE rb_ns, VALUE rb_qname, VALUE rb_value)
285
+ {
286
+ lxb_dom_node_t *node = mkr_node_unwrap_mutable(self);
287
+ if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
288
+ rb_raise(mkr_eError, "cannot set an attribute on a non-element node");
289
+ }
290
+ lxb_dom_element_t *el = lxb_dom_interface_element(node);
291
+
292
+ mkr_ruby_borrowed_text_t qv = mkr_ruby_verified_text(rb_qname, "attribute qualified name");
293
+ mkr_ruby_borrowed_text_t vv = mkr_ruby_verified_text(rb_value, "attribute value");
294
+
295
+ mkr_ruby_borrowed_text_t nv = {0};
296
+ bool have_ns = false;
297
+ if (!NIL_P(rb_ns)) {
298
+ nv = mkr_ruby_verified_text(rb_ns, "namespace");
299
+ have_ns = nv.len > 0;
300
+ }
301
+
302
+ /* Intern the wanted namespace (null/"" => LXB_NS__UNDEF) so the existing
303
+ * attribute is matched on (namespace, local name) - the DOM key - rather than
304
+ * the qualified name. */
305
+ lxb_ns_id_t want_ns = LXB_NS__UNDEF;
306
+ if (have_ns && node->owner_document != NULL && node->owner_document->ns != NULL) {
307
+ const lxb_ns_data_t *d = lxb_ns_append(node->owner_document->ns,
308
+ (const lxb_char_t *)nv.ptr, nv.len);
309
+ if (d != NULL) {
310
+ want_ns = d->ns_id;
311
+ }
312
+ }
313
+
314
+ const lxb_char_t *qn = (const lxb_char_t *)qv.ptr;
315
+ const lxb_char_t *colon = memchr(qn, ':', qv.len);
316
+ const lxb_char_t *local = colon ? colon + 1 : qn;
317
+ size_t local_len = colon ? (size_t)(qv.len - (colon - qn) - 1) : qv.len;
318
+
319
+ /* A match keeps its qualified name (so re-setting with a different prefix
320
+ * leaves the prefix unchanged); only the value updates. A miss appends a new
321
+ * attribute, even when its qualified name collides with an existing one in a
322
+ * different namespace - the namespace-aware setter splits prefix/local and
323
+ * records the namespace; a null namespace just sets the bare name. */
324
+ lxb_dom_attr_t *attr = mkr_attr_find_ns(el, want_ns, local, local_len);
325
+ if (attr != NULL) {
326
+ if (lxb_dom_attr_set_value(attr, (const lxb_char_t *)vv.ptr, vv.len) != LXB_STATUS_OK) {
327
+ rb_raise(mkr_eError, "failed to set attribute value");
328
+ }
329
+ }
330
+ else {
331
+ attr = lxb_dom_attr_interface_create(node->owner_document);
332
+ if (attr == NULL) {
333
+ rb_raise(mkr_eError, "failed to create attribute");
334
+ }
335
+ /* A fresh attr is calloc'd, so node.ns is already LXB_NS__UNDEF for the
336
+ * null-namespace case; only the namespaced setter changes it. */
337
+ lxb_status_t st;
338
+ if (have_ns) {
339
+ st = lxb_dom_attr_set_name_ns(attr, (const lxb_char_t *)nv.ptr, nv.len,
340
+ (const lxb_char_t *)qv.ptr, qv.len, false);
341
+ }
342
+ else {
343
+ st = lxb_dom_attr_set_name(attr, (const lxb_char_t *)qv.ptr, qv.len, false);
344
+ }
345
+ if (st != LXB_STATUS_OK
346
+ || lxb_dom_attr_set_value(attr, (const lxb_char_t *)vv.ptr, vv.len) != LXB_STATUS_OK) {
347
+ /* Leave the un-appended attr for the document arena to free wholesale
348
+ * (the module's "never destroy a detached node" convention). */
349
+ rb_raise(mkr_eError, "failed to set namespaced attribute");
350
+ }
351
+ lxb_dom_element_attr_append(el, attr);
352
+ }
353
+
354
+ RB_GC_GUARD(qv.value);
355
+ RB_GC_GUARD(vv.value);
356
+ RB_GC_GUARD(nv.value);
357
+ mkr_invalidate_index(self);
358
+ return rb_value;
359
+ }
360
+
361
+ /* element.remove_attribute_ns(namespace_or_nil, local_name) -> nil. Removes the
362
+ * attribute matching (namespace, local name) - the DOM key - so a namespaced
363
+ * attribute is removed without disturbing a same-qualified-name one in another
364
+ * namespace (which removal by qualified name, case-insensitive for HTML, would). */
365
+ static VALUE
366
+ mkr_node_remove_attribute_ns(VALUE self, VALUE rb_ns, VALUE rb_local)
367
+ {
368
+ lxb_dom_node_t *node = mkr_node_unwrap_mutable(self);
369
+ if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
370
+ return Qnil;
371
+ }
372
+ lxb_dom_element_t *el = lxb_dom_interface_element(node);
373
+
374
+ mkr_ruby_borrowed_text_t lv = mkr_ruby_verified_text(rb_local, "attribute local name");
375
+
376
+ lxb_ns_id_t want_ns = LXB_NS__UNDEF;
377
+ VALUE ns_guard = Qnil;
378
+ if (!NIL_P(rb_ns)) {
379
+ mkr_ruby_borrowed_text_t nv = mkr_ruby_verified_text(rb_ns, "namespace");
380
+ ns_guard = nv.value;
381
+ if (nv.len > 0 && node->owner_document != NULL && node->owner_document->ns != NULL) {
382
+ const lxb_ns_data_t *d = lxb_ns_append(node->owner_document->ns,
383
+ (const lxb_char_t *)nv.ptr, nv.len);
384
+ if (d != NULL) {
385
+ want_ns = d->ns_id;
386
+ }
387
+ }
388
+ }
389
+
390
+ lxb_dom_attr_t *attr = mkr_attr_find_ns(el, want_ns,
391
+ (const lxb_char_t *)lv.ptr, lv.len);
392
+ if (attr != NULL) {
393
+ lxb_dom_element_attr_remove(el, attr);
394
+ mkr_invalidate_index(self);
395
+ }
396
+
397
+ RB_GC_GUARD(lv.value);
398
+ RB_GC_GUARD(ns_guard);
399
+ return Qnil;
400
+ }
401
+
213
402
  /* element.name = new_name -> new_name. Renames the element in place (identity
214
403
  * preserved): create a throwaway element with the new name so the document
215
404
  * interns it, copy its name fields onto this node, then discard it. */
216
405
  static VALUE
217
406
  mkr_node_set_name(VALUE self, VALUE rb_name)
218
407
  {
219
- lxb_dom_node_t *node = mkr_node_unwrap(self);
408
+ lxb_dom_node_t *node = mkr_node_unwrap_mutable(self);
220
409
  if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
221
410
  rb_raise(mkr_eError, "name= is only supported on elements");
222
411
  }
@@ -245,7 +434,7 @@ mkr_node_set_name(VALUE self, VALUE rb_name)
245
434
  static VALUE
246
435
  mkr_node_set_content(VALUE self, VALUE rb_text)
247
436
  {
248
- lxb_dom_node_t *node = mkr_node_unwrap(self);
437
+ lxb_dom_node_t *node = mkr_node_unwrap_mutable(self);
249
438
  mkr_ruby_borrowed_text_t tv = mkr_ruby_verified_text(rb_text, "node content");
250
439
  lxb_status_t st = lxb_dom_node_text_content_set(
251
440
  node, (const lxb_char_t *)tv.ptr, tv.len);
@@ -261,7 +450,7 @@ mkr_node_set_content(VALUE self, VALUE rb_text)
261
450
  static VALUE
262
451
  mkr_node_delete(VALUE self, VALUE rb_name)
263
452
  {
264
- lxb_dom_node_t *node = mkr_node_unwrap(self);
453
+ lxb_dom_node_t *node = mkr_node_unwrap_mutable(self);
265
454
  if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
266
455
  return self;
267
456
  }
@@ -327,7 +516,7 @@ mkr_parse_fragment_into(lxb_dom_node_t *context_el, VALUE rb_html,
327
516
  static VALUE
328
517
  mkr_node_set_inner_html(VALUE self, VALUE rb_html)
329
518
  {
330
- lxb_dom_node_t *node = mkr_node_unwrap(self);
519
+ lxb_dom_node_t *node = mkr_node_unwrap_mutable(self);
331
520
  if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) {
332
521
  rb_raise(mkr_eError, "inner_html= requires an element");
333
522
  }
@@ -348,7 +537,7 @@ mkr_node_set_inner_html(VALUE self, VALUE rb_html)
348
537
  static VALUE
349
538
  mkr_node_set_outer_html(VALUE self, VALUE rb_html)
350
539
  {
351
- lxb_dom_node_t *node = mkr_node_unwrap(self);
540
+ lxb_dom_node_t *node = mkr_node_unwrap_mutable(self);
352
541
  lxb_dom_node_t *parent = node->parent;
353
542
  if (parent == NULL || parent->type != LXB_DOM_NODE_TYPE_ELEMENT) {
354
543
  rb_raise(mkr_eError, "outer_html= requires a node with a parent element");
@@ -369,7 +558,7 @@ mkr_node_set_outer_html(VALUE self, VALUE rb_html)
369
558
  static VALUE
370
559
  mkr_doc_create_element(VALUE self, VALUE rb_name)
371
560
  {
372
- lxb_dom_document_t *doc = mkr_doc_unwrap(self);
561
+ lxb_dom_document_t *doc = mkr_html_doc_unwrap(self);
373
562
  mkr_ruby_borrowed_text_t nv = mkr_ruby_verified_text(rb_name, "element name");
374
563
  lxb_dom_element_t *el = lxb_dom_document_create_element(
375
564
  doc, (const lxb_char_t *)nv.ptr, nv.len, NULL);
@@ -377,13 +566,13 @@ mkr_doc_create_element(VALUE self, VALUE rb_name)
377
566
  if (el == NULL) {
378
567
  rb_raise(mkr_eError, "failed to create element");
379
568
  }
380
- return mkr_wrap_node(lxb_dom_interface_node(el), self);
569
+ return mkr_wrap_html_node(lxb_dom_interface_node(el), self);
381
570
  }
382
571
 
383
572
  static VALUE
384
573
  mkr_doc_create_text_node(VALUE self, VALUE rb_text)
385
574
  {
386
- lxb_dom_document_t *doc = mkr_doc_unwrap(self);
575
+ lxb_dom_document_t *doc = mkr_html_doc_unwrap(self);
387
576
  mkr_ruby_borrowed_text_t tv = mkr_ruby_verified_text(rb_text, "text content");
388
577
  lxb_dom_text_t *t = lxb_dom_document_create_text_node(
389
578
  doc, (const lxb_char_t *)tv.ptr, tv.len);
@@ -391,13 +580,13 @@ mkr_doc_create_text_node(VALUE self, VALUE rb_text)
391
580
  if (t == NULL) {
392
581
  rb_raise(mkr_eError, "failed to create text node");
393
582
  }
394
- return mkr_wrap_node(lxb_dom_interface_node(t), self);
583
+ return mkr_wrap_html_node(lxb_dom_interface_node(t), self);
395
584
  }
396
585
 
397
586
  static VALUE
398
587
  mkr_doc_create_comment(VALUE self, VALUE rb_text)
399
588
  {
400
- lxb_dom_document_t *doc = mkr_doc_unwrap(self);
589
+ lxb_dom_document_t *doc = mkr_html_doc_unwrap(self);
401
590
  mkr_ruby_borrowed_text_t tv = mkr_ruby_verified_text(rb_text, "comment content");
402
591
  lxb_dom_comment_t *c = lxb_dom_document_create_comment(
403
592
  doc, (const lxb_char_t *)tv.ptr, tv.len);
@@ -405,16 +594,16 @@ mkr_doc_create_comment(VALUE self, VALUE rb_text)
405
594
  if (c == NULL) {
406
595
  rb_raise(mkr_eError, "failed to create comment");
407
596
  }
408
- return mkr_wrap_node(lxb_dom_interface_node(c), self);
597
+ return mkr_wrap_html_node(lxb_dom_interface_node(c), self);
409
598
  }
410
599
 
411
- /* Document#create_processing_instruction(target, data) DOM
600
+ /* Document#create_processing_instruction(target, data) - DOM
412
601
  * createProcessingInstruction: a detached ProcessingInstruction owned by this
413
602
  * document. Lexbor validates the target, so an invalid one fails closed. */
414
603
  static VALUE
415
604
  mkr_doc_create_processing_instruction(VALUE self, VALUE rb_target, VALUE rb_data)
416
605
  {
417
- lxb_dom_document_t *doc = mkr_doc_unwrap(self);
606
+ lxb_dom_document_t *doc = mkr_html_doc_unwrap(self);
418
607
  mkr_ruby_borrowed_text_t tv = mkr_ruby_verified_text(rb_target, "processing instruction target");
419
608
  mkr_ruby_borrowed_text_t dv = mkr_ruby_verified_text(rb_data, "processing instruction data");
420
609
  lxb_dom_processing_instruction_t *pi = lxb_dom_document_create_processing_instruction(
@@ -424,50 +613,52 @@ mkr_doc_create_processing_instruction(VALUE self, VALUE rb_target, VALUE rb_data
424
613
  if (pi == NULL) {
425
614
  rb_raise(mkr_eError, "failed to create processing instruction");
426
615
  }
427
- return mkr_wrap_node(lxb_dom_interface_node(pi), self);
616
+ return mkr_wrap_html_node(lxb_dom_interface_node(pi), self);
428
617
  }
429
618
 
430
- /* Document#create_document_fragment DOM createDocumentFragment: an empty
619
+ /* Document#create_document_fragment - DOM createDocumentFragment: an empty
431
620
  * DocumentFragment owned by this document (unlike #fragment / DocumentFragment.parse,
432
621
  * which parse HTML; this makes an empty one to build up programmatically). */
433
622
  static VALUE
434
623
  mkr_doc_create_document_fragment(VALUE self)
435
624
  {
436
- lxb_dom_document_t *doc = mkr_doc_unwrap(self);
625
+ lxb_dom_document_t *doc = mkr_html_doc_unwrap(self);
437
626
  lxb_dom_document_fragment_t *f = lxb_dom_document_create_document_fragment(doc);
438
627
  if (f == NULL) {
439
628
  rb_raise(mkr_eError, "failed to create document fragment");
440
629
  }
441
- return mkr_wrap_node(lxb_dom_interface_node(f), self);
630
+ return mkr_wrap_html_node(lxb_dom_interface_node(f), self);
442
631
  }
443
632
 
444
633
  void
445
634
  mkr_init_mutate(void)
446
635
  {
447
- rb_define_method(mkr_cNode, "add_child", mkr_node_add_child, 1);
448
- rb_define_method(mkr_cNode, "<<", mkr_node_append, 1);
449
- rb_define_method(mkr_cNode, "add_previous_sibling", mkr_node_add_previous_sibling, 1);
450
- rb_define_method(mkr_cNode, "before", mkr_node_add_previous_sibling, 1);
451
- rb_define_method(mkr_cNode, "add_next_sibling", mkr_node_add_next_sibling, 1);
452
- rb_define_method(mkr_cNode, "after", mkr_node_add_next_sibling, 1);
453
- rb_define_method(mkr_cNode, "remove", mkr_node_remove, 0);
454
- rb_define_method(mkr_cNode, "unlink", mkr_node_remove, 0);
455
- rb_define_method(mkr_cNode, "replace", mkr_node_replace, 1);
456
-
457
- rb_define_method(mkr_cNode, "inner_html=", mkr_node_set_inner_html, 1);
458
- rb_define_method(mkr_cNode, "outer_html=", mkr_node_set_outer_html, 1);
459
-
460
- rb_define_method(mkr_cNode, "[]=", mkr_node_aset, 2);
461
- rb_define_method(mkr_cNode, "delete", mkr_node_delete, 1);
462
- rb_define_method(mkr_cNode, "remove_attribute", mkr_node_delete, 1);
463
- rb_define_method(mkr_cNode, "content=", mkr_node_set_content, 1);
464
- rb_define_method(mkr_cNode, "name=", mkr_node_set_name, 1);
465
-
466
- rb_define_method(mkr_cDocument, "create_element", mkr_doc_create_element, 1);
467
- rb_define_method(mkr_cDocument, "create_text_node", mkr_doc_create_text_node, 1);
468
- rb_define_method(mkr_cDocument, "create_comment", mkr_doc_create_comment, 1);
469
- rb_define_method(mkr_cDocument, "create_processing_instruction",
636
+ rb_define_method(mkr_mHtmlNodeMethods, "add_child", mkr_node_add_child, 1);
637
+ rb_define_method(mkr_mHtmlNodeMethods, "<<", mkr_node_append, 1);
638
+ rb_define_method(mkr_mHtmlNodeMethods, "add_previous_sibling", mkr_node_add_previous_sibling, 1);
639
+ rb_define_method(mkr_mHtmlNodeMethods, "before", mkr_node_add_previous_sibling, 1);
640
+ rb_define_method(mkr_mHtmlNodeMethods, "add_next_sibling", mkr_node_add_next_sibling, 1);
641
+ rb_define_method(mkr_mHtmlNodeMethods, "after", mkr_node_add_next_sibling, 1);
642
+ rb_define_method(mkr_mHtmlNodeMethods, "remove", mkr_node_remove, 0);
643
+ rb_define_method(mkr_mHtmlNodeMethods, "unlink", mkr_node_remove, 0);
644
+ rb_define_method(mkr_mHtmlNodeMethods, "replace", mkr_node_replace, 1);
645
+
646
+ rb_define_method(mkr_mHtmlNodeMethods, "inner_html=", mkr_node_set_inner_html, 1);
647
+ rb_define_method(mkr_mHtmlNodeMethods, "outer_html=", mkr_node_set_outer_html, 1);
648
+
649
+ rb_define_method(mkr_mHtmlNodeMethods, "[]=", mkr_node_aset, 2);
650
+ rb_define_method(mkr_mHtmlNodeMethods, "set_attribute_ns", mkr_node_set_attribute_ns, 3);
651
+ rb_define_method(mkr_mHtmlNodeMethods, "remove_attribute_ns", mkr_node_remove_attribute_ns, 2);
652
+ rb_define_method(mkr_mHtmlNodeMethods, "delete", mkr_node_delete, 1);
653
+ rb_define_method(mkr_mHtmlNodeMethods, "remove_attribute", mkr_node_delete, 1);
654
+ rb_define_method(mkr_mHtmlNodeMethods, "content=", mkr_node_set_content, 1);
655
+ rb_define_method(mkr_mHtmlNodeMethods, "name=", mkr_node_set_name, 1);
656
+
657
+ rb_define_method(mkr_cHtmlDocument, "create_element", mkr_doc_create_element, 1);
658
+ rb_define_method(mkr_cHtmlDocument, "create_text_node", mkr_doc_create_text_node, 1);
659
+ rb_define_method(mkr_cHtmlDocument, "create_comment", mkr_doc_create_comment, 1);
660
+ rb_define_method(mkr_cHtmlDocument, "create_processing_instruction",
470
661
  mkr_doc_create_processing_instruction, 2);
471
- rb_define_method(mkr_cDocument, "create_document_fragment",
662
+ rb_define_method(mkr_cHtmlDocument, "create_document_fragment",
472
663
  mkr_doc_create_document_fragment, 0);
473
664
  }