nokogiri 1.4.3.1 → 1.4.4

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 (57) hide show
  1. data/CHANGELOG.ja.rdoc +26 -0
  2. data/CHANGELOG.rdoc +26 -0
  3. data/Manifest.txt +3 -0
  4. data/README.ja.rdoc +0 -4
  5. data/README.rdoc +0 -4
  6. data/Rakefile +1 -0
  7. data/bin/nokogiri +6 -1
  8. data/ext/nokogiri/depend +358 -32
  9. data/ext/nokogiri/extconf.rb +1 -3
  10. data/ext/nokogiri/nokogiri.c +2 -0
  11. data/ext/nokogiri/nokogiri.h +7 -0
  12. data/ext/nokogiri/xml_dtd.c +2 -2
  13. data/ext/nokogiri/xml_io.c +2 -2
  14. data/ext/nokogiri/xml_node.c +31 -6
  15. data/ext/nokogiri/xml_node_set.c +1 -1
  16. data/ext/nokogiri/xml_sax_parser.c +1 -1
  17. data/ext/nokogiri/xml_sax_parser_context.c +40 -0
  18. data/ext/nokogiri/xml_xpath_context.c +33 -2
  19. data/ext/nokogiri/xslt_stylesheet.c +116 -4
  20. data/lib/nokogiri/css/generated_tokenizer.rb +1 -2
  21. data/lib/nokogiri/css/xpath_visitor.rb +15 -7
  22. data/lib/nokogiri/decorators/slop.rb +5 -3
  23. data/lib/nokogiri/ffi/libxml.rb +9 -0
  24. data/lib/nokogiri/ffi/structs/xml_parser_context.rb +2 -1
  25. data/lib/nokogiri/ffi/structs/xml_parser_input.rb +19 -0
  26. data/lib/nokogiri/ffi/xml/dtd.rb +2 -2
  27. data/lib/nokogiri/ffi/xml/node.rb +9 -4
  28. data/lib/nokogiri/ffi/xml/sax/parser_context.rb +12 -0
  29. data/lib/nokogiri/ffi/xml/xpath_context.rb +5 -0
  30. data/lib/nokogiri/ffi/xslt/stylesheet.rb +21 -1
  31. data/lib/nokogiri/html/document.rb +3 -3
  32. data/lib/nokogiri/html/document_fragment.rb +19 -17
  33. data/lib/nokogiri/version.rb +1 -1
  34. data/lib/nokogiri/xml/document.rb +26 -1
  35. data/lib/nokogiri/xml/document_fragment.rb +2 -2
  36. data/lib/nokogiri/xml/dtd.rb +11 -0
  37. data/lib/nokogiri/xml/node.rb +156 -45
  38. data/lib/nokogiri/xml/node_set.rb +2 -2
  39. data/lib/nokogiri/xml/reader.rb +36 -0
  40. data/lib/nokogiri/xml/sax/document.rb +4 -2
  41. data/lib/nokogiri/xslt.rb +9 -5
  42. data/tasks/cross_compile.rb +24 -2
  43. data/test/css/test_parser.rb +29 -18
  44. data/test/decorators/test_slop.rb +16 -0
  45. data/test/html/test_document_fragment.rb +46 -3
  46. data/test/html/test_node.rb +9 -0
  47. data/test/xml/sax/test_parser.rb +11 -3
  48. data/test/xml/sax/test_parser_context.rb +50 -0
  49. data/test/xml/sax/test_push_parser.rb +18 -1
  50. data/test/xml/test_document_fragment.rb +14 -2
  51. data/test/xml/test_dtd.rb +15 -0
  52. data/test/xml/test_node.rb +31 -2
  53. data/test/xml/test_node_reparenting.rb +59 -31
  54. data/test/xml/test_node_set.rb +13 -0
  55. data/test/xml/test_xpath.rb +32 -0
  56. data/test/xslt/test_custom_functions.rb +94 -0
  57. metadata +84 -80
@@ -42,7 +42,7 @@ module Nokogiri
42
42
  end
43
43
 
44
44
  ###
45
- # Returns the index of the first node in self that is == to +node+. Returns nil if no match is found.
45
+ # Returns the index of the first node in self that is == to +node+. Returns nil if no match is found.
46
46
  def index(node)
