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.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +12 -7
- data/CHANGELOG.md +93 -14
- data/README.md +173 -7
- data/Rakefile +103 -7
- data/ext/makiri/bridge/bridge.h +28 -0
- data/ext/makiri/bridge/ruby_string.c +217 -0
- data/ext/makiri/core/mkr_alloc.h +1 -1
- data/ext/makiri/core/mkr_buf.c +35 -1
- data/ext/makiri/core/mkr_buf.h +37 -3
- data/ext/makiri/core/mkr_core.h +1 -1
- data/ext/makiri/core/mkr_hash.h +1 -1
- data/ext/makiri/core/mkr_text.h +8 -8
- data/ext/makiri/extconf.rb +20 -2
- data/ext/makiri/glue/glue.h +47 -11
- data/ext/makiri/glue/ruby_doc.c +117 -43
- data/ext/makiri/glue/ruby_html_css.c +246 -0
- data/ext/makiri/glue/{ruby_mutate.c → ruby_html_mutate.c} +242 -51
- data/ext/makiri/glue/ruby_html_node.c +888 -0
- data/ext/makiri/glue/ruby_html_serialize.c +154 -0
- data/ext/makiri/glue/ruby_node.c +54 -748
- data/ext/makiri/glue/ruby_node_set.c +167 -32
- data/ext/makiri/glue/ruby_xml.c +420 -0
- data/ext/makiri/glue/ruby_xml_node.c +1386 -0
- data/ext/makiri/glue/ruby_xpath.c +59 -26
- data/ext/makiri/glue/ruby_xpath.h +19 -0
- data/ext/makiri/lexbor_compat/compat.h +42 -9
- data/ext/makiri/lexbor_compat/compat_internal.h +1 -1
- data/ext/makiri/lexbor_compat/dom_index.c +2 -2
- data/ext/makiri/lexbor_compat/post_parse.c +100 -10
- data/ext/makiri/lexbor_compat/source_loc.c +13 -9
- data/ext/makiri/lexbor_compat/text_index.c +14 -8
- data/ext/makiri/lexbor_compat/utf8_input.c +85 -26
- data/ext/makiri/makiri.c +139 -6
- data/ext/makiri/makiri.h +43 -2
- data/ext/makiri/xml/mkr_xml.h +126 -0
- data/ext/makiri/xml/mkr_xml_chars.c +225 -0
- data/ext/makiri/xml/mkr_xml_mutate.c +875 -0
- data/ext/makiri/xml/mkr_xml_mutate.h +139 -0
- data/ext/makiri/xml/mkr_xml_node.c +267 -0
- data/ext/makiri/xml/mkr_xml_node.h +119 -0
- data/ext/makiri/xml/mkr_xml_tree.c +1479 -0
- data/ext/makiri/xpath/mkr_xpath.c +59 -32
- data/ext/makiri/xpath/mkr_xpath.h +96 -4
- data/ext/makiri/xpath/mkr_xpath_engine_html.c +17 -0
- data/ext/makiri/xpath/mkr_xpath_engine_xml.c +12 -0
- data/ext/makiri/xpath/{mkr_xpath_eval.c → mkr_xpath_eval_body.h} +202 -175
- data/ext/makiri/xpath/{mkr_xpath_funcs.c → mkr_xpath_funcs_body.h} +110 -86
- data/ext/makiri/xpath/mkr_xpath_internal.h +91 -200
- data/ext/makiri/xpath/mkr_xpath_lex.c +2 -2
- data/ext/makiri/xpath/mkr_xpath_node_access_html.h +138 -0
- data/ext/makiri/xpath/mkr_xpath_node_access_xml.h +142 -0
- data/ext/makiri/xpath/mkr_xpath_parse.c +5 -5
- data/ext/makiri/xpath/mkr_xpath_prelude_html.h +30 -0
- data/ext/makiri/xpath/mkr_xpath_prelude_xml.h +28 -0
- data/ext/makiri/xpath/mkr_xpath_shared.c +593 -0
- data/ext/makiri/xpath/{mkr_xpath_value.c → mkr_xpath_value_body.h} +145 -656
- data/ext/makiri/xpath/mkr_xpath_xml_selftest.c +76 -0
- data/lib/makiri/{attribute.rb → attr.rb} +7 -3
- data/lib/makiri/cdata_section.rb +21 -0
- data/lib/makiri/comment.rb +12 -0
- data/lib/makiri/compat_aliases.rb +30 -0
- data/lib/makiri/document.rb +4 -76
- data/lib/makiri/document_fragment.rb +14 -9
- data/lib/makiri/element.rb +5 -3
- data/lib/makiri/html/document.rb +106 -0
- data/lib/makiri/html/node_methods.rb +19 -0
- data/lib/makiri/html.rb +12 -0
- data/lib/makiri/node.rb +58 -15
- data/lib/makiri/node_set.rb +8 -0
- data/lib/makiri/processing_instruction.rb +12 -0
- data/lib/makiri/text.rb +2 -0
- data/lib/makiri/version.rb +1 -1
- data/lib/makiri/xml/document.rb +24 -0
- data/lib/makiri/xml/node_methods.rb +37 -0
- data/lib/makiri/xml.rb +10 -0
- data/lib/makiri/xpath_context.rb +1 -1
- data/lib/makiri.rb +23 -5
- data/script/build_native_gem.rb +2 -2
- data/script/check_c_safety.rb +32 -0
- data/script/check_c_safety_allowlist.yml +83 -0
- metadata +35 -9
- data/ext/makiri/glue/ruby_css.c +0 -185
- data/ext/makiri/glue/ruby_serialize.c +0 -92
- 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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
597
|
+
return mkr_wrap_html_node(lxb_dom_interface_node(c), self);
|
|
409
598
|
}
|
|
410
599
|
|
|
411
|
-
/* Document#create_processing_instruction(target, data)
|
|
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 =
|
|
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
|
|
616
|
+
return mkr_wrap_html_node(lxb_dom_interface_node(pi), self);
|
|
428
617
|
}
|
|
429
618
|
|
|
430
|
-
/* Document#create_document_fragment
|
|
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 =
|
|
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
|
|
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(
|
|
448
|
-
rb_define_method(
|
|
449
|
-
rb_define_method(
|
|
450
|
-
rb_define_method(
|
|
451
|
-
rb_define_method(
|
|
452
|
-
rb_define_method(
|
|
453
|
-
rb_define_method(
|
|
454
|
-
rb_define_method(
|
|
455
|
-
rb_define_method(
|
|
456
|
-
|
|
457
|
-
rb_define_method(
|
|
458
|
-
rb_define_method(
|
|
459
|
-
|
|
460
|
-
rb_define_method(
|
|
461
|
-
rb_define_method(
|
|
462
|
-
rb_define_method(
|
|
463
|
-
rb_define_method(
|
|
464
|
-
rb_define_method(
|
|
465
|
-
|
|
466
|
-
rb_define_method(
|
|
467
|
-
|
|
468
|
-
rb_define_method(
|
|
469
|
-
rb_define_method(
|
|
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(
|
|
662
|
+
rb_define_method(mkr_cHtmlDocument, "create_document_fragment",
|
|
472
663
|
mkr_doc_create_document_fragment, 0);
|
|
473
664
|
}
|