nokogiri 1.19.4-java → 1.19.5-java
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/LICENSE-DEPENDENCIES.md +29 -0
- data/ext/java/nokogiri/XmlNode.java +69 -38
- data/ext/java/nokogiri/XmlReader.java +4 -1
- data/ext/java/nokogiri/XmlSchema.java +5 -74
- data/ext/java/nokogiri/XmlXpathContext.java +5 -15
- data/ext/java/nokogiri/internals/NokogiriEntityResolver.java +5 -7
- data/ext/java/nokogiri/internals/NokogiriHelpers.java +68 -0
- data/ext/java/nokogiri/internals/NokogiriXPathContext.java +37 -0
- data/ext/nokogiri/extconf.rb +1 -0
- data/ext/nokogiri/gumbo.c +83 -6
- data/ext/nokogiri/html4_document.c +10 -3
- data/ext/nokogiri/libxml2_polyfill.c +12 -0
- data/ext/nokogiri/nokogiri.c +84 -2
- data/ext/nokogiri/xml_document.c +42 -16
- data/ext/nokogiri/xml_node.c +73 -38
- data/ext/nokogiri/xml_node_set.c +9 -9
- data/ext/nokogiri/xml_reader.c +198 -107
- data/ext/nokogiri/xml_sax_parser.c +3 -3
- data/ext/nokogiri/xml_sax_parser_context.c +38 -7
- data/ext/nokogiri/xml_sax_push_parser.c +12 -0
- data/ext/nokogiri/xml_xpath_context.c +124 -26
- data/ext/nokogiri/xslt_stylesheet.c +123 -27
- data/lib/nokogiri/css/parser.rb +35 -18
- data/lib/nokogiri/css/parser.y +35 -17
- data/lib/nokogiri/css/xpath_visitor.rb +71 -28
- data/lib/nokogiri/html5/document.rb +9 -0
- data/lib/nokogiri/html5/document_fragment.rb +16 -1
- data/lib/nokogiri/html5/node.rb +1 -1
- data/lib/nokogiri/html5.rb +16 -0
- data/lib/nokogiri/nokogiri.jar +0 -0
- data/lib/nokogiri/version/constant.rb +1 -1
- data/lib/nokogiri/xml/attr.rb +2 -0
- data/lib/nokogiri/xml/attribute_decl.rb +2 -1
- data/lib/nokogiri/xml/builder.rb +24 -32
- data/lib/nokogiri/xml/document.rb +1 -0
- data/lib/nokogiri/xml/dtd.rb +2 -1
- data/lib/nokogiri/xml/element_decl.rb +2 -1
- data/lib/nokogiri/xml/entity_decl.rb +2 -1
- data/lib/nokogiri/xml/node.rb +8 -6
- metadata +3 -2
data/ext/nokogiri/gumbo.c
CHANGED
|
@@ -113,6 +113,71 @@ set_line(xmlNodePtr node, size_t line)
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// This function is essentially xmlNewNsProp, but we skip the full list traversal to append by
|
|
117
|
+
// providing the last property in the linked list as a parameter.
|
|
118
|
+
static xmlAttrPtr
|
|
119
|
+
append_property(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name, const xmlChar *value, xmlAttrPtr last_prop)
|
|
120
|
+
{
|
|
121
|
+
xmlAttrPtr cur = (xmlAttrPtr) xmlMalloc(sizeof(xmlAttr));
|
|
122
|
+
if (cur == NULL) {
|
|
123
|
+
return NULL;
|
|
124
|
+
}
|
|
125
|
+
memset(cur, 0, sizeof(xmlAttr));
|
|
126
|
+
cur->type = XML_ATTRIBUTE_NODE;
|
|
127
|
+
cur->parent = node;
|
|
128
|
+
xmlDocPtr doc = node->doc;
|
|
129
|
+
cur->doc = doc;
|
|
130
|
+
cur->ns = ns;
|
|
131
|
+
|
|
132
|
+
if ((doc != NULL) && (doc->dict != NULL)) {
|
|
133
|
+
cur->name = (xmlChar *) xmlDictLookup(doc->dict, name, -1);
|
|
134
|
+
} else {
|
|
135
|
+
cur->name = xmlStrdup(name);
|
|
136
|
+
}
|
|
137
|
+
if (cur->name == NULL) {
|
|
138
|
+
goto error;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (value != NULL) {
|
|
142
|
+
cur->children = xmlNewDocText(doc, value);
|
|
143
|
+
if (cur->children == NULL) {
|
|
144
|
+
goto error;
|
|
145
|
+
}
|
|
146
|
+
cur->last = NULL;
|
|
147
|
+
xmlNodePtr tmp = cur->children;
|
|
148
|
+
while (tmp != NULL) {
|
|
149
|
+
tmp->parent = (xmlNodePtr) cur;
|
|
150
|
+
if (tmp->next == NULL) {
|
|
151
|
+
cur->last = tmp;
|
|
152
|
+
}
|
|
153
|
+
tmp = tmp->next;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (doc != NULL) {
|
|
157
|
+
int res = xmlIsID(doc, node, cur);
|
|
158
|
+
if (res < 0) {
|
|
159
|
+
goto error;
|
|
160
|
+
}
|
|
161
|
+
if ((res == 1) && (xmlAddIDSafe(cur, value) < 0)) {
|
|
162
|
+
goto error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (node->properties == NULL) {
|
|
168
|
+
node->properties = cur;
|
|
169
|
+
} else {
|
|
170
|
+
last_prop->next = cur;
|
|
171
|
+
cur->prev = last_prop;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return cur;
|
|
175
|
+
|
|
176
|
+
error:
|
|
177
|
+
xmlFreeProp(cur);
|
|
178
|
+
return (NULL);
|
|
179
|
+
}
|
|
180
|
+
|
|
116
181
|
// Construct an XML tree rooted at xml_output_node from the Gumbo tree rooted
|
|
117
182
|
// at gumbo_node.
|
|
118
183
|
static void
|
|
@@ -200,6 +265,7 @@ build_tree(
|
|
|
200
265
|
xmlAddChild(xml_node, xml_child);
|
|
201
266
|
|
|
202
267
|
// Add the attributes.
|
|
268
|
+
xmlAttrPtr last_prop = NULL;
|
|
203
269
|
const GumboVector *attrs = &gumbo_child->v.element.attributes;
|
|
204
270
|
for (size_t i = 0; i < attrs->length; i++) {
|
|
205
271
|
const GumboAttribute *attr = attrs->data[i];
|
|
@@ -220,7 +286,9 @@ build_tree(
|
|
|
220
286
|
default:
|
|
221
287
|
ns = NULL;
|
|
222
288
|
}
|
|
223
|
-
|
|
289
|
+
|
|
290
|
+
// We micromanage the attribute list for performance reasons.
|
|
291
|
+
last_prop = append_property(xml_child, ns, (const xmlChar *)attr->name, (const xmlChar *)attr->value, last_prop);
|
|
224
292
|
}
|
|
225
293
|
|
|
226
294
|
// Add children for this element.
|
|
@@ -373,12 +441,20 @@ parse_continue(VALUE parse_args)
|
|
|
373
441
|
} else {
|
|
374
442
|
doc = new_html_doc(NULL, NULL, NULL);
|
|
375
443
|
}
|
|
376
|
-
|
|
444
|
+
|
|
445
|
+
// We're about to build the libxml2 tree structure from the Gumbo tree
|
|
446
|
+
// structure. If an exception is raised during tree building, control passes
|
|
447
|
+
// to `parse_cleanup()` which needs to free the newly allocated `xmlDoc`,
|
|
448
|
+
// `doc`. If the tree is successfully constructed, then
|
|
449
|
+
// `noko_xml_document_wrap()` will create a new Ruby object which will own
|
|
450
|
+
// `doc`. In that case, `parse_cleanup()` should not free `doc`.
|
|
451
|
+
args->doc = doc;
|
|
377
452
|
build_tree(doc, (xmlNodePtr)doc, output->document);
|
|
453
|
+
args->doc = NULL;
|
|
378
454
|
VALUE rdoc = noko_xml_document_wrap(args->klass, doc);
|
|
455
|
+
|
|
379
456
|
rb_iv_set(rdoc, "@url", args->url_or_frag);
|
|
380
457
|
rb_iv_set(rdoc, "@quirks_mode", INT2NUM(output->document->v.document.doc_type_quirks_mode));
|
|
381
|
-
args->doc = NULL; // The Ruby runtime now owns doc so don't delete it.
|
|
382
458
|
add_errors(output, rdoc, args->input, args->url_or_frag);
|
|
383
459
|
return rdoc;
|
|
384
460
|
}
|
|
@@ -512,6 +588,7 @@ error:
|
|
|
512
588
|
continue;
|
|
513
589
|
}
|
|
514
590
|
VALUE element_name = rb_funcall(node, name, 0);
|
|
591
|
+
Check_Type(element_name, T_STRING);
|
|
515
592
|
if (RSTRING_LEN(element_name) == 4
|
|
516
593
|
&& !st_strcasecmp(RSTRING_PTR(element_name), "form")
|
|
517
594
|
&& lookup_namespace(node, false) == GUMBO_NAMESPACE_HTML) {
|
|
@@ -568,7 +645,7 @@ error:
|
|
|
568
645
|
.output = output,
|
|
569
646
|
.input = tags,
|
|
570
647
|
.url_or_frag = doc_fragment,
|
|
571
|
-
.doc =
|
|
648
|
+
.doc = NULL,
|
|
572
649
|
};
|
|
573
650
|
rb_ensure(fragment_continue, (VALUE)(&args), parse_cleanup, (VALUE)(&args));
|
|
574
651
|
return Qnil;
|
|
@@ -580,9 +657,9 @@ fragment_continue(VALUE parse_args)
|
|
|
580
657
|
ParseArgs *args = (ParseArgs *)parse_args;
|
|
581
658
|
GumboOutput *output = args->output;
|
|
582
659
|
VALUE doc_fragment = args->url_or_frag;
|
|
583
|
-
|
|
660
|
+
VALUE doc = rb_funcall(doc_fragment, rb_intern_const("document"), 0);
|
|
661
|
+
xmlDocPtr xml_doc = (xmlDocPtr)extract_xml_node(doc);
|
|
584
662
|
|
|
585
|
-
args->doc = NULL; // The Ruby runtime owns doc so make sure we don't delete it.
|
|
586
663
|
xmlNodePtr xml_frag = extract_xml_node(doc_fragment);
|
|
587
664
|
build_tree(xml_doc, xml_frag, output->root);
|
|
588
665
|
rb_iv_set(doc_fragment, "@quirks_mode", INT2NUM(output->document->v.document.doc_type_quirks_mode));
|
|
@@ -18,8 +18,8 @@ rb_html_document_s_new(int argc, VALUE *argv, VALUE klass)
|
|
|
18
18
|
htmlDocPtr doc;
|
|
19
19
|
|
|
20
20
|
rb_scan_args(argc, argv, "0*", &rest);
|
|
21
|
-
uri = rb_ary_entry(rest, (
|
|
22
|
-
external_id = rb_ary_entry(rest, (
|
|
21
|
+
uri = rb_ary_entry(rest, (rb_len_t)0);
|
|
22
|
+
external_id = rb_ary_entry(rest, (rb_len_t)1);
|
|
23
23
|
|
|
24
24
|
doc = htmlNewDoc(
|
|
25
25
|
RTEST(uri) ? (const xmlChar *)StringValueCStr(uri) : NULL,
|
|
@@ -48,10 +48,17 @@ rb_html_document_s_read_io(VALUE klass, VALUE rb_io, VALUE rb_url, VALUE rb_enco
|
|
|
48
48
|
|
|
49
49
|
xmlSetStructuredErrorFunc((void *)rb_error_list, noko__error_array_pusher);
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
noko_io_read_reset_exception();
|
|
52
|
+
c_doc = htmlReadIO(noko_io_read_protected, noko_io_close, (void *)rb_io, c_url, c_encoding, options);
|
|
53
|
+
VALUE rb_io_read_exception = noko_io_read_take_exception();
|
|
52
54
|
|
|
53
55
|
xmlSetStructuredErrorFunc(NULL, NULL);
|
|
54
56
|
|
|
57
|
+
if (!NIL_P(rb_io_read_exception)) {
|
|
58
|
+
xmlFreeDoc(c_doc);
|
|
59
|
+
noko_io_read_raise_exception(rb_io_read_exception);
|
|
60
|
+
}
|
|
61
|
+
|
|
55
62
|
/*
|
|
56
63
|
* If EncodingFound has occurred in EncodingReader, make sure to do
|
|
57
64
|
* a cleanup and propagate the error.
|
|
@@ -112,3 +112,15 @@ xmlSwitchEncodingName(xmlParserCtxtPtr ctxt, const char *encoding)
|
|
|
112
112
|
return (xmlSwitchToEncoding(ctxt, handler));
|
|
113
113
|
}
|
|
114
114
|
#endif
|
|
115
|
+
|
|
116
|
+
#ifndef HAVE_XMLADDIDSAFE
|
|
117
|
+
int
|
|
118
|
+
xmlAddIDSafe(xmlAttrPtr attr, const xmlChar *value)
|
|
119
|
+
{
|
|
120
|
+
xmlIDPtr id = xmlAddID(NULL, attr->doc, value, attr);
|
|
121
|
+
if (id) {
|
|
122
|
+
return 1;
|
|
123
|
+
}
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
#endif
|
data/ext/nokogiri/nokogiri.c
CHANGED
|
@@ -50,7 +50,7 @@ void noko_init_html4_sax_parser(void);
|
|
|
50
50
|
void noko_init_gumbo(void);
|
|
51
51
|
void noko_init_test_global_handlers(void);
|
|
52
52
|
|
|
53
|
-
static ID id_read, id_write, id_external_encoding;
|
|
53
|
+
static ID id_read, id_write, id_external_encoding, id_io_read_exception;
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
static VALUE
|
|
@@ -92,6 +92,87 @@ noko_io_read(void *io, char *c_buffer, int c_buffer_len)
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
typedef struct {
|
|
96
|
+
void *io;
|
|
97
|
+
char *c_buffer;
|
|
98
|
+
int c_buffer_len;
|
|
99
|
+
int n_bytes_read;
|
|
100
|
+
} noko_io_read_args_t;
|
|
101
|
+
|
|
102
|
+
static VALUE
|
|
103
|
+
noko_io_read_call(VALUE data)
|
|
104
|
+
{
|
|
105
|
+
noko_io_read_args_t *args = (noko_io_read_args_t *)data;
|
|
106
|
+
args->n_bytes_read = noko_io_read(args->io, args->c_buffer, args->c_buffer_len);
|
|
107
|
+
return Qnil;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
/*
|
|
112
|
+
* Like noko_io_read, a StandardError raised by IO#read is swallowed, and libxml2 sees a read
|
|
113
|
+
* error. Any other exception (e.g., Interrupt or Timeout::ExitException) must not unwind through
|
|
114
|
+
* libxml2's stack frames, because that skips libxml2's cleanup and leaks memory, and skips the
|
|
115
|
+
* reset of the error handlers. So it is saved in a fiber-local variable, and $! is cleared until
|
|
116
|
+
* it is re-raised.
|
|
117
|
+
*
|
|
118
|
+
* A caller of libxml2 that passes this callback must call noko_io_read_reset_exception before
|
|
119
|
+
* calling libxml2, and noko_io_read_take_exception after libxml2 returns, and must re-raise a
|
|
120
|
+
* non-nil result with noko_io_read_raise_exception.
|
|
121
|
+
*/
|
|
122
|
+
int
|
|
123
|
+
noko_io_read_protected(void *io, char *c_buffer, int c_buffer_len)
|
|
124
|
+
{
|
|
125
|
+
noko_io_read_args_t args = { io, c_buffer, c_buffer_len, -1 };
|
|
126
|
+
int state = 0;
|
|
127
|
+
|
|
128
|
+
rb_protect(noko_io_read_call, (VALUE)&args, &state);
|
|
129
|
+
|
|
130
|
+
if (state) {
|
|
131
|
+
VALUE rb_errinfo_value = rb_errinfo();
|
|
132
|
+
if (RB_TYPE_P(rb_errinfo_value, T_OBJECT) && rb_obj_is_kind_of(rb_errinfo_value, rb_eException)) {
|
|
133
|
+
rb_set_errinfo(Qnil);
|
|
134
|
+
} else {
|
|
135
|
+
rb_errinfo_value = Qnil;
|
|
136
|
+
}
|
|
137
|
+
rb_thread_local_aset(rb_thread_current(), id_io_read_exception,
|
|
138
|
+
rb_assoc_new(INT2FIX(state), rb_errinfo_value));
|
|
139
|
+
return -1;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return args.n_bytes_read;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
void
|
|
147
|
+
noko_io_read_reset_exception(void)
|
|
148
|
+
{
|
|
149
|
+
rb_thread_local_aset(rb_thread_current(), id_io_read_exception, Qnil);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
VALUE
|
|
154
|
+
noko_io_read_take_exception(void)
|
|
155
|
+
{
|
|
156
|
+
VALUE rb_thread = rb_thread_current();
|
|
157
|
+
VALUE rb_exception = rb_thread_local_aref(rb_thread, id_io_read_exception);
|
|
158
|
+
|
|
159
|
+
rb_thread_local_aset(rb_thread, id_io_read_exception, Qnil);
|
|
160
|
+
return rb_exception;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
void
|
|
165
|
+
noko_io_read_raise_exception(VALUE rb_exception)
|
|
166
|
+
{
|
|
167
|
+
VALUE rb_errinfo_value = rb_ary_entry(rb_exception, 1);
|
|
168
|
+
|
|
169
|
+
if (!NIL_P(rb_errinfo_value)) {
|
|
170
|
+
rb_set_errinfo(rb_errinfo_value);
|
|
171
|
+
}
|
|
172
|
+
rb_jump_tag(FIX2INT(rb_ary_entry(rb_exception, 0)));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
95
176
|
static VALUE
|
|
96
177
|
noko_io_write_check(VALUE rb_args)
|
|
97
178
|
{
|
|
@@ -122,7 +203,7 @@ noko_io_write(void *io, char *c_buffer, int c_buffer_len)
|
|
|
122
203
|
io_encoding = RB_NIL_P(rb_enc) ? rb_ascii8bit_encoding() : rb_to_encoding(rb_enc);
|
|
123
204
|
|
|
124
205
|
rb_args[0] = rb_io;
|
|
125
|
-
rb_args[1] = rb_enc_str_new(c_buffer, (
|
|
206
|
+
rb_args[1] = rb_enc_str_new(c_buffer, (rb_len_t)c_buffer_len, io_encoding);
|
|
126
207
|
|
|
127
208
|
rb_n_bytes_written = rb_rescue(noko_io_write_check, (VALUE)rb_args, noko_io_write_failed, 0);
|
|
128
209
|
if (rb_n_bytes_written == Qundef) { return -1; }
|
|
@@ -292,6 +373,7 @@ Init_nokogiri(void)
|
|
|
292
373
|
noko_init_test_global_handlers();
|
|
293
374
|
|
|
294
375
|
id_read = rb_intern("read");
|
|
376
|
+
id_io_read_exception = rb_intern("__nokogiri_io_read_exception");
|
|
295
377
|
id_write = rb_intern("write");
|
|
296
378
|
id_external_encoding = rb_intern("external_encoding");
|
|
297
379
|
}
|
data/ext/nokogiri/xml_document.c
CHANGED
|
@@ -384,17 +384,24 @@ noko_xml_document_s_read_io(VALUE rb_class,
|
|
|
384
384
|
|
|
385
385
|
const char *c_url = NIL_P(rb_url) ? NULL : StringValueCStr(rb_url);
|
|
386
386
|
const char *c_enc = NIL_P(rb_encoding) ? NULL : StringValueCStr(rb_encoding);
|
|
387
|
+
noko_io_read_reset_exception();
|
|
387
388
|
xmlDocPtr c_document = xmlReadIO(
|
|
388
|
-
(xmlInputReadCallback)
|
|
389
|
+
(xmlInputReadCallback)noko_io_read_protected,
|
|
389
390
|
(xmlInputCloseCallback)noko_io_close,
|
|
390
391
|
(void *)rb_io,
|
|
391
392
|
c_url,
|
|
392
393
|
c_enc,
|
|
393
394
|
(int)NUM2INT(rb_options)
|
|
394
395
|
);
|
|
396
|
+
VALUE rb_io_read_exception = noko_io_read_take_exception();
|
|
395
397
|
|
|
396
398
|
noko__structured_error_func_restore(&handler_state);
|
|
397
399
|
|
|
400
|
+
if (!NIL_P(rb_io_read_exception)) {
|
|
401
|
+
xmlFreeDoc(c_document);
|
|
402
|
+
noko_io_read_raise_exception(rb_io_read_exception);
|
|
403
|
+
}
|
|
404
|
+
|
|
398
405
|
if (c_document == NULL) {
|
|
399
406
|
xmlFreeDoc(c_document);
|
|
400
407
|
|
|
@@ -464,7 +471,7 @@ new (int argc, VALUE *argv, VALUE klass)
|
|
|
464
471
|
VALUE version, rest, rb_doc ;
|
|
465
472
|
|
|
466
473
|
rb_scan_args(argc, argv, "0*", &rest);
|
|
467
|
-
version = rb_ary_entry(rest, (
|
|
474
|
+
version = rb_ary_entry(rest, (rb_len_t)0);
|
|
468
475
|
if (NIL_P(version)) { version = rb_str_new2("1.0"); }
|
|
469
476
|
|
|
470
477
|
doc = xmlNewDoc((xmlChar *)StringValueCStr(version));
|
|
@@ -609,7 +616,8 @@ rb_xml_document_canonicalize(int argc, VALUE *argv, VALUE self)
|
|
|
609
616
|
VALUE rb_namespaces;
|
|
610
617
|
VALUE rb_comments_p;
|
|
611
618
|
int c_mode = 0;
|
|
612
|
-
xmlChar **c_namespaces;
|
|
619
|
+
xmlChar **c_namespaces = NULL;
|
|
620
|
+
VALUE rb_namespaces_buffer = 0;
|
|
613
621
|
|
|
614
622
|
xmlDocPtr c_doc;
|
|
615
623
|
xmlOutputBufferPtr c_obuf;
|
|
@@ -629,6 +637,35 @@ rb_xml_document_canonicalize(int argc, VALUE *argv, VALUE self)
|
|
|
629
637
|
if (c_mode == XML_C14N_1_0 || c_mode == XML_C14N_1_1) {
|
|
630
638
|
rb_raise(rb_eRuntimeError, "This canonicalizer does not support this operation");
|
|
631
639
|
}
|
|
640
|
+
|
|
641
|
+
// Copy every prefix into one temporary buffer before libxml2 sees it. Converting first means a
|
|
642
|
+
// raise from #to_str or a null byte leaks nothing. Copying means GC compaction, which moves the
|
|
643
|
+
// bytes of embedded strings, cannot move a prefix while libxml2 reads it. No allocation may
|
|
644
|
+
// happen between reading a prefix's pointer and copying its bytes.
|
|
645
|
+
rb_len_t ns_len = RARRAY_LEN(rb_namespaces);
|
|
646
|
+
VALUE rb_prefixes = rb_ary_new_capa(ns_len);
|
|
647
|
+
for (rb_len_t j = 0; j < ns_len; j++) {
|
|
648
|
+
VALUE entry = rb_ary_entry(rb_namespaces, j);
|
|
649
|
+
StringValueCStr(entry);
|
|
650
|
+
rb_ary_push(rb_prefixes, entry);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
size_t size = ((size_t)ns_len + 1) * sizeof(xmlChar *);
|
|
654
|
+
for (rb_len_t j = 0; j < ns_len; j++) {
|
|
655
|
+
size += (size_t)RSTRING_LEN(RARRAY_AREF(rb_prefixes, j)) + 1;
|
|
656
|
+
}
|
|
657
|
+
c_namespaces = ALLOCV(rb_namespaces_buffer, size);
|
|
658
|
+
|
|
659
|
+
xmlChar *c_prefix = (xmlChar *)(c_namespaces + ns_len + 1);
|
|
660
|
+
for (rb_len_t j = 0; j < ns_len; j++) {
|
|
661
|
+
VALUE prefix = RARRAY_AREF(rb_prefixes, j);
|
|
662
|
+
size_t prefix_len = (size_t)RSTRING_LEN(prefix);
|
|
663
|
+
memcpy(c_prefix, RSTRING_PTR(prefix), prefix_len);
|
|
664
|
+
c_prefix[prefix_len] = '\0';
|
|
665
|
+
c_namespaces[j] = c_prefix;
|
|
666
|
+
c_prefix += prefix_len + 1;
|
|
667
|
+
}
|
|
668
|
+
c_namespaces[ns_len] = NULL;
|
|
632
669
|
}
|
|
633
670
|
|
|
634
671
|
c_doc = noko_xml_document_unwrap(self);
|
|
@@ -646,24 +683,13 @@ rb_xml_document_canonicalize(int argc, VALUE *argv, VALUE self)
|
|
|
646
683
|
rb_callback = (void *)rb_block_proc();
|
|
647
684
|
}
|
|
648
685
|
|
|
649
|
-
if (NIL_P(rb_namespaces)) {
|
|
650
|
-
c_namespaces = NULL;
|
|
651
|
-
} else {
|
|
652
|
-
long ns_len = RARRAY_LEN(rb_namespaces);
|
|
653
|
-
c_namespaces = ruby_xcalloc((size_t)ns_len + 1, sizeof(xmlChar *));
|
|
654
|
-
for (int j = 0 ; j < ns_len ; j++) {
|
|
655
|
-
VALUE entry = rb_ary_entry(rb_namespaces, j);
|
|
656
|
-
c_namespaces[j] = (xmlChar *)StringValueCStr(entry);
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
686
|
int ret = xmlC14NExecute(c_doc, c_callback_wrapper, rb_callback,
|
|
661
687
|
c_mode,
|
|
662
688
|
c_namespaces,
|
|
663
689
|
(int)RTEST(rb_comments_p),
|
|
664
690
|
c_obuf);
|
|
665
691
|
|
|
666
|
-
|
|
692
|
+
ALLOCV_END(rb_namespaces_buffer);
|
|
667
693
|
xmlOutputBufferClose(c_obuf);
|
|
668
694
|
|
|
669
695
|
if (ret < 0) {
|
|
@@ -733,7 +759,7 @@ noko_xml_document_has_wrapped_blank_nodes_p(xmlDocPtr c_document)
|
|
|
733
759
|
return 0;
|
|
734
760
|
}
|
|
735
761
|
|
|
736
|
-
for (
|
|
762
|
+
for (rb_len_t jnode = 0; jnode < RARRAY_LEN(cache); jnode++) {
|
|
737
763
|
xmlNodePtr node;
|
|
738
764
|
VALUE element = rb_ary_entry(cache, jnode);
|
|
739
765
|
|
data/ext/nokogiri/xml_node.c
CHANGED
|
@@ -115,14 +115,10 @@ relink_namespace(xmlNodePtr reparented)
|
|
|
115
115
|
xmlNsPtr prev = NULL;
|
|
116
116
|
|
|
117
117
|
while (curr) {
|
|
118
|
-
xmlNsPtr ns =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
);
|
|
123
|
-
/* If we find the namespace is already declared, remove it from this
|
|
124
|
-
* definition list. */
|
|
125
|
-
if (ns && ns != curr && xmlStrEqual(ns->prefix, curr->prefix)) {
|
|
118
|
+
xmlNsPtr ns = xmlSearchNs(reparented->doc, reparented->parent, curr->prefix);
|
|
119
|
+
/* If the parent already binds this prefix to the same href, remove it
|
|
120
|
+
* from this definition list. */
|
|
121
|
+
if (ns && ns != curr && xmlStrEqual(ns->href, curr->href)) {
|
|
126
122
|
if (prev) {
|
|
127
123
|
prev->next = curr->next;
|
|
128
124
|
} else {
|
|
@@ -1232,6 +1228,29 @@ namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
|
|
|
1232
1228
|
return Qfalse;
|
|
1233
1229
|
}
|
|
1234
1230
|
|
|
1231
|
+
/* If a matching attribute node already exists, then xmlSetNsProp will destroy
|
|
1232
|
+
* the existing node's children. However, if Nokogiri has a node object
|
|
1233
|
+
* pointing to one of those children, we are left with a broken reference.
|
|
1234
|
+
*
|
|
1235
|
+
* We can avoid this by unlinking these nodes first.
|
|
1236
|
+
*/
|
|
1237
|
+
static void
|
|
1238
|
+
unlink_wrapped_attr_children(xmlAttrPtr attr)
|
|
1239
|
+
{
|
|
1240
|
+
xmlNodePtr cur, next;
|
|
1241
|
+
|
|
1242
|
+
if (!attr || attr->type != XML_ATTRIBUTE_NODE) {
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
for (cur = attr->children; cur; cur = next) {
|
|
1246
|
+
next = cur->next;
|
|
1247
|
+
if (cur->_private) {
|
|
1248
|
+
noko_xml_document_pin_node(cur);
|
|
1249
|
+
xmlUnlinkNode(cur);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1235
1254
|
/*
|
|
1236
1255
|
* call-seq:
|
|
1237
1256
|
* []=(property, value)
|
|
@@ -1241,31 +1260,36 @@ namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
|
|
|
1241
1260
|
static VALUE
|
|
1242
1261
|
set(VALUE self, VALUE property, VALUE value)
|
|
1243
1262
|
{
|
|
1244
|
-
xmlNodePtr node
|
|
1245
|
-
|
|
1263
|
+
xmlNodePtr node;
|
|
1264
|
+
xmlNsPtr ns = NULL;
|
|
1265
|
+
const xmlChar *name, *local_name, *c_value;
|
|
1266
|
+
xmlChar *prefix;
|
|
1267
|
+
int prefix_len;
|
|
1246
1268
|
Noko_Node_Get_Struct(self, xmlNode, node);
|
|
1247
1269
|
|
|
1248
|
-
/* If a matching attribute node already exists, then xmlSetProp will destroy
|
|
1249
|
-
* the existing node's children. However, if Nokogiri has a node object
|
|
1250
|
-
* pointing to one of those children, we are left with a broken reference.
|
|
1251
|
-
*
|
|
1252
|
-
* We can avoid this by unlinking these nodes first.
|
|
1253
|
-
*/
|
|
1254
1270
|
if (node->type != XML_ELEMENT_NODE) {
|
|
1255
1271
|
return (Qnil);
|
|
1256
1272
|
}
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1273
|
+
name = (const xmlChar *)StringValueCStr(property);
|
|
1274
|
+
c_value = (const xmlChar *)StringValueCStr(value);
|
|
1275
|
+
|
|
1276
|
+
/* Resolve a QName the way xmlSetProp does, so that we find the same attribute it would replace. */
|
|
1277
|
+
local_name = xmlSplitQName3(name, &prefix_len);
|
|
1278
|
+
if (local_name) {
|
|
1279
|
+
prefix = xmlStrndup(name, prefix_len);
|
|
1280
|
+
if (!prefix) {
|
|
1281
|
+
rb_memerror();
|
|
1264
1282
|
}
|
|
1283
|
+
ns = xmlSearchNs(node->doc, node, prefix);
|
|
1284
|
+
xmlFree(prefix);
|
|
1285
|
+
}
|
|
1286
|
+
if (!ns) {
|
|
1287
|
+
local_name = name;
|
|
1265
1288
|
}
|
|
1266
1289
|
|
|
1267
|
-
|
|
1268
|
-
|
|
1290
|
+
unlink_wrapped_attr_children(xmlHasNsProp(node, local_name, ns ? ns->href : NULL));
|
|
1291
|
+
|
|
1292
|
+
xmlSetNsProp(node, ns, local_name, c_value);
|
|
1269
1293
|
|
|
1270
1294
|
return value;
|
|
1271
1295
|
}
|
|
@@ -1338,6 +1362,11 @@ set_namespace(VALUE self, VALUE namespace)
|
|
|
1338
1362
|
|
|
1339
1363
|
xmlSetNs(node, ns);
|
|
1340
1364
|
|
|
1365
|
+
if (ns && node->type == XML_ELEMENT_NODE && !xmlSearchNs(node->doc, node, ns->prefix)) {
|
|
1366
|
+
xmlNsPtr definition = xmlNewNs(node, ns->href, ns->prefix);
|
|
1367
|
+
if (definition) { xmlSetNs(node, definition); }
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1341
1370
|
return self;
|
|
1342
1371
|
}
|
|
1343
1372
|
|
|
@@ -1566,6 +1595,7 @@ set_lang(VALUE self_rb, VALUE lang_rb)
|
|
|
1566
1595
|
Noko_Node_Get_Struct(self_rb, xmlNode, self);
|
|
1567
1596
|
lang = (xmlChar *)StringValueCStr(lang_rb);
|
|
1568
1597
|
|
|
1598
|
+
unlink_wrapped_attr_children(xmlHasNsProp(self, BAD_CAST "lang", XML_XML_NAMESPACE));
|
|
1569
1599
|
xmlNodeSetLang(self, lang);
|
|
1570
1600
|
|
|
1571
1601
|
return Qnil ;
|
|
@@ -1744,7 +1774,7 @@ static inline void
|
|
|
1744
1774
|
output_partial_string(VALUE out, char const *str, size_t length)
|
|
1745
1775
|
{
|
|
1746
1776
|
if (length) {
|
|
1747
|
-
rb_enc_str_buf_cat(out, str, (
|
|
1777
|
+
rb_enc_str_buf_cat(out, str, (rb_len_t)length, rb_utf8_encoding());
|
|
1748
1778
|
}
|
|
1749
1779
|
}
|
|
1750
1780
|
|
|
@@ -1834,9 +1864,9 @@ output_escaped_string(VALUE out, xmlChar const *start, bool attr)
|
|
|
1834
1864
|
replaced_bytes = 2;
|
|
1835
1865
|
} else if (attr && ch == '"') {
|
|
1836
1866
|
replacement = """;
|
|
1837
|
-
} else if (
|
|
1867
|
+
} else if (ch == '<') {
|
|
1838
1868
|
replacement = "<";
|
|
1839
|
-
} else if (
|
|
1869
|
+
} else if (ch == '>') {
|
|
1840
1870
|
replacement = ">";
|
|
1841
1871
|
} else {
|
|
1842
1872
|
++next;
|
|
@@ -1897,7 +1927,8 @@ static void
|
|
|
1897
1927
|
output_node(
|
|
1898
1928
|
VALUE out,
|
|
1899
1929
|
xmlNodePtr node,
|
|
1900
|
-
bool preserve_newline
|
|
1930
|
+
bool preserve_newline,
|
|
1931
|
+
bool scripting_enabled
|
|
1901
1932
|
)
|
|
1902
1933
|
{
|
|
1903
1934
|
static char const *const VOID_ELEMENTS[] = {
|
|
@@ -1906,9 +1937,11 @@ output_node(
|
|
|
1906
1937
|
};
|
|
1907
1938
|
|
|
1908
1939
|
static char const *const UNESCAPED_TEXT_ELEMENTS[] = {
|
|
1909
|
-
"style", "script", "xmp", "iframe", "noembed", "noframes", "plaintext",
|
|
1940
|
+
"style", "script", "xmp", "iframe", "noembed", "noframes", "plaintext",
|
|
1910
1941
|
};
|
|
1911
1942
|
|
|
1943
|
+
static char const *const NOSCRIPT_ELEMENT[] = { "noscript" };
|
|
1944
|
+
|
|
1912
1945
|
switch (node->type) {
|
|
1913
1946
|
case XML_ELEMENT_NODE:
|
|
1914
1947
|
// Serialize the start tag.
|
|
@@ -1918,7 +1951,7 @@ output_node(
|
|
|
1918
1951
|
// Add attributes.
|
|
1919
1952
|
for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) {
|
|
1920
1953
|
output_char(out, ' ');
|
|
1921
|
-
output_node(out, (xmlNodePtr)attr, preserve_newline);
|
|
1954
|
+
output_node(out, (xmlNodePtr)attr, preserve_newline, scripting_enabled);
|
|
1922
1955
|
}
|
|
1923
1956
|
output_char(out, '>');
|
|
1924
1957
|
|
|
@@ -1928,7 +1961,7 @@ output_node(
|
|
|
1928
1961
|
output_char(out, '\n');
|
|
1929
1962
|
}
|
|
1930
1963
|
for (xmlNodePtr child = node->children; child; child = child->next) {
|
|
1931
|
-
output_node(out, child, preserve_newline);
|
|
1964
|
+
output_node(out, child, preserve_newline, scripting_enabled);
|
|
1932
1965
|
}
|
|
1933
1966
|
output_string(out, "</");
|
|
1934
1967
|
output_tagname(out, node);
|
|
@@ -1954,8 +1987,9 @@ output_node(
|
|
|
1954
1987
|
|
|
1955
1988
|
case XML_TEXT_NODE:
|
|
1956
1989
|
if (node->parent
|
|
1957
|
-
&& is_one_of(node->parent, UNESCAPED_TEXT_ELEMENTS,
|
|
1958
|
-
|
|
1990
|
+
&& (is_one_of(node->parent, UNESCAPED_TEXT_ELEMENTS,
|
|
1991
|
+
sizeof UNESCAPED_TEXT_ELEMENTS / sizeof UNESCAPED_TEXT_ELEMENTS[0])
|
|
1992
|
+
|| (scripting_enabled && is_one_of(node->parent, NOSCRIPT_ELEMENT, 1)))) {
|
|
1959
1993
|
output_string(out, (char const *)node->content);
|
|
1960
1994
|
} else {
|
|
1961
1995
|
output_escaped_string(out, node->content, false);
|
|
@@ -1991,7 +2025,7 @@ output_node(
|
|
|
1991
2025
|
case XML_DOCUMENT_FRAG_NODE:
|
|
1992
2026
|
case XML_HTML_DOCUMENT_NODE:
|
|
1993
2027
|
for (xmlNodePtr child = node->children; child; child = child->next) {
|
|
1994
|
-
output_node(out, child, preserve_newline);
|
|
2028
|
+
output_node(out, child, preserve_newline, scripting_enabled);
|
|
1995
2029
|
}
|
|
1996
2030
|
break;
|
|
1997
2031
|
|
|
@@ -2004,13 +2038,14 @@ output_node(
|
|
|
2004
2038
|
static VALUE
|
|
2005
2039
|
html_standard_serialize(
|
|
2006
2040
|
VALUE self,
|
|
2007
|
-
VALUE preserve_newline
|
|
2041
|
+
VALUE preserve_newline,
|
|
2042
|
+
VALUE scripting_enabled
|
|
2008
2043
|
)
|
|
2009
2044
|
{
|
|
2010
2045
|
xmlNodePtr node;
|
|
2011
2046
|
Noko_Node_Get_Struct(self, xmlNode, node);
|
|
2012
2047
|
VALUE output = rb_str_buf_new(4096);
|
|
2013
|
-
output_node(output, node, RTEST(preserve_newline));
|
|
2048
|
+
output_node(output, node, RTEST(preserve_newline), RTEST(scripting_enabled));
|
|
2014
2049
|
return output;
|
|
2015
2050
|
}
|
|
2016
2051
|
|
|
@@ -2512,7 +2547,7 @@ noko_init_xml_node(void)
|
|
|
2512
2547
|
rb_define_private_method(cNokogiriXmlNode, "in_context", in_context, 2);
|
|
2513
2548
|
rb_define_private_method(cNokogiriXmlNode, "native_write_to", native_write_to, 4);
|
|
2514
2549
|
rb_define_private_method(cNokogiriXmlNode, "prepend_newline?", rb_prepend_newline, 0);
|
|
2515
|
-
rb_define_private_method(cNokogiriXmlNode, "html_standard_serialize", html_standard_serialize,
|
|
2550
|
+
rb_define_private_method(cNokogiriXmlNode, "html_standard_serialize", html_standard_serialize, 2);
|
|
2516
2551
|
rb_define_private_method(cNokogiriXmlNode, "process_xincludes", noko_xml_node__process_xincludes, 1);
|
|
2517
2552
|
rb_define_private_method(cNokogiriXmlNode, "replace_node", replace, 1);
|
|
2518
2553
|
rb_define_private_method(cNokogiriXmlNode, "set", set, 2);
|