47
47
  each_with_index { |member, j| return j if member == node }
48
48
  nil
@@ -257,7 +257,7 @@ module Nokogiri
257
257
  # Wrap this NodeSet with +html+ or the results of the builder in +blk+
258
258
  def wrap(html, &blk)
259
259
  each do |j|
260
- new_parent = document.root.parse(html).first
260
+ new_parent = document.parse(html).first
261
261
  j.add_next_sibling(new_parent)
262
262
  new_parent.add_child(j)
263
263
  end
@@ -30,6 +30,42 @@ module Nokogiri
30
30
  class Reader
31
31
  include Enumerable
32
32
 
33
+ TYPE_NONE = 0
34
+ # Element node type
35
+ TYPE_ELEMENT = 1
36
+ # Attribute node type
37
+ TYPE_ATTRIBUTE = 2
38
+ # Text node type
39
+ TYPE_TEXT = 3
40
+ # CDATA node type
41
+ TYPE_CDATA = 4
42
+ # Entity Reference node type
43
+ TYPE_ENTITY_REFERENCE = 5
44
+ # Entity node type
45
+ TYPE_ENTITY = 6
46
+ # PI node type
47
+ TYPE_PROCESSING_INSTRUCTION = 7
48
+ # Comment node type
49
+ TYPE_COMMENT = 8
50
+ # Document node type
51
+ TYPE_DOCUMENT = 9
52
+ # Document Type node type
53
+ TYPE_DOCUMENT_TYPE = 10
54
+ # Document Fragment node type
55
+ TYPE_DOCUMENT_FRAGMENT = 11
56
+ # Notation node type
57
+ TYPE_NOTATION = 12
58
+ # Whitespace node type
59
+ TYPE_WHITESPACE = 13
60
+ # Significant Whitespace node type
61
+ TYPE_SIGNIFICANT_WHITESPACE = 14
62
+ # Element end node type
63
+ TYPE_END_ELEMENT = 15
64
+ # Entity end node type
65
+ TYPE_END_ENTITY = 16
66
+ # XML Declaration node type
67
+ TYPE_XML_DECLARATION = 17
68
+
33
69
  # A list of errors encountered while parsing
34
70
  attr_accessor :errors
35
71
 
@@ -85,7 +85,9 @@ module Nokogiri
85
85
 
86
86
  ###
87
87
  # Called at the beginning of an element
88
- # +name+ is the name of the tag with +attrs+ as attributes
88
+ # * +name+ is the name of the tag
89
+ # * +attrs+ are an assoc list of namespaces and attributes, e.g.:
90
+ # [ ["xmlns:foo", "http://sample.net"], ["size", "large"] ]
89
91
  def start_element name, attrs = []
90
92
  end
91
93
 
@@ -110,7 +112,7 @@ module Nokogiri
110
112
  [['xmlns', ns_prefix].compact.join(':'), ns_uri]
111
113
  } + attrs.map { |attr|
112
114
  [[attr.prefix, attr.localname].compact.join(':'), attr.value]
113
- }.flatten
115
+ }
114
116
  start_element name, attributes
115
117
  end
116
118
 
@@ -9,8 +9,8 @@ module Nokogiri
9
9
  #
10
10
  # xslt = Nokogiri::XSLT(File.read(ARGV[0]))
11
11
  #
12
- def XSLT stylesheet
13
- XSLT.parse(stylesheet)
12
+ def XSLT stylesheet, modules = {}
13
+ XSLT.parse(stylesheet, modules)
14
14
  end
15
15
  end
16
16
 
@@ -20,11 +20,15 @@ module Nokogiri
20
20
  module XSLT
21
21
  class << self
22
22
  ###
23
- # Parse the stylesheet in +string+
24
- def parse string
23
+ # Parse the stylesheet in +string+, register any +modules+
24
+ def parse string, modules = {}
25
+ modules.each do |url, klass|
26
+ XSLT.register url, klass
27
+ end
28
+
25
29
  Stylesheet.parse_stylesheet_doc(XML.parse(string))
26
30
  end
27
-
31
+
28
32
  ###
