nokogiri 1.12.5 → 1.13.6

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of nokogiri might be problematic. Click here for more details.

Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/README.md +9 -7
  4. data/bin/nokogiri +63 -50
  5. data/dependencies.yml +13 -64
  6. data/ext/nokogiri/extconf.rb +64 -44
  7. data/ext/nokogiri/html4_sax_parser_context.c +2 -3
  8. data/ext/nokogiri/xml_document.c +35 -35
  9. data/ext/nokogiri/xml_document_fragment.c +0 -2
  10. data/ext/nokogiri/xml_dtd.c +2 -2
  11. data/ext/nokogiri/xml_encoding_handler.c +25 -11
  12. data/ext/nokogiri/xml_node.c +638 -333
  13. data/ext/nokogiri/xml_reader.c +37 -11
  14. data/ext/nokogiri/xml_sax_parser_context.c +10 -3
  15. data/ext/nokogiri/xml_xpath_context.c +72 -49
  16. data/ext/nokogiri/xslt_stylesheet.c +107 -9
  17. data/gumbo-parser/src/parser.c +0 -11
  18. data/lib/nokogiri/class_resolver.rb +67 -0
  19. data/lib/nokogiri/css/node.rb +9 -8
  20. data/lib/nokogiri/css/parser.rb +360 -341
  21. data/lib/nokogiri/css/parser.y +249 -244
  22. data/lib/nokogiri/css/parser_extras.rb +22 -20
  23. data/lib/nokogiri/css/syntax_error.rb +1 -0
  24. data/lib/nokogiri/css/tokenizer.rb +4 -3
  25. data/lib/nokogiri/css/tokenizer.rex +3 -2
  26. data/lib/nokogiri/css/xpath_visitor.rb +179 -82
  27. data/lib/nokogiri/css.rb +38 -6
  28. data/lib/nokogiri/decorators/slop.rb +8 -7
  29. data/lib/nokogiri/extension.rb +1 -1
  30. data/lib/nokogiri/gumbo.rb +1 -0
  31. data/lib/nokogiri/html.rb +16 -10
  32. data/lib/nokogiri/html4/builder.rb +1 -0
  33. data/lib/nokogiri/html4/document.rb +88 -77
  34. data/lib/nokogiri/html4/document_fragment.rb +11 -7
  35. data/lib/nokogiri/html4/element_description.rb +1 -0
  36. data/lib/nokogiri/html4/element_description_defaults.rb +426 -520
  37. data/lib/nokogiri/html4/entity_lookup.rb +2 -1
  38. data/lib/nokogiri/html4/sax/parser.rb +5 -2
  39. data/lib/nokogiri/html4/sax/parser_context.rb +1 -0
  40. data/lib/nokogiri/html4/sax/push_parser.rb +7 -7
  41. data/lib/nokogiri/html4.rb +11 -5
  42. data/lib/nokogiri/html5/document.rb +27 -10
  43. data/lib/nokogiri/html5/document_fragment.rb +5 -2
  44. data/lib/nokogiri/html5/node.rb +10 -3
  45. data/lib/nokogiri/html5.rb +69 -64
  46. data/lib/nokogiri/jruby/dependencies.rb +10 -9
  47. data/lib/nokogiri/syntax_error.rb +1 -0
  48. data/lib/nokogiri/version/constant.rb +2 -1
  49. data/lib/nokogiri/version/info.rb +20 -13
  50. data/lib/nokogiri/version.rb +1 -0
  51. data/lib/nokogiri/xml/attr.rb +5 -3
  52. data/lib/nokogiri/xml/attribute_decl.rb +2 -1
  53. data/lib/nokogiri/xml/builder.rb +34 -32
  54. data/lib/nokogiri/xml/cdata.rb +2 -1
  55. data/lib/nokogiri/xml/character_data.rb +1 -0
  56. data/lib/nokogiri/xml/document.rb +144 -103
  57. data/lib/nokogiri/xml/document_fragment.rb +41 -38
  58. data/lib/nokogiri/xml/dtd.rb +3 -2
  59. data/lib/nokogiri/xml/element_content.rb +1 -0
  60. data/lib/nokogiri/xml/element_decl.rb +2 -1
  61. data/lib/nokogiri/xml/entity_decl.rb +3 -2
  62. data/lib/nokogiri/xml/entity_reference.rb +1 -0
  63. data/lib/nokogiri/xml/namespace.rb +2 -0
  64. data/lib/nokogiri/xml/node/save_options.rb +8 -4
  65. data/lib/nokogiri/xml/node.rb +521 -351
  66. data/lib/nokogiri/xml/node_set.rb +50 -54
  67. data/lib/nokogiri/xml/notation.rb +12 -0
  68. data/lib/nokogiri/xml/parse_options.rb +12 -7
  69. data/lib/nokogiri/xml/pp/character_data.rb +8 -6
  70. data/lib/nokogiri/xml/pp/node.rb +24 -26
  71. data/lib/nokogiri/xml/pp.rb +1 -0
  72. data/lib/nokogiri/xml/processing_instruction.rb +2 -1
  73. data/lib/nokogiri/xml/reader.rb +17 -19
  74. data/lib/nokogiri/xml/relax_ng.rb +1 -0
  75. data/lib/nokogiri/xml/sax/document.rb +20 -19
  76. data/lib/nokogiri/xml/sax/parser.rb +37 -34
  77. data/lib/nokogiri/xml/sax/parser_context.rb +7 -3
  78. data/lib/nokogiri/xml/sax/push_parser.rb +5 -5
  79. data/lib/nokogiri/xml/sax.rb +1 -0
  80. data/lib/nokogiri/xml/schema.rb +7 -6
  81. data/lib/nokogiri/xml/searchable.rb +93 -62
  82. data/lib/nokogiri/xml/syntax_error.rb +5 -4
  83. data/lib/nokogiri/xml/text.rb +1 -0
  84. data/lib/nokogiri/xml/xpath/syntax_error.rb +2 -1
  85. data/lib/nokogiri/xml/xpath.rb +12 -0
  86. data/lib/nokogiri/xml/xpath_context.rb +2 -3
  87. data/lib/nokogiri/xml.rb +4 -3
  88. data/lib/nokogiri/xslt/stylesheet.rb +1 -0
  89. data/lib/nokogiri/xslt.rb +21 -13
  90. data/lib/nokogiri.rb +19 -16
  91. data/lib/xsd/xmlparser/nokogiri.rb +25 -24
  92. data/patches/libxml2/0004-use-glibc-strlen.patch +3 -3
  93. data/patches/libxml2/0006-update-automake-files-for-arm64.patch +2443 -1914
  94. data/patches/libxml2/0008-htmlParseComment-handle-abruptly-closed-comments.patch +61 -0
  95. data/patches/libxml2/0009-allow-wildcard-namespaces.patch +77 -0
  96. data/patches/libxslt/0001-update-automake-files-for-arm64.patch +2445 -1919
  97. data/ports/archives/libxml2-2.9.14.tar.xz +0 -0
  98. data/ports/archives/libxslt-1.1.35.tar.xz +0 -0
  99. metadata +109 -31
  100. data/patches/libxml2/0007-Fix-XPath-recursion-limit.patch +0 -31
  101. data/patches/libxslt/0002-Fix-xml2-config-check-in-configure-script.patch +0 -19
  102. data/ports/archives/libxml2-2.9.12.tar.gz +0 -0
  103. data/ports/archives/libxslt-1.1.34.tar.gz +0 -0
