nokogiri 1.11.0.rc3-java → 1.11.0.rc4-java
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.
- checksums.yaml +4 -4
- data/LICENSE-DEPENDENCIES.md +1015 -947
- data/README.md +1 -1
- data/ext/java/nokogiri/HtmlSaxParserContext.java +1 -1
- data/ext/java/nokogiri/HtmlSaxPushParser.java +1 -1
- data/ext/java/nokogiri/XmlNode.java +4 -6
- data/ext/java/nokogiri/XmlReader.java +2 -2
- data/ext/java/nokogiri/XmlRelaxng.java +10 -3
- data/ext/java/nokogiri/XmlSaxParserContext.java +8 -3
- data/ext/java/nokogiri/XmlSchema.java +84 -24
- data/ext/java/nokogiri/XmlSyntaxError.java +2 -6
- data/ext/java/nokogiri/XmlXpathContext.java +48 -18
- data/ext/java/nokogiri/internals/HtmlDomParserContext.java +51 -39
- data/ext/java/nokogiri/internals/NokogiriHandler.java +1 -1
- data/ext/java/nokogiri/internals/NokogiriNamespaceContext.java +9 -2
- data/ext/java/nokogiri/internals/NokogiriXPathFunction.java +62 -6
- data/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java +4 -2
- data/ext/java/nokogiri/internals/XmlDomParserContext.java +2 -2
- data/ext/nokogiri/depend +476 -357
- data/ext/nokogiri/extconf.rb +441 -321
- data/ext/nokogiri/html_document.c +79 -78
- data/ext/nokogiri/html_sax_parser_context.c +2 -2
- data/ext/nokogiri/nokogiri.c +34 -46
- data/ext/nokogiri/xml_document.c +2 -2
- data/ext/nokogiri/xml_node.c +1 -1
- data/ext/nokogiri/xml_node_set.c +1 -1
- data/ext/nokogiri/xml_relax_ng.c +29 -11
- data/ext/nokogiri/xml_sax_parser.c +2 -7
- data/ext/nokogiri/xml_sax_parser_context.c +2 -2
- data/ext/nokogiri/xml_schema.c +55 -13
- data/ext/nokogiri/xml_xpath_context.c +80 -4
- data/ext/nokogiri/xslt_stylesheet.c +1 -4
- data/lib/nokogiri.rb +1 -1
- data/lib/nokogiri/css/parser.rb +3 -3
- data/lib/nokogiri/css/parser.y +2 -2
- data/lib/nokogiri/css/xpath_visitor.rb +70 -42
- data/lib/nokogiri/html/document.rb +12 -26
- data/lib/nokogiri/nokogiri.jar +0 -0
- data/lib/nokogiri/version.rb +2 -149
- data/lib/nokogiri/version/constant.rb +5 -0
- data/lib/nokogiri/version/info.rb +182 -0
- data/lib/nokogiri/xml/document.rb +17 -7
- data/lib/nokogiri/xml/document_fragment.rb +4 -6
- data/lib/nokogiri/xml/node.rb +50 -27
- data/lib/nokogiri/xml/parse_options.rb +6 -0
- data/lib/nokogiri/xml/relax_ng.rb +6 -2
- data/lib/nokogiri/xml/schema.rb +12 -4
- data/lib/nokogiri/xml/searchable.rb +3 -1
- metadata +47 -73
- data/ext/nokogiri/html_document.h +0 -10
- data/ext/nokogiri/html_element_description.h +0 -10
- data/ext/nokogiri/html_entity_lookup.h +0 -8
- data/ext/nokogiri/html_sax_parser_context.h +0 -11
- data/ext/nokogiri/html_sax_push_parser.h +0 -9
- data/ext/nokogiri/nokogiri.h +0 -134
- data/ext/nokogiri/xml_attr.h +0 -9
- data/ext/nokogiri/xml_attribute_decl.h +0 -9
- data/ext/nokogiri/xml_cdata.h +0 -9
- data/ext/nokogiri/xml_comment.h +0 -9
- data/ext/nokogiri/xml_document.h +0 -23
- data/ext/nokogiri/xml_document_fragment.h +0 -10
- data/ext/nokogiri/xml_dtd.h +0 -10
- data/ext/nokogiri/xml_element_content.h +0 -10
- data/ext/nokogiri/xml_element_decl.h +0 -9
- data/ext/nokogiri/xml_encoding_handler.h +0 -8
- data/ext/nokogiri/xml_entity_decl.h +0 -10
- data/ext/nokogiri/xml_entity_reference.h +0 -9
- data/ext/nokogiri/xml_io.h +0 -11
- data/ext/nokogiri/xml_libxml2_hacks.h +0 -12
- data/ext/nokogiri/xml_namespace.h +0 -14
- data/ext/nokogiri/xml_node.h +0 -13
- data/ext/nokogiri/xml_node_set.h +0 -12
- data/ext/nokogiri/xml_processing_instruction.h +0 -9
- data/ext/nokogiri/xml_reader.h +0 -10
- data/ext/nokogiri/xml_relax_ng.h +0 -9
- data/ext/nokogiri/xml_sax_parser.h +0 -39
- data/ext/nokogiri/xml_sax_parser_context.h +0 -10
- data/ext/nokogiri/xml_sax_push_parser.h +0 -9
- data/ext/nokogiri/xml_schema.h +0 -9
- data/ext/nokogiri/xml_syntax_error.h +0 -13
- data/ext/nokogiri/xml_text.h +0 -9
- data/ext/nokogiri/xml_xpath_context.h +0 -10
- data/ext/nokogiri/xslt_stylesheet.h +0 -14
@@ -1,8 +1,5 @@
|
|
1
1
|
#include <xml_sax_parser.h>
|
2
2
|
|
3
|
-
int vasprintf (char **strp, const char *fmt, va_list ap);
|
4
|
-
void vasprintf_free (void *p);
|
5
|
-
|
6
3
|
static ID id_start_document, id_end_document, id_start_element, id_end_element;
|
7
4
|
static ID id_start_element_namespace, id_end_element_namespace;
|
8
5
|
static ID id_comment, id_characters, id_xmldecl, id_error, id_warning;
|
@@ -206,7 +203,7 @@ static void warning_func(void * ctx, const char *msg, ...)
|
|
206
203
|
va_end(args);
|
207
204
|
|
208
205
|
ruby_message = NOKOGIRI_STR_NEW2(message);
|
209
|
-
|
206
|
+
free(message);
|
210
207
|
rb_funcall(doc, id_warning, 1, ruby_message);
|
211
208
|
}
|
212
209
|
|
@@ -223,7 +220,7 @@ static void error_func(void * ctx, const char *msg, ...)
|
|
223
220
|
va_end(args);
|
224
221
|
|
225
222
|
ruby_message = NOKOGIRI_STR_NEW2(message);
|
226
|
-
|
223
|
+
free(message);
|
227
224
|
rb_funcall(doc, id_error, 1, ruby_message);
|
228
225
|
}
|
229
226
|
|
@@ -262,8 +259,6 @@ static VALUE allocate(VALUE klass)
|
|
262
259
|
{
|
263
260
|
xmlSAXHandlerPtr handler = calloc((size_t)1, sizeof(xmlSAXHandler));
|
264
261
|
|
265
|
-
xmlSetStructuredErrorFunc(NULL, NULL);
|
266
|
-
|
267
262
|
handler->startDocument = start_document;
|
268
263
|
handler->endDocument = end_document;
|
269
264
|
handler->startElement = start_element;
|
@@ -4,13 +4,13 @@ VALUE cNokogiriXmlSaxParserContext ;
|
|
4
4
|
|
5
5
|
static void deallocate(xmlParserCtxtPtr ctxt)
|
6
6
|
{
|
7
|
-
NOKOGIRI_DEBUG_START(
|
7
|
+
NOKOGIRI_DEBUG_START(ctxt);
|
8
8
|
|
9
9
|
ctxt->sax = NULL;
|
10
10
|
|
11
11
|
xmlFreeParserCtxt(ctxt);
|
12
12
|
|
13
|
-
NOKOGIRI_DEBUG_END(
|
13
|
+
NOKOGIRI_DEBUG_END(ctxt);
|
14
14
|
}
|
15
15
|
|
16
16
|
/*
|
data/ext/nokogiri/xml_schema.c
CHANGED
@@ -93,15 +93,27 @@ static VALUE validate_file(VALUE self, VALUE rb_filename)
|
|
93
93
|
*
|
94
94
|
* Create a new Schema from the contents of +string+
|
95
95
|
*/
|
96
|
-
static VALUE read_memory(VALUE
|
96
|
+
static VALUE read_memory(int argc, VALUE *argv, VALUE klass)
|
97
97
|
{
|
98
|
+
VALUE content;
|
99
|
+
VALUE parse_options;
|
100
|
+
int parse_options_int;
|
101
|
+
xmlSchemaParserCtxtPtr ctx;
|
98
102
|
xmlSchemaPtr schema;
|
99
|
-
|
100
|
-
(const char *)StringValuePtr(content),
|
101
|
-
(int)RSTRING_LEN(content)
|
102
|
-
);
|
103
|
+
VALUE errors;
|
103
104
|
VALUE rb_schema;
|
104
|
-
|
105
|
+
int scanned_args = 0;
|
106
|
+
xmlExternalEntityLoader old_loader = 0;
|
107
|
+
|
108
|
+
scanned_args = rb_scan_args(argc, argv, "11", &content, &parse_options);
|
109
|
+
if (scanned_args == 1) {
|
110
|
+
parse_options = rb_const_get(rb_const_get(mNokogiriXml, rb_intern("ParseOptions")), rb_intern("DEFAULT_SCHEMA"));
|
111
|
+
}
|
112
|
+
parse_options_int = (int)NUM2INT(rb_funcall(parse_options, rb_intern("to_i"), 0));
|
113
|
+
|
114
|
+
ctx = xmlSchemaNewMemParserCtxt((const char *)StringValuePtr(content), (int)RSTRING_LEN(content));
|
115
|
+
|
116
|
+
errors = rb_ary_new();
|
105
117
|
xmlSetStructuredErrorFunc((void *)errors, Nokogiri_error_array_pusher);
|
106
118
|
|
107
119
|
#ifdef HAVE_XMLSCHEMASETPARSERSTRUCTUREDERRORS
|
@@ -109,10 +121,19 @@ static VALUE read_memory(VALUE klass, VALUE content)
|
|
109
121
|
ctx,
|
110
122
|
Nokogiri_error_array_pusher,
|
111
123
|
(void *)errors
|
112
|
-
|
124
|
+
);
|
113
125
|
#endif
|
114
126
|
|
115
|
-
|
127
|
+
if (parse_options_int & XML_PARSE_NONET) {
|
128
|
+
old_loader = xmlGetExternalEntityLoader();
|
129
|
+
xmlSetExternalEntityLoader(xmlNoNetExternalEntityLoader);
|
130
|
+
}
|
131
|
+
|
132
|
+
schema = xmlSchemaParse(ctx);
|
133
|
+
|
134
|
+
if (old_loader) {
|
135
|
+
xmlSetExternalEntityLoader(old_loader);
|
136
|
+
}
|
116
137
|
|
117
138
|
xmlSetStructuredErrorFunc(NULL, NULL);
|
118
139
|
xmlSchemaFreeParserCtxt(ctx);
|
@@ -129,6 +150,7 @@ static VALUE read_memory(VALUE klass, VALUE content)
|
|
129
150
|
|
130
151
|
rb_schema = Data_Wrap_Struct(klass, 0, dealloc, schema);
|
131
152
|
rb_iv_set(rb_schema, "@errors", errors);
|
153
|
+
rb_iv_set(rb_schema, "@parse_options", parse_options);
|
132
154
|
|
133
155
|
return rb_schema;
|
134
156
|
}
|
@@ -164,18 +186,28 @@ static int has_blank_nodes_p(VALUE cache)
|
|
164
186
|
*
|
165
187
|
* Create a new Schema from the Nokogiri::XML::Document +doc+
|
166
188
|
*/
|
167
|
-
static VALUE from_document(VALUE
|
189
|
+
static VALUE from_document(int argc, VALUE *argv, VALUE klass)
|
168
190
|
{
|
191
|
+
VALUE document;
|
192
|
+
VALUE parse_options;
|
193
|
+
int parse_options_int;
|
169
194
|
xmlDocPtr doc;
|
170
195
|
xmlSchemaParserCtxtPtr ctx;
|
171
196
|
xmlSchemaPtr schema;
|
172
197
|
VALUE errors;
|
173
198
|
VALUE rb_schema;
|
199
|
+
int scanned_args = 0;
|
200
|
+
xmlExternalEntityLoader old_loader = 0;
|
201
|
+
|
202
|
+
scanned_args = rb_scan_args(argc, argv, "11", &document, &parse_options);
|
174
203
|
|
175
204
|
Data_Get_Struct(document, xmlDoc, doc);
|
205
|
+
doc = doc->doc; /* In case someone passes us a node. ugh. */
|
176
206
|
|
177
|
-
|
178
|
-
|
207
|
+
if (scanned_args == 1) {
|
208
|
+
parse_options = rb_const_get(rb_const_get(mNokogiriXml, rb_intern("ParseOptions")), rb_intern("DEFAULT_SCHEMA"));
|
209
|
+
}
|
210
|
+
parse_options_int = (int)NUM2INT(rb_funcall(parse_options, rb_intern("to_i"), 0));
|
179
211
|
|
180
212
|
if (has_blank_nodes_p(DOC_NODE_CACHE(doc))) {
|
181
213
|
rb_raise(rb_eArgError, "Creating a schema from a document that has blank nodes exposed to Ruby is dangerous");
|
@@ -194,8 +226,17 @@ static VALUE from_document(VALUE klass, VALUE document)
|
|
194
226
|
);
|
195
227
|
#endif
|
196
228
|
|
229
|
+
if (parse_options_int & XML_PARSE_NONET) {
|
230
|
+
old_loader = xmlGetExternalEntityLoader();
|
231
|
+
xmlSetExternalEntityLoader(xmlNoNetExternalEntityLoader);
|
232
|
+
}
|
233
|
+
|
197
234
|
schema = xmlSchemaParse(ctx);
|
198
235
|
|
236
|
+
if (old_loader) {
|
237
|
+
xmlSetExternalEntityLoader(old_loader);
|
238
|
+
}
|
239
|
+
|
199
240
|
xmlSetStructuredErrorFunc(NULL, NULL);
|
200
241
|
xmlSchemaFreeParserCtxt(ctx);
|
201
242
|
|
@@ -211,6 +252,7 @@ static VALUE from_document(VALUE klass, VALUE document)
|
|
211
252
|
|
212
253
|
rb_schema = Data_Wrap_Struct(klass, 0, dealloc, schema);
|
213
254
|
rb_iv_set(rb_schema, "@errors", errors);
|
255
|
+
rb_iv_set(rb_schema, "@parse_options", parse_options);
|
214
256
|
|
215
257
|
return rb_schema;
|
216
258
|
|
@@ -226,8 +268,8 @@ void init_xml_schema()
|
|
226
268
|
|
227
269
|
cNokogiriXmlSchema = klass;
|
228
270
|
|
229
|
-
rb_define_singleton_method(klass, "read_memory", read_memory, 1);
|
230
|
-
rb_define_singleton_method(klass, "from_document", from_document, 1);
|
271
|
+
rb_define_singleton_method(klass, "read_memory", read_memory, -1);
|
272
|
+
rb_define_singleton_method(klass, "from_document", from_document, -1);
|
231
273
|
|
232
274
|
rb_define_private_method(klass, "validate_document", validate_document, 1);
|
233
275
|
rb_define_private_method(klass, "validate_file", validate_file, 1);
|
@@ -1,6 +1,11 @@
|
|
1
1
|
#include <xml_xpath_context.h>
|
2
2
|
|
3
|
-
|
3
|
+
/*
|
4
|
+
* these constants have matching declarations in
|
5
|
+
* ext/java/nokogiri/internals/NokogiriNamespaceContext.java
|
6
|
+
*/
|
7
|
+
static const xmlChar *NOKOGIRI_BUILTIN_PREFIX = (const xmlChar *)"nokogiri-builtin";
|
8
|
+
static const xmlChar *NOKOGIRI_BUILTIN_URI = (const xmlChar *)"https://www.nokogiri.org/default_ns/ruby/builtins";
|
4
9
|
|
5
10
|
static void deallocate(xmlXPathContextPtr ctx)
|
6
11
|
{
|
@@ -9,6 +14,73 @@ static void deallocate(xmlXPathContextPtr ctx)
|
|
9
14
|
NOKOGIRI_DEBUG_END(ctx);
|
10
15
|
}
|
11
16
|
|
17
|
+
/* find a CSS class in an HTML element's `class` attribute */
|
18
|
+
const xmlChar* builtin_css_class(const xmlChar* str, const xmlChar *val)
|
19
|
+
{
|
20
|
+
int val_len;
|
21
|
+
|
22
|
+
if (str == NULL) { return(NULL); }
|
23
|
+
if (val == NULL) { return(NULL); }
|
24
|
+
|
25
|
+
val_len = xmlStrlen(val);
|
26
|
+
if (val_len == 0) { return(str); }
|
27
|
+
|
28
|
+
while (*str != 0) {
|
29
|
+
if ((*str == *val) && !xmlStrncmp(str, val, val_len)) {
|
30
|
+
const xmlChar* next_byte = str + val_len;
|
31
|
+
|
32
|
+
/* only match if the next byte is whitespace or end of string */
|
33
|
+
if ((*next_byte == 0) || (IS_BLANK_CH(*next_byte))) {
|
34
|
+
return((const xmlChar*)str);
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
/* advance str to whitespace */
|
39
|
+
while ((*str != 0) && !IS_BLANK_CH(*str)) {
|
40
|
+
str++;
|
41
|
+
}
|
42
|
+
|
43
|
+
/* advance str to start of next word or end of string */
|
44
|
+
while ((*str != 0) && IS_BLANK_CH(*str)) {
|
45
|
+
str++;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
return(NULL);
|
50
|
+
}
|
51
|
+
|
52
|
+
/* xmlXPathFunction to wrap builtin_css_class() */
|
53
|
+
static void xpath_builtin_css_class(xmlXPathParserContextPtr ctxt, int nargs)
|
54
|
+
{
|
55
|
+
xmlXPathObjectPtr hay, needle;
|
56
|
+
|
57
|
+
CHECK_ARITY(2);
|
58
|
+
|
59
|
+
CAST_TO_STRING;
|
60
|
+
needle = valuePop(ctxt);
|
61
|
+
if ((needle == NULL) || (needle->type != XPATH_STRING)) {
|
62
|
+
xmlXPathFreeObject(needle);
|
63
|
+
XP_ERROR(XPATH_INVALID_TYPE);
|
64
|
+
}
|
65
|
+
|
66
|
+
CAST_TO_STRING;
|
67
|
+
hay = valuePop(ctxt);
|
68
|
+
if ((hay == NULL) || (hay->type != XPATH_STRING)) {
|
69
|
+
xmlXPathFreeObject(hay);
|
70
|
+
xmlXPathFreeObject(needle);
|
71
|
+
XP_ERROR(XPATH_INVALID_TYPE);
|
72
|
+
}
|
73
|
+
|
74
|
+
if (builtin_css_class(hay->stringval, needle->stringval)) {
|
75
|
+
valuePush(ctxt, xmlXPathNewBoolean(1));
|
76
|
+
} else {
|
77
|
+
valuePush(ctxt, xmlXPathNewBoolean(0));
|
78
|
+
}
|
79
|
+
|
80
|
+
xmlXPathFreeObject(hay);
|
81
|
+
xmlXPathFreeObject(needle);
|
82
|
+
}
|
83
|
+
|
12
84
|
/*
|
13
85
|
* call-seq:
|
14
86
|
* register_ns(prefix, uri)
|
@@ -263,14 +335,18 @@ static VALUE new(VALUE klass, VALUE nodeobj)
|
|
263
335
|
xmlXPathContextPtr ctx;
|
264
336
|
VALUE self;
|
265
337
|
|
266
|
-
xmlXPathInit();
|
267
|
-
|
268
338
|
Data_Get_Struct(nodeobj, xmlNode, node);
|
269
339
|
|
340
|
+
xmlXPathInit();
|
341
|
+
|
270
342
|
ctx = xmlXPathNewContext(node->doc);
|
271
343
|
ctx->node = node;
|
344
|
+
|
345
|
+
xmlXPathRegisterNs(ctx, NOKOGIRI_BUILTIN_PREFIX, NOKOGIRI_BUILTIN_URI);
|
346
|
+
xmlXPathRegisterFuncNS(ctx, (const xmlChar *)"css-class", NOKOGIRI_BUILTIN_URI,
|
347
|
+
xpath_builtin_css_class);
|
348
|
+
|
272
349
|
self = Data_Wrap_Struct(klass, 0, deallocate, ctx);
|
273
|
-
/*rb_iv_set(self, "@xpath_handler", Qnil); */
|
274
350
|
return self;
|
275
351
|
}
|
276
352
|
|
@@ -7,9 +7,6 @@
|
|
7
7
|
|
8
8
|
VALUE xslt;
|
9
9
|
|
10
|
-
int vasprintf (char **strp, const char *fmt, va_list ap);
|
11
|
-
void vasprintf_free (void *p);
|
12
|
-
|
13
10
|
static void mark(nokogiriXsltStylesheetTuple *wrapper)
|
14
11
|
{
|
15
12
|
rb_gc_mark(wrapper->func_instances);
|
@@ -37,7 +34,7 @@ static void xslt_generic_error_handler(void * ctx, const char *msg, ...)
|
|
37
34
|
|
38
35
|
rb_str_cat2((VALUE)ctx, message);
|
39
36
|
|
40
|
-
|
37
|
+
free(message);
|
41
38
|
}
|
42
39
|
|
43
40
|
VALUE Nokogiri_wrap_xslt_stylesheet(xsltStylesheetPtr ss)
|
data/lib/nokogiri.rb
CHANGED
@@ -113,7 +113,7 @@ module Nokogiri
|
|
113
113
|
end
|
114
114
|
|
115
115
|
###
|
116
|
-
#
|
116
|
+
# Parse a document contained in +args+. Nokogiri will try to guess what
|
117
117
|
# type of document you are attempting to parse. For more information, see
|
118
118
|
# Nokogiri.parse
|
119
119
|
#
|
data/lib/nokogiri/css/parser.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
#
|
3
3
|
# DO NOT MODIFY!!!!
|
4
|
-
# This file is automatically generated by Racc 1.
|
4
|
+
# This file is automatically generated by Racc 1.5.1
|
5
5
|
# from Racc grammar file "".
|
6
6
|
#
|
7
7
|
|
@@ -476,7 +476,7 @@ def _reduce_26(val, _values, result)
|
|
476
476
|
end
|
477
477
|
|
478
478
|
def _reduce_27(val, _values, result)
|
479
|
-
#
|
479
|
+
# non-standard, from hpricot
|
480
480
|
result = Node.new(:PSEUDO_CLASS,
|
481
481
|
[Node.new(:FUNCTION, ['nth-child(', val[1]])]
|
482
482
|
)
|
@@ -558,7 +558,7 @@ def _reduce_40(val, _values, result)
|
|
558
558
|
when 'n'
|
559
559
|
result = Node.new(:NTH, ['1','n','+','0'])
|
560
560
|
else
|
561
|
-
#
|
561
|
+
# non-standard to support custom functions:
|
562
562
|
# assert_xpath("//a[foo(., @href)]", @parser.parse('a:foo(@href)'))
|
563
563
|
# assert_xpath("//a[foo(., @a, b)]", @parser.parse('a:foo(@a, b)'))
|
564
564
|
# assert_xpath("//a[foo(., a, 10)]", @parser.parse('a:foo(a, 10)'))
|
data/lib/nokogiri/css/parser.y
CHANGED
@@ -88,7 +88,7 @@ rule
|
|
88
88
|
)
|
89
89
|
}
|
90
90
|
| LSQUARE NUMBER RSQUARE {
|
91
|
-
#
|
91
|
+
# non-standard, from hpricot
|
92
92
|
result = Node.new(:PSEUDO_CLASS,
|
93
93
|
[Node.new(:FUNCTION, ['nth-child(', val[1]])]
|
94
94
|
)
|
@@ -139,7 +139,7 @@ rule
|
|
139
139
|
when 'n'
|
140
140
|
result = Node.new(:NTH, ['1','n','+','0'])
|
141
141
|
else
|
142
|
-
#
|
142
|
+
# non-standard to support custom functions:
|
143
143
|
# assert_xpath("//a[foo(., @href)]", @parser.parse('a:foo(@href)'))
|
144
144
|
# assert_xpath("//a[foo(., @a, b)]", @parser.parse('a:foo(@a, b)'))
|
145
145
|
# assert_xpath("//a[foo(., a, 10)]", @parser.parse('a:foo(a, 10)'))
|
@@ -3,7 +3,6 @@ module Nokogiri
|
|
3
3
|
module CSS
|
4
4
|
class XPathVisitor # :nodoc:
|
5
5
|
def visit_function node
|
6
|
-
|
7
6
|
msg = :"visit_function_#{node.value.first.gsub(/[(]/, '')}"
|
8
7
|
return self.send(msg, node) if self.respond_to?(msg)
|
9
8
|
|
@@ -13,50 +12,51 @@ module Nokogiri
|
|
13
12
|
when /^self\(/
|
14
13
|
"self::#{node.value[1]}"
|
15
14
|
when /^eq\(/
|
16
|
-
"position()
|
15
|
+
"position()=#{node.value[1]}"
|
17
16
|
when /^(nth|nth-of-type)\(/
|
18
17
|
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :NTH
|
19
18
|
nth(node.value[1])
|
20
19
|
else
|
21
|
-
"position()
|
20
|
+
"position()=#{node.value[1]}"
|
22
21
|
end
|
23
22
|
when /^nth-child\(/
|
24
23
|
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :NTH
|
25
24
|
nth(node.value[1], :child => true)
|
26
25
|
else
|
27
|
-
"count(preceding-sibling::*)
|
26
|
+
"count(preceding-sibling::*)=#{node.value[1].to_i-1}"
|
28
27
|
end
|
29
28
|
when /^nth-last-of-type\(/
|
30
29
|
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :NTH
|
31
30
|
nth(node.value[1], :last => true)
|
32
31
|
else
|
33
32
|
index = node.value[1].to_i - 1
|
34
|
-
index == 0 ? "position()
|
33
|
+
index == 0 ? "position()=last()" : "position()=last()-#{index}"
|
35
34
|
end
|
36
35
|
when /^nth-last-child\(/
|
37
36
|
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :NTH
|
38
37
|
nth(node.value[1], :last => true, :child => true)
|
39
38
|
else
|
40
|
-
"count(following-sibling::*)
|
39
|
+
"count(following-sibling::*)=#{node.value[1].to_i-1}"
|
41
40
|
end
|
42
41
|
when /^(first|first-of-type)\(/
|
43
|
-
"position()
|
42
|
+
"position()=1"
|
44
43
|
when /^(last|last-of-type)\(/
|
45
|
-
"position()
|
44
|
+
"position()=last()"
|
46
45
|
when /^contains\(/
|
47
|
-
"contains(
|
46
|
+
"contains(.,#{node.value[1]})"
|
48
47
|
when /^gt\(/
|
49
|
-
"position()
|
48
|
+
"position()>#{node.value[1]}"
|
50
49
|
when /^only-child\(/
|
51
|
-
"last()
|
50
|
+
"last()=1"
|
52
51
|
when /^comment\(/
|
53
52
|
"comment()"
|
54
53
|
when /^has\(/
|
55
54
|
is_direct = node.value[1].value[0].nil? # e.g. "has(> a)", "has(~ a)", "has(+ a)"
|
56
55
|
".#{"//" if !is_direct}#{node.value[1].accept(self)}"
|
57
56
|
else
|
57
|
+
# non-standard. this looks like a function call.
|
58
58
|
args = ['.'] + node.value[1..-1]
|
59
|
-
"#{node.value.first}#{args.join(',
|
59
|
+
"#{node.value.first}#{args.join(',')})"
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -71,18 +71,18 @@ module Nokogiri
|
|
71
71
|
|
72
72
|
def visit_id node
|
73
73
|
node.value.first =~ /^#(.*)$/
|
74
|
-
"@id
|
74
|
+
"@id='#{$1}'"
|
75
75
|
end
|
76
76
|
|
77
77
|
def visit_attribute_condition node
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
78
|
+
attribute = if (node.value.first.type == :FUNCTION) or (node.value.first.value.first =~ /::/)
|
79
|
+
''
|
80
|
+
else
|
81
|
+
'@'
|
82
|
+
end
|
83
83
|
attribute += node.value.first.accept(self)
|
84
84
|
|
85
|
-
#
|
85
|
+
# non-standard. attributes starting with '@'
|
86
86
|
attribute.gsub!(/^@@/, '@')
|
87
87
|
|
88
88
|
return attribute unless node.value.length == 3
|
@@ -90,29 +90,30 @@ module Nokogiri
|
|
90
90
|
value = node.value.last
|
91
91
|
value = "'#{value}'" if value !~ /^['"]/
|
92
92
|
|
93
|
+
# quoted values - see test_attribute_value_with_quotes in test/css/test_parser.rb
|
93
94
|
if (value[0]==value[-1]) && %q{"'}.include?(value[0])
|
94
95
|
str_value = value[1..-2]
|
95
96
|
if str_value.include?(value[0])
|
96
|
-
value = 'concat("' + str_value.split('"', -1).join(%q{",
|
97
|
+
value = 'concat("' + str_value.split('"', -1).join(%q{",'"',"}) + '","")'
|
97
98
|
end
|
98
99
|
end
|
99
100
|
|
100
101
|
case node.value[1]
|
101
102
|
when :equal
|
102
|
-
attribute + "
|
103
|
+
attribute + "=" + "#{value}"
|
103
104
|
when :not_equal
|
104
|
-
attribute + "
|
105
|
+
attribute + "!=" + "#{value}"
|
105
106
|
when :substring_match
|
106
|
-
"contains(#{attribute}
|
107
|
+
"contains(#{attribute},#{value})"
|
107
108
|
when :prefix_match
|
108
|
-
"starts-with(#{attribute}
|
109
|
+
"starts-with(#{attribute},#{value})"
|
109
110
|
when :dash_match
|
110
|
-
"#{attribute}
|
111
|
+
"#{attribute}=#{value} or starts-with(#{attribute},concat(#{value},'-'))"
|
111
112
|
when :includes
|
112
|
-
|
113
|
+
value = value[1..-2] # strip quotes
|
114
|
+
css_class(attribute, value)
|
113
115
|
when :suffix_match
|
114
|
-
"substring(#{attribute},
|
115
|
-
"string-length(#{value}) + 1, string-length(#{value})) = #{value}"
|
116
|
+
"substring(#{attribute},string-length(#{attribute})-string-length(#{value})+1,string-length(#{value}))=#{value}"
|
116
117
|
else
|
117
118
|
attribute + " #{node.value[1]} " + "#{value}"
|
118
119
|
end
|
@@ -126,14 +127,14 @@ module Nokogiri
|
|
126
127
|
return self.send(msg, node) if self.respond_to?(msg)
|
127
128
|
|
128
129
|
case node.value.first
|
129
|
-
when "first" then "position()
|
130
|
-
when "first-child" then "count(preceding-sibling::*)
|
131
|
-
when "last" then "position()
|
132
|
-
when "last-child" then "count(following-sibling::*)
|
133
|
-
when "first-of-type" then "position()
|
134
|
-
when "last-of-type" then "position()
|
135
|
-
when "only-child" then "count(preceding-sibling::*)
|
136
|
-
when "only-of-type" then "last()
|
130
|
+
when "first" then "position()=1"
|
131
|
+
when "first-child" then "count(preceding-sibling::*)=0"
|
132
|
+
when "last" then "position()=last()"
|
133
|
+
when "last-child" then "count(following-sibling::*)=0"
|
134
|
+
when "first-of-type" then "position()=1"
|
135
|
+
when "last-of-type" then "position()=last()"
|
136
|
+
when "only-child" then "count(preceding-sibling::*)=0 and count(following-sibling::*)=0"
|
137
|
+
when "only-of-type" then "last()=1"
|
137
138
|
when "empty" then "not(node())"
|
138
139
|
when "parent" then "node()"
|
139
140
|
when "root" then "not(parent::*)"
|
@@ -144,7 +145,7 @@ module Nokogiri
|
|
144
145
|
end
|
145
146
|
|
146
147
|
def visit_class_condition node
|
147
|
-
"
|
148
|
+
css_class("@class", node.value.first)
|
148
149
|
end
|
149
150
|
|
150
151
|
def visit_combinator node
|
@@ -181,25 +182,26 @@ module Nokogiri
|
|
181
182
|
node.accept(self)
|
182
183
|
end
|
183
184
|
|
184
|
-
|
185
|
+
private
|
186
|
+
|
185
187
|
def nth node, options={}
|
186
188
|
raise ArgumentError, "expected an+b node to contain 4 tokens, but is #{node.value.inspect}" unless node.value.size == 4
|
187
189
|
|
188
190
|
a, b = read_a_and_positive_b node.value
|
189
191
|
position = if options[:child]
|
190
|
-
options[:last] ? "(count(following-sibling::*)
|
192
|
+
options[:last] ? "(count(following-sibling::*)+1)" : "(count(preceding-sibling::*)+1)"
|
191
193
|
else
|
192
194
|
options[:last] ? "(last()-position()+1)" : "position()"
|
193
195
|
end
|
194
196
|
|
195
197
|
if b.zero?
|
196
|
-
"(#{position} mod #{a})
|
198
|
+
"(#{position} mod #{a})=0"
|
197
199
|
else
|
198
200
|
compare = a < 0 ? "<=" : ">="
|
199
201
|
if a.abs == 1
|
200
|
-
"#{position}
|
202
|
+
"#{position}#{compare}#{b}"
|
201
203
|
else
|
202
|
-
"(#{position}
|
204
|
+
"(#{position}#{compare}#{b}) and (((#{position}-#{b}) mod #{a.abs})=0)"
|
203
205
|
end
|
204
206
|
end
|
205
207
|
end
|
@@ -227,6 +229,32 @@ module Nokogiri
|
|
227
229
|
end =~ /(nth|first|last|only)-of-type(\()?/
|
228
230
|
end
|
229
231
|
end
|
232
|
+
|
233
|
+
# use only ordinary xpath functions
|
234
|
+
def css_class_standard(hay, needle)
|
235
|
+
"contains(concat(' ',normalize-space(#{hay}),' '),' #{needle} ')"
|
236
|
+
end
|
237
|
+
|
238
|
+
# use the builtin implementation
|
239
|
+
def css_class_builtin(hay, needle)
|
240
|
+
"nokogiri-builtin:css-class(#{hay},'#{needle}')"
|
241
|
+
end
|
242
|
+
|
243
|
+
alias_method :css_class, :css_class_standard
|
244
|
+
end
|
245
|
+
|
246
|
+
class XPathVisitorAlwaysUseBuiltins < XPathVisitor # :nodoc:
|
247
|
+
private
|
248
|
+
alias_method :css_class, :css_class_builtin
|
249
|
+
end
|
250
|
+
|
251
|
+
class XPathVisitorOptimallyUseBuiltins < XPathVisitor # :nodoc:
|
252
|
+
private
|
253
|
+
if Nokogiri.uses_libxml?
|
254
|
+
alias_method :css_class, :css_class_builtin
|
255
|
+
else
|
256
|
+
alias_method :css_class, :css_class_standard
|
257
|
+
end
|
230
258
|
end
|
231
259
|
end
|
232
260
|
end
|