29
33
  # Quote parameters in +params+ for stylesheet safety
30
34
  def quote_params params
@@ -6,6 +6,7 @@ ZLIB = 'zlib-1.2.5'
6
6
  ICONV = 'libiconv-1.13.1'
7
7
  LIBXML = 'libxml2-2.7.7'
8
8
  LIBXSLT = 'libxslt-1.1.26'
9
+ RAKE_COMPILER_PKGCONFIG = File.expand_path(File.join(Dir.pwd, "tmp/cross/lib/pkgconfig/"))
9
10
 
10
11
  ### Build zlib ###
11
12
  file "tmp/cross/download/#{ZLIB}" do |t|
@@ -108,7 +109,7 @@ file "tmp/cross/download/#{LIBXSLT}" do |t|
108
109
 
109
110
  Dir.chdir t.name do
110
111
  # FIXME: need to make the host dynamic
111
- sh "CFLAGS='-DIN_LIBXML' ./configure --host=#{HOST} --target=#{TARGET} --enable-static --disable-shared --prefix=#{CROSS_DIR} --with-libxml-prefix=#{CROSS_DIR} --without-python"
112
+ sh "CFLAGS='-DIN_LIBXML' ./configure --host=#{HOST} --target=#{TARGET} --enable-static --disable-shared --prefix=#{CROSS_DIR} --with-libxml-prefix=#{CROSS_DIR} --without-python --without-crypto"
112
113
  end
113
114
  end
114
115
 
@@ -120,7 +121,7 @@ file 'tmp/cross/bin/xslt-config' => "tmp/cross/download/#{LIBXSLT}" do |t|
120
121
  end
121
122
  ### End build libxslt ###
122
123
 
123
- file 'lib/nokogiri/nokogiri.rb' => 'cross:libxslt' do
124
+ file 'lib/nokogiri/nokogiri.rb' => 'cross:check' do
124
125
  File.open("lib/#{HOE.name}/#{HOE.name}.rb", 'wb') do |f|
125
126
  f.write <<-eoruby
126
127
  require "#{HOE.name}/\#{RUBY_VERSION.sub(/\\.\\d+$/, '')}/#{HOE.name}"
@@ -134,6 +135,12 @@ namespace :cross do
134
135
  task :libxml2 => ['cross:zlib', 'cross:iconv', 'tmp/cross/bin/xml2-config']
135
136
  task :libxslt => ['cross:libxml2', 'tmp/cross/bin/xslt-config']
136
137
 
138
+ task :check => ["cross:libxslt"] do
139
+ unless File.directory?(RAKE_COMPILER_PKGCONFIG)
140
+ raise RuntimeError.new("looks like rake-compiler changed where pkgconfig info is kept. (#{RAKE_COMPILER_PKGCONFIG})")
141
+ end
142
+ end
143
+
137
144
  task :copy_dlls do
138
145
  Dir['tmp/cross/bin/*.dll'].each do |file|
139
146
  cp file, "ext/nokogiri"
@@ -156,3 +163,18 @@ if Rake::Task.task_defined?(:cross)
156
163
  Rake::Task[:cross].prerequisites << "lib/nokogiri/nokogiri.rb"
157
164
  Rake::Task[:cross].prerequisites << "cross:file_list"
158
165
  end
166
+
167
+ desc "build a windows gem without all the ceremony."
168
+ task "gem:windows" do
169
+ # check that rake-compiler config contains the right patchlevels of 1.8.6 and 1.9.1
170
+ rake_compiler_config = YAML.load_file("#{ENV['HOME']}/.rake-compiler/config.yml")
171
+ ["1.8.6-p383", "1.9.1-p243"].each do |version|
172
+ majmin, patchlevel = version.split("-")
173
+ rbconfig = "rbconfig-#{majmin}"
174
+ unless rake_compiler_config.key?(rbconfig) && rake_compiler_config[rbconfig] =~ /-#{patchlevel}/
175
+ raise "rake-compiler '#{rbconfig}' not #{patchlevel}. try running 'rake-compiler cross-ruby VERSION=#{version}'"
176
+ end
177
+ end
178
+
179
+ system("env PKG_CONFIG_PATH=#{RAKE_COMPILER_PKGCONFIG} RUBY_CC_VERSION=1.8.6:1.9.1 rake cross native gem") || raise("build failed!")
180
+ end
@@ -142,30 +142,31 @@ module Nokogiri
142
142
 