@@ -5,256 +5,261 @@ token COMMA NUMBER PREFIXMATCH SUFFIXMATCH SUBSTRINGMATCH TILDE NOT_EQUAL
5
5
  token SLASH DOUBLESLASH NOT EQUAL RPAREN LSQUARE RSQUARE HAS
6
6
 
7
7
  rule
8
- selector
9
- : selector COMMA simple_selector_1toN {
10
- result = [val.first, val.last].flatten
11
- }
12
- | prefixless_combinator_selector { result = val.flatten }
13
- | optional_S simple_selector_1toN { result = [val.last].flatten }
14
- ;
15
- combinator
16
- : PLUS { result = :DIRECT_ADJACENT_SELECTOR }
17
- | GREATER { result = :CHILD_SELECTOR }
18
- | TILDE { result = :FOLLOWING_SELECTOR }
19
- | DOUBLESLASH { result = :DESCENDANT_SELECTOR }
20
- | SLASH { result = :CHILD_SELECTOR }
21
- ;
22
- simple_selector
23
- : element_name hcap_0toN {
24
- result = if val[1].nil?
25
- val.first
26
- else
27
- Node.new(:CONDITIONAL_SELECTOR, [val.first, val[1]])
28
- end
29
- }
30
- | function
31
- | function pseudo {
32
- result = Node.new(:CONDITIONAL_SELECTOR, val)
33
- }
34
- | function attrib {
35
- result = Node.new(:CONDITIONAL_SELECTOR, val)
36
- }
37
- | hcap_1toN {
38
- result = Node.new(:CONDITIONAL_SELECTOR,
39
- [Node.new(:ELEMENT_NAME, ['*']), val.first]
40
- )
41
- }
42
- ;
43
- prefixless_combinator_selector
44
- : combinator simple_selector_1toN {
45
- result = Node.new(val.first, [nil, val.last])
46
- }
47
- ;
48
- simple_selector_1toN
49
- : simple_selector combinator simple_selector_1toN {
50
- result = Node.new(val[1], [val.first, val.last])
51
- }
52
- | simple_selector S simple_selector_1toN {
53
- result = Node.new(:DESCENDANT_SELECTOR, [val.first, val.last])
54
- }
55
- | simple_selector
56
- ;
57
- class
58
- : '.' IDENT { result = Node.new(:CLASS_CONDITION, [unescape_css_identifier(val[1])]) }
59
- ;
60
- element_name
61
- : namespaced_ident
62
- | '*' { result = Node.new(:ELEMENT_NAME, val) }
63
- ;
64
- namespaced_ident
65
- : namespace '|' IDENT {
66
- result = Node.new(:ELEMENT_NAME,
67
- [[val.first, val.last].compact.join(':')]
68
- )
69
- }
70
- | IDENT {
71
- name = @namespaces.key?('xmlns') ? "xmlns:#{val.first}" : val.first
72
- result = Node.new(:ELEMENT_NAME, [name])
73
- }
74
- ;
75
- namespace
76
- : IDENT { result = val[0] }
77
- |
78
- ;
79
- attrib
80
- : LSQUARE attrib_name attrib_val_0or1 RSQUARE {
81
- result = Node.new(:ATTRIBUTE_CONDITION,
82
- [val[1]] + (val[2] || [])
83
- )
84
- }
85
- | LSQUARE function attrib_val_0or1 RSQUARE {
86
- result = Node.new(:ATTRIBUTE_CONDITION,
87
- [val[1]] + (val[2] || [])
88
- )
89
- }
90
- | LSQUARE NUMBER RSQUARE {
91
- # non-standard, from hpricot
92
- result = Node.new(:PSEUDO_CLASS,
93
- [Node.new(:FUNCTION, ['nth-child(', val[1]])]
94
- )
95
- }
96
- ;
97
- attrib_name
98
- : namespace '|' IDENT {
99
- result = Node.new(:ELEMENT_NAME,
100
- [[val.first, val.last].compact.join(':')]
101
- )
102
- }
103
- | IDENT {
104
- # Default namespace is not applied to attributes.
105
- # So we don't add prefix "xmlns:" as in namespaced_ident.
106
- result = Node.new(:ELEMENT_NAME, [val.first])
107
- }
108
- ;
109
- function
110
- : FUNCTION RPAREN {
111
- result = Node.new(:FUNCTION, [val.first.strip])
112
- }
113
- | FUNCTION expr RPAREN {
114
- result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
115
- }
116
- | FUNCTION nth RPAREN {
117
- result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
118
- }
119
- | NOT expr RPAREN {
120
- result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
121
- }
122
- | HAS selector RPAREN {
123
- result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
124
- }
125
- ;
126
- expr
127
- : NUMBER COMMA expr { result = [val.first, val.last] }
128
- | STRING COMMA expr { result = [val.first, val.last] }
129
- | IDENT COMMA expr { result = [val.first, val.last] }
130
- | NUMBER
131
- | STRING
132
- | IDENT # even, odd
133
- {
134
- case val[0]
135
- when 'even'
136
- result = Node.new(:NTH, ['2','n','+','0'])
137
- when 'odd'
138
- result = Node.new(:NTH, ['2','n','+','1'])
139
- when 'n'
140
- result = Node.new(:NTH, ['1','n','+','0'])
141
- else
142
- # non-standard to support custom functions:
143
- # assert_xpath("//a[foo(., @href)]", @parser.parse('a:foo(@href)'))
144
- # assert_xpath("//a[foo(., @a, b)]", @parser.parse('a:foo(@a, b)'))
145
- # assert_xpath("//a[foo(., a, 10)]", @parser.parse('a:foo(a, 10)'))
146
- result = val
147
- end
148
- }
149
- ;
150
- nth
151
- : NUMBER IDENT PLUS NUMBER # 5n+3 -5n+3
152
- {
153
- if val[1] == 'n'
154
- result = Node.new(:NTH, val)
155
- else
156
- raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
157
- end
158
- }
159
- | IDENT PLUS NUMBER { # n+3, -n+3
160
- if val[0] == 'n'
161
- val.unshift("1")
162
- result = Node.new(:NTH, val)
163
- elsif val[0] == '-n'
164
- val[0] = 'n'
165
- val.unshift("-1")
166
- result = Node.new(:NTH, val)
167
- else
168
- raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
169
- end
170
- }
171
- | NUMBER IDENT { # 5n, -5n, 10n-1
172
- n = val[1]
173
- if n[0, 2] == 'n-'
174
- val[1] = 'n'
175
- val << "-"
176
- # b is contained in n as n is the string "n-b"
177
- val << n[2, n.size]
178
- result = Node.new(:NTH, val)
179
- elsif n == 'n'
180
- val << "+"
181
- val << "0"
182
- result = Node.new(:NTH, val)
183
- else
184
- raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
185
- end
186
- }
187
- ;
188
- pseudo
189
- : ':' function {
190
- result = Node.new(:PSEUDO_CLASS, [val[1]])
191
- }
192
- | ':' IDENT { result = Node.new(:PSEUDO_CLASS, [val[1]]) }
193
- ;
194
- hcap_0toN
195
- : hcap_1toN
196
- |
197
- ;
198
- hcap_1toN
199
- : attribute_id hcap_1toN {
200
- result = Node.new(:COMBINATOR, val)
201
- }
202
- | class hcap_1toN {
203
- result = Node.new(:COMBINATOR, val)
204
- }
205
- | attrib hcap_1toN {
206
- result = Node.new(:COMBINATOR, val)
207
- }
208
- | pseudo hcap_1toN {
209
- result = Node.new(:COMBINATOR, val)
210
- }
211
- | negation hcap_1toN {
212
- result = Node.new(:COMBINATOR, val)
213
- }
214
- | attribute_id
215
- | class
216
- | attrib
217
- | pseudo
218
- | negation
219
- ;
220
- attribute_id
221
- : HASH { result = Node.new(:ID, [unescape_css_identifier(val.first)]) }
222
- ;
223
- attrib_val_0or1
224
- : eql_incl_dash IDENT { result = [val.first, unescape_css_identifier(val[1])] }
225
- | eql_incl_dash STRING { result = [val.first, unescape_css_string(val[1])] }
226
- | eql_incl_dash NUMBER { result = [val.first, val[1]] }
227
- |
228
- ;
229
- eql_incl_dash
230
- : EQUAL { result = :equal }
231
- | PREFIXMATCH { result = :prefix_match }
232
- | SUFFIXMATCH { result = :suffix_match }
233
- | SUBSTRINGMATCH { result = :substring_match }
234
- | NOT_EQUAL { result = :not_equal }
235
- | INCLUDES { result = :includes }
236
- | DASHMATCH { result = :dash_match }
237
- ;
238
- negation
239
- : NOT negation_arg RPAREN {
240
- result = Node.new(:NOT, [val[1]])
241
- }
242
- ;
243
- negation_arg
244
- : element_name
245
- | element_name hcap_1toN
246
- | hcap_1toN
247
- ;
248
- optional_S
249
- : S
250
- |
251
- ;
8
+ selector:
9
+ selector COMMA simple_selector_1toN {
10
+ result = [val[0], val[2]].flatten
11
+ }
12
+ | prefixless_combinator_selector { result = val.flatten }
13
+ | optional_S simple_selector_1toN { result = [val[1]].flatten }
14
+ ;
15
+
16
+ combinator:
17
+ PLUS { result = :DIRECT_ADJACENT_SELECTOR }
18
+ | GREATER { result = :CHILD_SELECTOR }
19
+ | TILDE { result = :FOLLOWING_SELECTOR }
20
+ | DOUBLESLASH { result = :DESCENDANT_SELECTOR }
21
+ | SLASH { result = :CHILD_SELECTOR }
22
+ ;
23
+
24
+ xpath_attribute_name:
25
+ '@' IDENT { result = val[1] }
26
+ ;
27
+
28
+ xpath_attribute:
29
+ xpath_attribute_name { result = Node.new(:ATTRIB_NAME, [val[0]]) }
30
+ ;
31
+
32
+ simple_selector:
33
+ element_name hcap_0toN {
34
+ result = if val[1].nil?
35
+ val[0]
36
+ else
37
+ Node.new(:CONDITIONAL_SELECTOR, [val[0], val[1]])
38
+ end
39
+ }
40
+ | function
41
+ | function pseudo { result = Node.new(:CONDITIONAL_SELECTOR, val) }
42
+ | function attrib { result = Node.new(:CONDITIONAL_SELECTOR, val) }
43
+ | hcap_1toN { result = Node.new(:CONDITIONAL_SELECTOR, [Node.new(:ELEMENT_NAME, ['*']), val[0]]) }
44
+ | xpath_attribute
45
+ ;
46
+
47
+ prefixless_combinator_selector:
48
+ combinator simple_selector_1toN { result = Node.new(val[0], [nil, val[1]]) }
49
+ ;
50
+
51
+ simple_selector_1toN:
52
+ simple_selector combinator simple_selector_1toN { result = Node.new(val[1], [val[0], val[2]]) }
53
+ | simple_selector S simple_selector_1toN { result = Node.new(:DESCENDANT_SELECTOR, [val[0], val[2]]) }
54
+ | simple_selector
55
+ ;
56
+
57
+ class:
58
+ '.' IDENT { result = Node.new(:CLASS_CONDITION, [unescape_css_identifier(val[1])]) }
59
+ ;
60
+
61
+ element_name:
62
+ namespaced_ident
63
+ | '*' { result = Node.new(:ELEMENT_NAME, val) }
64
+ ;
65
+
66
+ namespaced_ident:
67
+ namespace '|' IDENT { result = Node.new(:ELEMENT_NAME, [[val[0], val[2]].compact.join(':')]) }
68
+ | IDENT {
69
+ name = @namespaces.key?('xmlns') ? "xmlns:#{val[0]}" : val[0]
70
+ result = Node.new(:ELEMENT_NAME, [name])
71
+ }
72
+ ;
73
+
74
+ namespace:
75
+ IDENT { result = val[0] }
76
+ |
77
+ ;
78
+
79
+ attrib:
80
+ LSQUARE attrib_name attrib_val_0or1 RSQUARE {
81
+ result = Node.new(:ATTRIBUTE_CONDITION, [val[1]] + (val[2] || []))
82
+ }
83
+ | LSQUARE function attrib_val_0or1 RSQUARE {
84
+ result = Node.new(:ATTRIBUTE_CONDITION, [val[1]] + (val[2] || []))
85
+ }
86
+ | LSQUARE NUMBER RSQUARE {
87
+ result = Node.new(:PSEUDO_CLASS, [Node.new(:FUNCTION, ['nth-child(', val[1]])])
88
+ }
89
+ ;
90
+
91
+ attrib_name:
92
+ namespace '|' IDENT { result = Node.new(:ATTRIB_NAME, [[val[0], val[2]].compact.join(':')]) }
93
+ | IDENT { result = Node.new(:ATTRIB_NAME, [val[0]]) }
94
+ | xpath_attribute
95
+ ;
96
+
97
+ function:
98
+ FUNCTION RPAREN {
99
+ result = Node.new(:FUNCTION, [val[0].strip])
100
+ }
101
+ | FUNCTION expr RPAREN {
102
+ result = Node.new(:FUNCTION, [val[0].strip, val[1]].flatten)
103
+ }
104
+ | FUNCTION nth RPAREN {
105
+ result = Node.new(:FUNCTION, [val[0].strip, val[1]].flatten)
106
+ }
107
+ | NOT expr RPAREN {
108
+ result = Node.new(:FUNCTION, [val[0].strip, val[1]].flatten)
109
+ }
110
+ | HAS selector RPAREN {
111
+ result = Node.new(:FUNCTION, [val[0].strip, val[1]].flatten)
112
+ }
113
+ ;
114
+
115
+ expr:
116
+ NUMBER COMMA expr { result = [val[0], val[2]] }
117
+ | STRING COMMA expr { result = [val[0], val[2]] }
118
+ | IDENT COMMA expr { result = [val[0], val[2]] }
119
+ | xpath_attribute COMMA expr { result = [val[0], val[2]] }
120
+ | NUMBER
121
+ | STRING
122
+ | IDENT {
123
+ case val[0]
124
+ when 'even'
125
+ result = Node.new(:NTH, ['2','n','+','0'])
126
+ when 'odd'
127
+ result = Node.new(:NTH, ['2','n','+','1'])
128
+ when 'n'
129
+ result = Node.new(:NTH, ['1','n','+','0'])
130
+ else
131
+ result = val
132
+ end
133
+ }
134
+ | xpath_attribute
135
+ ;
136
+
137
+ nth:
138
+ NUMBER IDENT PLUS NUMBER # 5n+3 -5n+3
139
+ {
140
+ if val[1] == 'n'
141
+ result = Node.new(:NTH, val)
142
+ else
143
+ raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
144
+ end
145
+ }
146
+ | IDENT PLUS NUMBER { # n+3, -n+3
147
+ if val[0] == 'n'
148
+ val.unshift("1")
149
+ result = Node.new(:NTH, val)
150
+ elsif val[0] == '-n'
151
+ val[0] = 'n'
152
+ val.unshift("-1")
153
+ result = Node.new(:NTH, val)
154
+ else
155
+ raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
156
+ end
157
+ }
158
+ | NUMBER IDENT { # 5n, -5n, 10n-1
159
+ n = val[1]
160
+ if n[0, 2] == 'n-'
161
+ val[1] = 'n'
162
+ val << "-"
163
+ # b is contained in n as n is the string "n-b"
164
+ val << n[2, n.size]
165
+ result = Node.new(:NTH, val)
166
+ elsif n == 'n'
167
+ val << "+"
168
+ val << "0"
169
+ result = Node.new(:NTH, val)
170
+ else
171
+ raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
172
+ end
173
+ }
174
+ ;
175
+
176
+ pseudo:
177
+ ':' function {
178
+ result = Node.new(:PSEUDO_CLASS, [val[1]])
179
+ }
180
+ | ':' IDENT { result = Node.new(:PSEUDO_CLASS, [val[1]]) }
181
+ ;
182
+
183
+ hcap_0toN:
184
+ hcap_1toN
185
+ |
186
+ ;
187
+
188
+ hcap_1toN:
189
+ attribute_id hcap_1toN {
190
+ result = Node.new(:COMBINATOR, val)
191
+ }
192
+ | class hcap_1toN {
193
+ result = Node.new(:COMBINATOR, val)
194
+ }
195
+ | attrib hcap_1toN {
196
+ result = Node.new(:COMBINATOR, val)
197
+ }
198
+ | pseudo hcap_1toN {
199
+ result = Node.new(:COMBINATOR, val)
200
+ }
201
+ | negation hcap_1toN {
202
+ result = Node.new(:COMBINATOR, val)
203
+ }
204
+ | attribute_id
205
+ | class
206
+ | attrib
207
+ | pseudo
208
+ | negation
209
+ ;
210
+
211
+ attribute_id:
212
+ HASH { result = Node.new(:ID, [unescape_css_identifier(val[0])]) }
213
+ ;
214
+
215
+ attrib_val_0or1:
216
+ eql_incl_dash IDENT { result = [val[0], unescape_css_identifier(val[1])] }
217
+ | eql_incl_dash STRING { result = [val[0], unescape_css_string(val[1])] }
218
+ | eql_incl_dash NUMBER { result = [val[0], val[1]] }
219
+ |
220
+ ;
221
+
222
+ eql_incl_dash:
223
+ EQUAL { result = :equal }
224
+ | PREFIXMATCH { result = :prefix_match }
225
+ | SUFFIXMATCH { result = :suffix_match }
226
+ | SUBSTRINGMATCH { result = :substring_match }
227
+ | NOT_EQUAL { result = :not_equal }
228
+ | INCLUDES { result = :includes }
229
+ | DASHMATCH { result = :dash_match }
230
+ ;
231
+
232
+ negation:
233
+ NOT negation_arg RPAREN {
234
+ result = Node.new(:NOT, [val[1]])
235
+ }
236
+ ;
237
+
238
+ negation_arg:
239
+ element_name
240
+ | element_name hcap_1toN
241
+ | hcap_1toN
242
+ ;
243
+
244
+ optional_S:
245
+ S
246
+ |
247
+ ;
248
+
252
249
  end