143
143
  def test_nonstandard_nth_selectors
144
144
  ## These are non standard CSS
145
- assert_xpath '//a[position() = 99]', @parser.parse('a:eq(99)')
146
- assert_xpath '//a[position() = 1]', @parser.parse('a:first') # no parens
147
- assert_xpath '//a[position() = last()]', @parser.parse('a:last') # no parens
148
- assert_xpath '//a[position() = 99]', @parser.parse('a:nth(99)')
149
- assert_xpath '//a[position() = 1]', @parser.parse('a:first()')
150
- assert_xpath '//a[position() = last()]', @parser.parse('a:last()')
151
- assert_xpath '//a[node()]', @parser.parse('a:parent')
145
+ assert_xpath '//a[position() = 1]', @parser.parse('a:first()')
146
+ assert_xpath '//a[position() = 1]', @parser.parse('a:first') # no parens
147
+ assert_xpath '//a[position() = 99]', @parser.parse('a:eq(99)')
148
+ assert_xpath '//a[position() = 99]', @parser.parse('a:nth(99)')
149
+ assert_xpath '//a[position() = last()]', @parser.parse('a:last()')
150
+ assert_xpath '//a[position() = last()]', @parser.parse('a:last') # no parens
151
+ assert_xpath '//a[node()]', @parser.parse('a:parent')
152
152
  end
153
153
 
154
154
  def test_standard_nth_selectors
155
- assert_xpath '//a[position() = 99]', @parser.parse('a:nth-of-type(99)')
156
- assert_xpath '//a[position() = 1]', @parser.parse('a:first-of-type()')
157
- assert_xpath '//a[position() = last()]', @parser.parse('a:last-of-type()')
158
- assert_xpath '//a[position() = 1]', @parser.parse('a:first-of-type') # no parens
159
- assert_xpath '//a[position() = last()]', @parser.parse('a:last-of-type') # no parens
160
- assert_xpath '//a[position() = last() - 99]', @parser.parse('a:nth-last-of-type(99)')
161
- assert_xpath '//a[position() = last() - 99]', @parser.parse('a:nth-last-of-type(99)')
155
+ assert_xpath '//a[position() = 1]', @parser.parse('a:first-of-type()')
156
+ assert_xpath '//a[position() = 1]', @parser.parse('a:first-of-type') # no parens
157
+ assert_xpath '//a[position() = 99]', @parser.parse('a:nth-of-type(99)')
158
+ assert_xpath '//a[position() = last()]', @parser.parse('a:last-of-type()')
159
+ assert_xpath '//a[position() = last()]', @parser.parse('a:last-of-type') # no parens
160
+ assert_xpath '//a[position() = last()]', @parser.parse('a:nth-last-of-type(1)')
161
+ assert_xpath '//a[position() = last() - 98]', @parser.parse('a:nth-last-of-type(99)')
162
162
  end
163
163
 
164
164
  def test_nth_child_selectors
165
- assert_xpath '//*[position() = 1 and self::a]', @parser.parse('a:first-child')
166
- assert_xpath '//*[position() = last() and self::a]', @parser.parse('a:last-child')
167
- assert_xpath '//*[position() = 99 and self::a]', @parser.parse('a:nth-child(99)')
168
- assert_xpath '//*[position() = last() - 99 and self::a]', @parser.parse('a:nth-last-child(99)')
165
+ assert_xpath '//*[position() = 1 and self::a]', @parser.parse('a:first-child')
166
+ assert_xpath '//*[position() = 99 and self::a]', @parser.parse('a:nth-child(99)')
167
+ assert_xpath '//*[position() = last() and self::a]', @parser.parse('a:last-child')
168
+ assert_xpath '//*[position() = last() and self::a]', @parser.parse('a:nth-last-child(1)')
169
+ assert_xpath '//*[position() = last() - 98 and self::a]', @parser.parse('a:nth-last-child(99)')
169
170
  end
170
171
 
171
172
  def test_miscellaneous_selectors
@@ -185,6 +186,16 @@ module Nokogiri
185
186
  assert_xpath '//a[(position() <= 3) and (((position()-3) mod 1) = 0)]', @parser.parse('a:nth-of-type(-n+3)')
186
187
  assert_xpath '//a[(position() >= 3) and (((position()-3) mod 1) = 0)]', @parser.parse('a:nth-of-type(1n+3)')
187
188
  assert_xpath '//a[(position() >= 3) and (((position()-3) mod 1) = 0)]', @parser.parse('a:nth-of-type(n+3)')
189
+
190
+ assert_xpath '//a[((last()-position()+1) mod 2) = 0]', @parser.parse('a:nth-last-of-type(2n)')
191
+ assert_xpath '//a[((last()-position()+1) >= 1) and ((((last()-position()+1)-1) mod 2) = 0)]', @parser.parse('a:nth-last-of-type(2n+1)')
192
+ assert_xpath '//a[((last()-position()+1) mod 2) = 0]', @parser.parse('a:nth-last-of-type(even)')
193
+ assert_xpath '//a[((last()-position()+1) >= 1) and ((((last()-position()+1)-1) mod 2) = 0)]', @parser.parse('a:nth-last-of-type(odd)')
194
+ assert_xpath '//a[((last()-position()+1) >= 3) and ((((last()-position()+1)-3) mod 4) = 0)]', @parser.parse('a:nth-last-of-type(4n+3)')
195
+ assert_xpath '//a[((last()-position()+1) <= 3) and ((((last()-position()+1)-3) mod 1) = 0)]', @parser.parse('a:nth-last-of-type(-1n+3)')
196
+ assert_xpath '//a[((last()-position()+1) <= 3) and ((((last()-position()+1)-3) mod 1) = 0)]', @parser.parse('a:nth-last-of-type(-n+3)')
197
+ assert_xpath '//a[((last()-position()+1) >= 3) and ((((last()-position()+1)-3) mod 1) = 0)]', @parser.parse('a:nth-last-of-type(1n+3)')
198
+ assert_xpath '//a[((last()-position()+1) >= 3) and ((((last()-position()+1)-3) mod 1) = 0)]', @parser.parse('a:nth-last-of-type(n+3)')
188
199
  end
189
200
 
190
201
  def test_preceding_selector
@@ -0,0 +1,16 @@
1
+ require "helper"
2
+
3
+ module Nokogiri
4
+ class TestSlop < Nokogiri::TestCase
5
+ def test_description_tag
6
+ doc = Nokogiri.Slop(<<-eoxml)
7
+ <item>
8
+ <title>foo</title>
9
+ <description>this is the foo thing</description>
10
+ </item>
11
+ eoxml
12
+ assert doc.item.title
13
+ assert doc.item._description, 'should have description'
14
+ end
15
+ end
16
+ end
@@ -74,6 +74,16 @@ module Nokogiri
74
74
  assert Nokogiri::HTML::DocumentFragment.new(@html)
75
75
  end
76
76
 
77
+ def test_body_fragment_should_contain_body
78
+ fragment = Nokogiri::HTML::DocumentFragment.parse(" <body><div>foo</div></body>")
79
+ assert_match(/^<body>/, fragment.to_s)
80
+ end
81
+
82
+ def test_nonbody_fragment_should_not_contain_body
83
+ fragment = Nokogiri::HTML::DocumentFragment.parse("<div>foo</div>")
84
+ assert_match(/^<div>/, fragment.to_s)
85
+ end
86
+
77
87
  def test_fragment_should_have_document
78
88
  fragment = Nokogiri::HTML::DocumentFragment.new(@html)
79
89
  assert_equal @html, fragment.document
@@ -139,13 +149,13 @@ module Nokogiri
139
149
  def test_html_fragment_with_leading_whitespace
140
150
  doc = " <div>b</div> "
141
151
  fragment = Nokogiri::HTML::Document.new.fragment(doc)
142
- assert_equal "<div>b</div>", fragment.to_s
152
+ assert_match %r% <div>b</div> *%, fragment.to_s
143
153
  end
144
154
 
145
155
  def test_html_fragment_with_leading_whitespace_and_newline