253
250
 
254
251
  ---- header
255
252
 
256
253
  require_relative "parser_extras"
257
254
 
255
+ module Nokogiri
256
+ module CSS
257
+ # :nodoc: all
258
+ class Parser < Racc::Parser
259
+ end
260
+ end
261
+ end
262
+
258
263
  ---- inner
259
264
 
260
265
  def unescape_css_identifier(identifier)
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "thread"
3
4
 
4
5
  module Nokogiri
5
6
  module CSS
6
- class Parser < Racc::Parser
7
+ class Parser < Racc::Parser # :nodoc:
7
8
  CACHE_SWITCH_NAME = :nokogiri_css_parser_cache_is_off
8
9
 
9
10
  @cache = {}
@@ -16,19 +17,21 @@ module Nokogiri
16
17
  end
17
18
 
18
19
  # Set a thread-local boolean to turn cacheing on and off. Truthy values turn the cache on, falsey values turn the cache off.
19
- def set_cache(value)
20
+ def set_cache(value) # rubocop:disable Naming/AccessorMethodName
20
21
  Thread.current[CACHE_SWITCH_NAME] = !value
21
22
  end
22
23
 
23
24
  # Get the css selector in +string+ from the cache
24
25
  def [](string)
25
- return unless cache_on?
26
+ return nil unless cache_on?
27
+
26
28
  @mutex.synchronize { @cache[string] }