146
156
  doc = " \n<div>b</div> "
147
157
  fragment = Nokogiri::HTML::Document.new.fragment(doc)
148
- assert_equal "<div>b</div>", fragment.to_s
158
+ assert_match %r% \n<div>b</div> *%, fragment.to_s
149
159
  end
150
160
 
151
161
  def test_html_fragment_with_leading_text_and_newline
@@ -155,7 +165,7 @@ module Nokogiri
155
165
 
156
166
  def test_html_fragment_with_leading_whitespace_and_text_and_newline
157
167
  fragment = HTML::Document.new.fragment(" First line\nSecond line<br>Broken line")
158
- assert_equal "First line\nSecond line<br>Broken line", fragment.to_s
168
+ assert_equal " First line\nSecond line<br>Broken line", fragment.to_s
159
169
  end
160
170
 
161
171
  def test_html_fragment_with_leading_entity
@@ -205,6 +215,39 @@ module Nokogiri
205
215
  assert_equal("<p>hello<!-- your ad here --></p>",
206
216
  fragment.to_s)
207
217
  end
218
+
219
+ def test_malformed_fragment_is_corrected
220
+ fragment = HTML::DocumentFragment.parse("<div </div>")
221
+ assert_equal "<div></div>", fragment.to_s
222
+ end
223
+
224
+ def test_unclosed_script_tag
225
+ # see GH#315
226
+ fragment = HTML::DocumentFragment.parse("foo <script>bar")
227
+ assert_equal "foo <script>bar</script>", fragment.to_html
228
+ end
229
+
230
+ def test_error_propagation_on_fragment_parse
231
+ frag = Nokogiri::HTML::DocumentFragment.parse "<hello>oh, hello there.</hello>"
232
+ assert frag.errors.any?{|err| err.to_s =~ /Tag hello invalid/}, "errors should be copied to the fragment"
233
+ end
234
+
235
+ def test_error_propagation_on_fragment_parse_in_node_context
236
+ doc = Nokogiri::HTML::Document.parse "<html><body><div></div></body></html>"
237
+ context_node = doc.at_css "div"
238
+ frag = Nokogiri::HTML::DocumentFragment.new doc, "<hello>oh, hello there.</hello>", context_node
239
+ assert frag.errors.any?{|err| err.to_s =~ /Tag hello invalid/}, "errors should be on the context node's document"
240
+ end
241
+
242
+ def test_error_propagation_on_fragment_parse_in_node_context_should_not_include_preexisting_errors
243
+ doc = Nokogiri::HTML::Document.parse "<html><body><div></div><jimmy></jimmy></body></html>"
244
+ assert doc.errors.any?{|err| err.to_s =~ /jimmy/}, "assert on setup"
245
+
246
+ context_node = doc.at_css "div"
247
+ frag = Nokogiri::HTML::DocumentFragment.new doc, "<hello>oh, hello there.</hello>", context_node
248
+ assert frag.errors.any?{|err| err.to_s =~ /Tag hello invalid/}, "errors should be on the context node's document"
249
+ assert frag.errors.none?{|err| err.to_s =~ /jimmy/}, "errors should not include pre-existing document errors"
250
+ end
208
251
  end
209
252
  end
210
253
  end
@@ -127,6 +127,15 @@ module Nokogiri
127
127
  assert_equal 'foo&bar&baz', node['href']
128
128
  end
129
129
 
130
+ def test_parse_config_option
131
+ node = @html.at('div')
132
+ options = nil
133
+ node.parse("<div></div>") do |config|
134
+ options = config
135
+ end
136
+ assert_equal Nokogiri::XML::ParseOptions::DEFAULT_HTML, options.to_i
137
+ end
138
+
130
139
  def test_fragment_handler_does_not_regurge_on_invalid_attributes
131
140
  iframe = %Q{<iframe style="width: 0%; height: 0px" src="http://someurl" allowtransparency></iframe>}
132
141
  assert_nothing_raised { @html.at('div').fragment(iframe) }
@@ -24,7 +24,7 @@ module Nokogiri
24
24
 
25
25
  assert block_called
26
26
 
27
- assert_equal ['a', '&b'], doc.start_elements.first.last
27
+ assert_equal [['foo', [['a', '&b']]]], doc.start_elements
28
28
  end
29
29
 
30
30
  def test_parser_context_yielded_in_memory
@@ -40,7 +40,7 @@ module Nokogiri
40
40
 
41
41
  assert block_called
42
42
 
43
- assert_equal ['a', '&b'], doc.start_elements.first.last
43
+ assert_equal [['foo', [['a', '&b']]]], doc.start_elements
44
44
  end
45
45
 
46
46
  def test_xml_decl
@@ -291,7 +291,15 @@ module Nokogiri
291
291
  @parser.parse_memory(<<-eoxml)
292
292
  <p id="asdfasdf">Paragraph 1</p>
293
293
  eoxml
294
- assert_equal [["p", ["id", "asdfasdf"]]],
294
+ assert_equal [["p", [["id", "asdfasdf"]]]],
295
+ @parser.document.start_elements
296
+ end
297
+
298
+ def test_start_element_attrs_include_namespaces
299
+ @parser.parse_memory(<<-eoxml)
300
+ <p xmlns:foo='http://foo.example.com/'>Paragraph 1</p>
301
+ eoxml
302
+ assert_equal [["p", [['xmlns:foo', 'http://foo.example.com/']]]],
295
303
  @parser.document.start_elements
296
304
  end
297
305
 
@@ -6,6 +6,56 @@ module Nokogiri
6
6
  module XML
7
7
  module SAX
8
8
  class TestParserContext < Nokogiri::SAX::TestCase
9
+ def setup
10
+ @xml = '<hello>
11
+
12
+ world
13
+ <inter>
14
+ <net>
15
+ </net>
16
+ </inter>
17
+
18
+ </hello>'
19
+ end
20
+
21
+ class Counter < Nokogiri::XML::SAX::Document
22
+ attr_accessor :context, :lines, :columns
23
+ def initialize
24
+ @context = nil
25
+ @lines = []
26
+ @columns = []
27
+ end
28
+
29
+ def start_element name, attrs = []
30
+ @lines << [name, context.line]
31
+ @columns << [name, context.column]
32
+ end
33
+ end
34
+
35
+ def test_line_numbers
36
+ sax_handler = Counter.new
37
+
38
+ parser = Nokogiri::XML::SAX::Parser.new(sax_handler)
39
+ parser.parse(@xml) do |ctx|
40
+ sax_handler.context = ctx
41
+ end
42
+
43
+ assert_equal [["hello", 1], ["inter", 4], ["net", 5]],
44
+ sax_handler.lines
45
+ end
46
+
47
+ def test_column_numbers
48
+ sax_handler = Counter.new
49
+
50
+ parser = Nokogiri::XML::SAX::Parser.new(sax_handler)
51
+ parser.parse(@xml) do |ctx|
52
+ sax_handler.context = ctx
53
+ end
54
+
55
+ assert_equal [["hello", 7], ["inter", 7], ["net", 9]],
56
+ sax_handler.columns
57
+ end
58
+
9
59
  def test_replace_entities
10
60
  pc = ParserContext.new StringIO.new('<root />'), 'UTF-8'
11
61
  pc.replace_entities = false
@@ -38,7 +38,24 @@ module Nokogiri
38
38
  <p id="asdfasdf">
39
39
  eoxml
40
40
 
41
- assert_equal [["p", ["id", "asdfasdf"]]],
41
+ assert_equal [["p", [["id", "asdfasdf"]]]],
42
+ @parser.document.start_elements
43
+
44
+ @parser.<<(<<-eoxml)
45
+ <!-- This is a comment -->
46
+ Paragraph 1
47
+ </p>
48
+ eoxml
49
+ assert_equal [' This is a comment '], @parser.document.comments
50
+ @parser.finish
51
+ end
52
+
53
+ def test_start_element_with_namespaces
54
+ @parser.<<(<<-eoxml)
55
+ <p xmlns:foo="http://foo.example.com/">
56
+ eoxml
57
+
58
+ assert_equal [["p", [["xmlns:foo", "http://foo.example.com/"]]]],
42
59
  @parser.document.start_elements
43
60
 
44
61
  @parser.<<(<<-eoxml)