27
29
  end
28
30
 
29
31
  # Set the css selector in +string+ in the cache to +value+
30
32
  def []=(string, value)
31
33
  return value unless cache_on?
34
+
32
35
  @mutex.synchronize { @cache[string] = value }
33
36
  end
34
37
 
@@ -46,10 +49,10 @@ module Nokogiri
46
49
  # Execute +block+ without cache
47
50
  def without_cache(&block)
48
51
  original_cache_setting = cache_on?
49
- set_cache false
50
- block.call
52
+ set_cache(false)
53
+ yield
51
54
  ensure
52
- set_cache original_cache_setting
55
+ set_cache(original_cache_setting)
53
56
  end
54
57
  end
55
58
 
@@ -61,7 +64,7 @@ module Nokogiri
61
64
  end
62
65
 
63
66
  def parse(string)
64
- @tokenizer.scan_setup string
67
+ @tokenizer.scan_setup(string)
65
68
  do_parse
66
69
  end
67
70
 
@@ -70,24 +73,23 @@ module Nokogiri
70
73
  end
71
74
 
72
75
  # Get the xpath for +string+ using +options+
73
- def xpath_for(string, options = {})
74
- key = "#{string}#{options[:ns]}#{options[:prefix]}"
75
- v = self.class[key]
76
- return v if v
77
-
78
- args = [
79
- options[:prefix] || "//",
80
- options[:visitor] || XPathVisitor.new,
81
- ]
82
- self.class[key] = parse(string).map { |ast|
83
- ast.to_xpath(*args)
84
- }
76
+ def xpath_for(string, prefix, visitor)
77
+ key = cache_key(string, prefix, visitor)
78
+ self.class[key] ||= parse(string).map do |ast|
79
+ ast.to_xpath(prefix, visitor)
80
+ end
85
81
  end
86
82
 
87
83
  # On CSS parser error, raise an exception
88
84
  def on_error(error_token_id, error_value, value_stack)
89
85
  after = value_stack.compact.last
90
- raise SyntaxError.new("unexpected '#{error_value}' after '#{after}'")
86
+ raise SyntaxError, "unexpected '#{error_value}' after '#{after}'"
87
+ end
88
+
89
+ def cache_key(query, prefix, visitor)
90
+ if self.class.cache_on?
91
+ [query, prefix, @namespaces, visitor.config]
92
+ end
91
93
  end
92
94
  end
93
95
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative "../syntax_error"
3
4
  module Nokogiri
4
5
  module CSS
@@ -7,7 +7,8 @@
7
7
 
8
8
  module Nokogiri
9
9
  module CSS
10
- class Tokenizer # :nodoc:
10
+ # :nodoc: all
11
+ class Tokenizer
11
12
  require 'strscan'
12
13
 
13
14
  class ScanError < StandardError ; end
@@ -62,10 +63,10 @@ class Tokenizer # :nodoc:
62
63
  when (text = @ss.scan(/has\([\s]*/))
63
64
  action { [:HAS, text] }
64
65
 
65
- when (text = @ss.scan(/[-@]?([_A-Za-z]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*\([\s]*/))
66
+ when (text = @ss.scan(/-?([_A-Za-z]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*\([\s]*/))
66
67
  action { [:FUNCTION, text] }
67
68
 
68
- when (text = @ss.scan(/[-@]?([_A-Za-z]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*/))
69
+ when (text = @ss.scan(/-?([_A-Za-z]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*/))
69
70
  action { [:IDENT, text] }
70
71
 
71
72
  when (text = @ss.scan(/\#([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])+/))
@@ -1,6 +1,7 @@
1
1
  module Nokogiri
2
2
  module CSS
3
- class Tokenizer # :nodoc:
3
+ # :nodoc: all
4
+ class Tokenizer
4
5
 
5
6
  macro
6
7
  nl \n|\r\n|\r|\f
@@ -12,7 +13,7 @@ macro
12
13
  escape {unicode}|\\[^\n\r\f0-9A-Fa-f]
13
14
  nmchar [_A-Za-z0-9-]|{nonascii}|{escape}
14
15
  nmstart [_A-Za-z]|{nonascii}|{escape}
15
- ident [-@]?({nmstart})({nmchar})*
16
+ ident -?({nmstart})({nmchar})*
16
17
  name ({nmchar})+
17
18
  string1 "([^\n\r\f"]|{nl}|{nonascii}|{escape})*(?<!\\)(?:\\{2})*"
18
19
  string2 '([^\n\r\f']|{nl}|{nonascii}|{escape})*(?<!\\)(?:\\{2})*'