diff -pruN 1.8.4-1/build_all 1.10.0+dfsg1-2/build_all
--- 1.8.4-1/build_all	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/build_all	2019-01-04 15:20:12.000000000 +0000
@@ -1,44 +1,24 @@
 #! /usr/bin/env bash
 #
-#  script to build gems for all relevant platforms:
-#  - MRI et al (standard gem)
-#  - windows (x86-mingw32 and x64-mingw32)
-#  - jruby
+#  script to build gems for all relevant platforms
 #
-
-# Load RVM into a shell session *as a function*
-if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
-    source "$HOME/.rvm/scripts/rvm"
-elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
-    source "/usr/local/rvm/scripts/rvm"
-else
-    echo "ERROR: An RVM installation was not found.\n"
-fi
-
 set -o errexit
 
-rm -rf tmp pkg
-bundle exec rake clean clobber
-
-# holding pen
-rm -rf gems
+rm -rf tmp pkg gems
 mkdir -p gems
 
-# windows
-bundle exec rake gem:windows
-cp -v pkg/nokogiri*{x86,x64}-mingw32*.gem gems
-
-# MRI
+# MRI et al (standard gem)
 bundle exec rake clean
+bundle exec rake test
 bundle exec rake gem
 cp -v pkg/nokogiri*.gem gems
 
 # jruby
-bundle exec rake clean clobber
-bundle exec rake generate
-
-rvm jruby
-gem install bundler --conservative
-bundle install --quiet --local || bundle install
-bundle exec ruby -S rake gem
+bundle exec rake clean
+bundle exec rake gem:jruby
 cp -v pkg/nokogiri*java.gem gems
+
+# windows (x86-mingw32 and x64-mingw32)
+bundle exec rake clean
+bundle exec rake gem:windows
+cp -v pkg/nokogiri*{x86,x64}-mingw32*.gem gems
diff -pruN 1.8.4-1/CHANGELOG.md 1.10.0+dfsg1-2/CHANGELOG.md
--- 1.8.4-1/CHANGELOG.md	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/CHANGELOG.md	2019-01-04 15:20:12.000000000 +0000
@@ -1,13 +1,95 @@
-# 1.8.4 / 2018-07-03
+# Nokogiri Changelog
 
-## Bug fixes
+## 1.10.0 / 2019-01-04
+
+### Features
+
+* [MRI] Cross-built Windows gems now support Ruby 2.6 [#1842, #1850]
+
+
+### Backwards incompatibilities
+
+This release ends support for:
+
+* Ruby 2.2, for which [official support ended on 2018-03-31](https://www.ruby-lang.org/en/news/2018/06/20/support-of-ruby-2-2-has-ended/) [#1841]
+* JRuby 1.7, for which [official support ended on 2017-11-21](https://github.com/jruby/jruby/issues/4112) [#1741]
+
+
+### Dependencies
+
+* [MRI] libxml2 is updated from 2.9.8 to 2.9.9
+* [MRI] libxslt is updated from 1.1.32 to 1.1.33
+
+
+## 1.9.1 / 2018-12-17
+
+### Bug fixes
+
+* Fix a bug introduced in v1.9.0 where `XML::DocumentFragment#dup` no longer returned an instance of the callee's class, instead always returning an `XML::DocumentFragment`. This notably broke any subclass of `XML::DocumentFragment` including `HTML::DocumentFragment` as well as the Loofah gem's `Loofah::HTML::DocumentFragment`. [#1846]
+
+
+## 1.9.0 / 2018-12-17
+
+### Security Notes
+
+* [JRuby] Upgrade Xerces dependency from 2.11.0 to 2.12.0 to address upstream vulnerability CVE-2012-0881 [#1831] (Thanks @grajagandev for reporting.)
+
+
+### Notable non-functional changes
+
+* Decrease installation size by removing many unneeded files (e.g., `/test`) from the packaged gems. [#1719] (Thanks, @stevecrozz!)
+
+
+### Features
+
+* `XML::Attr#value=` allows HTML node attribute values to be set to either a blank string or an empty boolean attribute. [#1800]
+* Introduce `XML::Node#wrap` which does what `XML::NodeSet#wrap` has always done, but for a single node. [#1531] (Thanks, @ethirajsrinivasan!)
+* [MRI] Improve installation experience on macOS High Sierra (Darwin). [#1812, #1813] (Thanks, @gpakosz and @nurse!)
+* [MRI] Node#dup supports copying a node directly to a new document. See the method documentation for details.
+* [MRI] DocumentFragment#dup is now more memory-efficient, avoiding making unnecessary copies. [#1063]
+* [JRuby] NodeSet has been rewritten to improve performance! [#1795]
+
+
+### Bug fixes
+
+* `NodeSet#each` now returns `self` instead of zero. [#1822] (Thanks, @olehif!)
+* [MRI] Address a memory leak when using XML::Builder to create nodes with namespaces. [#1810]
+* [MRI] Address a memory leak when unparenting a DTD. [#1784] (Thanks, @stevecheckoway!)
+* [MRI] Use RbConfig::CONFIG instead of ::MAKEFILE_CONFIG to fix installations that use Makefile macros. [#1820] (Thanks, @nobu!)
+* [JRuby] Decrease large memory usage when making nested XPath queries. [#1749]
+* [JRuby] Fix failing tests on JRuby 9.2.x
+* [JRuby] Fix default namespaces in nodes reparented into a different document [#1774]
+* [JRuby] Fix support for Java 9. [#1759] (Thanks, @Taywee!)
+
+
+### Dependencies
+
+* [MRI] Upgrade mini_portile2 dependency from `~> 2.3.0` to `~> 2.4.0`
+
+
+## 1.8.5 / 2018-10-04
+
+### Security Notes
+
+[MRI] Pulled in upstream patches from libxml2 that address CVE-2018-14404 and CVE-2018-14567. Full details are available in [#1785](https://github.com/sparklemotion/nokogiri/issues/1785). Note that these patches are not yet (as of 2018-10-04) in an upstream release of libxml2.
+
+
+### Bug fixes
+
+* [MRI] Fix regression in installation when building against system libraries, where some systems would not be able to find libxml2 or libxslt when present. (Regression introduced in v1.8.3.) [#1722]
+* [JRuby] Fix node reparenting when the destination doc is empty. [#1773]
+
+
+## 1.8.4 / 2018-07-03
+
+### Bug fixes
 
 * [MRI] Fix memory leak when creating nodes with namespaces. (Introduced in v1.5.7) [#1771]
 
 
-# 1.8.3 / 2018-06-16
+## 1.8.3 / 2018-06-16
 
-## Security Notes
+### Security Notes
 
 [MRI] Behavior in libxml2 has been reverted which caused CVE-2018-8048 (loofah gem), CVE-2018-3740 (sanitize gem), and CVE-2018-3741 (rails-html-sanitizer gem). The commit in question is here:
 
@@ -24,12 +106,12 @@ If you're offended by what happened here
 > https://bugzilla.gnome.org/show_bug.cgi?id=769760
 
 
-## Dependencies
+### Dependencies
 
 * [MRI] libxml2 is updated from 2.9.7 to 2.9.8
 
 
-## Features
+### Features
 
 * Node#classes, #add_class, #append_class, and #remove_class are added.
 * NodeSet#append_class is added.
@@ -38,7 +120,7 @@ If you're offended by what happened here
 * [JRuby] General improvements in JRuby implementation (Thanks, @kares!)
 
 
-## Bug fixes
+### Bug fixes
 
 * CSS attribute selectors now gracefully handle queries using integers. [#711]
 * Handle ASCII-8BIT encoding on fragment input [#553]
@@ -47,35 +129,35 @@ If you're offended by what happened here
 * [JRuby] Stability and speed improvements to `Node`, `Sax::PushParser`, and the JRuby implementation [#1708, #1710, #1501]
 
 
-# 1.8.2 / 2018-01-29
+## 1.8.2 / 2018-01-29
 
-## Security Notes
+### Security Notes
 
 [MRI] The update of vendored libxml2 from 2.9.5 to 2.9.7 addresses at least one published vulnerability, CVE-2017-15412. [#1714 has complete details]
 
 
-## Dependencies
+### Dependencies
 
 * [MRI] libxml2 is updated from 2.9.5 to 2.9.7
 * [MRI] libxslt is updated from 1.1.30 to 1.1.32
 
 
-## Features
+### Features
 
 * [MRI] OpenBSD installation should be a bit easier now. [#1685] (Thanks, @jeremyevans!)
 * [MRI] Cross-built Windows gems now support Ruby 2.5
 
 
-## Bug fixes
+### Bug fixes
 
 * Node#serialize once again returns UTF-8-encoded strings. [#1659]
 * [JRuby] made SAX parsing of characters consistent with C implementation [#1676] (Thanks, @andrew-aladev!)
 * [MRI] Predefined entities, when inspected, no longer cause a segfault. [#1238]
 
 
-# 1.8.1 / 2017-09-19
+## 1.8.1 / 2017-09-19
 
-## Dependencies
+### Dependencies
 
 * [MRI] libxml2 is updated from 2.9.4 to 2.9.5.
 * [MRI] libxslt is updated from 1.1.29 to 1.1.30.
@@ -83,22 +165,22 @@ If you're offended by what happened here
 * [MRI] Upgrade mini_portile2 dependency from `~> 2.2.0` to `~> 2.3.0`, which will validate checksums on the vendored libxml2 and libxslt tarballs before using them.
 
 
-## Bugs
+### Bugs
 
 * NodeSet#first with an integer argument longer than the length of the NodeSet now correctly clamps the length of the returned NodeSet to the original length. [#1650] (Thanks, @Derenge!)
 * [MRI] Ensure CData.new raises TypeError if the `content` argument is not implicitly convertible into a string. [#1669]
 
 
-# 1.8.0 / 2017-06-04
+## 1.8.0 / 2017-06-04
 
-## Backwards incompatibilities
+### Backwards incompatibilities
 
 This release ends support for Ruby 2.1 on Windows in the `x86-mingw32` and `x64-mingw32` platform gems (containing pre-compiled DLLs). Official support ended for Ruby 2.1 on 2017-04-01.
 
 Please note that this deprecation note only applies to the precompiled Windows gems. Ruby 2.1 continues to be supported (for now) in the default gem when compiled on installation.
 
 
-## Dependencies
+### Dependencies
 
 * [Windows] Upgrade iconv from 1.14 to 1.15 (unless --use-system-libraries)
 * [Windows] Upgrade zlib from 1.2.8 to 1.2.11 (unless --use-system-libraries)
@@ -106,13 +188,13 @@ Please note that this deprecation note o
 * [MRI] Upgrade mini-portile2 dependency from `~> 2.1.0` to `~> 2.2.0`
 
 
-## Compatibility notes
+### Compatibility notes
 
 * [JRuby] Removed support for `jruby --1.8` code paths. [#1607] (Thanks, @kares!)
 * [MRI Windows] Retrieve zlib source from http://zlib.net/fossils to avoid deprecation issues going forward. See #1632 for details around this problem.
 
 
-## Features
+### Features
 
 * NodeSet#clone is not an alias for NodeSet#dup [#1503] (Thanks, @stephankaag!)
 * Allow Processing Instructions and Comments as children of a document root. [#1033] (Thanks, @windwiny!)
@@ -127,7 +209,7 @@ Please note that this deprecation note o
 * [JRuby] (performance) more performance improvements, particularly in XPath, Reader, XmlNode, and XmlNodeSet [#1597] (Thanks, @kares!)
 
 
-## Bugs
+### Bugs
 
 * HTML::SAX::Parser#parse_io now correctly parses HTML and not XML [#1577] (Thanks for the test case, @gregors!)
 * Support installation on systems with a `lib64` site config. [#1562]
@@ -142,9 +224,9 @@ Please note that this deprecation note o
 * [JRuby] Merged nodes no longer results in Java exceptions during XPath queries. [#1320] (Thanks, @kares!)
 
 
-# 1.7.2 / 2017-05-09
+## 1.7.2 / 2017-05-09
 
-## Security Notes
+### Security Notes
 
 [MRI] Upstream libxslt patches are applied to the vendored libxslt 1.1.29 which address CVE-2017-5029 and CVE-2016-4738.
 
@@ -155,9 +237,9 @@ For more information:
 * http://people.canonical.com/~ubuntu-security/cve/2016/CVE-2016-4738.html
 
 
-# 1.7.1 / 2017-03-19
+## 1.7.1 / 2017-03-19
 
-## Security Notes
+### Security Notes
 
 [MRI] Upstream libxml2 patches are applied to the vendored libxml 2.9.4 which address CVE-2016-4658 and CVE-2016-5131.
 
@@ -168,22 +250,22 @@ For more information:
 * http://people.canonical.com/~ubuntu-security/cve/2016/CVE-2016-5131.html
 
 
-# 1.7.0.1 / 2017-01-04
+## 1.7.0.1 / 2017-01-04
 
-## Bugs
+### Bugs
 
 * Fix OpenBSD support. (#1569) (related to #1543)
 
 
-# 1.7.0 / 2016-12-26
+## 1.7.0 / 2016-12-26
 
-## Features
+### Features
 
 * Remove deprecation warnings in Ruby 2.4.0 (#1545) (Thanks, @matthewd!)
 * Support egcc compiler on OpenBSD (#1543) (Thanks, @frenkel and @knu!)
 
 
-## Backwards incompatibilities.
+### Backwards incompatibilities.
 
 This release ends support for:
 
@@ -193,9 +275,9 @@ This release ends support for:
 * MacRuby, which hasn't been actively supported since 2015-01-13 (see https://github.com/MacRuby/MacRuby/commit/f76b9d6e99c18236db617e8aceb12c27d593a483)
 
 
-# 1.6.8.1 / 2016-10-03
+## 1.6.8.1 / 2016-10-03
 
-## Dependency License Notes
+### Dependency License Notes
 
 Removes required dependency on the `pkg-config` gem. This dependency
 was introduced in v1.6.8 and, because it's distributed under LGPL, was
@@ -206,9 +288,9 @@ installed, it's used; but otherwise Noko
 around its absence.
 
 
-# 1.6.8 / 2016-06-06
+## 1.6.8 / 2016-06-06
 
-## Security Notes
+### Security Notes
 
 [MRI] Bundled libxml2 is upgraded to 2.9.4, which fixes many security issues. Many of these had previously been patched in the vendored libxml 2.9.2 in the 1.6.7.x branch, but some are newer.
 
@@ -229,7 +311,7 @@ See this libxslt email post for more:
 * https://mail.gnome.org/archives/xslt/2016-May/msg00004.html
 
 
-## Features
+### Features
 
 Several changes were made to improve performance:
 
@@ -241,7 +323,7 @@ Several changes were made to improve per
 * Set document encoding appropriately even on blank document. (#1043) (Thanks, @batter!)
 
 
-## Bug Fixes
+### Bug Fixes
 
 * [JRuby] fix slow add_child (#692)
 * [JRuby] fix load errors when deploying to JRuby/Torquebox (#1114) (Thanks, @atambo and @jvshahid!)
@@ -268,7 +350,7 @@ Several changes were made to improve per
 
 
 
-## Other Notes
+### Other Notes
 
 * Removed legacy code remaining from Ruby 1.8.x support.
 * Removed legacy code remaining from REE support.
@@ -276,7 +358,7 @@ Several changes were made to improve per
 * Handling C strings in a forward-compatible manner, see https://github.com/ruby/ruby/blob/v2_2_0/NEWS#L319
 
 
-# 1.6.7.2 / 2016-01-20
+## 1.6.7.2 / 2016-01-20
 
 This version pulls in several upstream patches to the vendored libxml2 and libxslt to address:
 
@@ -287,7 +369,7 @@ Ubuntu classifies this as "Priority: Low
 MITRE record is https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-7499
 
 
-# 1.6.7.1 / 2015-12-16
+## 1.6.7.1 / 2015-12-16
 
 This version pulls in several upstream patches to the vendored libxml2 and libxslt to address:
 
@@ -303,9 +385,9 @@ This version pulls in several upstream p
 See also http://www.ubuntu.com/usn/usn-2834-1/
 
 
-# 1.6.7 / 2015-11-29
+## 1.6.7 / 2015-11-29
 
-## Notes
+### Notes
 
 This version supports native builds on Windows using the RubyInstaller
 DevKit. It also supports Ruby 2.2.x on Windows, as well as making
@@ -316,7 +398,7 @@ This version also includes the security
 v1.6.6.3 and v1.6.6.4 to the vendored libxml2 and libxslt source.
 See #1374 and #1376 for details.
 
-## Features
+### Features
 
 * Cross-built gems now have a proper ruby version requirement. (#1266)
 * Ruby 2.2.x is supported on Windows.
@@ -325,7 +407,7 @@ See #1374 and #1376 for details.
 * [JRuby] fix error in validating files with jruby (#1355, #1361) (Thanks, @twalpole!)
 * [MRI, OSX] Patch to handle nonstandard location of `iconv.h`. (#1206, #1210, #1218, #1345) (Thanks, @neonichu!)
 
-## Bug Fixes
+### Bug Fixes
 
 * [JRuby] reset the namespace cache when replacing the document's innerHtml (#1265) (Thanks, @mkristian!)
 * [JRuby] Document#parse should support IO objects that respond to #read. (#1124) (Thanks, Jake Byman!)
@@ -334,7 +416,7 @@ See #1374 and #1376 for details.
 * [JRuby] Namespaced attributes aren't removed by remove_attribute. (#1299)
 
 
-# 1.6.6.4 / 2015-11-19
+## 1.6.6.4 / 2015-11-19
 
 This version pulls in an upstream patch to the vendored libxml2 to address:
 
@@ -343,7 +425,7 @@ This version pulls in an upstream patch
 This issue was assigned CVE-2015-8710 after the fact. See http://seclists.org/oss-sec/2015/q4/616 for details.
 
 
-# 1.6.6.3 / 2015-11-16
+## 1.6.6.3 / 2015-11-16
 
 This version pulls in several upstream patches to the vendored libxml2 and libxslt to address:
 
@@ -358,19 +440,19 @@ This version pulls in several upstream p
 See #1374 for details.
 
 
-# 1.6.6.2 / 2015-01-23
+## 1.6.6.2 / 2015-01-23
 
-## Bug fixes
+### Bug fixes
 
 * Fixed installation issue affecting compiler arguments. (#1230)
 
 
-# 1.6.6.1 / 2015-01-22
+## 1.6.6.1 / 2015-01-22
 
 Note that 1.6.6.0 was not released.
 
 
-## Features
+### Features
 
 * Unified Node and NodeSet implementations of #search, #xpath and #css.
 * Added Node#lang and Node#lang=.
@@ -381,7 +463,7 @@ Note that 1.6.6.0 was not released.
 * JRuby 9K support.
 
 
-## Bug fixes
+### Bug fixes
 
 * DocumentFragment#search now matches against root nodes. (#1205)
 * (MRI) More fixes related to handling libxml2 parse errors during DocumentFragment#dup. (#1196)
@@ -393,31 +475,31 @@ Note that 1.6.6.0 was not released.
 * (JRuby) HtmlSaxPushParser now exists. (#1147) (Thanks, Piotr Szmielew!)
 
 
-# 1.6.5 / 2014-11-26
+## 1.6.5 / 2014-11-26
 
-## Features
+### Features
 
 * Implement Slop#respond_to_missing?. (#1176)
 * Optimized the XPath query generated by an `an+b` CSS query.
 
 
-## Bug fixes
+### Bug fixes
 
 * Capture non-parse errors from Document#dup in Document#errors. (#1196)
 * (JRuby) Document#canonicalize parameters are now consistent with MRI. (#1189)
 
 
-# 1.6.4.1 / 2014-11-05
+## 1.6.4.1 / 2014-11-05
 
-## Bug fixes
+### Bug fixes
 
 * (MRI) Fix a bug where CFLAGS passed in are dropped. (#1188)
 * Fix a bug where CSS selector :nth(n) did not work. (#1187)
 
 
-# 1.6.4 / 2014-11-04
+## 1.6.4 / 2014-11-04
 
-## Features
+### Features
 
 * (MRI) Bundled Libxml2 is upgraded to 2.9.2.
 * (MRI) `nokogiri --version` will include a list of applied patches.
@@ -425,27 +507,27 @@ Note that 1.6.6.0 was not released.
 * (MRI) Detect and help user fix a missing /usr/include/iconv.h on OS X. (#1111)
 * (MRI) Improve the iconv detection for building libxml2.
 
-## Bug fixes
+### Bug fixes
 
 * (MRI) Fix DocumentFragment#element_children (#1138).
 * Fix a bug with CSS attribute selector without any prefix where "foo [bar]" was treated as "foo[bar]". (#1174)
 
 
-# 1.6.3.1 / 2014-07-21
+## 1.6.3.1 / 2014-07-21
 
-## Bug fixes
+### Bug fixes
 
 * Addressing an Apple Macintosh installation problem for GCC users. #1130 (Thanks, @zenspider!)
 
 
-# 1.6.3 / 2014-07-20
+## 1.6.3 / 2014-07-20
 
-## Features
+### Features
 
 * Added Node#document? and Node#processing_instruction?
 
 
-## Bug fixes
+### Bug fixes
 
 * [JRuby] Fix Ruby memory exhaustion vulnerability. #1087 (Thanks, @ocher)
 * [MRI] Fix segfault during GC when using `libxml-ruby` and `nokogiri` together in multi-threaded environment. #895 (Thanks, @ender672!)
@@ -454,18 +536,18 @@ Note that 1.6.6.0 was not released.
 * Processing instructions can now be added via Node#add_next_sibling.
 
 
-# 1.6.2.1 / 2014-05-13
+## 1.6.2.1 / 2014-05-13
 
-## Bug fixes
+### Bug fixes
 
 * Fix statically-linked libxml2 installation when using universal builds of Ruby. #1104
 * Patching `mini_portile` to address the git dependency detailed in #1102.
 * Library load fix to address segfault reported on some systems. #1097
 
 
-# 1.6.2 / 2014-05-12
+## 1.6.2 / 2014-05-12
 
-## Security Note
+### Security Note
 
 A set of security and bugfix patches have been backported from the libxml2 and libxslt repositories onto the version of 2.8.0 packaged with Nokogiri, including these notable security fixes:
 
@@ -475,11 +557,11 @@ A set of security and bugfix patches hav
 
 It is recommended that you upgrade from 1.6.x to this version as soon as possible.
 
-## Compatibility Note
+### Compatibility Note
 
 Now requires libxml >= 2.6.21 (was previously >= 2.6.17).
 
-## Features
+### Features
 
 * Add cross building of fat binary gems for 64-Bit Windows (x64-mingw32) and add support for native builds on Windows. #864, #989, #1072
 * (MRI) Alias CP932 to Windows-31J if iconv does not support Windows-31J.
@@ -498,7 +580,7 @@ Now requires libxml >= 2.6.21 (was previ
 * Fix documentation for XML::Node#namespace. #803 #802 (Thanks, Hoylen Sue)
 * Allow Nokogiri::XML::Node#parse from unparented non-element nodes. #407
 
-## Bugfixes
+### Bugfixes
 
 * Ensure :only-child pseudo class works within :not pseudo class. #858 (Thanks, Yamagishi Kazutoshi!)
 * Don't call pkg_config when using bundled libraries in extconf.rb #931 (Thanks, Shota Fukumori!)
@@ -518,7 +600,7 @@ Now requires libxml >= 2.6.21 (was previ
 * (JRuby) JRuby-Nokogiri has different comment node name #1080
 * (JRuby) JAXPExtensionsProvider / Java 7 / Secure Processing #1070
 
-# 1.6.1 / 2013-12-14
+## 1.6.1 / 2013-12-14
 
 * Bugfixes
 
@@ -526,7 +608,7 @@ Now requires libxml >= 2.6.21 (was previ
   * (JRuby) Fix regression of billion-laughs vulnerability. #586
 
 
-# 1.6.0 / 2013-06-08
+## 1.6.0 / 2013-06-08
 
 This release was based on v1.5.10 and 1.6.0.rc1, and contains changes
 mentioned in both.
@@ -536,7 +618,7 @@ mentioned in both.
   * Remove pre 1.9 monitoring from Travis.
 
 
-# 1.6.0.rc1 / 2013-04-14
+## 1.6.0.rc1 / 2013-04-14
 
 This release was based on v1.5.9, and so does not contain any fixes
 mentioned in the notes for v1.5.10.
@@ -562,7 +644,7 @@ mentioned in the notes for v1.5.10.
   * Support for Ruby 1.8.7 and prior has been dropped
 
 
-# 1.5.11 / 2013-12-14
+## 1.5.11 / 2013-12-14
 
 * Bugfixes
 
@@ -570,7 +652,7 @@ mentioned in the notes for v1.5.10.
   * (JRuby) Fix regression of billion-laughs vulnerability. #586
 
 
-# 1.5.10 / 2013-06-07
+## 1.5.10 / 2013-06-07
 
 * Bugfixes
 
@@ -585,7 +667,7 @@ mentioned in the notes for v1.5.10.
   * Fix TypeError when running tests. #900 (Thanks, Cédric Boutillier!)
 
 
-# 1.5.9 / 2013-03-21
+## 1.5.9 / 2013-03-21
 
 * Bugfixes
 
@@ -594,7 +676,7 @@ mentioned in the notes for v1.5.10.
   * (MRI) Fixed a memory leak in fragment parsing if nodes are not all subsequently reparented. #856
 
 
-# 1.5.8 / 2013-03-19
+## 1.5.8 / 2013-03-19
 
 * Bugfixes
 
@@ -603,7 +685,7 @@ mentioned in the notes for v1.5.10.
   * Allow use of a prefixed namespace on a root node using Nokogiri::XML::Builder #868
 
 
-# 1.5.7 / 2013-03-18
+## 1.5.7 / 2013-03-18
 
 * Features
 
@@ -626,7 +708,7 @@ mentioned in the notes for v1.5.10.
   * (MRI) SAX parser handles empty processing instructions. #845
 
 
-# 1.5.6 / 2012-12-19
+## 1.5.6 / 2012-12-19
 
 * Features
 
@@ -665,7 +747,7 @@ mentioned in the notes for v1.5.10.
   * (JRuby) builder requires textwrappers for valid utf8 in jruby, not in mri. #784
 
 
-# 1.5.5 / 2012-06-24
+## 1.5.5 / 2012-06-24
 
 * Features
 
@@ -687,7 +769,7 @@ mentioned in the notes for v1.5.10.
   * JRuby's Entity resolving should be consistent with C-Nokogiri #704, #647, #703
 
 
-# 1.5.4 / 2012-06-12
+## 1.5.4 / 2012-06-12
 
 * Features
 
@@ -713,7 +795,7 @@ mentioned in the notes for v1.5.10.
     Insert your own joke about double-negatives here.
 
 
-# 1.5.3 / 2012-06-01
+## 1.5.3 / 2012-06-01
 
 * Features
 
@@ -743,12 +825,12 @@ mentioned in the notes for v1.5.10.
   * (JRuby) Concurrency issue in XPath parsing. #682
 
 
-# 1.5.2 / 2012-03-09
+## 1.5.2 / 2012-03-09
 
 Repackaging of 1.5.1 with a gemspec that is compatible with older Rubies. #631, #632.
 
 
-# 1.5.1 / 2012-03-09
+## 1.5.1 / 2012-03-09
 
 * Features
 
@@ -793,7 +875,7 @@ Repackaging of 1.5.1 with a gemspec that
   * C14n cleanup and Node#canonicalize (thanks, Ivan Pirlik!) #563
 
 
-# 1.5.0 / 2011-07-01
+## 1.5.0 / 2011-07-01
 
 * Notes
 
@@ -814,7 +896,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Add support for <meta charset="...">.
 
 
-# 1.5.0 beta3 / 2010/12/02
+## 1.5.0 beta3 / 2010/12/02
 
 * Notes
 
@@ -826,14 +908,14 @@ Repackaging of 1.5.1 with a gemspec that
   * Node#inner_text no longer returns nil. (JRuby) #264
 
 
-# 1.5.0 beta2 / 2010/07/30
+## 1.5.0 beta2 / 2010/07/30
 
 * Notes
 
   * See changelog from 1.4.3
 
 
-# 1.5.0 beta1 / 2010/05/22
+## 1.5.0 beta1 / 2010/05/22
 
 * Notes
 
@@ -846,7 +928,7 @@ Repackaging of 1.5.1 with a gemspec that
   * FFI support is removed.
 
 
-# 1.4.7 / 2011-07-01
+## 1.4.7 / 2011-07-01
 
 * Bugfixes
 
@@ -855,7 +937,7 @@ Repackaging of 1.5.1 with a gemspec that
     encoding. Thanks, Timothy Elliott (@ender672)! #478
 
 
-# 1.4.6 / 2011-06-19
+## 1.4.6 / 2011-06-19
 
 * Notes
 
@@ -863,7 +945,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Ruby 1.8.6 support has been restored.
 
 
-# 1.4.5 / 2011-05-19
+## 1.4.5 / 2011-05-19
 
 * New Features
 
@@ -886,7 +968,7 @@ Repackaging of 1.5.1 with a gemspec that
   * XML::Reader no longer segfaults when under GC pressure. #439
 
 
-# 1.4.4 / 2010-11-15
+## 1.4.4 / 2010-11-15
 
 * New Features
 
@@ -913,7 +995,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Fixed NodeSet#wrap on nodes within a fragment. #331
 
 
-# 1.4.3 / 2010/07/28
+## 1.4.3 / 2010/07/28
 
 * New Features
 
@@ -937,7 +1019,7 @@ Repackaging of 1.5.1 with a gemspec that
     "//F//G[preceding-sibling::E]".
 
 
-# 1.4.2 / 2010/05/22
+## 1.4.2 / 2010/05/22
 
 * New Features
 
@@ -990,7 +1072,7 @@ Repackaging of 1.5.1 with a gemspec that
     were bundled.
 
 
-# 1.4.1 / 2009/12/10
+## 1.4.1 / 2009/12/10
 
 * New Features
 
@@ -1022,7 +1104,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Fragments containing leading text nodes with newlines now parse properly. GH #178.
 
 
-# 1.4.0 / 2009/10/30
+## 1.4.0 / 2009/10/30
 
 * Happy Birthday!
 
@@ -1065,7 +1147,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Hpricot compatibility layer removed
 
 
-# 1.3.3 / 2009/07/26
+## 1.3.3 / 2009/07/26
 
 * New Features
 
@@ -1086,7 +1168,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Hpricot compatibility layer will be removed in 1.4.0
 
 
-# 1.3.2 / 2009-06-22
+## 1.3.2 / 2009-06-22
 
 * New Features
 
@@ -1110,7 +1192,7 @@ Repackaging of 1.5.1 with a gemspec that
     to Nokogiri::XML::SAX::Document#end_element_namespace
 
 
-# 1.3.1 / 2009-06-07
+## 1.3.1 / 2009-06-07
 
 * Bugfixes
 
@@ -1118,7 +1200,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Namespace nodes are added to the Document node cache
 
 
-# 1.3.0 / 2009-05-30
+## 1.3.0 / 2009-05-30
 
 * New Features
 
@@ -1162,7 +1244,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Fixed intolerance of HTML attributes without values in Node#before/after/inner_html=. (GH#35)
 
 
-# 1.2.3 / 2009-03-22
+## 1.2.3 / 2009-03-22
 
 * Bugfixes
 
@@ -1174,7 +1256,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Builder methods take a hash as a second argument
 
 
-# 1.2.2 / 2009-03-14
+## 1.2.2 / 2009-03-14
 
 * New features
 
@@ -1201,7 +1283,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Document should not have a parent method (LH #64)
 
 
-# 1.2.1 / 2009-02-23
+## 1.2.1 / 2009-02-23
 
 * Bugfixes
 
@@ -1209,7 +1291,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Fixed Ruby 1.9 String Encoding (Thanks 角谷さん！)
 
 
-# 1.2.0 / 2009-02-22
+## 1.2.0 / 2009-02-22
 
 * New features
 
@@ -1246,7 +1328,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Nokogiri::HTML.fragment now returns an XML::DocumentFragment (LH #32)
 
 
-# 1.1.1
+## 1.1.1
 
 * New features
 
@@ -1264,7 +1346,7 @@ Repackaging of 1.5.1 with a gemspec that
   * XML::NodeSet implements to_xml
 
 
-# 1.1.0
+## 1.1.0
 
 * New Features
 
@@ -1281,7 +1363,7 @@ Repackaging of 1.5.1 with a gemspec that
   * XML::Node#add_previous_sibling returns new sibling node.
 
 
-# 1.0.7
+## 1.0.7
 
 * Bugfixes
 
@@ -1293,7 +1375,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Fixed a bug in complex CSS negation selectors
 
 
-# 1.0.6
+## 1.0.6
 
 * 5 Bugfixes
 
@@ -1304,7 +1386,7 @@ Repackaging of 1.5.1 with a gemspec that
   * CSS to XPath conversion is now cached
 
 
-# 1.0.5
+## 1.0.5
 
 * Bugfixes
 
@@ -1313,7 +1395,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Caching results of NodeSet#[] on Document
 
 
-# 1.0.4
+## 1.0.4
 
 * Bugfixes
 
@@ -1322,7 +1404,7 @@ Repackaging of 1.5.1 with a gemspec that
   * Builder blocks can call methods from surrounding contexts
 
 
-# 1.0.3
+## 1.0.3
 
 * 5 Bugfixes
 
@@ -1333,14 +1415,14 @@ Repackaging of 1.5.1 with a gemspec that
   * inner_html fixed. (Thanks Yehuda!)
 
 
-# 1.0.2
+## 1.0.2
 
 * 1 Bugfix
 
   * extconf.rb should not check for frex and racc
 
 
-# 1.0.1
+## 1.0.1
 
 * 1 Bugfix
 
@@ -1348,7 +1430,7 @@ Repackaging of 1.5.1 with a gemspec that
     will link properly.  Thanks lucsky!
 
 
-# 1.0.0 / 2008-07-13
+## 1.0.0 / 2008-07-13
 
 * 1 major enhancement
 
diff -pruN 1.8.4-1/.codeclimate.yml 1.10.0+dfsg1-2/.codeclimate.yml
--- 1.8.4-1/.codeclimate.yml	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/.codeclimate.yml	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,3 @@
+exclude_paths:
+  - "lib/nokogiri/css/parser.rb"
+  - "lib/nokogiri/css/tokenizer.rb"
diff -pruN 1.8.4-1/concourse/nokogiri.final.yml 1.10.0+dfsg1-2/concourse/nokogiri.final.yml
--- 1.8.4-1/concourse/nokogiri.final.yml	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/concourse/nokogiri.final.yml	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,924 @@
+groups:
+  - name: master
+    jobs:
+      
+      - "ruby-2.3-system"
+      - "ruby-2.3-vendored"
+      - "ruby-2.3-valgrind"
+      
+      - "ruby-2.4-system"
+      - "ruby-2.4-vendored"
+      - "ruby-2.4-valgrind"
+      
+      - "ruby-2.5-system"
+      - "ruby-2.5-vendored"
+      - "ruby-2.5-valgrind"
+      
+      - "ruby-2.6-system"
+      - "ruby-2.6-vendored"
+      - "ruby-2.6-valgrind"
+      
+      - "ruby-vanilla-system"
+      
+      - "ruby-libxmlruby-system"
+      - "ruby-libxmlruby-vendored"
+      - "ruby-libxmlruby-valgrind"
+      
+      
+      - "jruby-9.1"
+      
+      - "jruby-9.2"
+      
+      
+      - "win-ruby-2.3-devkit"
+      
+      - build-success
+
+
+  - name: PRs
+    jobs:
+      - pr-pending
+      - ruby-2.3-system-pr
+      - ruby-2.3-vendored-pr
+      - ruby-2.3-valgrind-pr
+      - ruby-2.6-system-pr
+      - ruby-2.6-vendored-pr
+      - ruby-2.6-valgrind-pr
+      - ruby-vanilla-system-pr
+      - jruby-9.2-pr
+      - pr-success
+
+  - name: allow-failure
+    jobs:
+      
+      - "win-ruby-2.4-devkit"
+      
+      - "win-ruby-2.5-devkit"
+      
+      - "win-ruby-2.6-devkit"
+      
+      
+      - "rbx-latest-system"
+      - "rbx-latest-vendored"
+      
+      - rbx-latest-pr
+      
+
+resource_types:
+- name: pull-request
+  type: docker-image
+  source:
+    repository: jtarchie/pr
+- name: irc-notification
+  type: docker-image
+  source:
+    repository: flavorjones/irc-notification-resource
+
+resources:
+  - name: ci
+    type: git
+    source:
+      uri: https://github.com/sparklemotion/nokogiri/
+      branch: master
+      disable_ci_skip: true # always get the latest pipeline configuration
+  - name: nokogiri
+    type: git
+    source:
+      uri: https://github.com/sparklemotion/nokogiri/
+      branch: master
+      ignore_paths:
+        - concourse/**
+        - suppressions/**
+  - name: nokogiri-pr
+    type: pull-request
+    source:
+      repo: sparklemotion/nokogiri
+      access_token: {{github-repo-status-access-token}}
+      ignore_paths:
+        - concourse/**
+        - suppressions/**
+  - name: nokogiri-irc
+    type: irc-notification
+    source:
+      server: chat.freenode.net
+      port: 7070
+      channel: "#nokogiri"
+      user: {{nokobot-irc-username}}
+      password: {{nokobot-irc-password}}
+
+anchors:
+  notify_failure_to_irc: &notify_failure_to_irc
+    put: nokogiri-irc
+    params: {message: "($BUILD_PIPELINE_NAME/$BUILD_JOB_NAME) The build failed ($BUILD_URL)"}
+  notify_failure_to_pr: &notify_failure_to_pr
+    put: nokogiri-pr
+    params: {path: nokogiri-pr, status: failure}
+
+jobs:
+  #
+  #  master
+  #
+  
+  - name: ruby-2.3-system
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.3"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-2.3-vendored
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-2.3-system"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.3"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-2.3-valgrind
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-2.3-vendored"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.3"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            TEST_WITH_VALGRIND: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+  
+  - name: ruby-2.4-system
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.4"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-2.4-vendored
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-2.4-system"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.4"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-2.4-valgrind
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-2.4-vendored"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.4"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            TEST_WITH_VALGRIND: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+  
+  - name: ruby-2.5-system
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.5"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-2.5-vendored
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-2.5-system"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.5"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-2.5-valgrind
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-2.5-vendored"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.5"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            TEST_WITH_VALGRIND: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+  
+  - name: ruby-2.6-system
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.6"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-2.6-vendored
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-2.6-system"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.6"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-2.6-valgrind
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-2.6-vendored"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.6"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            TEST_WITH_VALGRIND: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+  
+
+  - name: ruby-vanilla-system
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-2.6-system"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ubuntu, tag: xenial}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+            TEST_WITH_APT_REPO_RUBY: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-libxmlruby-system
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.6"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            BUNDLE_GEMFILE: "Gemfile-libxml-ruby"
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-libxmlruby-vendored
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-libxmlruby-system"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.6"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            BUNDLE_GEMFILE: "Gemfile-libxml-ruby"
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-libxmlruby-valgrind
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-libxmlruby-vendored"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.6"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            BUNDLE_GEMFILE: "Gemfile-libxml-ruby"
+            TEST_WITH_VALGRIND: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  
+  - name: jruby-9.1
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        attempts: 3
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: jruby, tag: "9.1-jdk"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            JAVA_OPTS: "-Dfile.encoding=UTF8" # https://github.com/docker-library/openjdk/issues/32
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+  
+  - name: jruby-9.2
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        attempts: 3
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: jruby, tag: "9.2-jdk"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            JAVA_OPTS: "-Dfile.encoding=UTF8" # https://github.com/docker-library/openjdk/issues/32
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+  
+
+  
+  - name: rbx-latest-system
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          image_resource:
+            type: docker-image
+            source: {repository: rubinius/docker, tag: "latest"}
+          platform: linux
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+            TMPDIR: /scratch
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+#        on_failure: *notify_failure_to_irc
+
+  - name: rbx-latest-vendored
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["rbx-latest-system"]
+      - task: rake-test
+        config:
+          image_resource:
+            type: docker-image
+            source: {repository: rubinius/docker, tag: "latest"}
+          platform: linux
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            TMPDIR: /scratch
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+#        on_failure: *notify_failure_to_irc
+  
+
+  
+  - name: win-ruby-2.3-devkit
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          platform: windows
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            WIN_RUBY_VERSION: "2.3"
+          run:
+            path: powershell
+            args: ["-File", "ci/concourse/tasks/rake-test/run.ps1"]
+  
+  - name: win-ruby-2.4-devkit
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          platform: windows
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            WIN_RUBY_VERSION: "2.4"
+          run:
+            path: powershell
+            args: ["-File", "ci/concourse/tasks/rake-test/run.ps1"]
+  
+  - name: win-ruby-2.5-devkit
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          platform: windows
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            WIN_RUBY_VERSION: "2.5"
+          run:
+            path: powershell
+            args: ["-File", "ci/concourse/tasks/rake-test/run.ps1"]
+  
+  - name: win-ruby-2.6-devkit
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          platform: windows
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            WIN_RUBY_VERSION: "2.6"
+          run:
+            path: powershell
+            args: ["-File", "ci/concourse/tasks/rake-test/run.ps1"]
+  
+
+  - name: build-success
+    public: true
+    disable_manual_trigger: true
+    plan:
+      - get: nokogiri
+        trigger: true
+        version: every
+        passed:
+          
+          - "ruby-2.3-valgrind"
+          
+          - "ruby-2.4-valgrind"
+          
+          - "ruby-2.5-valgrind"
+          
+          - "ruby-2.6-valgrind"
+          
+          
+          - "ruby-libxmlruby-valgrind"
+          
+          
+          - "jruby-9.1"
+          
+          - "jruby-9.2"
+          
+          # 
+          # - "rbx-latest-vendored"
+          # 
+          # 
+          # - "win-ruby-2.3-devkit"
+          # 
+      - put: nokogiri-irc
+        params: {message: "($BUILD_PIPELINE_NAME/$BUILD_JOB_NAME) The build passed ($BUILD_URL)"}
+
+  #
+  #  PRs
+  #
+  - name: pr-pending
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+      - put: nokogiri-pr
+        params: {path: nokogiri-pr, status: pending}
+
+  
+  - name: ruby-2.3-system-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: [pr-pending]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.3"}
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+
+  - name: ruby-2.3-vendored-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: [pr-pending]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.3"}
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+
+  - name: ruby-2.3-valgrind-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: [pr-pending]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.3"}
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          params:
+            TEST_WITH_VALGRIND: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+  
+  - name: ruby-2.6-system-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: [pr-pending]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.6"}
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+
+  - name: ruby-2.6-vendored-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: [pr-pending]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.6"}
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+
+  - name: ruby-2.6-valgrind-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: [pr-pending]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "2.6"}
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          params:
+            TEST_WITH_VALGRIND: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+  
+
+  - name: ruby-vanilla-system-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: ["ruby-2.6-system-pr"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ubuntu, tag: xenial}
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+            TEST_WITH_APT_REPO_RUBY: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+
+  - name: jruby-9.2-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: [pr-pending]
+      - task: rake-test
+        attempts: 3
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: jruby, tag: "9.2-jdk"}
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          params:
+            JAVA_OPTS: "-Dfile.encoding=UTF8" # https://github.com/docker-library/openjdk/issues/32
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+
+  - name: rbx-latest-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: [pr-pending]
+      - task: rake-test
+        config:
+          image_resource:
+            type: docker-image
+            source: {repository: rubinius/docker, tag: "latest"}
+          platform: linux
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+            TMPDIR: /scratch
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+#        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+
+  - name: pr-success
+    public: true
+    disable_manual_trigger: true
+    plan:
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed:
+          - ruby-2.3-system-pr
+          - ruby-2.3-vendored-pr
+          - ruby-2.3-valgrind-pr
+          - ruby-2.6-system-pr
+          - ruby-2.6-vendored-pr
+          - ruby-2.6-valgrind-pr
+          - ruby-vanilla-system-pr
+          - jruby-9.2-pr
+#          - rbx-latest-pr
+      - put: nokogiri-pr
+        params: {path: nokogiri-pr, status: success}
+      - put: nokogiri-irc
+        params: {message: "($BUILD_PIPELINE_NAME/$BUILD_JOB_NAME) The build passed ($BUILD_URL)"}
diff -pruN 1.8.4-1/concourse/nokogiri.yml 1.10.0+dfsg1-2/concourse/nokogiri.yml
--- 1.8.4-1/concourse/nokogiri.yml	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/concourse/nokogiri.yml	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,574 @@
+groups:
+  - name: master
+    jobs:
+      <% Concourse.production_rubies.each do |ruby_version| %>
+      - "ruby-<%= ruby_version %>-system"
+      - "ruby-<%= ruby_version %>-vendored"
+      - "ruby-<%= ruby_version %>-valgrind"
+      <% end %>
+      - "ruby-vanilla-system"
+      <% Concourse.production_rubies.last(1).each do |ruby_version| %>
+      - "ruby-libxmlruby-system"
+      - "ruby-libxmlruby-vendored"
+      - "ruby-libxmlruby-valgrind"
+      <% end %>
+      <% for jruby_version in RUBIES[:jruby] %>
+      - "jruby-<%= jruby_version %>"
+      <% end %>
+      <% RUBIES[:windows].first(1).each do |ruby_version| %>
+      - "win-ruby-<%= ruby_version %>-devkit"
+      <% end %>
+      - build-success
+
+
+  - name: PRs
+    jobs:
+      - pr-pending
+      - ruby-<%= Concourse.production_rubies.first %>-system-pr
+      - ruby-<%= Concourse.production_rubies.first %>-vendored-pr
+      - ruby-<%= Concourse.production_rubies.first %>-valgrind-pr
+      - ruby-<%= Concourse.production_rubies.last %>-system-pr
+      - ruby-<%= Concourse.production_rubies.last %>-vendored-pr
+      - ruby-<%= Concourse.production_rubies.last %>-valgrind-pr
+      - ruby-vanilla-system-pr
+      - jruby-<%= RUBIES[:jruby].last %>-pr
+      - pr-success
+
+  - name: allow-failure
+    jobs:
+      <% RUBIES[:windows][1..-1].each do |ruby_version| %>
+      - "win-ruby-<%= ruby_version %>-devkit"
+      <% end %>
+      <% for rbx_version in RUBIES[:rbx] %>
+      - "rbx-<%= rbx_version %>-system"
+      - "rbx-<%= rbx_version %>-vendored"
+      <% end %>
+      - rbx-<%= RUBIES[:rbx].last %>-pr
+      <% Concourse.rc_rubies.each do |ruby_version| %>
+      - "ruby-<%= ruby_version %>-system"
+      - "ruby-<%= ruby_version %>-vendored"
+      - "ruby-<%= ruby_version %>-valgrind"
+      <% end %>
+
+resource_types:
+- name: pull-request
+  type: docker-image
+  source:
+    repository: jtarchie/pr
+- name: irc-notification
+  type: docker-image
+  source:
+    repository: flavorjones/irc-notification-resource
+
+resources:
+  - name: ci
+    type: git
+    source:
+      uri: https://github.com/sparklemotion/nokogiri/
+      branch: master
+      disable_ci_skip: true # always get the latest pipeline configuration
+  - name: nokogiri
+    type: git
+    source:
+      uri: https://github.com/sparklemotion/nokogiri/
+      branch: master
+      ignore_paths:
+        - concourse/**
+        - suppressions/**
+  - name: nokogiri-pr
+    type: pull-request
+    source:
+      repo: sparklemotion/nokogiri
+      access_token: {{github-repo-status-access-token}}
+      ignore_paths:
+        - concourse/**
+        - suppressions/**
+  - name: nokogiri-irc
+    type: irc-notification
+    source:
+      server: chat.freenode.net
+      port: 7070
+      channel: "#nokogiri"
+      user: {{nokobot-irc-username}}
+      password: {{nokobot-irc-password}}
+
+anchors:
+  notify_failure_to_irc: &notify_failure_to_irc
+    put: nokogiri-irc
+    params: {message: "($BUILD_PIPELINE_NAME/$BUILD_JOB_NAME) The build failed ($BUILD_URL)"}
+  notify_failure_to_pr: &notify_failure_to_pr
+    put: nokogiri-pr
+    params: {path: nokogiri-pr, status: failure}
+
+jobs:
+  #
+  #  master
+  #
+  <% RUBIES[:mri].each do |ruby_version| %>
+  - name: ruby-<%= ruby_version %>-system
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "<%= ruby_version %>"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+% if Concourse.production_rubies.include? ruby_version
+        on_failure: *notify_failure_to_irc
+% end
+
+  - name: ruby-<%= ruby_version %>-vendored
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-<%= ruby_version %>-system"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "<%= ruby_version %>"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+% if Concourse.production_rubies.include? ruby_version
+        on_failure: *notify_failure_to_irc
+% end
+
+  - name: ruby-<%= ruby_version %>-valgrind
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-<%= ruby_version %>-vendored"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "<%= ruby_version %>"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            TEST_WITH_VALGRIND: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+% if Concourse.production_rubies.include? ruby_version
+        on_failure: *notify_failure_to_irc
+% end
+  <% end %>
+
+% ruby_version = Concourse.production_rubies.last
+  - name: ruby-vanilla-system
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-<%= ruby_version %>-system"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ubuntu, tag: xenial}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+            TEST_WITH_APT_REPO_RUBY: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-libxmlruby-system
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "<%= ruby_version %>"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            BUNDLE_GEMFILE: "Gemfile-libxml-ruby"
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-libxmlruby-vendored
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-libxmlruby-system"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "<%= ruby_version %>"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            BUNDLE_GEMFILE: "Gemfile-libxml-ruby"
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  - name: ruby-libxmlruby-valgrind
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["ruby-libxmlruby-vendored"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "<%= ruby_version %>"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            BUNDLE_GEMFILE: "Gemfile-libxml-ruby"
+            TEST_WITH_VALGRIND: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+
+  <% for jruby_version in RUBIES[:jruby] %>
+  - name: jruby-<%= jruby_version %>
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        attempts: 3
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: jruby, tag: "<%= jruby_version %>-jdk"}
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            JAVA_OPTS: "-Dfile.encoding=UTF8" # https://github.com/docker-library/openjdk/issues/32
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: *notify_failure_to_irc
+  <% end %>
+
+  <% for rbx_version in RUBIES[:rbx] %>
+  - name: rbx-<%= rbx_version %>-system
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          image_resource:
+            type: docker-image
+            source: {repository: rubinius/docker, tag: "<%= rbx_version %>"}
+          platform: linux
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+            TMPDIR: /scratch
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+#        on_failure: *notify_failure_to_irc
+
+  - name: rbx-<%= rbx_version %>-vendored
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+        passed: ["rbx-<%= rbx_version %>-system"]
+      - task: rake-test
+        config:
+          image_resource:
+            type: docker-image
+            source: {repository: rubinius/docker, tag: "<%= rbx_version %>"}
+          platform: linux
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            TMPDIR: /scratch
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+#        on_failure: *notify_failure_to_irc
+  <% end %>
+
+  <% for ruby_version in RUBIES[:windows] %>
+  - name: win-ruby-<%= ruby_version %>-devkit
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri
+        trigger: true
+      - task: rake-test
+        config:
+          platform: windows
+          inputs:
+            - name: ci
+            - name: nokogiri
+          params:
+            WIN_RUBY_VERSION: "<%= ruby_version %>"
+          run:
+            path: powershell
+            args: ["-File", "ci/concourse/tasks/rake-test/run.ps1"]
+  <% end %>
+
+  - name: build-success
+    public: true
+    disable_manual_trigger: true
+    plan:
+      - get: nokogiri
+        trigger: true
+        version: every
+        passed:
+          <% Concourse.production_rubies.each do |ruby_version| %>
+          - "ruby-<%= ruby_version %>-valgrind"
+          <% end %>
+          <% Concourse.production_rubies.last(1).each do |ruby_version| %>
+          - "ruby-libxmlruby-valgrind"
+          <% end %>
+          <% for jruby_version in RUBIES[:jruby] %>
+          - "jruby-<%= jruby_version %>"
+          <% end %>
+          # <% for rbx_version in RUBIES[:rbx] %>
+          # - "rbx-<%= rbx_version %>-vendored"
+          # <% end %>
+          # <% RUBIES[:windows].first(1).each do |ruby_version| %>
+          # - "win-ruby-<%= ruby_version %>-devkit"
+          # <% end %>
+      - put: nokogiri-irc
+        params: {message: "($BUILD_PIPELINE_NAME/$BUILD_JOB_NAME) The build passed ($BUILD_URL)"}
+
+  #
+  #  PRs
+  #
+  - name: pr-pending
+    public: true
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+      - put: nokogiri-pr
+        params: {path: nokogiri-pr, status: pending}
+
+  <% [Concourse.production_rubies.first, Concourse.production_rubies.last].each do |ruby_version| %>
+  - name: ruby-<%= ruby_version %>-system-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: [pr-pending]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "<%= ruby_version %>"}
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+
+  - name: ruby-<%= ruby_version %>-vendored-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: [pr-pending]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "<%= ruby_version %>"}
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+
+  - name: ruby-<%= ruby_version %>-valgrind-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: [pr-pending]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ruby, tag: "<%= ruby_version %>"}
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          params:
+            TEST_WITH_VALGRIND: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+  <% end %>
+
+% ruby_version = Concourse.production_rubies.last
+  - name: ruby-vanilla-system-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: ["ruby-<%= ruby_version %>-system-pr"]
+      - task: rake-test
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: ubuntu, tag: xenial}
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+            TEST_WITH_APT_REPO_RUBY: t
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+
+% jruby_version = RUBIES[:jruby].last
+  - name: jruby-<%= jruby_version %>-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: [pr-pending]
+      - task: rake-test
+        attempts: 3
+        config:
+          platform: linux
+          image_resource:
+            type: docker-image
+            source: {repository: jruby, tag: "<%= jruby_version %>-jdk"}
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          params:
+            JAVA_OPTS: "-Dfile.encoding=UTF8" # https://github.com/docker-library/openjdk/issues/32
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+
+% rbx_version = RUBIES[:rbx].last
+  - name: rbx-<%= rbx_version %>-pr
+    public: true
+    max_in_flight: 1
+    plan:
+      - get: ci
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed: [pr-pending]
+      - task: rake-test
+        config:
+          image_resource:
+            type: docker-image
+            source: {repository: rubinius/docker, tag: "<%= rbx_version %>"}
+          platform: linux
+          inputs:
+            - name: ci
+            - name: nokogiri-pr
+              path: nokogiri
+          params:
+            NOKOGIRI_USE_SYSTEM_LIBRARIES: t
+            TMPDIR: /scratch
+          run:
+            path: ci/concourse/tasks/rake-test/run.sh
+#        on_failure: { aggregate: [*notify_failure_to_irc, *notify_failure_to_pr] }
+
+  - name: pr-success
+    public: true
+    disable_manual_trigger: true
+    plan:
+      - get: nokogiri-pr
+        trigger: true
+        version: every
+        passed:
+          - ruby-<%= Concourse.production_rubies.first %>-system-pr
+          - ruby-<%= Concourse.production_rubies.first %>-vendored-pr
+          - ruby-<%= Concourse.production_rubies.first %>-valgrind-pr
+          - ruby-<%= Concourse.production_rubies.last %>-system-pr
+          - ruby-<%= Concourse.production_rubies.last %>-vendored-pr
+          - ruby-<%= Concourse.production_rubies.last %>-valgrind-pr
+          - ruby-vanilla-system-pr
+          - jruby-<%= RUBIES[:jruby].last %>-pr
+#          - rbx-<%= RUBIES[:rbx].last %>-pr
+      - put: nokogiri-pr
+        params: {path: nokogiri-pr, status: success}
+      - put: nokogiri-irc
+        params: {message: "($BUILD_PIPELINE_NAME/$BUILD_JOB_NAME) The build passed ($BUILD_URL)"}
diff -pruN 1.8.4-1/concourse/tasks/rake-test/run.ps1 1.10.0+dfsg1-2/concourse/tasks/rake-test/run.ps1
--- 1.8.4-1/concourse/tasks/rake-test/run.ps1	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/concourse/tasks/rake-test/run.ps1	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,11 @@
+. "c:\var\vcap\packages\windows-ruby-dev-tools\prelude.ps1"
+
+$env:RUBYOPT = "-rdevkit"
+
+push-location nokogiri
+
+    system-cmd "gem install bundler"
+    system-cmd "bundle install"
+    system-cmd "bundle exec rake test"
+
+pop-location
diff -pruN 1.8.4-1/concourse/tasks/rake-test/run.sh 1.10.0+dfsg1-2/concourse/tasks/rake-test/run.sh
--- 1.8.4-1/concourse/tasks/rake-test/run.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/concourse/tasks/rake-test/run.sh	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,87 @@
+#! /usr/bin/env bash
+
+set -e -x -u
+
+APT_UPDATED=false
+
+function ensure-apt-update {
+  if [[ $APT_UPDATED != "false" ]] ; then
+    return
+  fi
+
+  apt-get update
+  APT_UPDATED=true
+}
+
+if [[ ${TEST_WITH_APT_REPO_RUBY:-} != "" ]] ; then
+  ensure-apt-update
+  apt-get install -y ruby ruby-dev bundler libxslt-dev libxml2-dev pkg-config
+fi
+
+VERSION_INFO=$(ruby -v)
+RUBY_ENGINE=$(cut -d" " -f1 <<< "${VERSION_INFO}")
+RUBY_VERSION=$(cut -d" " -f2 <<< "${VERSION_INFO}")
+
+FROZEN_STRING_REF="53f9b66"
+
+function mri-24-or-greater {
+  if [[ $RUBY_ENGINE != "ruby" ]] ; then
+    return 1
+  fi
+
+  if echo $RUBY_VERSION | grep "^[0-2]\.[0-3]\." > /dev/null ; then
+    return 1
+  fi
+
+  return 0
+}
+
+function commit-is-post-frozen-string-support {
+  if git merge-base --is-ancestor ${FROZEN_STRING_REF} HEAD ; then
+    return 0
+  fi
+  return 1
+}
+
+function rbx-engine {
+  if [[ $RUBY_ENGINE == "rubinius" ]] ; then
+    return 0
+  fi
+  return 1
+}
+
+pushd nokogiri
+
+  RAKE_TASK="test"
+
+  if rbx-engine ; then
+    ensure-apt-update
+    apt-get install -y ca-certificates gcc pkg-config libxml2-dev libxslt-dev patch
+  fi
+
+  if [[ ${TEST_WITH_VALGRIND:-} != "" ]] ; then
+    ensure-apt-update
+    apt-get install -y valgrind
+  fi
+
+  bundle install
+  bundle exec rake generate # do this before setting frozen string option, because racc isn't compatible with frozen string literals yet
+
+  if mri-24-or-greater && commit-is-post-frozen-string-support ; then
+    export RUBYOPT="--enable-frozen-string-literal --debug=frozen-string-literal"
+  fi
+
+  if [[ ${TEST_WITH_VALGRIND:-} != "" ]] ; then
+    RAKE_TASK="test:valgrind" # override
+    export TESTOPTS="-v" # see more verbose output to help narrow down warnings
+
+    # always use the CI suppressions if they exist
+    if [[ -d ../ci/suppressions ]] ; then
+      rm -rf suppressions
+      cp -var ../ci/suppressions .
+    fi
+  fi
+
+  bundle exec rake ${RAKE_TASK}
+
+popd
diff -pruN 1.8.4-1/concourse/TODO.md 1.10.0+dfsg1-2/concourse/TODO.md
--- 1.8.4-1/concourse/TODO.md	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/concourse/TODO.md	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,48 @@
+# nokogiri concourse to-do
+
+## concourse.yml
+
+* [x] real ssl cert
+* [x] github authentication
+* [x] bastion host
+* [x] upgrade bbl
+
+## nokogiri.yml
+
+* [x] test using system libraries
+* [x] handle pull requests
+* [x] run windows tests under devkit
+* [ ] osx
+  * system
+  * system-homebrew
+  * vendored
+* [ ] build an rc gem and upload to rubygems [→ rubygems resource]
+  * should always check manifest
+* install gem and test:
+  * [ ] osx
+  * [ ] linux (system)
+  * [ ] linux (vendored)
+  * [ ] linux (vendored, --disable-static)
+  * [ ] OpenSuse with site_config (lib64, #1562)
+  * [ ] windows (fat binary)
+  * [ ] windows (devkit)
+* notifications on failure / success
+  * [x] irc [→ irc resource]
+
+## other projects
+
+* [x] pipeline: mini_portile [→ bosh release]
+* [x] pipeline: chromedriver-helper
+* [x] bosh release for windows worker config:
+  * [ ] ruby of all supported versions
+  * [ ] devkit installed in all rubies
+  * [ ] cmake
+* [x] resource: irc
+* [ ] resource: rubygems
+
+## nokogiri stretch goals
+
+* [ ] get openbsd / freebsd / etc. people to donate worker machines
+* [ ] use an S3 bucket for sub-artifacts:
+  * source tarballs
+  * compiled .dlls
diff -pruN 1.8.4-1/.cross_rubies 1.10.0+dfsg1-2/.cross_rubies
--- 1.8.4-1/.cross_rubies	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/.cross_rubies	2019-01-04 15:20:12.000000000 +0000
@@ -1,8 +1,8 @@
+2.6.0:i686-w64-mingw32
+2.6.0:x86_64-w64-mingw32
 2.5.0:i686-w64-mingw32
 2.5.0:x86_64-w64-mingw32
 2.4.0:i686-w64-mingw32
 2.4.0:x86_64-w64-mingw32
 2.3.0:i686-w64-mingw32
 2.3.0:x86_64-w64-mingw32
-2.2.2:i686-w64-mingw32
-2.2.2:x86_64-w64-mingw32
diff -pruN 1.8.4-1/debian/changelog 1.10.0+dfsg1-2/debian/changelog
--- 1.8.4-1/debian/changelog	2018-07-30 22:56:29.000000000 +0000
+++ 1.10.0+dfsg1-2/debian/changelog	2019-02-08 08:54:03.000000000 +0000
@@ -1,3 +1,47 @@
+ruby-nokogiri (1.10.0+dfsg1-2) unstable; urgency=medium
+
+  * Team upload
+  * Add Breaks for ruby-sanitize
+
+ -- Utkarsh Gupta <guptautkarsh2102@gmail.com>  Fri, 08 Feb 2019 14:24:03 +0530
+
+ruby-nokogiri (1.10.0+dfsg1-1) unstable; urgency=medium
+
+  [ Cédric Boutillier ]
+  * New upstream version 1.10.0+dfsg1
+  * Use Github as source
+  * Update Files-Excluded paragraph in copyright file to exclude jar files
+    from source
+  * Remove ourdated Readme.source file
+  * Refresh 003-Remove-dependency-on-mini_portile2.patch
+  * Create rule to retrieve gemspec from corresponding .gem
+
+  [ Utkarsh Gupta ]
+  * Bump Standards-Version to 4.3.0 (no changes needed)
+  * Update watch and rules file
+
+ -- Cédric Boutillier <boutil@debian.org>  Wed, 09 Jan 2019 10:15:39 +0100
+
+ruby-nokogiri (1.10.0-1) unstable; urgency=medium
+
+  * Team upload
+  * New upstream version 1.10.0
+  * Update patch file names with the serial number
+  * Refresh patches
+
+ -- Utkarsh Gupta <guptautkarsh2102@gmail.com>  Tue, 08 Jan 2019 07:41:47 +0530
+
+ruby-nokogiri (1.9.1-1) unstable; urgency=medium
+
+  * Team upload
+  * New upstream version 1.9.1
+  * Bump Standards-Version to 4.3.0 (no changes needed)
+  * Move debian/watch to gemwatch.debian.net
+  * Refresh patches
+  * Update debian/rules
+
+ -- Utkarsh Gupta <guptautkarsh2102@gmail.com>  Fri, 04 Jan 2019 00:28:17 +0530
+
 ruby-nokogiri (1.8.4-1) unstable; urgency=medium
 
   * New upstream version 1.8.4
diff -pruN 1.8.4-1/debian/control 1.10.0+dfsg1-2/debian/control
--- 1.8.4-1/debian/control	2018-07-30 22:56:29.000000000 +0000
+++ 1.10.0+dfsg1-2/debian/control	2019-02-08 08:54:03.000000000 +0000
@@ -14,7 +14,7 @@ Build-Depends: debhelper (>= 11~),
                rake,
                ruby-minitest,
                ruby-pkg-config
-Standards-Version: 4.1.5
+Standards-Version: 4.3.0
 Vcs-Git: https://salsa.debian.org/ruby-team/ruby-nokogiri.git
 Vcs-Browser: https://salsa.debian.org/ruby-team/ruby-nokogiri
 Homepage: http://nokogiri.org
@@ -28,6 +28,7 @@ Depends: ruby | ruby-interpreter,
          ruby-pkg-config (>= 1.1.7),
          ${misc:Depends},
          ${shlibs:Depends}
+Breaks: ruby-sanitize (<< 4.6.6-2)
 Description: HTML, XML, SAX, and Reader parser for Ruby
  Nokogiri (鋸) is an HTML, XML, SAX, and Reader parser.  It is able to
  search documents via XPath or CSS3 selectors, and is a drop-in replacement
diff -pruN 1.8.4-1/debian/copyright 1.10.0+dfsg1-2/debian/copyright
--- 1.8.4-1/debian/copyright	2018-07-30 22:56:29.000000000 +0000
+++ 1.10.0+dfsg1-2/debian/copyright	2019-02-08 08:54:03.000000000 +0000
@@ -1,10 +1,8 @@
 Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 Upstream-Name: nokogiri
 Homepage: http://nokogiri.org/
-Files-Excluded: ports/archives/
-Comment: the upstream tarball is repacked to remove ports/archives/
- containing of convenience copies of the libraries
- libxml2 (version 2.8.0) and libxslt (version 1.1.26)
+Files-Excluded: lib/*.jar
+Comment: remove convenience copy of java libraries
 
 Files: *
 Copyright: 2008-2012 Aaron Patterson <aaron.patterson@gmail.com>
diff -pruN 1.8.4-1/debian/gemspec 1.10.0+dfsg1-2/debian/gemspec
--- 1.8.4-1/debian/gemspec	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/debian/gemspec	2019-02-08 08:54:03.000000000 +0000
@@ -0,0 +1,73 @@
+# -*- encoding: utf-8 -*-
+# stub: nokogiri 1.10.0 ruby lib
+# stub: ext/nokogiri/extconf.rb
+
+Gem::Specification.new do |s|
+  s.name = "nokogiri".freeze
+  s.version = "1.10.0"
+
+  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
+  s.require_paths = ["lib".freeze]
+  s.authors = ["Aaron Patterson".freeze, "Mike Dalessio".freeze, "Yoko Harada".freeze, "Tim Elliott".freeze, "Akinori MUSHA".freeze, "John Shahid".freeze, "Lars Kanis".freeze]
+  s.date = "2019-01-04"
+  s.description = "Nokogiri (\u92F8) is an HTML, XML, SAX, and Reader parser.  Among\nNokogiri's many features is the ability to search documents via XPath\nor CSS3 selectors.".freeze
+  s.email = ["aaronp@rubyforge.org".freeze, "mike.dalessio@gmail.com".freeze, "yokolet@gmail.com".freeze, "tle@holymonkey.com".freeze, "knu@idaemons.org".freeze, "jvshahid@gmail.com".freeze, "lars@greiz-reinsdorf.de".freeze]
+  s.executables = ["nokogiri".freeze]
+  s.extensions = ["ext/nokogiri/extconf.rb".freeze]
+  s.extra_rdoc_files = ["LICENSE-DEPENDENCIES.md".freeze, "LICENSE.md".freeze, "README.md".freeze, "ext/nokogiri/html_document.c".freeze, "ext/nokogiri/html_element_description.c".freeze, "ext/nokogiri/html_entity_lookup.c".freeze, "ext/nokogiri/html_sax_parser_context.c".freeze, "ext/nokogiri/html_sax_push_parser.c".freeze, "ext/nokogiri/nokogiri.c".freeze, "ext/nokogiri/xml_attr.c".freeze, "ext/nokogiri/xml_attribute_decl.c".freeze, "ext/nokogiri/xml_cdata.c".freeze, "ext/nokogiri/xml_comment.c".freeze, "ext/nokogiri/xml_document.c".freeze, "ext/nokogiri/xml_document_fragment.c".freeze, "ext/nokogiri/xml_dtd.c".freeze, "ext/nokogiri/xml_element_content.c".freeze, "ext/nokogiri/xml_element_decl.c".freeze, "ext/nokogiri/xml_encoding_handler.c".freeze, "ext/nokogiri/xml_entity_decl.c".freeze, "ext/nokogiri/xml_entity_reference.c".freeze, "ext/nokogiri/xml_io.c".freeze, "ext/nokogiri/xml_libxml2_hacks.c".freeze, "ext/nokogiri/xml_namespace.c".freeze, "ext/nokogiri/xml_node.c".freeze, "ext/nokogiri/xml_node_set.c".freeze, "ext/nokogiri/xml_processing_instruction.c".freeze, "ext/nokogiri/xml_reader.c".freeze, "ext/nokogiri/xml_relax_ng.c".freeze, "ext/nokogiri/xml_sax_parser.c".freeze, "ext/nokogiri/xml_sax_parser_context.c".freeze, "ext/nokogiri/xml_sax_push_parser.c".freeze, "ext/nokogiri/xml_schema.c".freeze, "ext/nokogiri/xml_syntax_error.c".freeze, "ext/nokogiri/xml_text.c".freeze, "ext/nokogiri/xml_xpath_context.c".freeze, "ext/nokogiri/xslt_stylesheet.c".freeze]
+  s.files = ["LICENSE-DEPENDENCIES.md".freeze, "LICENSE.md".freeze, "README.md".freeze, "bin/nokogiri".freeze, "dependencies.yml".freeze, "ext/nokogiri/depend".freeze, "ext/nokogiri/extconf.rb".freeze, "ext/nokogiri/html_document.c".freeze, "ext/nokogiri/html_document.h".freeze, "ext/nokogiri/html_element_description.c".freeze, "ext/nokogiri/html_element_description.h".freeze, "ext/nokogiri/html_entity_lookup.c".freeze, "ext/nokogiri/html_entity_lookup.h".freeze, "ext/nokogiri/html_sax_parser_context.c".freeze, "ext/nokogiri/html_sax_parser_context.h".freeze, "ext/nokogiri/html_sax_push_parser.c".freeze, "ext/nokogiri/html_sax_push_parser.h".freeze, "ext/nokogiri/nokogiri.c".freeze, "ext/nokogiri/nokogiri.h".freeze, "ext/nokogiri/xml_attr.c".freeze, "ext/nokogiri/xml_attr.h".freeze, "ext/nokogiri/xml_attribute_decl.c".freeze, "ext/nokogiri/xml_attribute_decl.h".freeze, "ext/nokogiri/xml_cdata.c".freeze, "ext/nokogiri/xml_cdata.h".freeze, "ext/nokogiri/xml_comment.c".freeze, "ext/nokogiri/xml_comment.h".freeze, "ext/nokogiri/xml_document.c".freeze, "ext/nokogiri/xml_document.h".freeze, "ext/nokogiri/xml_document_fragment.c".freeze, "ext/nokogiri/xml_document_fragment.h".freeze, "ext/nokogiri/xml_dtd.c".freeze, "ext/nokogiri/xml_dtd.h".freeze, "ext/nokogiri/xml_element_content.c".freeze, "ext/nokogiri/xml_element_content.h".freeze, "ext/nokogiri/xml_element_decl.c".freeze, "ext/nokogiri/xml_element_decl.h".freeze, "ext/nokogiri/xml_encoding_handler.c".freeze, "ext/nokogiri/xml_encoding_handler.h".freeze, "ext/nokogiri/xml_entity_decl.c".freeze, "ext/nokogiri/xml_entity_decl.h".freeze, "ext/nokogiri/xml_entity_reference.c".freeze, "ext/nokogiri/xml_entity_reference.h".freeze, "ext/nokogiri/xml_io.c".freeze, "ext/nokogiri/xml_io.h".freeze, "ext/nokogiri/xml_libxml2_hacks.c".freeze, "ext/nokogiri/xml_libxml2_hacks.h".freeze, "ext/nokogiri/xml_namespace.c".freeze, "ext/nokogiri/xml_namespace.h".freeze, "ext/nokogiri/xml_node.c".freeze, "ext/nokogiri/xml_node.h".freeze, "ext/nokogiri/xml_node_set.c".freeze, "ext/nokogiri/xml_node_set.h".freeze, "ext/nokogiri/xml_processing_instruction.c".freeze, "ext/nokogiri/xml_processing_instruction.h".freeze, "ext/nokogiri/xml_reader.c".freeze, "ext/nokogiri/xml_reader.h".freeze, "ext/nokogiri/xml_relax_ng.c".freeze, "ext/nokogiri/xml_relax_ng.h".freeze, "ext/nokogiri/xml_sax_parser.c".freeze, "ext/nokogiri/xml_sax_parser.h".freeze, "ext/nokogiri/xml_sax_parser_context.c".freeze, "ext/nokogiri/xml_sax_parser_context.h".freeze, "ext/nokogiri/xml_sax_push_parser.c".freeze, "ext/nokogiri/xml_sax_push_parser.h".freeze, "ext/nokogiri/xml_schema.c".freeze, "ext/nokogiri/xml_schema.h".freeze, "ext/nokogiri/xml_syntax_error.c".freeze, "ext/nokogiri/xml_syntax_error.h".freeze, "ext/nokogiri/xml_text.c".freeze, "ext/nokogiri/xml_text.h".freeze, "ext/nokogiri/xml_xpath_context.c".freeze, "ext/nokogiri/xml_xpath_context.h".freeze, "ext/nokogiri/xslt_stylesheet.c".freeze, "ext/nokogiri/xslt_stylesheet.h".freeze, "lib/nokogiri.rb".freeze, "lib/nokogiri/css.rb".freeze, "lib/nokogiri/css/node.rb".freeze, "lib/nokogiri/css/parser.rb".freeze, "lib/nokogiri/css/parser.y".freeze, "lib/nokogiri/css/parser_extras.rb".freeze, "lib/nokogiri/css/syntax_error.rb".freeze, "lib/nokogiri/css/tokenizer.rb".freeze, "lib/nokogiri/css/tokenizer.rex".freeze, "lib/nokogiri/css/xpath_visitor.rb".freeze, "lib/nokogiri/decorators/slop.rb".freeze, "lib/nokogiri/html.rb".freeze, "lib/nokogiri/html/builder.rb".freeze, "lib/nokogiri/html/document.rb".freeze, "lib/nokogiri/html/document_fragment.rb".freeze, "lib/nokogiri/html/element_description.rb".freeze, "lib/nokogiri/html/element_description_defaults.rb".freeze, "lib/nokogiri/html/entity_lookup.rb".freeze, "lib/nokogiri/html/sax/parser.rb".freeze, "lib/nokogiri/html/sax/parser_context.rb".freeze, "lib/nokogiri/html/sax/push_parser.rb".freeze, "lib/nokogiri/syntax_error.rb".freeze, "lib/nokogiri/version.rb".freeze, "lib/nokogiri/xml.rb".freeze, "lib/nokogiri/xml/attr.rb".freeze, "lib/nokogiri/xml/attribute_decl.rb".freeze, "lib/nokogiri/xml/builder.rb".freeze, "lib/nokogiri/xml/cdata.rb".freeze, "lib/nokogiri/xml/character_data.rb".freeze, "lib/nokogiri/xml/document.rb".freeze, "lib/nokogiri/xml/document_fragment.rb".freeze, "lib/nokogiri/xml/dtd.rb".freeze, "lib/nokogiri/xml/element_content.rb".freeze, "lib/nokogiri/xml/element_decl.rb".freeze, "lib/nokogiri/xml/entity_decl.rb".freeze, "lib/nokogiri/xml/entity_reference.rb".freeze, "lib/nokogiri/xml/namespace.rb".freeze, "lib/nokogiri/xml/node.rb".freeze, "lib/nokogiri/xml/node/save_options.rb".freeze, "lib/nokogiri/xml/node_set.rb".freeze, "lib/nokogiri/xml/notation.rb".freeze, "lib/nokogiri/xml/parse_options.rb".freeze, "lib/nokogiri/xml/pp.rb".freeze, "lib/nokogiri/xml/pp/character_data.rb".freeze, "lib/nokogiri/xml/pp/node.rb".freeze, "lib/nokogiri/xml/processing_instruction.rb".freeze, "lib/nokogiri/xml/reader.rb".freeze, "lib/nokogiri/xml/relax_ng.rb".freeze, "lib/nokogiri/xml/sax.rb".freeze, "lib/nokogiri/xml/sax/document.rb".freeze, "lib/nokogiri/xml/sax/parser.rb".freeze, "lib/nokogiri/xml/sax/parser_context.rb".freeze, "lib/nokogiri/xml/sax/push_parser.rb".freeze, "lib/nokogiri/xml/schema.rb".freeze, "lib/nokogiri/xml/searchable.rb".freeze, "lib/nokogiri/xml/syntax_error.rb".freeze, "lib/nokogiri/xml/text.rb".freeze, "lib/nokogiri/xml/xpath.rb".freeze, "lib/nokogiri/xml/xpath/syntax_error.rb".freeze, "lib/nokogiri/xml/xpath_context.rb".freeze, "lib/nokogiri/xslt.rb".freeze, "lib/nokogiri/xslt/stylesheet.rb".freeze, "lib/xsd/xmlparser/nokogiri.rb".freeze, "patches/libxml2/0001-Revert-Do-not-URI-escape-in-server-side-includes.patch".freeze, "ports/archives/libxml2-2.9.9.tar.gz".freeze, "ports/archives/libxslt-1.1.33.tar.gz".freeze]
+  s.licenses = ["MIT".freeze]
+  s.rdoc_options = ["--main".freeze, "README.md".freeze]
+  s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze)
+  s.rubygems_version = "2.7.6".freeze
+  s.summary = "Nokogiri (\u92F8) is an HTML, XML, SAX, and Reader parser".freeze
+
+  if s.respond_to? :specification_version then
+    s.specification_version = 4
+
+    if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+      s.add_development_dependency(%q<hoe-bundler>.freeze, ["~> 1.2"])
+      s.add_development_dependency(%q<hoe-debugging>.freeze, ["~> 2.0"])
+      s.add_development_dependency(%q<hoe-gemspec>.freeze, ["~> 1.0"])
+      s.add_development_dependency(%q<hoe-git>.freeze, ["~> 1.6"])
+      s.add_development_dependency(%q<minitest>.freeze, ["~> 5.8.4"])
+      s.add_development_dependency(%q<rake>.freeze, ["~> 12.0"])
+      s.add_development_dependency(%q<rake-compiler>.freeze, ["~> 1.0.3"])
+      s.add_development_dependency(%q<rake-compiler-dock>.freeze, ["~> 0.7.0"])
+      s.add_development_dependency(%q<racc>.freeze, ["~> 1.4.14"])
+      s.add_development_dependency(%q<rexical>.freeze, ["~> 1.0.5"])
+      s.add_development_dependency(%q<concourse>.freeze, ["~> 0.15"])
+      s.add_development_dependency(%q<rdoc>.freeze, ["< 7", ">= 4.0"])
+      s.add_development_dependency(%q<hoe>.freeze, ["~> 3.17"])
+    else
+      s.add_dependency(%q<hoe-bundler>.freeze, ["~> 1.2"])
+      s.add_dependency(%q<hoe-debugging>.freeze, ["~> 2.0"])
+      s.add_dependency(%q<hoe-gemspec>.freeze, ["~> 1.0"])
+      s.add_dependency(%q<hoe-git>.freeze, ["~> 1.6"])
+      s.add_dependency(%q<minitest>.freeze, ["~> 5.8.4"])
+      s.add_dependency(%q<rake>.freeze, ["~> 12.0"])
+      s.add_dependency(%q<rake-compiler>.freeze, ["~> 1.0.3"])
+      s.add_dependency(%q<rake-compiler-dock>.freeze, ["~> 0.7.0"])
+      s.add_dependency(%q<racc>.freeze, ["~> 1.4.14"])
+      s.add_dependency(%q<rexical>.freeze, ["~> 1.0.5"])
+      s.add_dependency(%q<concourse>.freeze, ["~> 0.15"])
+      s.add_dependency(%q<rdoc>.freeze, ["< 7", ">= 4.0"])
+      s.add_dependency(%q<hoe>.freeze, ["~> 3.17"])
+    end
+  else
+    s.add_dependency(%q<hoe-bundler>.freeze, ["~> 1.2"])
+    s.add_dependency(%q<hoe-debugging>.freeze, ["~> 2.0"])
+    s.add_dependency(%q<hoe-gemspec>.freeze, ["~> 1.0"])
+    s.add_dependency(%q<hoe-git>.freeze, ["~> 1.6"])
+    s.add_dependency(%q<minitest>.freeze, ["~> 5.8.4"])
+    s.add_dependency(%q<rake>.freeze, ["~> 12.0"])
+    s.add_dependency(%q<rake-compiler>.freeze, ["~> 1.0.3"])
+    s.add_dependency(%q<rake-compiler-dock>.freeze, ["~> 0.7.0"])
+    s.add_dependency(%q<racc>.freeze, ["~> 1.4.14"])
+    s.add_dependency(%q<rexical>.freeze, ["~> 1.0.5"])
+    s.add_dependency(%q<concourse>.freeze, ["~> 0.15"])
+    s.add_dependency(%q<rdoc>.freeze, ["< 7", ">= 4.0"])
+    s.add_dependency(%q<hoe>.freeze, ["~> 3.17"])
+  end
+end
+
diff -pruN 1.8.4-1/debian/patches/0003-Remove-dependency-on-mini_portile2.patch 1.10.0+dfsg1-2/debian/patches/0003-Remove-dependency-on-mini_portile2.patch
--- 1.8.4-1/debian/patches/0003-Remove-dependency-on-mini_portile2.patch	2018-07-30 22:56:29.000000000 +0000
+++ 1.10.0+dfsg1-2/debian/patches/0003-Remove-dependency-on-mini_portile2.patch	2019-02-08 08:54:03.000000000 +0000
@@ -12,42 +12,16 @@ Signed-off-by: Antonio Terceiro <terceir
 
 --- a/Rakefile
 +++ b/Rakefile
-@@ -125,12 +125,6 @@
+@@ -124,12 +124,6 @@
    ]
    self.clean_globs += Dir.glob("ports/*").reject { |d| d =~ %r{/archives$} }
  
 -  unless java?
 -    self.extra_deps += [
--      ["mini_portile2",    "~> 2.3.0"], # keep version in sync with extconf.rb
+-      ["mini_portile2",    "~> 2.4.0"], # keep version in sync with extconf.rb
 -    ]
 -  end
 -
    self.extra_dev_deps += [
      ["hoe-bundler",        "~> 1.2"],
-     ["hoe-debugging",      "~> 1.4"],
---- a/nokogiri.gemspec
-+++ b/nokogiri.gemspec
-@@ -35,7 +35,6 @@
-       s.add_development_dependency(%q<hoe-debugging>.freeze, ["~> 1.4"])
-       s.add_development_dependency(%q<hoe-gemspec>.freeze, ["~> 1.0"])
-       s.add_development_dependency(%q<hoe-git>.freeze, ["~> 1.6"])
--      s.add_runtime_dependency(%q<mini_portile2>.freeze, ["~> 2.3.0"])
-       s.add_development_dependency(%q<minitest>.freeze, ["~> 5.8.4"])
-       s.add_development_dependency(%q<racc>.freeze, ["~> 1.4.14"])
-       s.add_development_dependency(%q<rake>.freeze, ["~> 12.0"])
-@@ -50,7 +49,6 @@
-       s.add_dependency(%q<hoe-debugging>.freeze, ["~> 1.4"])
-       s.add_dependency(%q<hoe-gemspec>.freeze, ["~> 1.0"])
-       s.add_dependency(%q<hoe-git>.freeze, ["~> 1.6"])
--      s.add_dependency(%q<mini_portile2>.freeze, ["~> 2.3.0"])
-       s.add_dependency(%q<minitest>.freeze, ["~> 5.8.4"])
-       s.add_dependency(%q<racc>.freeze, ["~> 1.4.14"])
-       s.add_dependency(%q<rake>.freeze, ["~> 12.0"])
-@@ -66,7 +64,6 @@
-     s.add_dependency(%q<hoe-debugging>.freeze, ["~> 1.4"])
-     s.add_dependency(%q<hoe-gemspec>.freeze, ["~> 1.0"])
-     s.add_dependency(%q<hoe-git>.freeze, ["~> 1.6"])
--    s.add_dependency(%q<mini_portile2>.freeze, ["~> 2.3.0"])
-     s.add_dependency(%q<minitest>.freeze, ["~> 5.8.4"])
-     s.add_dependency(%q<racc>.freeze, ["~> 1.4.14"])
-     s.add_dependency(%q<rake>.freeze, ["~> 12.0"])
+     ["hoe-debugging",      "~> 2.0"],
diff -pruN 1.8.4-1/debian/README.source 1.10.0+dfsg1-2/debian/README.source
--- 1.8.4-1/debian/README.source	2018-07-30 22:56:29.000000000 +0000
+++ 1.10.0+dfsg1-2/debian/README.source	2019-02-08 08:54:03.000000000 +0000
@@ -1,12 +1,13 @@
-This package uses the Debian 3.0 (quilt) source package format.
+The ruby-nokogiri package uses the Github repository to fetch the test
+suite. Upstream does not want to provide a gemspec file in the
+repository for reasons explained in Y_U_NO_GEMSPEC.md. As it is crucial
+for integration between rubygems and Debian package management system,
+we decide to retrieve the gemspec file from the corresponding .gem as
+follows:
+- import the new tarball and open a changelog entry for this new version
+  (This can be done with the "new-upstream" script from the meta repo
+- run ./debian/rules gemspec this creates a debian/gemspec file which
+  can be committed in the repo.
 
-The upstream tarball has been repacked to remove ports/archives
-containing convenience copies of the following libraries:
-libxml2-2.8.0.tar.gz  libxslt-1.1.26.tar.gz
+-- Cédric Boutillier <boutil@debian.org>  Wed, 09 Jan 2019 10:15:39 +0100
 
-The removal has been done with git-buildpackage, during the import
-of the upstream tarball via git import-orig command and its filter
-option (see debian/gbp.conf).
-
-Cédric Boutillier <boutil@debian.org>
-Last Update: 2014-01-11
diff -pruN 1.8.4-1/debian/rules 1.10.0+dfsg1-2/debian/rules
--- 1.8.4-1/debian/rules	2018-07-30 22:56:29.000000000 +0000
+++ 1.10.0+dfsg1-2/debian/rules	2019-02-08 08:54:03.000000000 +0000
@@ -1,10 +1,21 @@
 #!/usr/bin/make -f
 
+include /usr/share/dpkg/pkg-info.mk
+
 export GEM2DEB_TEST_RUNNER = --check-dependencies
+GEM_VERSION=$(word 1, $(subst +, ,$(DEB_VERSION_UPSTREAM)))
 
 %:
 	dh $@ --buildsystem=ruby --with ruby
 
+gemspec:
+	gem fetch nokogiri
+	gem specification nokogiri-$(GEM_VERSION).gem --ruby > debian/gemspec
+	# remove reference to mini_portile2 gem
+	sed -i '/mini_portile2/ d' debian/gemspec
+	rm -rf nokogiri-$(GEM_VERSION).gem
+
+
 override_dh_installman:
 	mkdir man
 	pod2man --center "" --release "" --name NOKOGIRI --utf8 debian/nokogiri.1.pod man/nokogiri.1
diff -pruN 1.8.4-1/debian/watch 1.10.0+dfsg1-2/debian/watch
--- 1.8.4-1/debian/watch	2018-07-30 22:56:29.000000000 +0000
+++ 1.10.0+dfsg1-2/debian/watch	2019-02-08 08:54:03.000000000 +0000
@@ -1,3 +1,4 @@
 version=4
-opts=dversionmangle=s/\+ds(\d)*// \
-https://gemwatch.debian.net/nokogiri .*/nokogiri-(.*).tar.gz
+opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%nokogiri-$1.tar.gz%,dversionmangle=s/\+dfsg\d*$//,repacksuffix=+dfsg1" \
+https://github.com/sparklemotion/nokogiri/tags \
+(?:.*?/)?v?(\d[\d.]*)\.tar\.gz debian uupdate
diff -pruN 1.8.4-1/dependencies.yml 1.10.0+dfsg1-2/dependencies.yml
--- 1.8.4-1/dependencies.yml	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/dependencies.yml	2019-01-04 15:20:12.000000000 +0000
@@ -1,56 +1,59 @@
 libxml2:
-  version: "2.9.8"
-  sha256: "0b74e51595654f958148759cfef0993114ddccccbb6f31aee018f3558e8e2732"
-  # manually verified checksum:
-  #
-  #   $ gpg --verify libxml2-2.9.8.tar.gz.asc ./ports/archives/libxml2-2.9.8.tar.gz
-  #   gpg: Signature made Mon 05 Mar 2018 11:07:45 AM EST using RSA key ID 596BEA5D
-  #   gpg: Good signature from "Daniel Veillard (Red Hat work email) <veillard@redhat.com>"
-  #   gpg:                 aka "Daniel Veillard <Daniel.Veillard@w3.org>"
-  #   gpg: WARNING: This key is not certified with a trusted signature!
-  #   gpg:          There is no indication that the signature belongs to the owner.
-  #   Primary key fingerprint: C744 15BA 7C9C 7F78 F02E  1DC3 4606 B8A5 DE95 BC1F
-  #        Subkey fingerprint: DB46 681B B91A DCEA 170F  A2D4 1558 8B26 596B EA5D
-  #
-  # using this pgp signature:
-  #   -----BEGIN PGP SIGNATURE-----
-  #
-  #   iQEcBAABAgAGBQJanWtRAAoJEBVYiyZZa+pdV7oIAJWdFahwt+reN/Zt2RPmjjcr
-  #   eSsY7UV1RXjScnNjTzJT1h2hJ7SnUjCkqjR6VdtKDUIzpuX+S2U83joafJH6mxUb
-  #   yw2nO4RfjYTPxpz5JkvqT7jmgEIaD81BuwcMehqpMpIfiKa2NgO1DSfZxgs8a9E2
-  #   +ehc/kZWuI5gmNGrd84EEWUqpYW/Xx7jy02osioJuU5IMPjzZKNR3maXp9oAKeBc
-  #   S2QNa1ID/pUk3K3M/5nlwNgAtQ7lxQrqhrSma2dsKt/IpL6VXomxuD4Bh1r2MZhX
-  #   uZ456X/xJN8UmPewLZWGBU1MK9wqu3Zx5Qwz64H6UdlYIzXZ2jXj2YWZa6xkxPA=
-  #   =69xn
-  #   -----END PGP SIGNATURE-----
+  version: "2.9.9"
+  sha256: "94fb70890143e3c6549f265cee93ec064c80a84c42ad0f23e85ee1fd6540a871"
+  #  manually verified checksum:
+  #
+  #    $ gpg --verify libxml2-2.9.9.tar.gz.asc ports/archives/libxml2-2.9.9.tar.gz
+  #    gpg: Signature made Thu 03 Jan 2019 01:14:47 PM EST
+  #    gpg:                using RSA key 15588B26596BEA5D
+  #    gpg: Good signature from "Daniel Veillard (Red Hat work email) <veillard@redhat.com>" [unknown]
+  #    gpg:                 aka "Daniel Veillard <Daniel.Veillard@w3.org>" [unknown]
+  #    gpg: WARNING: This key is not certified with a trusted signature!
+  #    gpg:          There is no indication that the signature belongs to the owner.
+  #    Primary key fingerprint: C744 15BA 7C9C 7F78 F02E  1DC3 4606 B8A5 DE95 BC1F
+  #         Subkey fingerprint: DB46 681B B91A DCEA 170F  A2D4 1558 8B26 596B EA5D
+  #
+  #  using this pgp signature:
+  #
+  #    -----BEGIN PGP SIGNATURE-----
+  #
+  #    iQEbBAABAgAGBQJcLlEXAAoJEBVYiyZZa+pd1B8H93xeCYNBLx+eX0xe3qS3ReS/
+  #    YstjkXKUkmDQYwqQ/9Knmv1P6NX64hQL5E1pZX5sXp36giwXXJ5tCK72VRzektzU
+  #    Kpo+M1/QA9feZQs1GmyKaXYzNwTSJnsdKA9nWqTHZ3bzfdhFSZ0czo94vgY/cz5z
+  #    9P3FIgeldj1vi8p2rjXbArMFQyaxHnve9LdxI8hbudNSeUw/FEV6mjtXrlZ7MXqn
+  #    hmAkah2JwktOStF5tIlddCRqZeUPUX5flBxT95gfskXXlGEhaoGMXcC3izqqJyV2
+  #    sx5nY7fnXdkwfYsgRUXYWmDmbs8DnFjXH9lux9O4OWglLonaRoAqFPcOzE3aCw==
+  #    =4qWg
+  #    -----END PGP SIGNATURE-----
   #
 
 libxslt:
-  version: "1.1.32"
-  sha256: "526ecd0abaf4a7789041622c3950c0e7f2c4c8835471515fd77eec684a355460"
-  # manually verified checksum:
-  #
-  #   $ gpg --verify libxslt-1.1.32.tar.gz.asc libxslt-1.1.32.tar.gz
-  #   gpg: Signature made Thu 02 Nov 2017 04:35:04 PM EDT using RSA key ID 596BEA5D
-  #   gpg: Good signature from "Daniel Veillard (Red Hat work email) <veillard@redhat.com>"
-  #   gpg:                 aka "Daniel Veillard <Daniel.Veillard@w3.org>"
-  #   gpg: WARNING: This key is not certified with a trusted signature!
-  #   gpg:          There is no indication that the signature belongs to the owner.
-  #   Primary key fingerprint: C744 15BA 7C9C 7F78 F02E  1DC3 4606 B8A5 DE95 BC1F
-  #        Subkey fingerprint: DB46 681B B91A DCEA 170F  A2D4 1558 8B26 596B EA5D
-  #
-  # using this pgp signature:
-  #
-  #   -----BEGIN PGP SIGNATURE-----
-  #
-  #   iQEcBAABAgAGBQJZ+4F4AAoJEBVYiyZZa+pdy1IIAMX1DpzYGdnv6GCPSKeZ0woD
-  #   sHmSkygJep0/sUQD1cYunNsNZnGDgWhnsLAvHOn3opJgsiaZhmhJ8Uo7QNlT+ni1
-  #   AvRFgQoSXLWSF5kkun4u7RvnpDI6jYfCuYSwb9SO4EAYFAQQJXQaKCeFq71gad+p
-  #   XGHJFAy2TqUVLNZ5I1mQz/oBeDsJ7RzHpYqaBxsLDqrCzRQ9ai23q+dFGS3jvLBr
-  #   0gXw0MK73ceOwW12L5aLj4erNbATWmMFMDYZZwftysv3bgx2YfiOoZUTzufrB/Bc
-  #   MG8hP76aYBwIKNbhiDFGa2qdHGZGF7YQ4mi1/ZDX1K1G2tKKeEYxscM13JwiGb8=
-  #   =NuQO
-  #   -----END PGP SIGNATURE-----
+  version: "1.1.33"
+  sha256: "8e36605144409df979cab43d835002f63988f3dc94d5d3537c12796db90e38c8"
+  #  manually verified checksum:
+  #
+  #    $ gpg --verify libxslt-1.1.33.tar.gz.asc ports/archives/libxslt-1.1.33.tar.gz
+  #    gpg: Signature made Thu 03 Jan 2019 01:30:49 PM EST
+  #    gpg:                using RSA key 15588B26596BEA5D
+  #    gpg: Good signature from "Daniel Veillard (Red Hat work email) <veillard@redhat.com>" [unknown]
+  #    gpg:                 aka "Daniel Veillard <Daniel.Veillard@w3.org>" [unknown]
+  #    gpg: WARNING: This key is not certified with a trusted signature!
+  #    gpg:          There is no indication that the signature belongs to the owner.
+  #    Primary key fingerprint: C744 15BA 7C9C 7F78 F02E  1DC3 4606 B8A5 DE95 BC1F
+  #         Subkey fingerprint: DB46 681B B91A DCEA 170F  A2D4 1558 8B26 596B EA5D
+  #
+  #  using this pgp signature:
+  #
+  #    -----BEGIN PGP SIGNATURE-----
+  #
+  #    iQEcBAABAgAGBQJcLlTZAAoJEBVYiyZZa+pd9NkIAIf6ei2iSpR/0QOyS71esDq8
+  #    407PcUXd/yUjDANm4Uvm7kKK+SbbfBxFIPva4g984Noe1zYMfjK3u3iNs6jykySf
+  #    mN5eo2wNCxsZnqjbnsLgQvn5VCQpPInTddTuGUxgqJyvnR7p785L1oA2EStSPMP4
+  #    BGZ9dZGlbreK35WzgrhUi0VN5egJW2fpMsw7rTPvfwK+90gXL0DEm8v3WlA7fCDL
+  #    QsvuPm7jPOXxdt5bYrVP8wpNMTJIGqV6jxh7Vvl6kiGLldUjCyoCh0AGXLror0Gs
+  #    sAMlRKJNodpcCYkIWxzjLt74sUciKNrPLHZlXJcclZMONen1GWnVDcv83Tt9n6w=
+  #    =iAm8
+  #    -----END PGP SIGNATURE-----
   #
 
 zlib:
diff -pruN 1.8.4-1/ext/java/nokogiri/EncodingHandler.java 1.10.0+dfsg1-2/ext/java/nokogiri/EncodingHandler.java
--- 1.8.4-1/ext/java/nokogiri/EncodingHandler.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/EncodingHandler.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,124 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+
+import java.util.HashMap;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * Stub class to satisfy unit tests.  I'm not sure where this class is
+ * meant to be used. As coded it won't really interact with any other
+ * classes and will have no effect on character encodings reported by
+ * documents being parsed.
+ *
+ * @author Patrick Mahoney <pat@polycrstal.org>
+ */
+@JRubyClass(name="Nokogiri::EncodingHandler")
+public class EncodingHandler extends RubyObject {
+    protected static HashMap<String,String> map = new HashMap<String,String>();
+    static {
+        addInitial();
+    }
+
+    protected String name;
+
+    protected static void addInitial() {
+        map.put("UTF-8", "UTF-8");
+    }
+
+    public EncodingHandler(Ruby ruby, RubyClass klass, String value) {
+        super(ruby, klass);
+        name = value;
+    }
+
+    @JRubyMethod(name="[]", meta=true)
+    public static IRubyObject get(ThreadContext context,
+                                  IRubyObject _klass,
+                                  IRubyObject keyObj) {
+        Ruby ruby = context.getRuntime();
+        String key = keyObj.toString();
+        String value = map.get(key);
+        if (value == null)
+            return ruby.getNil();
+
+        return new EncodingHandler(
+            ruby,
+            getNokogiriClass(ruby, "Nokogiri::EncodingHandler"),
+            value);
+    }
+
+    @JRubyMethod(meta=true)
+    public static IRubyObject delete(ThreadContext context,
+                                     IRubyObject _klass,
+                                     IRubyObject keyObj) {
+        String key = keyObj.toString();
+        String value = map.remove(key);
+        if (value == null)
+            return context.getRuntime().getNil();
+        return context.getRuntime().newString(value);
+    }
+
+    @JRubyMethod(name="clear_aliases!", meta=true)
+    public static IRubyObject clear_aliases(ThreadContext context,
+                                            IRubyObject _klass) {
+        map.clear();
+        addInitial();
+        return context.getRuntime().getNil();
+    }
+
+    @JRubyMethod(meta=true)
+    public static IRubyObject alias(ThreadContext context,
+                                    IRubyObject _klass,
+                                    IRubyObject orig,
+                                    IRubyObject alias) {
+        String value = map.get(orig.toString());
+        if (value != null)
+            map.put(alias.toString(), value);
+
+        return context.getRuntime().getNil();
+    }
+
+    @JRubyMethod
+    public IRubyObject name(ThreadContext context) {
+        return context.getRuntime().newString(name);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/HtmlDocument.java 1.10.0+dfsg1-2/ext/java/nokogiri/HtmlDocument.java
--- 1.8.4-1/ext/java/nokogiri/HtmlDocument.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/HtmlDocument.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,189 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import nokogiri.internals.HtmlDomParserContext;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.javasupport.util.RuntimeHelpers;
+import org.jruby.runtime.Arity;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Class for Nokogiri::HTML::Document.
+ *
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::HTML::Document", parent="Nokogiri::XML::Document")
+public class HtmlDocument extends XmlDocument {
+    private static final String DEFAULT_CONTENT_TYPE = "html";
+    private static final String DEFAULT_PUBLIC_ID = "-//W3C//DTD HTML 4.01//EN";
+    private static final String DEFAULT_SYTEM_ID = "http://www.w3.org/TR/html4/strict.dtd";
+
+    private String parsed_encoding = null;
+
+    public HtmlDocument(Ruby ruby, RubyClass klazz) {
+        super(ruby, klazz);
+    }
+    
+    public HtmlDocument(Ruby ruby, RubyClass klazz, Document doc) {
+        super(ruby, klazz, doc);
+    }
+
+    @JRubyMethod(name="new", meta = true, rest = true, required=0)
+    public static IRubyObject rbNew(ThreadContext context, IRubyObject klazz,
+                                    IRubyObject[] args) {
+        HtmlDocument htmlDocument;
+        try {
+            Document docNode = createNewDocument();
+            htmlDocument = (HtmlDocument) NokogiriService.HTML_DOCUMENT_ALLOCATOR.allocate(context.getRuntime(), (RubyClass) klazz);
+            htmlDocument.setDocumentNode(context, docNode);
+        } catch (Exception ex) {
+            throw context.getRuntime().newRuntimeError("couldn't create document: " + ex);
+        }
+
+        RuntimeHelpers.invoke(context, htmlDocument, "initialize", args);
+
+        return htmlDocument;
+    }
+
+    public IRubyObject getInternalSubset(ThreadContext context) {
+        IRubyObject internalSubset = super.getInternalSubset(context);
+
+        // html documents are expected to have a default internal subset
+        // the default values are the same ones used when the following
+        // feature is turned on
+        // "http://cyberneko.org/html/features/insert-doctype"
+        // the reason we don't turn it on, is because it overrides the document's
+        // declared doctype declaration.
+
+        if (internalSubset.isNil()) {
+            internalSubset = XmlDtd.newEmpty(context.getRuntime(),
+                                             getDocument(),
+                                             context.getRuntime().newString(DEFAULT_CONTENT_TYPE),
+                                             context.getRuntime().newString(DEFAULT_PUBLIC_ID),
+                                             context.getRuntime().newString(DEFAULT_SYTEM_ID));
+            setInternalSubset(internalSubset);
+        }
+
+        return internalSubset;
+    }
+
+    public static IRubyObject do_parse(ThreadContext context,
+                                       IRubyObject klass,
+                                       IRubyObject[] args) {
+        Ruby ruby = context.getRuntime();
+        Arity.checkArgumentCount(ruby, args, 4, 4);
+        HtmlDomParserContext ctx =
+            new HtmlDomParserContext(ruby, args[2], args[3]);
+        ctx.setInputSource(context, args[0], args[1]);
+        return ctx.parse(context, klass, args[1]);
+    }
+    
+    public void setDocumentNode(ThreadContext context, Node node) {
+        super.setNode(context, node);
+        Ruby runtime = context.getRuntime();
+        if (node != null) {
+            Document document = (Document)node;
+            document.normalize();
+            stabilzeAttrValue(document.getDocumentElement());
+        }
+        setInstanceVariable("@decorators", runtime.getNil());
+    }
+    
+    private void stabilzeAttrValue(Node node) {
+        if (node == null) return;
+        if (node.hasAttributes()) {
+            NamedNodeMap nodeMap = node.getAttributes();
+            for (int i=0; i<nodeMap.getLength(); i++) {
+                Node n = nodeMap.item(i);
+                if (n instanceof Attr) {
+                    Attr attr = (Attr)n;
+                    String attrName = attr.getName();
+                    // not sure, but need to get value always before document is referred.
+                    // or lose attribute value
+                    String attrValue = attr.getValue(); // don't delete this line
+                }
+            }
+        }
+        NodeList children = node.getChildNodes();
+        for (int i=0; i<children.getLength(); i++) {
+            stabilzeAttrValue(children.item(i));
+        }
+    }
+    
+    public void setParsedEncoding(String encoding) {
+        parsed_encoding = encoding;
+    }
+    
+    public String getPraedEncoding() {
+        return parsed_encoding;
+    }
+
+    /*
+     * call-seq:
+     *  read_io(io, url, encoding, options)
+     *
+     * Read the HTML document from +io+ with given +url+, +encoding+,
+     * and +options+.  See Nokogiri::HTML.parse
+     */
+    @JRubyMethod(meta = true, rest = true)
+    public static IRubyObject read_io(ThreadContext context,
+                                      IRubyObject cls,
+                                      IRubyObject[] args) {
+        return do_parse(context, cls, args);
+    }
+
+    /*
+     * call-seq:
+     *  read_memory(string, url, encoding, options)
+     *
+     * Read the HTML document contained in +string+ with given +url+, +encoding+,
+     * and +options+.  See Nokogiri::HTML.parse
+     */
+    @JRubyMethod(meta = true, rest = true)
+    public static IRubyObject read_memory(ThreadContext context,
+                                          IRubyObject cls,
+                                          IRubyObject[] args) {
+        return do_parse(context, cls, args);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/HtmlElementDescription.java 1.10.0+dfsg1-2/ext/java/nokogiri/HtmlElementDescription.java
--- 1.8.4-1/ext/java/nokogiri/HtmlElementDescription.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/HtmlElementDescription.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,148 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.cyberneko.html.HTMLElements;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * Class for Nokogiri::HTML::ElementDescription.
+ * 
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ */
+@JRubyClass(name="Nokogiri::HTML::ElementDescription")
+public class HtmlElementDescription extends RubyObject {
+
+    /**
+     * Stores memoized hash of element -> list of valid subelements.
+     */
+    static protected Map<Short, List<String>> subElements;
+    static {
+        Map<Short, List<String>> _subElements =
+            new HashMap<Short, List<String>>();
+        subElements = Collections.synchronizedMap(_subElements);
+    }
+
+    protected HTMLElements.Element element;
+
+    public HtmlElementDescription(Ruby runtime, RubyClass rubyClass) {
+        super(runtime, rubyClass);
+    }
+
+    /**
+     * Lookup the list of sub elements of <code>code</code>.  If not
+     * already stored, iterate through all elements to find valid
+     * subelements; save this list and return it.
+     */
+    protected static List<String> findSubElements(HTMLElements.Element elem) {
+        List<String> subs = subElements.get(elem.code);
+
+        if (subs == null) {
+            subs = new ArrayList<String>();
+
+            /*
+             * A bit of a hack.  NekoHtml source code shows that
+             * UNKNOWN is the highest value element.  We cannot access
+             * the list of elements directly because it's protected.
+             */
+            for (short c = 0; c < HTMLElements.UNKNOWN; c++) {
+                HTMLElements.Element maybe_sub =
+                    HTMLElements.getElement(c);
+                if (maybe_sub.isParent(elem)) {
+                    subs.add(maybe_sub.name);
+                }
+            }
+
+            subElements.put(elem.code, subs);
+        }
+
+        return subs;
+    }
+
+    @JRubyMethod(name="[]", meta=true)
+    public static IRubyObject get(ThreadContext context,
+                                  IRubyObject klazz, IRubyObject name) {
+
+        // nekohtml will return an element even for invalid names, see
+        // http://sourceforge.net/p/nekohtml/code/HEAD/tree/trunk/src/org/cyberneko/html/HTMLElements.java#l514
+        // which breaks `test_fetch_nonexistent'
+        HTMLElements.Element elem = HTMLElements.getElement(name.asJavaString(), HTMLElements.NO_SUCH_ELEMENT);
+        if (elem == HTMLElements.NO_SUCH_ELEMENT)
+            return context.nil;
+
+        HtmlElementDescription desc =
+            new HtmlElementDescription(context.getRuntime(), (RubyClass)klazz);
+        desc.element = elem;
+        return desc;
+    }
+
+    @JRubyMethod()
+    public IRubyObject name(ThreadContext context) {
+        return context.getRuntime().newString(element.name.toLowerCase());
+    }
+
+    @JRubyMethod(name="inline?")
+    public IRubyObject inline_eh(ThreadContext context) {
+        return context.getRuntime().newBoolean(element.isInline());
+    }
+
+    @JRubyMethod(name="empty?")
+    public IRubyObject empty_eh(ThreadContext context) {
+        return context.getRuntime().newBoolean(element.isEmpty());
+    }
+
+    @JRubyMethod()
+    public IRubyObject sub_elements(ThreadContext context) {
+        Ruby ruby = context.getRuntime();
+        List<String> subs = findSubElements(element);
+        IRubyObject[] ary = new IRubyObject[subs.size()];
+        for (int i = 0; i < subs.size(); ++i) {
+            ary[i] = ruby.newString(subs.get(i));
+        }
+
+        return ruby.newArray(ary);
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/HtmlEntityLookup.java 1.10.0+dfsg1-2/ext/java/nokogiri/HtmlEntityLookup.java
--- 1.8.4-1/ext/java/nokogiri/HtmlEntityLookup.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/HtmlEntityLookup.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,79 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static org.jruby.javasupport.util.RuntimeHelpers.invoke;
+
+import org.cyberneko.html.HTMLEntities;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * Class for Nokogiri::HTML::EntityLookup.
+ * 
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ */
+@JRubyClass(name="Nokogiri::HTML::EntityLookup")
+public class HtmlEntityLookup extends RubyObject {
+
+    public HtmlEntityLookup(Ruby runtime, RubyClass rubyClass) {
+        super(runtime, rubyClass);
+    }
+
+    /**
+     * Looks up an HTML entity <code>key</code>.
+     *
+     * The description is a bit lacking.
+     */
+    @JRubyMethod()
+    public IRubyObject get(ThreadContext context, IRubyObject key) {
+        Ruby ruby = context.getRuntime();
+        String name = key.toString();
+        int val = HTMLEntities.get(name);
+        if (val == -1) return ruby.getNil();
+
+        IRubyObject edClass =
+            ruby.getClassFromPath("Nokogiri::HTML::EntityDescription");
+        IRubyObject edObj = invoke(context, edClass, "new",
+                                   ruby.newFixnum(val), ruby.newString(name),
+                                   ruby.newString(name + " entity"));
+
+        return edObj;
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/HtmlSaxParserContext.java 1.10.0+dfsg1-2/ext/java/nokogiri/HtmlSaxParserContext.java
--- 1.8.4-1/ext/java/nokogiri/HtmlSaxParserContext.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/HtmlSaxParserContext.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,252 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.EnumSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import nokogiri.internals.NokogiriHandler;
+
+import org.apache.xerces.parsers.AbstractSAXParser;
+import org.cyberneko.html.parsers.SAXParser;
+import org.jruby.*;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.xml.sax.SAXException;
+
+/**
+ * Class for Nokogiri::HTML::SAX::ParserContext.
+ *
+ * @author serabe
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+
+@JRubyClass(name="Nokogiri::HTML::SAX::ParserContext", parent="Nokogiri::XML::SAX::ParserContext")
+public class HtmlSaxParserContext extends XmlSaxParserContext {
+
+    public HtmlSaxParserContext(Ruby ruby, RubyClass rubyClass) {
+        super(ruby, rubyClass);
+    }
+    
+    @Override
+    protected AbstractSAXParser createParser() throws SAXException {
+        SAXParser parser = new SAXParser();
+
+        try{
+            parser.setProperty(
+                "http://cyberneko.org/html/properties/names/elems", "lower");
+            parser.setProperty(
+                "http://cyberneko.org/html/properties/names/attrs", "lower");
+            return parser;
+        } catch(SAXException ex) {
+            throw new SAXException(
+                "Problem while creating HTML SAX Parser: " + ex.toString());
+        }
+    }
+
+    @JRubyMethod(name="memory", meta=true)
+    public static IRubyObject parse_memory(ThreadContext context,
+                                           IRubyObject klazz,
+                                           IRubyObject data,
+                                           IRubyObject encoding) {
+        HtmlSaxParserContext ctx = (HtmlSaxParserContext) NokogiriService.HTML_SAXPARSER_CONTEXT_ALLOCATOR.allocate(context.getRuntime(), (RubyClass)klazz);
+        ctx.initialize(context.getRuntime());
+        String javaEncoding = findEncoding(context, encoding);
+        if (javaEncoding != null) {
+            String input = applyEncoding(rubyStringToString(data), javaEncoding);
+            ByteArrayInputStream istream = new ByteArrayInputStream(input.getBytes());
+            ctx.setInputSource(istream);
+            ctx.getInputSource().setEncoding(javaEncoding);
+        }
+        return ctx;
+    }
+    
+    public enum EncodingType {
+        NONE(0, "NONE"),
+        UTF_8(1, "UTF-8"),
+        UTF16LE(2, "UTF16LE"),
+        UTF16BE(3, "UTF16BE"),
+        UCS4LE(4, "UCS4LE"),
+        UCS4BE(5, "UCS4BE"),
+        EBCDIC(6, "EBCDIC"),
+        UCS4_2143(7, "ICS4-2143"),
+        UCS4_3412(8, "UCS4-3412"),
+        UCS2(9, "UCS2"),
+        ISO_8859_1(10, "ISO-8859-1"),
+        ISO_8859_2(11, "ISO-8859-2"),
+        ISO_8859_3(12, "ISO-8859-3"),
+        ISO_8859_4(13, "ISO-8859-4"),
+        ISO_8859_5(14, "ISO-8859-5"),
+        ISO_8859_6(15, "ISO-8859-6"),
+        ISO_8859_7(16, "ISO-8859-7"),
+        ISO_8859_8(17, "ISO-8859-8"),
+        ISO_8859_9(18, "ISO-8859-9"),
+        ISO_2022_JP(19, "ISO-2022-JP"),
+        SHIFT_JIS(20, "SHIFT-JIS"),
+        EUC_JP(21, "EUC-JP"),
+        ASCII(22, "ASCII");
+        
+        private final int value;
+        private final String name;
+
+        EncodingType(int value, String name) {
+            this.value = value;
+            this.name = name;
+        }
+        
+        public int getValue() {
+            return value;
+        }
+        
+        public String toString() {
+            return name;
+        }
+    }
+    
+    private static String findName(final int value) {
+        for (EncodingType type : EncodingType.values()) {
+            if (type.getValue() == value) return type.toString();
+        }
+        return null;
+    }
+    
+    private static String findEncoding(ThreadContext context, IRubyObject encoding) {
+        String rubyEncoding = null;
+        if (encoding instanceof RubyString) {
+            rubyEncoding = rubyStringToString(encoding);
+        }
+        else if (encoding instanceof RubyFixnum) {
+            int value = RubyFixnum.fix2int((RubyFixnum) encoding);
+            rubyEncoding = findName(value);
+        }
+        if (rubyEncoding == null) return null;
+        try {
+            return Charset.forName(rubyEncoding).displayName();
+        }
+        catch (UnsupportedCharsetException e) {
+            throw context.getRuntime().newEncodingCompatibilityError(rubyEncoding + "is not supported");
+        }
+        catch (IllegalCharsetNameException e) {
+            throw context.getRuntime().newInvalidEncoding(e.getMessage());
+        }
+    }
+
+    private static final Pattern CHARSET_PATTERN = Pattern.compile("charset(()|\\s)=(()|\\s)([a-z]|-|_|\\d)+");
+
+    private static String applyEncoding(String input, String enc) {
+        String str = input.toLowerCase();
+        int start_pos = 0;
+        int end_pos = 0;
+        if (input.contains("meta") && input.contains("charset")) {
+            Matcher m = CHARSET_PATTERN.matcher(str);
+            while (m.find()) {
+                start_pos = m.start();
+                end_pos = m.end();
+            }
+        }
+        if (start_pos != end_pos) {
+            String substr = input.substring(start_pos, end_pos);
+            input = input.replace(substr, "charset=" + enc);
+        }
+        return input;
+    }
+
+    @JRubyMethod(name="file", meta=true)
+    public static IRubyObject parse_file(ThreadContext context,
+                                         IRubyObject klazz,
+                                         IRubyObject data,
+                                         IRubyObject encoding) {
+        HtmlSaxParserContext ctx = (HtmlSaxParserContext) NokogiriService.HTML_SAXPARSER_CONTEXT_ALLOCATOR.allocate(context.getRuntime(), (RubyClass)klazz);
+        ctx.initialize(context.getRuntime());
+        ctx.setInputSourceFile(context, data);
+        String javaEncoding = findEncoding(context, encoding);
+        if (javaEncoding != null) {
+            ctx.getInputSource().setEncoding(javaEncoding);
+        }
+        return ctx;
+    }
+
+    @JRubyMethod(name="io", meta=true)
+    public static IRubyObject parse_io(ThreadContext context,
+                                       IRubyObject klazz,
+                                       IRubyObject data,
+                                       IRubyObject encoding) {
+        HtmlSaxParserContext ctx = (HtmlSaxParserContext) NokogiriService.HTML_SAXPARSER_CONTEXT_ALLOCATOR.allocate(context.getRuntime(), (RubyClass)klazz);
+        ctx.initialize(context.getRuntime());
+        ctx.setInputSource(context, data, context.getRuntime().getNil());
+        String javaEncoding = findEncoding(context, encoding);
+        if (javaEncoding != null) {
+            ctx.getInputSource().setEncoding(javaEncoding);
+        }
+        return ctx;
+    }
+
+    /**
+     * Create a new parser context that will read from a raw input stream.
+     * Meant to be run in a separate thread by HtmlSaxPushParser.
+     */
+    static HtmlSaxParserContext parse_stream(final Ruby runtime, RubyClass klazz, InputStream stream) {
+        HtmlSaxParserContext ctx = (HtmlSaxParserContext) NokogiriService.HTML_SAXPARSER_CONTEXT_ALLOCATOR.allocate(runtime, klazz);
+        ctx.initialize(runtime);
+        ctx.setInputSource(stream);
+        return ctx;
+    }
+
+    @Override
+    protected void preParse(final Ruby runtime, IRubyObject handlerRuby, NokogiriHandler handler) {
+        // final String path = "Nokogiri::XML::FragmentHandler";
+        // final String docFrag =
+        //     "http://cyberneko.org/html/features/balance-tags/document-fragment";
+        // RubyObjectAdapter adapter = JavaEmbedUtils.newObjectAdapter();
+        // IRubyObject doc = adapter.getInstanceVariable(handlerRuby, "@document");
+        // RubyModule mod = runtime.getClassFromPath(path);
+        // try {
+        //     if (doc != null && !doc.isNil() && adapter.isKindOf(doc, mod))
+        //         parser.setFeature(docFrag, true);
+        // } catch (Exception e) {
+        //     // ignore
+        // }
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/HtmlSaxPushParser.java 1.10.0+dfsg1-2/ext/java/nokogiri/HtmlSaxPushParser.java
--- 1.8.4-1/ext/java/nokogiri/HtmlSaxPushParser.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/HtmlSaxPushParser.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,222 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.XmlSaxPushParser.terminateExecution;
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static org.jruby.javasupport.util.RuntimeHelpers.invoke;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.ThreadFactory;
+
+import nokogiri.internals.*;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * Class for Nokogiri::HTML::SAX::PushParser
+ *
+ * @author 
+ * @author Piotr Szmielew <p.szmielew@ava.waw.pl> - based on Nokogiri::XML::SAX::PushParser
+ */
+@JRubyClass(name="Nokogiri::HTML::SAX::PushParser")
+public class HtmlSaxPushParser extends RubyObject {
+    ParserContext.Options options;
+    IRubyObject saxParser;
+
+    NokogiriBlockingQueueInputStream stream;
+
+    private ParserTask parserTask = null;
+    private FutureTask<HtmlSaxParserContext> futureTask = null;
+    private ExecutorService executor = null;
+
+    public HtmlSaxPushParser(Ruby ruby, RubyClass rubyClass) {
+        super(ruby, rubyClass);
+    }
+
+    @Override
+    public void finalize() {
+        try {
+            terminateImpl();
+        }
+        catch (Exception e) { /* ignored */ }
+    }
+
+    @JRubyMethod
+    public IRubyObject initialize_native(final ThreadContext context,
+                                         IRubyObject saxParser,
+                                         IRubyObject fileName,
+                                         IRubyObject encoding) {
+        // NOTE: Silently skips provided encoding
+        options = new ParserContext.Options(0);
+        this.saxParser = saxParser;
+        return this;
+    }
+
+    private transient IRubyObject parse_options;
+
+    private IRubyObject parse_options(final ThreadContext context) {
+        if (parse_options == null) {
+            parse_options = invoke(context, context.runtime.getClassFromPath("Nokogiri::XML::ParseOptions"), "new");
+        }
+        return parse_options;
+    }
+
+    @JRubyMethod(name="options")
+    public IRubyObject getOptions(ThreadContext context) {
+        return invoke(context, parse_options(context), "options");
+    }
+
+    @JRubyMethod(name="options=")
+    public IRubyObject setOptions(ThreadContext context, IRubyObject opts) {
+        invoke(context, parse_options(context), "options=", opts);
+        options = new ParserContext.Options(opts.convertToInteger().getLongValue());
+        return getOptions(context);
+    }
+
+    @JRubyMethod
+    public IRubyObject native_write(ThreadContext context, IRubyObject chunk, IRubyObject isLast) {
+        try {
+            initialize_task(context);
+        } catch (IOException e) {
+            throw context.getRuntime().newRuntimeError(e.getMessage());
+        }
+        final ByteArrayInputStream data = NokogiriHelpers.stringBytesToStream(chunk);
+        if (data == null) {
+            terminateTask(context.runtime);
+            throw new RaiseException(XmlSyntaxError.createHTMLSyntaxError(context.runtime)); // Nokogiri::HTML::SyntaxError
+        }
+
+        int errorCount0 = parserTask.getErrorCount();
+
+        if (isLast.isTrue()) {
+            IRubyObject document = invoke(context, this, "document");
+            invoke(context, document, "end_document");
+            terminateTask(context.runtime);
+        } else {
+            try {
+                Future<Void> task = stream.addChunk(data);
+                task.get();
+            }
+            catch (ClosedStreamException ex) {
+                // this means the stream is closed, ignore this exception
+            }
+            catch (Exception e) {
+                throw context.runtime.newRuntimeError(e.getMessage());
+            }
+
+        }
+
+        if (!options.recover && parserTask.getErrorCount() > errorCount0) {
+            terminateTask(context.runtime);
+            throw parserTask.getLastError();
+        }
+
+        return this;
+    }
+
+    @SuppressWarnings("unchecked")
+    private void initialize_task(ThreadContext context) throws IOException {
+        if (futureTask == null || stream == null) {
+            stream = new NokogiriBlockingQueueInputStream();
+
+            assert saxParser != null : "saxParser null";
+            parserTask = new ParserTask(context, saxParser, stream);
+            futureTask = new FutureTask<HtmlSaxParserContext>((Callable) parserTask);
+            executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
+              @Override
+              public Thread newThread(Runnable r) {
+                Thread t = new Thread(r);
+                t.setName("HtmlSaxPushParser");
+                t.setDaemon(true);
+                return t;
+              }
+            });
+            executor.submit(futureTask);
+        }
+    }
+
+    private void terminateTask(final Ruby runtime) {
+        if (executor == null) return;
+
+        try {
+            terminateImpl();
+        }
+        catch (InterruptedException e) {
+            throw runtime.newRuntimeError(e.toString());
+        }
+        catch (Exception e) {
+            throw runtime.newRuntimeError(e.toString());
+        }
+    }
+
+    private synchronized void terminateImpl() throws InterruptedException, ExecutionException {
+        terminateExecution(executor, stream, futureTask);
+
+        executor = null; stream = null; futureTask = null;
+    }
+
+    private static HtmlSaxParserContext parse(final Ruby runtime, final InputStream stream) {
+        RubyClass klazz = getNokogiriClass(runtime, "Nokogiri::HTML::SAX::ParserContext");
+        return HtmlSaxParserContext.parse_stream(runtime, klazz, stream);
+    }
+
+    static class ParserTask extends XmlSaxPushParser.ParserTask /* <HtmlSaxPushParser> */ {
+
+        private ParserTask(ThreadContext context, IRubyObject handler, InputStream stream) {
+            super(context, handler, parse(context.runtime, stream), stream);
+        }
+
+        @Override
+        public HtmlSaxParserContext call() throws Exception {
+            return (HtmlSaxParserContext) super.call();
+        }
+
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/AttrCompare.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/AttrCompare.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/AttrCompare.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/AttrCompare.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,119 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+
+import org.w3c.dom.Attr;
+
+/**
+ * Compares two attributes based on the C14n specification.
+ *
+ * <UL>
+ * <LI>Namespace nodes have a lesser document order position than attribute 
+ *   nodes.
+ * <LI> An element's namespace nodes are sorted lexicographically by
+ *   local name (the default namespace node, if one exists, has no
+ *   local name and is therefore lexicographically least).
+ * <LI> An element's attribute nodes are sorted lexicographically with
+ *   namespace URI as the primary key and local name as the secondary
+ *   key (an empty namespace URI is lexicographically least).
+ * </UL>
+ *
+ * @author Christian Geuer-Pollmann
+ */
+public class AttrCompare implements Comparator<Attr>, Serializable {
+
+    private static final long serialVersionUID = -7113259629930576230L;
+    private static final int ATTR0_BEFORE_ATTR1 = -1;
+    private static final int ATTR1_BEFORE_ATTR0 = 1;
+    private static final String XMLNS = Constants.NamespaceSpecNS;
+
+    /**
+     * Compares two attributes based on the C14n specification.
+     *
+     * <UL>
+     * <LI>Namespace nodes have a lesser document order position than 
+     *   attribute nodes.
+     * <LI> An element's namespace nodes are sorted lexicographically by
+     *   local name (the default namespace node, if one exists, has no
+     *   local name and is therefore lexicographically least).
+     * <LI> An element's attribute nodes are sorted lexicographically with
+     *   namespace URI as the primary key and local name as the secondary
+     *   key (an empty namespace URI is lexicographically least).
+     * </UL>
+     *
+     * @param attr0
+     * @param attr1
+     * @return returns a negative integer, zero, or a positive integer as 
+     *   obj0 is less than, equal to, or greater than obj1
+     *
+     */
+    public int compare(Attr attr0, Attr attr1) {
+        String namespaceURI0 = attr0.getNamespaceURI();      
+        String namespaceURI1 = attr1.getNamespaceURI();
+      
+        boolean isNamespaceAttr0 = XMLNS.equals(namespaceURI0);
+        boolean isNamespaceAttr1 = XMLNS.equals(namespaceURI1);
+
+        if (isNamespaceAttr0) {
+            if (isNamespaceAttr1) {
+                // both are namespaces
+                String localname0 = attr0.getLocalName();
+                String localname1 = attr1.getLocalName();
+
+                if ("xmlns".equals(localname0)) {
+                    localname0 = "";
+                }
+
+                if ("xmlns".equals(localname1)) {
+                    localname1 = "";
+                }
+
+                return localname0.compareTo(localname1);
+            }
+            // attr0 is a namespace, attr1 is not
+            return ATTR0_BEFORE_ATTR1;
+        } else if (isNamespaceAttr1) {
+            // attr1 is a namespace, attr0 is not
+            return ATTR1_BEFORE_ATTR0;
+        } 
+
+        // none is a namespace
+        if (namespaceURI0 == null) {
+            if (namespaceURI1 == null) {
+                String name0 = attr0.getName();
+                String name1 = attr1.getName();
+                return name0.compareTo(name1);
+            }
+            return ATTR0_BEFORE_ATTR1;
+        } else if (namespaceURI1 == null) {
+            return ATTR1_BEFORE_ATTR0;
+        } 
+
+        int a = namespaceURI0.compareTo(namespaceURI1);
+        if (a != 0) {
+            return a;
+        }
+      
+        return (attr0.getLocalName()).compareTo(attr1.getLocalName());
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/C14nHelper.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/C14nHelper.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/C14nHelper.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/C14nHelper.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,159 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+
+/**
+ * Temporary swapped static functions from the normalizer Section
+ *
+ * @author Christian Geuer-Pollmann
+ */
+public class C14nHelper {
+
+    /**
+     * Constructor C14nHelper
+     *
+     */
+    private C14nHelper() {
+        // don't allow instantiation
+    }
+
+    /**
+     * Method namespaceIsRelative
+     *
+     * @param namespace
+     * @return true if the given namespace is relative. 
+     */
+    public static boolean namespaceIsRelative(Attr namespace) {
+        return !namespaceIsAbsolute(namespace);
+    }
+
+    /**
+     * Method namespaceIsRelative
+     *
+     * @param namespaceValue
+     * @return true if the given namespace is relative.
+     */
+    public static boolean namespaceIsRelative(String namespaceValue) {
+        return !namespaceIsAbsolute(namespaceValue);
+    }
+
+    /**
+     * Method namespaceIsAbsolute
+     *
+     * @param namespace
+     * @return true if the given namespace is absolute.
+     */
+    public static boolean namespaceIsAbsolute(Attr namespace) {
+        return namespaceIsAbsolute(namespace.getValue());
+    }
+
+    /**
+     * Method namespaceIsAbsolute
+     *
+     * @param namespaceValue
+     * @return true if the given namespace is absolute.
+     */
+    public static boolean namespaceIsAbsolute(String namespaceValue) {
+        // assume empty namespaces are absolute
+        if (namespaceValue.length() == 0) {
+            return true;
+        }
+        return namespaceValue.indexOf(':') > 0;
+    }
+
+    /**
+     * This method throws an exception if the Attribute value contains
+     * a relative URI.
+     *
+     * @param attr
+     * @throws CanonicalizationException
+     */
+    public static void assertNotRelativeNS(Attr attr) throws CanonicalizationException {
+        if (attr == null) {
+            return;
+        }
+
+        String nodeAttrName = attr.getNodeName();
+        boolean definesDefaultNS = nodeAttrName.equals("xmlns");
+        boolean definesNonDefaultNS = nodeAttrName.startsWith("xmlns:");
+
+        if ((definesDefaultNS || definesNonDefaultNS) && namespaceIsRelative(attr)) {
+            String parentName = attr.getOwnerElement().getTagName();
+            String attrValue = attr.getValue();
+            Object exArgs[] = { parentName, nodeAttrName, attrValue };
+
+            throw new CanonicalizationException(
+                "c14n.Canonicalizer.RelativeNamespace", exArgs
+            );
+        }
+    }
+
+    /**
+     * This method throws a CanonicalizationException if the supplied Document
+     * is not able to be traversed using a TreeWalker.
+     *
+     * @param document
+     * @throws CanonicalizationException
+     */
+    public static void checkTraversability(Document document)
+        throws CanonicalizationException {
+        if (!document.isSupported("Traversal", "2.0")) {
+            Object exArgs[] = {document.getImplementation().getClass().getName() };
+
+            throw new CanonicalizationException(
+                "c14n.Canonicalizer.TraversalNotSupported", exArgs
+            );
+        }
+    }
+
+    /**
+     * This method throws a CanonicalizationException if the supplied Element
+     * contains any relative namespaces.
+     *
+     * @param ctxNode
+     * @throws CanonicalizationException
+     * @see C14nHelper#assertNotRelativeNS(Attr)
+     */
+    public static void checkForRelativeNamespace(Element ctxNode)
+        throws CanonicalizationException {
+        if (ctxNode != null) {
+            NamedNodeMap attributes = ctxNode.getAttributes();
+
+            for (int i = 0; i < attributes.getLength(); i++) {
+                C14nHelper.assertNotRelativeNS((Attr) attributes.item(i));
+            }
+        } else {
+            throw new CanonicalizationException("Called checkForRelativeNamespace() on null");
+        }
+    }
+    
+    public static String getErrorMessage(String message, Object... exArgs) {
+        StringBuffer sb = new StringBuffer(message);
+        for (Object arg : exArgs) {
+            sb.append(", ").append(arg.toString());
+        }
+        return new String(sb);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/CanonicalFilter.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/CanonicalFilter.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/CanonicalFilter.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/CanonicalFilter.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,37 @@
+package nokogiri.internals.c14n;
+
+import nokogiri.XmlNode;
+import nokogiri.internals.NokogiriHelpers;
+
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Node;
+
+public class CanonicalFilter {
+    private final Block block;
+    private final ThreadContext context;
+
+    public CanonicalFilter(ThreadContext context, Block block) {
+        this.context = context;
+        this.block = block;
+    }
+
+    public boolean includeNodes(Node currentNode, Node parentNode) {
+        if (block == null || !block.isGiven())
+            return true;
+
+        IRubyObject current = NokogiriHelpers.getCachedNodeOrCreate(context.getRuntime(), currentNode);
+        IRubyObject parent = NokogiriHelpers.getCachedNodeOrCreate(context.getRuntime(), parentNode);
+
+        if (parent.isNil()) {
+            IRubyObject doc = ((XmlNode) current).document(context);
+            boolean returnValue = block.call(context, current, doc).isTrue();
+            block.call(context, doc, context.nil);
+            return returnValue;
+        }
+
+        return block.call(context, current, parent).isTrue();
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/CanonicalizationException.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/CanonicalizationException.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/CanonicalizationException.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/CanonicalizationException.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,93 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+
+/**
+ * Class CanonicalizationException
+ *
+ * @author Christian Geuer-Pollmann
+ */
+public class CanonicalizationException extends Exception {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Constructor CanonicalizationException
+     *
+     */
+    public CanonicalizationException() {
+        super();
+    }
+
+    /**
+     * Constructor CanonicalizationException
+     *
+     * @param message
+     */
+    public CanonicalizationException(String message) {
+        super(message);
+    }
+    
+    /**
+     * Constructor CanonicalizationException
+     *
+     * @param message
+     * @param rootCause
+     */
+    public CanonicalizationException(Exception rootCause) {
+        super(rootCause);
+    }
+
+    /**
+     * Constructor CanonicalizationException
+     *
+     * @param msgID
+     * @param exArgs
+     */
+    public CanonicalizationException(String message, Object... exArgs) {     
+        super(C14nHelper.getErrorMessage(message, exArgs));
+    }
+    
+    /**
+     * Constructor CanonicalizationException
+     *
+     * @param message
+     * @param rootCause
+     */
+    public CanonicalizationException(String message, Exception rootCause) {
+        super(message, rootCause);
+    }
+
+    /**
+     * Constructor CanonicalizationException
+     *
+     * @param msgID
+     * @param exArgs
+     * @param originalException
+     */
+    public CanonicalizationException(String message, Exception rootCause, Object... exArgs) {
+        super(C14nHelper.getErrorMessage(message, exArgs), rootCause);
+    }
+    
+    
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer11.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer11.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer11.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer11.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,639 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * Implements <A HREF="http://www.w3.org/TR/2008/PR-xml-c14n11-20080129/">
+ * Canonical XML Version 1.1</A>, a W3C Proposed Recommendation from 29
+ * January 2008.
+ *
+ * @author Sean Mullan
+ * @author Raul Benito
+ */
+public abstract class Canonicalizer11 extends CanonicalizerBase {
+
+    private static final String XMLNS_URI = Constants.NamespaceSpecNS;
+    private static final String XML_LANG_URI = Constants.XML_LANG_SPACE_SpecNS;
+    private final SortedSet<Attr> result = new TreeSet<Attr>(COMPARE);
+
+    private boolean firstCall = true;
+
+    private static class XmlAttrStack {
+        static class XmlsStackElement {
+            int level;
+            boolean rendered = false;
+            List<Attr> nodes = new ArrayList<Attr>();
+        }
+
+        int currentLevel = 0;
+        int lastlevel = 0;
+        XmlsStackElement cur;
+        List<XmlsStackElement> levels = new ArrayList<XmlsStackElement>();
+
+        void push(int level) {
+            currentLevel = level;
+            if (currentLevel == -1) {
+                return;
+            }
+            cur = null;
+            while (lastlevel >= currentLevel) {
+                levels.remove(levels.size() - 1);
+                int newSize = levels.size();
+                if (newSize == 0) {
+                    lastlevel = 0;
+                    return;
+                }
+                lastlevel = (levels.get(newSize - 1)).level;
+            }
+        }
+
+        void addXmlnsAttr(Attr n) {
+            if (cur == null) {
+                cur = new XmlsStackElement();
+                cur.level = currentLevel;
+                levels.add(cur);
+                lastlevel = currentLevel;
+            }
+            cur.nodes.add(n);
+        }
+
+        void getXmlnsAttr(Collection<Attr> col) throws CanonicalizationException {
+            int size = levels.size() - 1;
+            if (cur == null) {
+                cur = new XmlsStackElement();
+                cur.level = currentLevel;
+                lastlevel = currentLevel;
+                levels.add(cur);
+            }
+            boolean parentRendered = false;
+            if (size == -1) {
+                parentRendered = true;
+            } else {
+                XmlsStackElement e = levels.get(size);
+                if (e.rendered && e.level + 1 == currentLevel) {
+                    parentRendered = true;
+                }
+            }
+            if (parentRendered) {
+                col.addAll(cur.nodes);
+                cur.rendered = true;
+                return;
+            }
+
+            Map<String, Attr> loa = new HashMap<String, Attr>();
+            List<Attr> baseAttrs = new ArrayList<Attr>();
+            boolean successiveOmitted = true;
+            for (; size >= 0; size--) {
+                XmlsStackElement e = levels.get(size);
+                if (e.rendered) {
+                    successiveOmitted = false;
+                }
+                Iterator<Attr> it = e.nodes.iterator();
+                while (it.hasNext() && successiveOmitted) {
+                    Attr n = it.next();
+                    if (n.getLocalName().equals("base") && !e.rendered) {
+                        baseAttrs.add(n);
+                    } else if (!loa.containsKey(n.getName())) {
+                        loa.put(n.getName(), n);
+                    }
+                }
+            }
+            if (!baseAttrs.isEmpty()) {
+                Iterator<Attr> it = col.iterator();
+                String base = null;
+                Attr baseAttr = null;
+                while (it.hasNext()) {
+                    Attr n = it.next();
+                    if (n.getLocalName().equals("base")) {
+                        base = n.getValue();
+                        baseAttr = n;
+                        break;
+                    }
+                }
+                it = baseAttrs.iterator();
+                while (it.hasNext()) {
+                    Attr n = it.next();
+                    if (base == null) {
+                        base = n.getValue();
+                        baseAttr = n;
+                    } else {
+                        try {
+                            base = joinURI(n.getValue(), base);
+                        } catch (URISyntaxException e1) {
+                            throw new CanonicalizationException(e1);
+                        }
+                    }
+                }
+                if (base != null && base.length() != 0) {
+                    baseAttr.setValue(base);
+                    col.add(baseAttr);
+                }
+            }
+
+            cur.rendered = true;
+            col.addAll(loa.values());
+        }
+    }
+
+    private final XmlAttrStack xmlattrStack = new XmlAttrStack();
+
+    /**
+     * Constructor Canonicalizer11
+     *
+     * @param includeComments
+     */
+    public Canonicalizer11(boolean includeComments) {
+        super(includeComments);
+    }
+
+    /**
+     * Always throws a CanonicalizationException because this is inclusive c14n.
+     *
+     * @param rootNode
+     * @param inclusiveNamespaces
+     * @return none it always fails
+     * @throws CanonicalizationException
+     */
+    public byte[] engineCanonicalizeSubTree(
+        Node rootNode, String inclusiveNamespaces, CanonicalFilter filter
+    ) throws CanonicalizationException {
+        throw new CanonicalizationException("c14n.Canonicalizer.UnsupportedOperation");
+    }
+
+    /**
+     * Returns the Attr[]s to be output for the given element.
+     * <br>
+     * The code of this method is a copy of {@link #handleAttributes(Element,
+     * NameSpaceSymbTable)},
+     * whereas it takes into account that subtree-c14n is -- well --
+     * subtree-based.
+     * So if the element in question isRoot of c14n, it's parent is not in the
+     * node set, as well as all other ancestors.
+     *
+     * @param element
+     * @param ns
+     * @return the Attr[]s to be output
+     * @throws CanonicalizationException
+     * @throws URISyntaxException
+     */
+    @Override
+    protected Iterator<Attr> handleAttributesSubtree(Element element, NameSpaceSymbTable ns)
+        throws CanonicalizationException {
+        if (!element.hasAttributes() && !firstCall) {
+            return null;
+        }
+        // result will contain the attrs which have to be output
+        final SortedSet<Attr> result = this.result;
+        result.clear();
+
+        if (element.hasAttributes()) {
+            NamedNodeMap attrs = element.getAttributes();
+            int attrsLength = attrs.getLength();
+
+            for (int i = 0; i < attrsLength; i++) {
+                Attr attribute = (Attr) attrs.item(i);
+                String NUri = attribute.getNamespaceURI();
+                String NName = attribute.getLocalName();
+                String NValue = attribute.getValue();
+
+                if (!XMLNS_URI.equals(NUri)) {
+                    // It's not a namespace attr node. Add to the result and continue.
+                    result.add(attribute);
+                } else if (!(XML.equals(NName) && XML_LANG_URI.equals(NValue))) {
+                    // The default mapping for xml must not be output.
+                    Node n = ns.addMappingAndRender(NName, NValue, attribute);
+
+                    if (n != null) {
+                        // Render the ns definition
+                        result.add((Attr)n);
+                        if (C14nHelper.namespaceIsRelative(attribute)) {
+                            Object exArgs[] = {element.getTagName(), NName, attribute.getNodeValue()};
+                            throw new CanonicalizationException(
+                                "c14n.Canonicalizer.RelativeNamespace", exArgs
+                            );
+                        }
+                    }
+                }
+            }
+        }
+
+        if (firstCall) {
+            // It is the first node of the subtree
+            // Obtain all the namespaces defined in the parents, and added to the output.
+            ns.getUnrenderedNodes(result);
+            // output the attributes in the xml namespace.
+            xmlattrStack.getXmlnsAttr(result);
+            firstCall = false;
+        }
+
+        return result.iterator();
+    }
+
+    /**
+     * Returns the Attr[]s to be output for the given element.
+     * <br>
+     * IMPORTANT: This method expects to work on a modified DOM tree, i.e. a
+     * DOM which has been prepared using
+     * {@link nokogiri.internals.c14n.security.utils.XMLUtils#circumventBug2650(
+     * org.w3c.dom.Document)}.
+     *
+     * @param element
+     * @param ns
+     * @return the Attr[]s to be output
+     * @throws CanonicalizationException
+     */
+    @Override
+    protected Iterator<Attr> handleAttributes(Element element, NameSpaceSymbTable ns)
+        throws CanonicalizationException {
+        // result will contain the attrs which have to be output
+        xmlattrStack.push(ns.getLevel());
+        boolean isRealVisible = isVisibleDO(element, ns.getLevel()) == 1;
+        final SortedSet<Attr> result = this.result;
+        result.clear();
+
+        if (element.hasAttributes()) {
+            NamedNodeMap attrs = element.getAttributes();
+            int attrsLength = attrs.getLength();
+
+            for (int i = 0; i < attrsLength; i++) {
+                Attr attribute = (Attr) attrs.item(i);
+                String NUri = attribute.getNamespaceURI();
+                String NName = attribute.getLocalName();
+                String NValue = attribute.getValue();
+
+                if (!XMLNS_URI.equals(NUri)) {
+                    //A non namespace definition node.
+                    if (XML_LANG_URI.equals(NUri)) {
+                        if (NName.equals("id")) {
+                            if (isRealVisible) {
+                                // treat xml:id like any other attribute
+                                // (emit it, but don't inherit it)
+                                result.add(attribute);
+                            }
+                        } else {
+                            xmlattrStack.addXmlnsAttr(attribute);
+                        }
+                    } else if (isRealVisible) {
+                        //The node is visible add the attribute to the list of output attributes.
+                        result.add(attribute);
+                    }
+                } else if (!XML.equals(NName) || !XML_LANG_URI.equals(NValue)) {
+                    /* except omit namespace node with local name xml, which defines
+                     * the xml prefix, if its string value is
+                     * http://www.w3.org/XML/1998/namespace.
+                     */
+                    // add the prefix binding to the ns symb table.
+                    if (isVisible(attribute))  {
+                        if (isRealVisible || !ns.removeMappingIfRender(NName)) {
+                            // The xpath select this node output it if needed.
+                            Node n = ns.addMappingAndRender(NName, NValue, attribute);
+                            if (n != null) {
+                                result.add((Attr)n);
+                                if (C14nHelper.namespaceIsRelative(attribute)) {
+                                    Object exArgs[] = { element.getTagName(), NName, attribute.getNodeValue() };
+                                    throw new CanonicalizationException(
+                                        "c14n.Canonicalizer.RelativeNamespace", exArgs
+                                    );
+                                }
+                            }
+                        }
+                    } else {
+                        if (isRealVisible && !XMLNS.equals(NName)) {
+                            ns.removeMapping(NName);
+                        } else {
+                            ns.addMapping(NName, NValue, attribute);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (isRealVisible) {
+            //The element is visible, handle the xmlns definition
+            Attr xmlns = element.getAttributeNodeNS(XMLNS_URI, XMLNS);
+            Node n = null;
+            if (xmlns == null) {
+                //No xmlns def just get the already defined.
+                n = ns.getMapping(XMLNS);
+            } else if (!isVisible(xmlns)) {
+                //There is a definition but the xmlns is not selected by the xpath.
+                //then xmlns=""
+                n = ns.addMappingAndRender(XMLNS, "", nullNode);
+            }
+            //output the xmlns def if needed.
+            if (n != null) {
+                result.add((Attr)n);
+            }
+            //Float all xml:* attributes of the unselected parent elements to this one.
+            xmlattrStack.getXmlnsAttr(result);
+            ns.getUnrenderedNodes(result);
+        }
+
+        return result.iterator();
+    }
+
+    @Override
+    protected void handleParent(Element e, NameSpaceSymbTable ns) {
+        if (!e.hasAttributes() && e.getNamespaceURI() == null) {
+            return;
+        }
+        xmlattrStack.push(-1);
+        NamedNodeMap attrs = e.getAttributes();
+        int attrsLength = attrs.getLength();
+        for (int i = 0; i < attrsLength; i++) {
+            Attr attribute = (Attr) attrs.item(i);
+            String NName = attribute.getLocalName();
+            String NValue = attribute.getNodeValue();
+
+            if (Constants.NamespaceSpecNS.equals(attribute.getNamespaceURI())) {
+                if (!XML.equals(NName) || !Constants.XML_LANG_SPACE_SpecNS.equals(NValue)) {
+                    ns.addMapping(NName, NValue, attribute);
+                }
+            } else if (!"id".equals(NName) && XML_LANG_URI.equals(attribute.getNamespaceURI())) {
+                xmlattrStack.addXmlnsAttr(attribute);
+            }
+        }
+        if (e.getNamespaceURI() != null) {
+            String NName = e.getPrefix();
+            String NValue = e.getNamespaceURI();
+            String Name;
+            if (NName == null || NName.equals("")) {
+                NName = "xmlns";
+                Name = "xmlns";
+            } else {
+                Name = "xmlns:" + NName;
+            }
+            Attr n = e.getOwnerDocument().createAttributeNS("http://www.w3.org/2000/xmlns/", Name);
+            n.setValue(NValue);
+            ns.addMapping(NName, NValue, n);
+        }
+    }
+
+    private static String joinURI(String baseURI, String relativeURI) throws URISyntaxException {
+        String bscheme = null;
+        String bauthority = null;
+        String bpath = "";
+        String bquery = null;
+
+        // pre-parse the baseURI
+        if (baseURI != null) {
+            if (baseURI.endsWith("..")) {
+                baseURI = baseURI + "/";
+            }
+            URI base = new URI(baseURI);
+            bscheme = base.getScheme();
+            bauthority = base.getAuthority();
+            bpath = base.getPath();
+            bquery = base.getQuery();
+        }
+
+        URI r = new URI(relativeURI);
+        String rscheme = r.getScheme();
+        String rauthority = r.getAuthority();
+        String rpath = r.getPath();
+        String rquery = r.getQuery();
+
+        String tscheme, tauthority, tpath, tquery;
+        if (rscheme != null && rscheme.equals(bscheme)) {
+            rscheme = null;
+        }
+        if (rscheme != null) {
+            tscheme = rscheme;
+            tauthority = rauthority;
+            tpath = removeDotSegments(rpath);
+            tquery = rquery;
+        } else {
+            if (rauthority != null) {
+                tauthority = rauthority;
+                tpath = removeDotSegments(rpath);
+                tquery = rquery;
+            } else {
+                if (rpath.length() == 0) {
+                    tpath = bpath;
+                    if (rquery != null) {
+                        tquery = rquery;
+                    } else {
+                        tquery = bquery;
+                    }
+                } else {
+                    if (rpath.startsWith("/")) {
+                        tpath = removeDotSegments(rpath);
+                    } else {
+                        if (bauthority != null && bpath.length() == 0) {
+                            tpath = "/" + rpath;
+                        } else {
+                            int last = bpath.lastIndexOf('/');
+                            if (last == -1) {
+                                tpath = rpath;
+                            } else {
+                                tpath = bpath.substring(0, last+1) + rpath;
+                            }
+                        }
+                        tpath = removeDotSegments(tpath);
+                    }
+                    tquery = rquery;
+                }
+                tauthority = bauthority;
+            }
+            tscheme = bscheme;
+        }
+        return new URI(tscheme, tauthority, tpath, tquery, null).toString();
+    }
+
+    private static String removeDotSegments(String path) {
+
+        // 1. The input buffer is initialized with the now-appended path
+        // components then replace occurrences of "//" in the input buffer
+        // with "/" until no more occurrences of "//" are in the input buffer.
+        String input = path;
+        while (input.indexOf("//") > -1) {
+            input = input.replaceAll("//", "/");
+        }
+
+        // Initialize the output buffer with the empty string.
+        StringBuilder output = new StringBuilder();
+
+        // If the input buffer starts with a root slash "/" then move this
+        // character to the output buffer.
+        if (input.charAt(0) == '/') {
+            output.append('/');
+            input = input.substring(1);
+        }
+
+        printStep("1 ", output, input);
+
+        // While the input buffer is not empty, loop as follows
+        while (input.length() != 0) {
+            // 2A. If the input buffer begins with a prefix of "./",
+            // then remove that prefix from the input buffer
+            // else if the input buffer begins with a prefix of "../", then
+            // if also the output does not contain the root slash "/" only,
+            // then move this prefix to the end of the output buffer else
+            // remove that prefix
+            if (input.startsWith("./")) {
+                input = input.substring(2);
+                printStep("2A", output, input);
+            } else if (input.startsWith("../")) {
+                input = input.substring(3);
+                if (!output.toString().equals("/")) {
+                    output.append("../");
+                }
+                printStep("2A", output, input);
+                // 2B. if the input buffer begins with a prefix of "/./" or "/.",
+                // where "." is a complete path segment, then replace that prefix
+                // with "/" in the input buffer; otherwise,
+            } else if (input.startsWith("/./")) {
+                input = input.substring(2);
+                printStep("2B", output, input);
+            } else if (input.equals("/.")) {
+                // FIXME: what is complete path segment?
+                input = input.replaceFirst("/.", "/");
+                printStep("2B", output, input);
+                // 2C. if the input buffer begins with a prefix of "/../" or "/..",
+                // where ".." is a complete path segment, then replace that prefix
+                // with "/" in the input buffer and if also the output buffer is
+                // empty, last segment in the output buffer equals "../" or "..",
+                // where ".." is a complete path segment, then append ".." or "/.."
+                // for the latter case respectively to the output buffer else
+                // remove the last segment and its preceding "/" (if any) from the
+                // output buffer and if hereby the first character in the output
+                // buffer was removed and it was not the root slash then delete a
+                // leading slash from the input buffer; otherwise,
+            } else if (input.startsWith("/../")) {
+                input = input.substring(3);
+                if (output.length() == 0) {
+                    output.append("/");
+                } else if (output.toString().endsWith("../")) {
+                    output.append("..");
+                } else if (output.toString().endsWith("..")) {
+                    output.append("/..");
+                } else {
+                    int index = output.lastIndexOf("/");
+                    if (index == -1) {
+                        output = new StringBuilder();
+                        if (input.charAt(0) == '/') {
+                            input = input.substring(1);
+                        }
+                    } else {
+                        output = output.delete(index, output.length());
+                    }
+                }
+                printStep("2C", output, input);
+            } else if (input.equals("/..")) {
+                // FIXME: what is complete path segment?
+                input = input.replaceFirst("/..", "/");
+                if (output.length() == 0) {
+                    output.append("/");
+                } else if (output.toString().endsWith("../")) {
+                    output.append("..");
+                } else if (output.toString().endsWith("..")) {
+                    output.append("/..");
+                } else {
+                    int index = output.lastIndexOf("/");
+                    if (index == -1) {
+                        output = new StringBuilder();
+                        if (input.charAt(0) == '/') {
+                            input = input.substring(1);
+                        }
+                    } else {
+                        output = output.delete(index, output.length());
+                    }
+                }
+                printStep("2C", output, input);
+                // 2D. if the input buffer consists only of ".", then remove
+                // that from the input buffer else if the input buffer consists
+                // only of ".." and if the output buffer does not contain only
+                // the root slash "/", then move the ".." to the output buffer
+                // else delte it.; otherwise,
+            } else if (input.equals(".")) {
+                input = "";
+                printStep("2D", output, input);
+            } else if (input.equals("..")) {
+                if (!output.toString().equals("/")) {
+                    output.append("..");
+                }
+                input = "";
+                printStep("2D", output, input);
+                // 2E. move the first path segment (if any) in the input buffer
+                // to the end of the output buffer, including the initial "/"
+                // character (if any) and any subsequent characters up to, but not
+                // including, the next "/" character or the end of the input buffer.
+            } else {
+                int end;
+                int begin = input.indexOf('/');
+                if (begin == 0) {
+                    end = input.indexOf('/', 1);
+                } else {
+                    end = begin;
+                    begin = 0;
+                }
+                String segment;
+                if (end == -1) {
+                    segment = input.substring(begin);
+                    input = "";
+                } else {
+                    segment = input.substring(begin, end);
+                    input = input.substring(end);
+                }
+                output.append(segment);
+                printStep("2E", output, input);
+            }
+        }
+
+        // 3. Finally, if the only or last segment of the output buffer is
+        // "..", where ".." is a complete path segment not followed by a slash
+        // then append a slash "/". The output buffer is returned as the result
+        // of remove_dot_segments
+        if (output.toString().endsWith("..")) {
+            output.append('/');
+            printStep("3 ", output, input);
+        }
+
+        return output.toString();
+    }
+
+    private static void printStep(String step, StringBuilder output, String input) {
+        //if (System.getProperty("nokogiri.c14.debug") == "on") { //
+        //    System.out.println(" " + step + ":   " + output);
+        //    if (output.length() == 0) {
+        //        System.out.println("\t\t\t\t" + input);
+        //    } else {
+        //        System.out.println("\t\t\t" + input);
+        //    }
+        //}
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer11_OmitComments.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer11_OmitComments.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer11_OmitComments.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer11_OmitComments.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+
+/**
+ * @author Sean Mullan
+ */
+public class Canonicalizer11_OmitComments extends Canonicalizer11 {
+
+    public Canonicalizer11_OmitComments() {
+        super(false);
+    }
+
+    public final String engineGetURI() {
+        return Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS;
+    }
+
+    public final boolean engineGetIncludeComments() {
+        return false;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer11_WithComments.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer11_WithComments.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer11_WithComments.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer11_WithComments.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+
+/**
+ * @author Sean Mullan
+ */
+public class Canonicalizer11_WithComments extends Canonicalizer11 {
+
+    public Canonicalizer11_WithComments() {
+        super(true);
+    }
+
+    public final String engineGetURI() {
+        return Canonicalizer.ALGO_ID_C14N11_WITH_COMMENTS;
+    }
+
+    public final boolean engineGetIncludeComments() {
+        return true;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer20010315Excl.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer20010315Excl.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer20010315Excl.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer20010315Excl.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,295 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * Implements &quot; <A
+ * HREF="http://www.w3.org/TR/2002/REC-xml-exc-c14n-20020718/">Exclusive XML
+ * Canonicalization, Version 1.0 </A>&quot; <BR />
+ * Credits: During restructuring of the Canonicalizer framework, Ren??
+ * Kollmorgen from Software AG submitted an implementation of ExclC14n which
+ * fitted into the old architecture and which based heavily on my old (and slow)
+ * implementation of "Canonical XML". A big "thank you" to Ren?? for this.
+ * <BR />
+ * <i>THIS </i> implementation is a complete rewrite of the algorithm.
+ *
+ * @author Christian Geuer-Pollmann <geuerp@apache.org>
+ * @version $Revision: 1147448 $
+ * @see <a href="http://www.w3.org/TR/2002/REC-xml-exc-c14n-20020718/ Exclusive#">
+ *          XML Canonicalization, Version 1.0</a>
+ */
+public abstract class Canonicalizer20010315Excl extends CanonicalizerBase {
+
+    private static final String XML_LANG_URI = Constants.XML_LANG_SPACE_SpecNS;
+    private static final String XMLNS_URI = Constants.NamespaceSpecNS;
+
+    /**
+      * This Set contains the names (Strings like "xmlns" or "xmlns:foo") of
+      * the inclusive namespaces.
+      */
+    private SortedSet<String> inclusiveNSSet;
+
+    private final SortedSet<Attr> result = new TreeSet<Attr>(COMPARE);
+
+    /**
+     * Constructor Canonicalizer20010315Excl
+     *
+     * @param includeComments
+     */
+    public Canonicalizer20010315Excl(boolean includeComments) {
+        super(includeComments);
+    }
+
+    /**
+     * Method engineCanonicalizeSubTree
+     * @inheritDoc
+     * @param rootNode
+     *
+     * @throws CanonicalizationException
+     */
+    @Override
+    public byte[] engineCanonicalizeSubTree(Node rootNode, CanonicalFilter filter)
+        throws CanonicalizationException {
+        return engineCanonicalizeSubTree(rootNode, "", null);
+    }
+
+    /**
+     * Method engineCanonicalizeSubTree
+     *  @inheritDoc
+     * @param rootNode
+     * @param inclusiveNamespaces
+     *
+     * @throws CanonicalizationException
+     */
+    @Override
+    public byte[] engineCanonicalizeSubTree(
+        Node rootNode, String inclusiveNamespaces, CanonicalFilter filter
+    ) throws CanonicalizationException {
+        return engineCanonicalizeSubTree(rootNode, inclusiveNamespaces, null, filter);
+    }
+
+    /**
+     * Method engineCanonicalizeSubTree
+     * @param rootNode
+     * @param inclusiveNamespaces
+     * @param excl A element to exclude from the c14n process.
+     * @return the rootNode c14n.
+     * @throws CanonicalizationException
+     */
+    public byte[] engineCanonicalizeSubTree(
+        Node rootNode, String inclusiveNamespaces, Node excl, CanonicalFilter filter
+    ) throws CanonicalizationException{
+        inclusiveNSSet = InclusiveNamespaces.prefixStr2Set(inclusiveNamespaces);
+        return super.engineCanonicalizeSubTree(rootNode, excl, filter);
+    }
+
+    @Override
+    protected Iterator<Attr> handleAttributesSubtree(Element element, NameSpaceSymbTable ns)
+        throws CanonicalizationException {
+        // result will contain the attrs which have to be output
+        final SortedSet<Attr> result = this.result;
+        result.clear();
+
+        // The prefix visibly utilized (in the attribute or in the name) in
+        // the element
+        SortedSet<String> visiblyUtilized = new TreeSet<String>();
+        if (inclusiveNSSet != null && !inclusiveNSSet.isEmpty()) {
+            visiblyUtilized.addAll(inclusiveNSSet);
+        }
+
+        if (element.hasAttributes()) {
+            NamedNodeMap attrs = element.getAttributes();
+            int attrsLength = attrs.getLength();
+            for (int i = 0; i < attrsLength; i++) {
+                Attr attribute = (Attr) attrs.item(i);
+                String NName = attribute.getLocalName();
+                String NNodeValue = attribute.getNodeValue();
+
+                if (!XMLNS_URI.equals(attribute.getNamespaceURI())) {
+                    // Not a namespace definition.
+                    // The Element is output element, add the prefix (if used) to
+                    // visiblyUtilized
+                    String prefix = attribute.getPrefix();
+                    if (prefix != null && !(prefix.equals(XML) || prefix.equals(XMLNS))) {
+                        visiblyUtilized.add(prefix);
+                    }
+                    // Add to the result.
+                    result.add(attribute);
+                } else if (!(XML.equals(NName) && XML_LANG_URI.equals(NNodeValue))
+                    && ns.addMapping(NName, NNodeValue, attribute)
+                    && C14nHelper.namespaceIsRelative(NNodeValue)) {
+                    // The default mapping for xml must not be output.
+                    // New definition check if it is relative.
+                    Object exArgs[] = {element.getTagName(), NName, attribute.getNodeValue()};
+                    throw new CanonicalizationException(
+                        "c14n.Canonicalizer.RelativeNamespace", exArgs
+                    );
+                }
+            }
+        }
+        String prefix;
+        if (element.getNamespaceURI() != null
+            && !(element.getPrefix() == null || element.getPrefix().length() == 0)) {
+            prefix = element.getPrefix();
+        } else {
+            prefix = XMLNS;
+        }
+        visiblyUtilized.add(prefix);
+
+        for (String s : visiblyUtilized) {
+            Attr key = ns.getMapping(s);
+            if (key != null) {
+                result.add(key);
+            }
+        }
+
+        return result.iterator();
+    }
+
+    /**
+     * @inheritDoc
+     * @param element
+     * @throws CanonicalizationException
+     */
+    @Override
+    protected final Iterator<Attr> handleAttributes(Element element, NameSpaceSymbTable ns)
+        throws CanonicalizationException {
+        // result will contain the attrs which have to be output
+        final SortedSet<Attr> result = this.result;
+        result.clear();
+
+        // The prefix visibly utilized (in the attribute or in the name) in
+        // the element
+        Set<String> visiblyUtilized = null;
+        // It's the output selected.
+        boolean isOutputElement = isVisibleDO(element, ns.getLevel()) == 1;
+        if (isOutputElement) {
+            visiblyUtilized = new TreeSet<String>();
+            if (inclusiveNSSet != null && !inclusiveNSSet.isEmpty()) {
+                visiblyUtilized.addAll(inclusiveNSSet);
+            }
+        }
+
+        if (element.hasAttributes()) {
+            NamedNodeMap attrs = element.getAttributes();
+            int attrsLength = attrs.getLength();
+            for (int i = 0; i < attrsLength; i++) {
+                Attr attribute = (Attr) attrs.item(i);
+
+                String NName = attribute.getLocalName();
+                String NNodeValue = attribute.getNodeValue();
+
+                if (!XMLNS_URI.equals(attribute.getNamespaceURI())) {
+                    if (isVisible(attribute) && isOutputElement) {
+                        // The Element is output element, add the prefix (if used)
+                        // to visibyUtilized
+                        String prefix = attribute.getPrefix();
+                        if (prefix != null && !(prefix.equals(XML) || prefix.equals(XMLNS))) {
+                            visiblyUtilized.add(prefix);
+                        }
+                        // Add to the result.
+                        result.add(attribute);
+                    }
+                } else if (isOutputElement && !isVisible(attribute) && !XMLNS.equals(NName)) {
+                    ns.removeMappingIfNotRender(NName);
+                } else {
+                    if (!isOutputElement && isVisible(attribute)
+                        && inclusiveNSSet.contains(NName)
+                        && !ns.removeMappingIfRender(NName)) {
+                        Node n = ns.addMappingAndRender(NName, NNodeValue, attribute);
+                        if (n != null) {
+                            result.add((Attr)n);
+                            if (C14nHelper.namespaceIsRelative(attribute)) {
+                                Object exArgs[] = { element.getTagName(), NName, attribute.getNodeValue() };
+                                throw new CanonicalizationException(
+                                    "c14n.Canonicalizer.RelativeNamespace", exArgs
+                                );
+                            }
+                        }
+                    }
+
+                    if (ns.addMapping(NName, NNodeValue, attribute)
+                        && C14nHelper.namespaceIsRelative(NNodeValue)) {
+                        // New definition check if it is relative
+                        Object exArgs[] = { element.getTagName(), NName, attribute.getNodeValue() };
+                        throw new CanonicalizationException(
+                            "c14n.Canonicalizer.RelativeNamespace", exArgs
+                        );
+                    }
+                }
+            }
+        }
+
+        if (isOutputElement) {
+            // The element is visible, handle the xmlns definition
+            Attr xmlns = element.getAttributeNodeNS(XMLNS_URI, XMLNS);
+            if (xmlns != null && !isVisible(xmlns)) {
+                // There is a definition but the xmlns is not selected by the
+                // xpath. then xmlns=""
+                ns.addMapping(XMLNS, "", nullNode);
+            }
+
+            String prefix;
+            if (element.getNamespaceURI() != null
+                && !(element.getPrefix() == null || element.getPrefix().length() == 0)) {
+                prefix = element.getPrefix();
+            } else {
+                prefix = XMLNS;
+            }
+            visiblyUtilized.add(prefix);
+
+            for (String s : visiblyUtilized) {
+                Attr key = ns.getMapping(s);
+                if (key != null) {
+                    result.add(key);
+                }
+            }
+        }
+
+        return result.iterator();
+    }
+
+    /*
+    protected void circumventBugIfNeeded(XMLSignatureInput input)
+        throws CanonicalizationException, ParserConfigurationException,
+               IOException, SAXException {
+        if (!input.isNeedsToBeExpanded() || inclusiveNSSet.isEmpty() || inclusiveNSSet.isEmpty()) {
+            return;
+        }
+        Document doc = null;
+        if (input.getSubNode() != null) {
+            doc = XMLUtils.getOwnerDocument(input.getSubNode());
+        } else {
+            doc = XMLUtils.getOwnerDocument(input.getNodeSet());
+        }
+        XMLUtils.circumventBug2650(doc);
+    }
+    */
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer20010315ExclOmitComments.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer20010315ExclOmitComments.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer20010315ExclOmitComments.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer20010315ExclOmitComments.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+
+public class Canonicalizer20010315ExclOmitComments extends Canonicalizer20010315Excl {
+
+    /**
+     * 
+     */
+    public Canonicalizer20010315ExclOmitComments() {
+        super(false);
+    }
+
+    /** @inheritDoc */
+    public final String engineGetURI() {
+        return Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
+    }
+
+    /** @inheritDoc */
+    public final boolean engineGetIncludeComments() {
+        return false;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer20010315ExclWithComments.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer20010315ExclWithComments.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer20010315ExclWithComments.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer20010315ExclWithComments.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+
+/**
+ * Class Canonicalizer20010315ExclWithComments
+ */
+public class Canonicalizer20010315ExclWithComments extends Canonicalizer20010315Excl {
+
+    /**
+     * Constructor Canonicalizer20010315ExclWithComments
+     *
+     */
+    public Canonicalizer20010315ExclWithComments() {
+        super(true);
+    }
+
+    /** @inheritDoc */
+    public final String engineGetURI() {
+        return Canonicalizer.ALGO_ID_C14N_EXCL_WITH_COMMENTS;
+    }
+
+    /** @inheritDoc */
+    public final boolean engineGetIncludeComments() {
+        return true;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer20010315.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer20010315.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer20010315.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer20010315.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,367 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * Implements <A HREF="http://www.w3.org/TR/2001/REC-xml-c14n-20010315">Canonical
+ * XML Version 1.0</A>, a W3C Recommendation from 15 March 2001.
+ *
+ * @author Christian Geuer-Pollmann <geuerp@apache.org>
+ */
+public abstract class Canonicalizer20010315 extends CanonicalizerBase {
+    private static final String XMLNS_URI = Constants.NamespaceSpecNS;
+    private static final String XML_LANG_URI = Constants.XML_LANG_SPACE_SpecNS;
+
+    private boolean firstCall = true;
+    private final SortedSet<Attr> result = new TreeSet<Attr>(COMPARE);
+
+    private static class XmlAttrStack {
+        static class XmlsStackElement {
+            int level;
+            boolean rendered = false;
+            List<Attr> nodes = new ArrayList<Attr>();
+        }
+
+        int currentLevel = 0;
+        int lastlevel = 0;
+        XmlsStackElement cur;
+        List<XmlsStackElement> levels = new ArrayList<XmlsStackElement>();
+
+        void push(int level) {
+            currentLevel = level;
+            if (currentLevel == -1) {
+                return;
+            }
+            cur = null;
+            while (lastlevel >= currentLevel) {
+                levels.remove(levels.size() - 1);
+                int newSize = levels.size();
+                if (newSize == 0) {
+                    lastlevel = 0;
+                    return;
+                }
+                lastlevel = (levels.get(newSize - 1)).level;
+            }
+        }
+
+        void addXmlnsAttr(Attr n) {
+            if (cur == null) {
+                cur = new XmlsStackElement();
+                cur.level = currentLevel;
+                levels.add(cur);
+                lastlevel = currentLevel;
+            }
+            cur.nodes.add(n);
+        }
+
+        void getXmlnsAttr(Collection<Attr> col) {
+            int size = levels.size() - 1;
+            if (cur == null) {
+                cur = new XmlsStackElement();
+                cur.level = currentLevel;
+                lastlevel = currentLevel;
+                levels.add(cur);
+            }
+            boolean parentRendered = false;
+            if (size == -1) {
+                parentRendered = true;
+            } else {
+                XmlsStackElement e = levels.get(size);
+                if (e.rendered && e.level + 1 == currentLevel) {
+                    parentRendered = true;
+                }
+            }
+            if (parentRendered) {
+                col.addAll(cur.nodes);
+                cur.rendered = true;
+                return;
+            }
+
+            Map<String, Attr> loa = new HashMap<String, Attr>();
+            for (; size >= 0; size--) {
+                XmlsStackElement e = levels.get(size);
+                Iterator<Attr> it = e.nodes.iterator();
+                while (it.hasNext()) {
+                    Attr n = it.next();
+                    if (!loa.containsKey(n.getName())) {
+                        loa.put(n.getName(), n);
+                    }
+                }
+            }
+
+            cur.rendered = true;
+            col.addAll(loa.values());
+        }
+
+    }
+
+    private final XmlAttrStack xmlattrStack = new XmlAttrStack();
+
+    /**
+     * Constructor Canonicalizer20010315
+     *
+     * @param includeComments
+     */
+    public Canonicalizer20010315(boolean includeComments) {
+        super(includeComments);
+    }
+
+    /**
+     * Always throws a CanonicalizationException because this is inclusive c14n.
+     *
+     * @param xpathNodeSet
+     * @param inclusiveNamespaces
+     * @return none it always fails
+     * @throws CanonicalizationException always
+     */
+    public byte[] engineCanonicalizeXPathNodeSet(Set<Node> xpathNodeSet, String inclusiveNamespaces, CanonicalFilter filter)
+        throws CanonicalizationException {
+
+        /** $todo$ well, should we throw UnsupportedOperationException ? */
+        throw new CanonicalizationException("c14n.Canonicalizer.UnsupportedOperation");
+    }
+
+    /**
+     * Always throws a CanonicalizationException because this is inclusive c14n.
+     *
+     * @param rootNode
+     * @param inclusiveNamespaces
+     * @return none it always fails
+     * @throws CanonicalizationException
+     */
+    @Override
+    public byte[] engineCanonicalizeSubTree(Node rootNode, String inclusiveNamespaces, CanonicalFilter filter)
+        throws CanonicalizationException {
+
+        /** $todo$ well, should we throw UnsupportedOperationException ? */
+        throw new CanonicalizationException("c14n.Canonicalizer.UnsupportedOperation");
+    }
+
+    /**
+     * Returns the Attr[]s to be output for the given element.
+     * <br>
+     * The code of this method is a copy of {@link #handleAttributes(Element,
+     * NameSpaceSymbTable)},
+     * whereas it takes into account that subtree-c14n is -- well -- subtree-based.
+     * So if the element in question isRoot of c14n, it's parent is not in the
+     * node set, as well as all other ancestors.
+     *
+     * @param element
+     * @param ns
+     * @return the Attr[]s to be output
+     * @throws CanonicalizationException
+     */
+    @Override
+    protected Iterator<Attr> handleAttributesSubtree(Element element, NameSpaceSymbTable ns)
+        throws CanonicalizationException {
+        if (!element.hasAttributes() && !firstCall) {
+            return null;
+        }
+        // result will contain the attrs which have to be output
+        final SortedSet<Attr> result = this.result;
+        result.clear();
+
+        if (element.hasAttributes()) {
+            NamedNodeMap attrs = element.getAttributes();
+            int attrsLength = attrs.getLength();
+
+            for (int i = 0; i < attrsLength; i++) {
+                Attr attribute = (Attr) attrs.item(i);
+                String NUri = attribute.getNamespaceURI();
+                String NName = attribute.getLocalName();
+                String NValue = attribute.getValue();
+
+                if (!XMLNS_URI.equals(NUri)) {
+                    //It's not a namespace attr node. Add to the result and continue.
+                    result.add(attribute);
+                } else if (!(XML.equals(NName) && XML_LANG_URI.equals(NValue))) {
+                    //The default mapping for xml must not be output.
+                    Node n = ns.addMappingAndRender(NName, NValue, attribute);
+
+                    if (n != null) {
+                        //Render the ns definition
+                        result.add((Attr)n);
+                        if (C14nHelper.namespaceIsRelative(attribute)) {
+                            Object exArgs[] = { element.getTagName(), NName, attribute.getNodeValue() };
+                            throw new CanonicalizationException(
+                                "c14n.Canonicalizer.RelativeNamespace", exArgs
+                            );
+                        }
+                    }
+                }
+            }
+        }
+
+        if (firstCall) {
+            //It is the first node of the subtree
+            //Obtain all the namespaces defined in the parents, and added to the output.
+            ns.getUnrenderedNodes(result);
+            //output the attributes in the xml namespace.
+            xmlattrStack.getXmlnsAttr(result);
+            firstCall = false;
+        }
+
+        return result.iterator();
+    }
+
+    /**
+     * Returns the Attr[]s to be output for the given element.
+     * <br>
+     * IMPORTANT: This method expects to work on a modified DOM tree, i.e. a DOM which has
+     * been prepared using {@link nokogiri.internals.c14n.security.utils.XMLUtils#circumventBug2650(
+     * org.w3c.dom.Document)}.
+     *
+     * @param element
+     * @param ns
+     * @return the Attr[]s to be output
+     * @throws CanonicalizationException
+     */
+    @Override
+    protected Iterator<Attr> handleAttributes(Element element, NameSpaceSymbTable ns)
+        throws CanonicalizationException {
+        // result will contain the attrs which have to be output
+        xmlattrStack.push(ns.getLevel());
+        boolean isRealVisible = isVisibleDO(element, ns.getLevel()) == 1;
+        final SortedSet<Attr> result = this.result;
+        result.clear();
+
+        if (element.hasAttributes()) {
+            NamedNodeMap attrs = element.getAttributes();
+            int attrsLength = attrs.getLength();
+
+            for (int i = 0; i < attrsLength; i++) {
+                Attr attribute = (Attr) attrs.item(i);
+                String NUri = attribute.getNamespaceURI();
+                String NName = attribute.getLocalName();
+                String NValue = attribute.getValue();
+
+                if (!XMLNS_URI.equals(NUri)) {
+                    //A non namespace definition node.
+                    if (XML_LANG_URI.equals(NUri)) {
+                        xmlattrStack.addXmlnsAttr(attribute);
+                    } else if (isRealVisible) {
+                        //The node is visible add the attribute to the list of output attributes.
+                        result.add(attribute);
+                    }
+                } else if (!XML.equals(NName) || !XML_LANG_URI.equals(NValue)) {
+                    /* except omit namespace node with local name xml, which defines
+                     * the xml prefix, if its string value is http://www.w3.org/XML/1998/namespace.
+                     */
+                    //add the prefix binding to the ns symb table.
+                    if (isVisible(attribute))  {
+                        if (isRealVisible || !ns.removeMappingIfRender(NName)) {
+                            //The xpath select this node output it if needed.
+                            Node n = ns.addMappingAndRender(NName, NValue, attribute);
+                            if (n != null) {
+                                result.add((Attr)n);
+                                if (C14nHelper.namespaceIsRelative(attribute)) {
+                                    Object exArgs[] = { element.getTagName(), NName, attribute.getNodeValue() };
+                                    throw new CanonicalizationException(
+                                        "c14n.Canonicalizer.RelativeNamespace", exArgs
+                                    );
+                                }
+                            }
+                        }
+                    } else {
+                        if (isRealVisible && !XMLNS.equals(NName)) {
+                            ns.removeMapping(NName);
+                        } else {
+                            ns.addMapping(NName, NValue, attribute);
+                        }
+                    }
+                }
+            }
+        }
+        if (isRealVisible) {
+            //The element is visible, handle the xmlns definition
+            Attr xmlns = element.getAttributeNodeNS(XMLNS_URI, XMLNS);
+            Node n = null;
+            if (xmlns == null) {
+                //No xmlns def just get the already defined.
+                n = ns.getMapping(XMLNS);
+            } else if (!isVisible(xmlns)) {
+                //There is a definition but the xmlns is not selected by the xpath.
+                //then xmlns=""
+                n = ns.addMappingAndRender(XMLNS, "", nullNode);
+            }
+            //output the xmlns def if needed.
+            if (n != null) {
+                result.add((Attr)n);
+            }
+            //Float all xml:* attributes of the unselected parent elements to this one.
+            xmlattrStack.getXmlnsAttr(result);
+            ns.getUnrenderedNodes(result);
+        }
+
+        return result.iterator();
+    }
+
+    @Override
+    protected void handleParent(Element e, NameSpaceSymbTable ns) {
+        if (!e.hasAttributes() && e.getNamespaceURI() == null) {
+            return;
+        }
+        xmlattrStack.push(-1);
+        NamedNodeMap attrs = e.getAttributes();
+        int attrsLength = attrs.getLength();
+        for (int i = 0; i < attrsLength; i++) {
+            Attr attribute = (Attr) attrs.item(i);
+            String NName = attribute.getLocalName();
+            String NValue = attribute.getNodeValue();
+
+            if (Constants.NamespaceSpecNS.equals(attribute.getNamespaceURI())) {
+                if (!XML.equals(NName) || !Constants.XML_LANG_SPACE_SpecNS.equals(NValue)) {
+                    ns.addMapping(NName, NValue, attribute);
+                }
+            } else if (XML_LANG_URI.equals(attribute.getNamespaceURI())) {
+                xmlattrStack.addXmlnsAttr(attribute);
+            }
+        }
+        if (e.getNamespaceURI() != null) {
+            String NName = e.getPrefix();
+            String NValue = e.getNamespaceURI();
+            String Name;
+            if (NName == null || NName.equals("")) {
+                NName = "xmlns";
+                Name = "xmlns";
+            } else {
+                Name = "xmlns:" + NName;
+            }
+            Attr n = e.getOwnerDocument().createAttributeNS("http://www.w3.org/2000/xmlns/", Name);
+            n.setValue(NValue);
+            ns.addMapping(NName, NValue, n);
+        }
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer20010315OmitComments.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer20010315OmitComments.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer20010315OmitComments.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer20010315OmitComments.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+
+/**
+ * @author Christian Geuer-Pollmann
+ */
+public class Canonicalizer20010315OmitComments extends Canonicalizer20010315 {
+
+    /**
+     * Constructor Canonicalizer20010315WithXPathOmitComments
+     *
+     */
+    public Canonicalizer20010315OmitComments() {
+        super(false);
+    }
+
+    /** @inheritDoc */
+    public final String engineGetURI() {
+        return Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS;
+    }
+
+    /** @inheritDoc */
+    public final boolean engineGetIncludeComments() {
+        return false;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer20010315WithComments.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer20010315WithComments.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer20010315WithComments.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer20010315WithComments.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+
+/**
+ * @author Christian Geuer-Pollmann
+ */
+public class Canonicalizer20010315WithComments extends Canonicalizer20010315 {
+
+    /**
+     * Constructor Canonicalizer20010315WithXPathWithComments
+     */
+    public Canonicalizer20010315WithComments() {
+        super(true);
+    }
+
+    /** @inheritDoc */
+    public final String engineGetURI() {
+        return Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS;
+    }
+
+    /** @inheritDoc */
+    public final boolean engineGetIncludeComments() {
+        return true;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/CanonicalizerBase.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/CanonicalizerBase.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/CanonicalizerBase.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/CanonicalizerBase.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,630 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.ProcessingInstruction;
+
+/**
+ * Abstract base class for canonicalization algorithms.
+ *
+ * @author Christian Geuer-Pollmann <geuerp@apache.org>
+ */
+public abstract class CanonicalizerBase extends CanonicalizerSpi {
+    public static final String XML = "xml";
+    public static final String XMLNS = "xmlns";
+
+    protected static final AttrCompare COMPARE = new AttrCompare();
+    protected static final Attr nullNode;
+
+    private static final byte[] END_PI = {'?','>'};
+    private static final byte[] BEGIN_PI = {'<','?'};
+    private static final byte[] END_COMM = {'-','-','>'};
+    private static final byte[] BEGIN_COMM = {'<','!','-','-'};
+    private static final byte[] XA = {'&','#','x','A',';'};
+    private static final byte[] X9 = {'&','#','x','9',';'};
+    private static final byte[] QUOT = {'&','q','u','o','t',';'};
+    private static final byte[] XD = {'&','#','x','D',';'};
+    private static final byte[] GT = {'&','g','t',';'};
+    private static final byte[] LT = {'&','l','t',';'};
+    private static final byte[] END_TAG = {'<','/'};
+    private static final byte[] AMP = {'&','a','m','p',';'};
+    private static final byte[] equalsStr = {'=','\"'};
+
+    protected static final int NODE_BEFORE_DOCUMENT_ELEMENT = -1;
+    protected static final int NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT = 0;
+    protected static final int NODE_AFTER_DOCUMENT_ELEMENT = 1;
+
+    static {
+        // The null xmlns definition.
+        try {
+            DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+            nullNode = documentBuilder.newDocument().createAttributeNS(Constants.NamespaceSpecNS, XMLNS);
+            nullNode.setValue("");
+        } catch (Exception e) {
+            throw new RuntimeException("Unable to create nullNode: " + e);
+        }
+    }
+
+    private List<NodeFilter> nodeFilter;
+
+    private final boolean includeComments;
+    //private Set<Node> xpathNodeSet;
+    /**
+     * The node to be skipped/excluded from the DOM tree
+     * in subtree canonicalizations.
+     */
+    private Node excludeNode;
+    private OutputStream writer = new ByteArrayOutputStream();
+
+    /**
+     * Constructor CanonicalizerBase
+     *
+     * @param includeComments
+     */
+    public CanonicalizerBase(boolean includeComments) {
+        this.includeComments = includeComments;
+    }
+
+    /**
+     * Method engineCanonicalizeSubTree
+     * @inheritDoc
+     * @param rootNode
+     * @throws CanonicalizationException
+     */
+    @Override
+    public byte[] engineCanonicalizeSubTree(Node rootNode, CanonicalFilter filter)
+        throws CanonicalizationException {
+        return engineCanonicalizeSubTree(rootNode, (Node)null, filter);
+    }
+
+    /**
+     * @param writer The writer to set.
+     */
+    @Override
+    public void setWriter(OutputStream writer) {
+        this.writer = writer;
+    }
+
+    /**
+     * Canonicalizes a Subtree node.
+     *
+     * @param rootNode
+     *            the root of the subtree to canonicalize
+     * @param excludeNode
+     *            a node to be excluded from the canonicalize operation
+     * @return The canonicalize stream.
+     * @throws CanonicalizationException
+     */
+    protected byte[] engineCanonicalizeSubTree(Node rootNode, Node excludeNode, CanonicalFilter filter)
+        throws CanonicalizationException {
+        this.excludeNode = excludeNode;
+        try {
+            NameSpaceSymbTable ns = new NameSpaceSymbTable();
+            int nodeLevel = NODE_BEFORE_DOCUMENT_ELEMENT;
+            if (rootNode != null && Node.ELEMENT_NODE == rootNode.getNodeType()) {
+                //Fills the nssymbtable with the definitions of the parent of the root subnode
+                getParentNameSpaces((Element)rootNode, ns);
+                nodeLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
+            }
+            this.canonicalizeSubTree(rootNode, ns, rootNode, nodeLevel, filter);
+            this.writer.flush();
+            if (this.writer instanceof ByteArrayOutputStream) {
+                byte[] result = ((ByteArrayOutputStream)this.writer).toByteArray();
+                if (reset) {
+                    ((ByteArrayOutputStream)this.writer).reset();
+                } else {
+                    this.writer.close();
+                }
+                return result;
+            } else {
+                this.writer.close();
+            }
+            return null;
+
+        } catch (UnsupportedEncodingException ex) {
+            throw new CanonicalizationException("empty", ex);
+        } catch (IOException ex) {
+            throw new CanonicalizationException("empty", ex);
+        }
+    }
+
+
+    /**
+     * Method canonicalizeSubTree, this function is a recursive one.
+     *
+     * @param currentNode
+     * @param ns
+     * @param endnode
+     * @throws CanonicalizationException
+     * @throws IOException
+     */
+    protected final void canonicalizeSubTree(
+        Node currentNode, NameSpaceSymbTable ns, Node endnode, int documentLevel, CanonicalFilter filter
+    ) throws CanonicalizationException, IOException {
+        if (isVisibleInt(currentNode) == -1) {
+            return;
+        }
+        Node sibling = null;
+        Node parentNode = null;
+        final OutputStream writer = this.writer;
+        final Node excludeNode = this.excludeNode;
+        final boolean includeComments = this.includeComments;
+        Map<String, byte[]> cache = new HashMap<String, byte[]>();
+        do {
+            switch (currentNode.getNodeType()) {
+
+            case Node.ENTITY_NODE :
+            case Node.NOTATION_NODE :
+            case Node.ATTRIBUTE_NODE :
+                // illegal node type during traversal
+                throw new CanonicalizationException("empty");
+
+            case Node.DOCUMENT_FRAGMENT_NODE :
+            case Node.DOCUMENT_NODE :
+                ns.outputNodePush();
+                sibling = currentNode.getFirstChild();
+                break;
+
+            case Node.COMMENT_NODE :
+                if (includeComments) {
+                    outputCommentToWriter((Comment) currentNode, writer, documentLevel);
+                }
+                break;
+
+            case Node.PROCESSING_INSTRUCTION_NODE :
+                outputPItoWriter((ProcessingInstruction) currentNode, writer, documentLevel);
+                break;
+
+            case Node.TEXT_NODE :
+            case Node.CDATA_SECTION_NODE :
+                outputTextToWriter(currentNode.getNodeValue(), writer);
+                break;
+
+            case Node.ELEMENT_NODE :
+                documentLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
+                if (currentNode == excludeNode) {
+                    break;
+                }
+                if (filter != null && !filter.includeNodes(currentNode, parentNode)) {
+                    break;
+                }
+
+                Element currentElement = (Element)currentNode;
+                //Add a level to the nssymbtable. So latter can be pop-back.
+                ns.outputNodePush();
+                writer.write('<');
+                String name = currentElement.getTagName();
+                UtfHelpper.writeByte(name, writer, cache);
+
+                Iterator<Attr> attrs = this.handleAttributesSubtree(currentElement, ns);
+                if (attrs != null) {
+                    //we output all Attrs which are available
+                    while (attrs.hasNext()) {
+                        Attr attr = attrs.next();
+                        outputAttrToWriter(attr.getNodeName(), attr.getNodeValue(), writer, cache);
+                    }
+                }
+                writer.write('>');
+                sibling = currentNode.getFirstChild();
+                if (sibling == null) {
+                    writer.write(END_TAG);
+                    UtfHelpper.writeStringToUtf8(name, writer);
+                    writer.write('>');
+                    //We finished with this level, pop to the previous definitions.
+                    ns.outputNodePop();
+                    if (parentNode != null) {
+                        sibling = currentNode.getNextSibling();
+                    }
+                } else {
+                    parentNode = currentElement;
+                }
+                break;
+            case Node.DOCUMENT_TYPE_NODE :
+            default :
+                break;
+            }
+            while (sibling == null && parentNode != null) {
+                writer.write(END_TAG);
+                UtfHelpper.writeByte(((Element)parentNode).getTagName(), writer, cache);
+                writer.write('>');
+                //We finished with this level, pop to the previous definitions.
+                ns.outputNodePop();
+                if (parentNode == endnode) {
+                    return;
+                }
+                sibling = parentNode.getNextSibling();
+                parentNode = parentNode.getParentNode();
+                if (parentNode == null || Node.ELEMENT_NODE != parentNode.getNodeType()) {
+                    documentLevel = NODE_AFTER_DOCUMENT_ELEMENT;
+                    parentNode = null;
+                }
+            }
+            if (sibling == null) {
+                return;
+            }
+            currentNode = sibling;
+            sibling = currentNode.getNextSibling();
+        } while(true);
+    }
+
+    protected int isVisibleDO(Node currentNode, int level) {
+        if (nodeFilter != null) {
+            Iterator<NodeFilter> it = nodeFilter.iterator();
+            while (it.hasNext()) {
+                int i = (it.next()).isNodeIncludeDO(currentNode, level);
+                if (i != 1) {
+                    return i;
+                }
+            }
+        }
+        //if ((this.xpathNodeSet != null) && !this.xpathNodeSet.contains(currentNode)) {
+        //    return 0;
+        //}
+        return 1;
+    }
+
+    protected int isVisibleInt(Node currentNode) {
+        if (nodeFilter != null) {
+            Iterator<NodeFilter> it = nodeFilter.iterator();
+            while (it.hasNext()) {
+                int i = (it.next()).isNodeInclude(currentNode);
+                if (i != 1) {
+                    return i;
+                }
+            }
+        }
+        //if ((this.xpathNodeSet != null) && !this.xpathNodeSet.contains(currentNode)) {
+        //    return 0;
+        //}
+        return 1;
+    }
+
+    protected boolean isVisible(Node currentNode) {
+        if (nodeFilter != null) {
+            Iterator<NodeFilter> it = nodeFilter.iterator();
+            while (it.hasNext()) {
+                if (it.next().isNodeInclude(currentNode) != 1) {
+                    return false;
+                }
+            }
+        }
+        //if ((this.xpathNodeSet != null) && !this.xpathNodeSet.contains(currentNode)) {
+        //    return false;
+        //}
+        return true;
+    }
+
+    protected void handleParent(Element e, NameSpaceSymbTable ns) {
+        if (!e.hasAttributes() && e.getNamespaceURI() == null) {
+            return;
+        }
+        NamedNodeMap attrs = e.getAttributes();
+        int attrsLength = attrs.getLength();
+        for (int i = 0; i < attrsLength; i++) {
+            Attr attribute = (Attr) attrs.item(i);
+            String NName = attribute.getLocalName();
+            String NValue = attribute.getNodeValue();
+
+            if (Constants.NamespaceSpecNS.equals(attribute.getNamespaceURI())
+                && (!XML.equals(NName) || !Constants.XML_LANG_SPACE_SpecNS.equals(NValue))) {
+                ns.addMapping(NName, NValue, attribute);
+            }
+        }
+        if (e.getNamespaceURI() != null) {
+            String NName = e.getPrefix();
+            String NValue = e.getNamespaceURI();
+            String Name;
+            if (NName == null || NName.equals("")) {
+                NName = XMLNS;
+                Name = XMLNS;
+            } else {
+                Name = XMLNS + ":" + NName;
+            }
+            Attr n = e.getOwnerDocument().createAttributeNS("http://www.w3.org/2000/xmlns/", Name);
+            n.setValue(NValue);
+            ns.addMapping(NName, NValue, n);
+        }
+    }
+
+    /**
+     * Adds to ns the definitions from the parent elements of el
+     * @param el
+     * @param ns
+     */
+    protected final void getParentNameSpaces(Element el, NameSpaceSymbTable ns)  {
+        Node n1 = el.getParentNode();
+        if (n1 == null || Node.ELEMENT_NODE != n1.getNodeType()) {
+            return;
+        }
+        //Obtain all the parents of the element
+        List<Element> parents = new ArrayList<Element>();
+        Node parent = n1;
+        while (parent != null && Node.ELEMENT_NODE == parent.getNodeType()) {
+            parents.add((Element)parent);
+            parent = parent.getParentNode();
+        }
+        //Visit them in reverse order.
+        ListIterator<Element> it = parents.listIterator(parents.size());
+        while (it.hasPrevious()) {
+            Element ele = it.previous();
+            handleParent(ele, ns);
+        }
+        parents.clear();
+        Attr nsprefix;
+        if (((nsprefix = ns.getMappingWithoutRendered(XMLNS)) != null)
+            && "".equals(nsprefix.getValue())) {
+            ns.addMappingAndRender(XMLNS, "", nullNode);
+        }
+    }
+
+    /**
+     * Obtain the attributes to output for this node in XPathNodeSet c14n.
+     *
+     * @param element
+     * @param ns
+     * @return the attributes nodes to output.
+     * @throws CanonicalizationException
+     */
+    abstract Iterator<Attr> handleAttributes(Element element, NameSpaceSymbTable ns)
+        throws CanonicalizationException;
+
+    /**
+     * Obtain the attributes to output for this node in a Subtree c14n.
+     *
+     * @param element
+     * @param ns
+     * @return the attributes nodes to output.
+     * @throws CanonicalizationException
+     */
+    abstract Iterator<Attr> handleAttributesSubtree(Element element, NameSpaceSymbTable ns)
+        throws CanonicalizationException;
+
+    //abstract void circumventBugIfNeeded(XMLSignatureInput input)
+    //    throws CanonicalizationException, ParserConfigurationException, IOException, SAXException;
+
+    /**
+     * Outputs an Attribute to the internal Writer.
+     *
+     * The string value of the node is modified by replacing
+     * <UL>
+     * <LI>all ampersands (&) with <CODE>&amp;amp;</CODE></LI>
+     * <LI>all open angle brackets (<) with <CODE>&amp;lt;</CODE></LI>
+     * <LI>all quotation mark characters with <CODE>&amp;quot;</CODE></LI>
+     * <LI>and the whitespace characters <CODE>#x9</CODE>, #xA, and #xD, with character
+     * references. The character references are written in uppercase
+     * hexadecimal with no leading zeroes (for example, <CODE>#xD</CODE> is represented
+     * by the character reference <CODE>&amp;#xD;</CODE>)</LI>
+     * </UL>
+     *
+     * @param name
+     * @param value
+     * @param writer
+     * @throws IOException
+     */
+    protected static final void outputAttrToWriter(
+        final String name, final String value,
+        final OutputStream writer, final Map<String, byte[]> cache
+    ) throws IOException {
+        writer.write(' ');
+        UtfHelpper.writeByte(name, writer, cache);
+        writer.write(equalsStr);
+        byte[] toWrite;
+        final int length = value.length();
+        int i = 0;
+        while (i < length) {
+            char c = value.charAt(i++);
+
+            switch (c) {
+
+            case '&' :
+                toWrite = AMP;
+                break;
+
+            case '<' :
+                toWrite = LT;
+                break;
+
+            case '"' :
+                toWrite = QUOT;
+                break;
+
+            case 0x09 :    // '\t'
+                toWrite = X9;
+                break;
+
+            case 0x0A :    // '\n'
+                toWrite = XA;
+                break;
+
+            case 0x0D :    // '\r'
+                toWrite = XD;
+                break;
+
+            default :
+                if (c < 0x80) {
+                    writer.write(c);
+                } else {
+                    UtfHelpper.writeCharToUtf8(c, writer);
+                }
+                continue;
+            }
+            writer.write(toWrite);
+        }
+
+        writer.write('\"');
+    }
+
+    /**
+     * Outputs a PI to the internal Writer.
+     *
+     * @param currentPI
+     * @param writer where to write the things
+     * @throws IOException
+     */
+    protected void outputPItoWriter(
+        ProcessingInstruction currentPI, OutputStream writer, int position
+    ) throws IOException {
+        if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
+            writer.write('\n');
+        }
+        writer.write(BEGIN_PI);
+
+        final String target = currentPI.getTarget();
+        int length = target.length();
+
+        for (int i = 0; i < length; i++) {
+            char c = target.charAt(i);
+            if (c == 0x0D) {
+                writer.write(XD);
+            } else {
+                if (c < 0x80) {
+                    writer.write(c);
+                } else {
+                    UtfHelpper.writeCharToUtf8(c, writer);
+                }
+            }
+        }
+
+        final String data = currentPI.getData();
+
+        length = data.length();
+
+        if (length > 0) {
+            writer.write(' ');
+
+            for (int i = 0; i < length; i++) {
+                char c = data.charAt(i);
+                if (c == 0x0D) {
+                    writer.write(XD);
+                } else {
+                    UtfHelpper.writeCharToUtf8(c, writer);
+                }
+            }
+        }
+
+        writer.write(END_PI);
+        if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
+            writer.write('\n');
+        }
+    }
+
+    /**
+     * Method outputCommentToWriter
+     *
+     * @param currentComment
+     * @param writer writer where to write the things
+     * @throws IOException
+     */
+    protected void outputCommentToWriter(
+        Comment currentComment, OutputStream writer, int position
+    ) throws IOException {
+        if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
+            writer.write('\n');
+        }
+        writer.write(BEGIN_COMM);
+
+        final String data = currentComment.getData();
+        final int length = data.length();
+
+        for (int i = 0; i < length; i++) {
+            char c = data.charAt(i);
+            if (c == 0x0D) {
+                writer.write(XD);
+            } else {
+                if (c < 0x80) {
+                    writer.write(c);
+                } else {
+                    UtfHelpper.writeCharToUtf8(c, writer);
+                }
+            }
+        }
+
+        writer.write(END_COMM);
+        if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
+            writer.write('\n');
+        }
+    }
+
+    /**
+     * Outputs a Text of CDATA section to the internal Writer.
+     *
+     * @param text
+     * @param writer writer where to write the things
+     * @throws IOException
+     */
+    protected static final void outputTextToWriter(
+        final String text, final OutputStream writer
+    ) throws IOException {
+        final int length = text.length();
+        byte[] toWrite;
+        for (int i = 0; i < length; i++) {
+            char c = text.charAt(i);
+
+            switch (c) {
+
+            case '&' :
+                toWrite = AMP;
+                break;
+
+            case '<' :
+                toWrite = LT;
+                break;
+
+            case '>' :
+                toWrite = GT;
+                break;
+
+            case 0xD :
+                toWrite = XD;
+                break;
+
+            default :
+                if (c < 0x80) {
+                    writer.write(c);
+                } else {
+                    UtfHelpper.writeCharToUtf8(c, writer);
+                }
+                continue;
+            }
+            writer.write(toWrite);
+        }
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/Canonicalizer.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Canonicalizer.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,252 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.w3c.dom.Node;
+
+/**
+ *
+ * @author Christian Geuer-Pollmann
+ */
+public class Canonicalizer {
+
+    /** The output encoding of canonicalized data */
+    public static final String ENCODING = "UTF8";
+
+    /**
+     * XPath Expression for selecting every node and continuous comments joined
+     * in only one node
+     */
+    public static final String XPATH_C14N_WITH_COMMENTS_SINGLE_NODE =
+        "(.//. | .//@* | .//namespace::*)";
+
+    /**
+     * The URL defined in XML-SEC Rec for inclusive c14n <b>without</b> comments.
+     */
+    public static final String ALGO_ID_C14N_OMIT_COMMENTS =
+        "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
+    /**
+     * The URL defined in XML-SEC Rec for inclusive c14n <b>with</b> comments.
+     */
+    public static final String ALGO_ID_C14N_WITH_COMMENTS =
+        ALGO_ID_C14N_OMIT_COMMENTS + "#WithComments";
+    /**
+     * The URL defined in XML-SEC Rec for exclusive c14n <b>without</b> comments.
+     */
+    public static final String ALGO_ID_C14N_EXCL_OMIT_COMMENTS =
+        "http://www.w3.org/2001/10/xml-exc-c14n#";
+    /**
+     * The URL defined in XML-SEC Rec for exclusive c14n <b>with</b> comments.
+     */
+    public static final String ALGO_ID_C14N_EXCL_WITH_COMMENTS =
+        ALGO_ID_C14N_EXCL_OMIT_COMMENTS + "WithComments";
+    /**
+     * The URI for inclusive c14n 1.1 <b>without</b> comments.
+     */
+    public static final String ALGO_ID_C14N11_OMIT_COMMENTS =
+        "http://www.w3.org/2006/12/xml-c14n11";
+    /**
+     * The URI for inclusive c14n 1.1 <b>with</b> comments.
+     */
+    public static final String ALGO_ID_C14N11_WITH_COMMENTS =
+        ALGO_ID_C14N11_OMIT_COMMENTS + "#WithComments";
+    /**
+     * Non-standard algorithm to serialize the physical representation for XML Encryption
+     */
+    public static final String ALGO_ID_C14N_PHYSICAL =
+        "http://santuario.apache.org/c14n/physical";
+
+    private static Map<String, Class<? extends CanonicalizerSpi>> canonicalizerHash = null;
+
+    private final CanonicalizerSpi canonicalizerSpi;
+
+    /**
+     * Constructor Canonicalizer
+     *
+     * @param algorithmURI
+     * @throws InvalidCanonicalizerException
+     */
+    private Canonicalizer(String algorithmURI) throws CanonicalizationException {
+        try {
+            Class<? extends CanonicalizerSpi> implementingClass =
+                canonicalizerHash.get(algorithmURI);
+
+            canonicalizerSpi = implementingClass.newInstance();
+            canonicalizerSpi.reset = true;
+        } catch (Exception e) {
+            Object exArgs[] = { algorithmURI };
+            throw new CanonicalizationException(
+                "signature.Canonicalizer.UnknownCanonicalizer", exArgs, e
+            );
+        }
+    }
+
+    /**
+     * Method getInstance
+     *
+     * @param algorithmURI
+     * @return a Canonicalizer instance ready for the job
+     * @throws InvalidCanonicalizerException
+     */
+    public static final Canonicalizer getInstance(String algorithmURI)
+        throws CanonicalizationException {
+        if (canonicalizerHash == null) {
+            canonicalizerHash = new ConcurrentHashMap<String, Class<? extends CanonicalizerSpi>>();
+            Canonicalizer.registerDefaultAlgorithms();
+        }
+        return new Canonicalizer(algorithmURI);
+    }
+
+    /**
+     * Method register
+     *
+     * @param algorithmURI
+     * @param implementingClass
+     * @throws CanonicalizationException
+     */
+    @SuppressWarnings("unchecked")
+    public static void register(String algorithmURI, String implementingClass)
+        throws CanonicalizationException, ClassNotFoundException {
+        // check whether URI is already registered
+        Class<? extends CanonicalizerSpi> registeredClass =
+            canonicalizerHash.get(algorithmURI);
+
+        if (registeredClass != null)  {
+            Object exArgs[] = { algorithmURI, registeredClass };
+            throw new CanonicalizationException("algorithm.alreadyRegistered", exArgs);
+        }
+
+        canonicalizerHash.put(
+            algorithmURI, (Class<? extends CanonicalizerSpi>)Class.forName(implementingClass)
+        );
+    }
+
+    /**
+     * Method register
+     *
+     * @param algorithmURI
+     * @param implementingClass
+     * @throws CanonicalizationException
+     */
+    public static void register(String algorithmURI, Class<? extends CanonicalizerSpi> implementingClass)
+        throws CanonicalizationException {
+        // check whether URI is already registered
+        Class<? extends CanonicalizerSpi> registeredClass = canonicalizerHash.get(algorithmURI);
+
+        if (registeredClass != null)  {
+            Object exArgs[] = { algorithmURI, registeredClass };
+            throw new CanonicalizationException("algorithm.alreadyRegistered", exArgs);
+        }
+
+        canonicalizerHash.put(algorithmURI, implementingClass);
+    }
+
+    /**
+     * This method registers the default algorithms.
+     */
+    private static void registerDefaultAlgorithms() {
+        canonicalizerHash.put(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS,
+                Canonicalizer20010315OmitComments.class);
+        canonicalizerHash.put(Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS,
+                Canonicalizer20010315WithComments.class);
+        canonicalizerHash.put(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS,
+                Canonicalizer20010315ExclOmitComments.class);
+        canonicalizerHash.put(Canonicalizer.ALGO_ID_C14N_EXCL_WITH_COMMENTS,
+                Canonicalizer20010315ExclWithComments.class);
+        canonicalizerHash.put(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS,
+                Canonicalizer11_OmitComments.class);
+        canonicalizerHash.put(Canonicalizer.ALGO_ID_C14N11_WITH_COMMENTS,
+                Canonicalizer11_WithComments.class);
+        canonicalizerHash.put(Canonicalizer.ALGO_ID_C14N_PHYSICAL,
+                CanonicalizerPhysical.class);
+    }
+
+    /**
+     * Method getURI
+     *
+     * @return the URI defined for this c14n instance.
+     */
+    public final String getURI() {
+        return canonicalizerSpi.engineGetURI();
+    }
+
+    /**
+     * Method getIncludeComments
+     *
+     * @return true if the c14n respect the comments.
+     */
+    public boolean getIncludeComments() {
+        return canonicalizerSpi.engineGetIncludeComments();
+    }
+
+    /**
+     * Canonicalizes the subtree rooted by <CODE>node</CODE>.
+     *
+     * @param node The node to canonicalize
+     * @return the result of the c14n.
+     *
+     * @throws CanonicalizationException
+     */
+    public byte[] canonicalizeSubtree(Node node, CanonicalFilter filter) throws CanonicalizationException {
+        return canonicalizerSpi.engineCanonicalizeSubTree(node, filter);
+    }
+
+    /**
+     * Canonicalizes the subtree rooted by <CODE>node</CODE>.
+     *
+     * @param node
+     * @param inclusiveNamespaces
+     * @return the result of the c14n.
+     * @throws CanonicalizationException
+     */
+    public byte[] canonicalizeSubtree(Node node, String inclusiveNamespaces, CanonicalFilter filter)
+        throws CanonicalizationException {
+        return canonicalizerSpi.engineCanonicalizeSubTree(node, inclusiveNamespaces, filter);
+    }
+
+    /**
+     * Sets the writer where the canonicalization ends.  ByteArrayOutputStream
+     * if none is set.
+     * @param os
+     */
+    public void setWriter(OutputStream os) {
+        canonicalizerSpi.setWriter(os);
+    }
+
+    /**
+     * Returns the name of the implementing {@link CanonicalizerSpi} class
+     *
+     * @return the name of the implementing {@link CanonicalizerSpi} class
+     */
+    public String getImplementingCanonicalizerClass() {
+        return canonicalizerSpi.getClass().getName();
+    }
+
+    /**
+     * Set the canonicalizer behaviour to not reset.
+     */
+    public void notReset() {
+        canonicalizerSpi.reset = false;
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/CanonicalizerPhysical.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/CanonicalizerPhysical.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/CanonicalizerPhysical.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/CanonicalizerPhysical.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,173 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.ProcessingInstruction;
+
+/**
+ * Serializes the physical representation of the subtree. All the attributes
+ * present in the subtree are emitted. The attributes are sorted within an element,
+ * with the namespace declarations appearing before the regular attributes.
+ * This algorithm is not a true canonicalization since equivalent subtrees
+ * may produce different output. It is therefore unsuitable for digital signatures.
+ * This same property makes it ideal for XML Encryption Syntax and Processing,
+ * because the decrypted XML content will share the same physical representation
+ * as the original XML content that was encrypted.
+ */
+public class CanonicalizerPhysical extends CanonicalizerBase {
+
+    private final SortedSet<Attr> result = new TreeSet<Attr>(COMPARE);
+
+    /**
+     * Constructor Canonicalizer20010315
+     */
+    public CanonicalizerPhysical() {
+        super(true);
+    }
+
+    /**
+     * Always throws a CanonicalizationException.
+     *
+     * @param xpathNodeSet
+     * @param inclusiveNamespaces
+     * @return none it always fails
+     * @throws CanonicalizationException always
+     */
+    public byte[] engineCanonicalizeXPathNodeSet(Set<Node> xpathNodeSet, String inclusiveNamespaces, CanonicalFilter filter)
+        throws CanonicalizationException {
+
+        /** $todo$ well, should we throw UnsupportedOperationException ? */
+        throw new CanonicalizationException("c14n.Canonicalizer.UnsupportedOperation");
+    }
+
+    /**
+     * Always throws a CanonicalizationException.
+     *
+     * @param rootNode
+     * @param inclusiveNamespaces
+     * @return none it always fails
+     * @throws CanonicalizationException
+     */
+    @Override
+    public byte[] engineCanonicalizeSubTree(Node rootNode, String inclusiveNamespaces, CanonicalFilter filter)
+        throws CanonicalizationException {
+
+        /** $todo$ well, should we throw UnsupportedOperationException ? */
+        throw new CanonicalizationException("c14n.Canonicalizer.UnsupportedOperation");
+    }
+
+    /**
+     * Returns the Attr[]s to be output for the given element.
+     * <br>
+     * The code of this method is a copy of {@link #handleAttributes(Element,
+     * NameSpaceSymbTable)},
+     * whereas it takes into account that subtree-c14n is -- well -- subtree-based.
+     * So if the element in question isRoot of c14n, it's parent is not in the
+     * node set, as well as all other ancestors.
+     *
+     * @param element
+     * @param ns
+     * @return the Attr[]s to be output
+     * @throws CanonicalizationException
+     */
+    @Override
+    protected Iterator<Attr> handleAttributesSubtree(Element element, NameSpaceSymbTable ns)
+        throws CanonicalizationException {
+        if (!element.hasAttributes()) {
+            return null;
+        }
+
+        // result will contain all the attrs declared directly on that element
+        final SortedSet<Attr> result = this.result;
+        result.clear();
+
+        if (element.hasAttributes()) {
+            NamedNodeMap attrs = element.getAttributes();
+            int attrsLength = attrs.getLength();
+
+            for (int i = 0; i < attrsLength; i++) {
+                Attr attribute = (Attr) attrs.item(i);
+                result.add(attribute);
+            }
+        }
+
+        return result.iterator();
+    }
+
+    /**
+     * Returns the Attr[]s to be output for the given element.
+     *
+     * @param element
+     * @param ns
+     * @return the Attr[]s to be output
+     * @throws CanonicalizationException
+     */
+    @Override
+    protected Iterator<Attr> handleAttributes(Element element, NameSpaceSymbTable ns)
+        throws CanonicalizationException {
+
+        /** $todo$ well, should we throw UnsupportedOperationException ? */
+        throw new CanonicalizationException("c14n.Canonicalizer.UnsupportedOperation");
+    }
+
+    @Override
+    protected void handleParent(Element e, NameSpaceSymbTable ns) {
+        // nothing to do
+    }
+
+    /** @inheritDoc */
+    @Override
+    public final String engineGetURI() {
+        return Canonicalizer.ALGO_ID_C14N_PHYSICAL;
+    }
+
+    /** @inheritDoc */
+    @Override
+    public final boolean engineGetIncludeComments() {
+        return true;
+    }
+
+    @Override
+    protected void outputPItoWriter(ProcessingInstruction currentPI,
+                                    OutputStream writer, int position) throws IOException {
+        // Processing Instructions before or after the document element are not treated specially
+        super.outputPItoWriter(currentPI, writer, NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT);
+    }
+
+    @Override
+    protected void outputCommentToWriter(Comment currentComment,
+                                         OutputStream writer, int position) throws IOException {
+        // Comments before or after the document element are not treated specially
+        super.outputCommentToWriter(currentComment, writer, NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT);
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/CanonicalizerSpi.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/CanonicalizerSpi.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/CanonicalizerSpi.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/CanonicalizerSpi.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.io.OutputStream;
+
+
+import org.w3c.dom.Node;
+
+/**
+ * Base class which all Canonicalization algorithms extend.
+ *
+ * @author Christian Geuer-Pollmann
+ */
+public abstract class CanonicalizerSpi {
+
+    /** Reset the writer after a c14n */
+    protected boolean reset = false;
+
+    /**
+     * Returns the URI of this engine.
+     * @return the URI
+     */
+    public abstract String engineGetURI();
+
+    /**
+     * Returns true if comments are included
+     * @return true if comments are included
+     */
+    public abstract boolean engineGetIncludeComments();
+
+    /**
+     * C14n a node tree.
+     *
+     * @param rootNode
+     * @return the c14n bytes
+     * @throws CanonicalizationException
+     */
+    public abstract byte[] engineCanonicalizeSubTree(Node rootNode, CanonicalFilter filter)
+        throws CanonicalizationException;
+
+    /**
+     * C14n a node tree.
+     *
+     * @param rootNode
+     * @param inclusiveNamespaces
+     * @return the c14n bytes
+     * @throws CanonicalizationException
+     */
+    public abstract byte[] engineCanonicalizeSubTree(Node rootNode, String inclusiveNamespaces, CanonicalFilter filter)
+        throws CanonicalizationException;
+
+    /**
+     * Sets the writer where the canonicalization ends. ByteArrayOutputStream if
+     * none is set.
+     * @param os
+     */
+    public abstract void setWriter(OutputStream os);
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/Constants.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Constants.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/Constants.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/Constants.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+/**
+ * Provides all constants and some translation functions for i18n.
+ *
+ * For the used Algorithm identifiers and Namespaces, look at the
+ * <A HREF="http://www.w3.org/TR/xmldsig-core/#sec-TransformAlg">XML
+ * Signature specification</A>.
+ *
+ * @author $Author: coheigea $
+ */
+public class Constants {
+    
+    /** The URI for XML spec*/
+    public static final String XML_LANG_SPACE_SpecNS = "http://www.w3.org/XML/1998/namespace";
+    
+    /** The URI for XMLNS spec*/
+    public static final String NamespaceSpecNS = "http://www.w3.org/2000/xmlns/";
+
+    private Constants() {
+        // we don't allow instantiation
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/ElementProxy.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/ElementProxy.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/ElementProxy.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/ElementProxy.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,293 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
+/**
+ * This is the base class to all Objects which have a direct 1:1 mapping to an
+ * Element in a particular namespace.
+ */
+public abstract class ElementProxy {
+
+    /** Field constructionElement */
+    protected Element constructionElement = null;
+
+    /** Field baseURI */
+    protected String baseURI = null;
+
+    /** Field doc */
+    protected Document doc = null;
+    
+    /** Field prefixMappings */
+    private static Map<String, String> prefixMappings = new ConcurrentHashMap<String, String>();
+
+    /**
+     * Constructor ElementProxy
+     *
+     */
+    public ElementProxy() {	   
+    }
+
+    /**
+     * Constructor ElementProxy
+     *
+     * @param doc
+     */
+    public ElementProxy(Document doc) {
+        if (doc == null) {
+            throw new RuntimeException("Document is null");
+        }
+
+        this.doc = doc;
+        this.constructionElement = 
+            createElementForFamilyLocal(this.doc, this.getBaseNamespace(), this.getBaseLocalName());      
+    }
+    
+    /**
+     * Constructor ElementProxy
+     *
+     * @param element
+     * @param BaseURI
+     * @throws XMLSecurityException
+     */
+    public ElementProxy(Element element, String BaseURI) throws CanonicalizationException {
+        if (element == null) {
+            throw new CanonicalizationException("ElementProxy.nullElement");
+        }
+
+        //if (System.getProperty("nokogiri.c14.debug") == "on") {
+        //    System.out.println("setElement(\"" + element.getTagName() + "\", \"" + BaseURI + "\")");
+        //}
+
+        this.doc = element.getOwnerDocument();
+        this.constructionElement = element;
+        this.baseURI = BaseURI;
+
+        this.guaranteeThatElementInCorrectSpace();
+    }
+    
+    /**
+     * Returns the namespace of the Elements of the sub-class.
+     *
+     * @return the namespace of the Elements of the sub-class.
+     */
+    public abstract String getBaseNamespace();
+
+    /**
+     * Returns the localname of the Elements of the sub-class.
+     *
+     * @return the localname of the Elements of the sub-class.
+     */
+    public abstract String getBaseLocalName();
+    
+    
+    protected Element createElementForFamilyLocal(
+        Document doc, String namespace, String localName
+    ) {	   	  
+        Element result;
+        if (namespace == null) {
+            result = doc.createElementNS(null, localName);
+        } else {
+            String baseName = this.getBaseNamespace();
+            String prefix = ElementProxy.getDefaultPrefix(baseName);
+            if ((prefix == null) || (prefix.length() == 0)) {
+                result = doc.createElementNS(namespace, localName);
+                result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", namespace);
+            } else {
+                result = doc.createElementNS(namespace, prefix + ":" + localName);
+                result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:" + prefix, namespace);
+            }
+        }	      
+        return result;
+    }
+
+
+    /**
+     * This method creates an Element in a given namespace with a given localname.
+     * It uses the {@link ElementProxy#getDefaultPrefix} method to decide whether
+     * a particular prefix is bound to that namespace.
+     * <BR />
+     * This method was refactored out of the constructor.
+     *
+     * @param doc
+     * @param namespace
+     * @param localName
+     * @return The element created.
+     */
+    public static Element createElementForFamily(Document doc, String namespace, String localName) {
+        Element result;
+        String prefix = ElementProxy.getDefaultPrefix(namespace);
+
+        if (namespace == null) {
+            result = doc.createElementNS(null, localName);
+        } else {
+            if ((prefix == null) || (prefix.length() == 0)) {
+                result = doc.createElementNS(namespace, localName);
+                result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", namespace);
+            } else {
+                result = doc.createElementNS(namespace, prefix + ":" + localName);
+                result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:" + prefix, namespace);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns the Element which was constructed by the Object.
+     *
+     * @return the Element which was constructed by the Object.
+     */
+    public final Element getElement() {
+        return this.constructionElement;
+    }
+
+    /**
+     * Returns the Element plus a leading and a trailing CarriageReturn Text node.
+     *
+     * @return the Element which was constructed by the Object.
+     */
+    public final NodeList getElementPlusReturns() {
+
+        HelperNodeList nl = new HelperNodeList();
+
+        nl.appendChild(this.doc.createTextNode("\n"));
+        nl.appendChild(this.getElement());
+        nl.appendChild(this.doc.createTextNode("\n"));
+
+        return nl;
+    }
+
+    /**
+     * Method getDocument
+     *
+     * @return the Document where this element is contained.
+     */
+    public Document getDocument() {
+        return this.doc;
+    }
+
+    /**
+     * Method getBaseURI
+     *
+     * @return the base uri of the namespace of this element
+     */
+    public String getBaseURI() {
+        return this.baseURI;
+    }
+
+    /**
+     * Method guaranteeThatElementInCorrectSpace
+     *
+     * @throws XMLSecurityException
+     */
+    void guaranteeThatElementInCorrectSpace() throws CanonicalizationException {
+
+        String expectedLocalName = this.getBaseLocalName();
+        String expectedNamespaceUri = this.getBaseNamespace();
+
+        String actualLocalName = this.constructionElement.getLocalName();
+        String actualNamespaceUri = this.constructionElement.getNamespaceURI();
+
+        if(!expectedNamespaceUri.equals(actualNamespaceUri) 
+            && !expectedLocalName.equals(actualLocalName)) {      
+            Object exArgs[] = { actualNamespaceUri + ":" + actualLocalName, 
+                                expectedNamespaceUri + ":" + expectedLocalName};
+            throw new CanonicalizationException("xml.WrongElement", exArgs);
+        }
+    }
+
+    /**
+     * Method addText
+     *
+     * @param text
+     */
+    public void addText(String text) {
+        if (text != null) {
+            Text t = this.doc.createTextNode(text);
+
+            this.constructionElement.appendChild(t);
+        }
+    }
+
+    /**
+     * Method getTextFromChildElement
+     *
+     * @param localname
+     * @param namespace
+     * @return the Text of the textNode
+     */
+    public String getTextFromChildElement(String localname, String namespace) {
+        return XMLUtils.selectNode(
+                this.constructionElement.getFirstChild(),
+                namespace,
+                localname,
+                0).getTextContent();
+    }
+
+    /**
+     * Method getTextFromTextChild
+     *
+     * @return the Text obtained by concatenating all the text nodes of this 
+     *    element
+     */
+    public String getTextFromTextChild() {
+        return XMLUtils.getFullTextChildrenFromElement(this.constructionElement);
+    }
+
+    /**
+     * Method length
+     *
+     * @param namespace
+     * @param localname
+     * @return the number of elements {namespace}:localname under this element
+     */
+    public int length(String namespace, String localname) {
+        int number = 0;
+        Node sibling = this.constructionElement.getFirstChild();
+        while (sibling != null) {        
+            if (localname.equals(sibling.getLocalName())
+                && namespace.equals(sibling.getNamespaceURI())) {
+                number++;
+            }
+            sibling = sibling.getNextSibling();
+        }
+        return number;
+    }
+
+    /**
+     * Method getDefaultPrefix
+     *
+     * @param namespace
+     * @return the default prefix bind to this element.
+     */
+    public static String getDefaultPrefix(String namespace) {
+        return prefixMappings.get(namespace);
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/HelperNodeList.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/HelperNodeList.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/HelperNodeList.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/HelperNodeList.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,93 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * @author Christian Geuer-Pollmann
+ */
+public class HelperNodeList implements NodeList {
+
+    /** Field nodes */
+    List<Node> nodes = new ArrayList<Node>();
+    boolean allNodesMustHaveSameParent = false;
+
+    /**
+     * 
+     */
+    public HelperNodeList() {
+        this(false);
+    }
+
+
+    /**
+     * @param allNodesMustHaveSameParent
+     */
+    public HelperNodeList(boolean allNodesMustHaveSameParent) {
+        this.allNodesMustHaveSameParent = allNodesMustHaveSameParent;
+    }
+
+    /**
+     * Method item
+     *
+     * @param index
+     * @return node with index i
+     */
+    public Node item(int index) {
+        return nodes.get(index);
+    }
+
+    /**
+     * Method getLength
+     *
+     *  @return length of the list
+     */
+    public int getLength() {
+        return nodes.size();
+    }
+
+    /**
+     * Method appendChild
+     *
+     * @param node
+     * @throws IllegalArgumentException
+     */
+    public void appendChild(Node node) throws IllegalArgumentException {
+        if (this.allNodesMustHaveSameParent && this.getLength() > 0
+            && this.item(0).getParentNode() != node.getParentNode()) {
+            throw new IllegalArgumentException("Nodes have not the same Parent");
+        }
+        nodes.add(node);
+    }
+
+    /**
+     * @return the document that contains this nodelist
+     */
+    public Document getOwnerDocument() {
+        if (this.getLength() == 0) {
+            return null;
+        }
+        return XMLUtils.getOwnerDocument(this.item(0));
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/IgnoreAllErrorHandler.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/IgnoreAllErrorHandler.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/IgnoreAllErrorHandler.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/IgnoreAllErrorHandler.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,79 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * This {@link org.xml.sax.ErrorHandler} does absolutely nothing but log
+ * the events.
+ *
+ * @author Christian Geuer-Pollmann
+ */
+public class IgnoreAllErrorHandler implements ErrorHandler {
+
+    /** Field throwExceptions */
+    private static final boolean warnOnExceptions =
+        System.getProperty("org.apache.xml.security.test.warn.on.exceptions", "false").equals("true");
+
+    /** Field throwExceptions           */
+    private static final boolean throwExceptions = 
+        System.getProperty("org.apache.xml.security.test.throw.exceptions", "false").equals("true");
+
+
+    /** @inheritDoc */
+    public void warning(SAXParseException ex) throws SAXException {
+        if (IgnoreAllErrorHandler.warnOnExceptions) {
+            // TODO
+            // get handler from upper layer
+            //log.warn("", ex);
+        }
+        if (IgnoreAllErrorHandler.throwExceptions) {
+            throw ex;
+        }
+    }
+
+
+    /** @inheritDoc */
+    public void error(SAXParseException ex) throws SAXException {
+        if (IgnoreAllErrorHandler.warnOnExceptions) {
+            // TODO
+            // get handler from upper layer
+            //log.error("", ex);
+        }
+        if (IgnoreAllErrorHandler.throwExceptions) {
+            throw ex;
+        }
+    }
+
+
+    /** @inheritDoc */
+    public void fatalError(SAXParseException ex) throws SAXException {
+        if (IgnoreAllErrorHandler.warnOnExceptions) {
+            // TODO
+            // get handler from upper layer
+            //log.warn("", ex);
+        }
+        if (IgnoreAllErrorHandler.throwExceptions) {
+            throw ex;
+        }
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/InclusiveNamespaces.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/InclusiveNamespaces.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/InclusiveNamespaces.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/InclusiveNamespaces.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,166 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+//import nokogiri.internals.org.apache.xml.security.exceptions.XMLSecurityException;
+//import nokogiri.internals.org.apache.xml.security.transforms.TransformParam;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * This Object serves as Content for the ds:Transforms for exclusive
+ * Canonicalization.
+ * <BR />
+ * It implements the {@link Element} interface
+ * and can be used directly in a DOM tree.
+ *
+ * @author Christian Geuer-Pollmann
+ */
+//public class InclusiveNamespaces extends ElementProxy implements TransformParam {
+public class InclusiveNamespaces extends ElementProxy {
+
+    /** Field _TAG_EC_INCLUSIVENAMESPACES */
+    public static final String _TAG_EC_INCLUSIVENAMESPACES =
+        "InclusiveNamespaces";
+
+    /** Field _ATT_EC_PREFIXLIST */
+    public static final String _ATT_EC_PREFIXLIST = "PrefixList";
+
+    /** Field ExclusiveCanonicalizationNamespace */
+    public static final String ExclusiveCanonicalizationNamespace =
+        "http://www.w3.org/2001/10/xml-exc-c14n#";
+
+    /**
+     * Constructor XPathContainer
+     *
+     * @param doc
+     * @param prefixList
+     */
+    public InclusiveNamespaces(Document doc, String prefixList) {
+        this(doc, InclusiveNamespaces.prefixStr2Set(prefixList));
+    }
+
+    /**
+     * Constructor InclusiveNamespaces
+     *
+     * @param doc
+     * @param prefixes
+     */
+    public InclusiveNamespaces(Document doc, Set<String> prefixes) {
+        super(doc);
+
+        SortedSet<String> prefixList;
+        if (prefixes instanceof SortedSet<?>) {
+            prefixList = (SortedSet<String>)prefixes;
+        } else {
+            prefixList = new TreeSet<String>(prefixes);
+        }
+
+        StringBuilder sb = new StringBuilder(prefixList.size() * 8);
+        for (String prefix : prefixList) {
+            if (prefix.equals("xmlns")) {
+                sb.append("#default ");
+            } else {
+                sb.append(prefix).append(' ');
+            }
+        }
+        int last = sb.length() - 1;
+        while (last >= 0 && sb.charAt(last) == ' ') sb.setLength(last--); // trim
+        
+        this.constructionElement.setAttributeNS(null, InclusiveNamespaces._ATT_EC_PREFIXLIST, sb.toString());
+    }
+
+    /**
+     * Constructor InclusiveNamespaces
+     *
+     * @param element
+     * @param BaseURI
+     * @throws XMLSecurityException
+     */
+    public InclusiveNamespaces(Element element, String BaseURI)
+        throws CanonicalizationException {
+        super(element, BaseURI);
+    }
+
+    /**
+     * Method getInclusiveNamespaces
+     *
+     * @return The Inclusive Namespace string
+     */
+    public String getInclusiveNamespaces() {
+        return this.constructionElement.getAttributeNS(null, InclusiveNamespaces._ATT_EC_PREFIXLIST);
+    }
+    
+    /**
+     * Decodes the <code>inclusiveNamespaces</code> String and returns all
+     * selected namespace prefixes as a Set. The <code>#default</code>
+     * namespace token is represented as an empty namespace prefix
+     * (<code>"xmlns"</code>).
+     * <BR/>
+     * The String <code>inclusiveNamespaces=" xenc    ds #default"</code>
+     * is returned as a Set containing the following Strings:
+     * <UL>
+     * <LI><code>xmlns</code></LI>
+     * <LI><code>xenc</code></LI>
+     * <LI><code>ds</code></LI>
+     * </UL>
+     *
+     * @param inclusiveNamespaces
+     * @return A set to string
+     */
+    public static SortedSet<String> prefixStr2Set(String inclusiveNamespaces) {
+        SortedSet<String> prefixes = new TreeSet<String>();
+
+        if ((inclusiveNamespaces == null) || (inclusiveNamespaces.length() == 0)) {
+            return prefixes;
+        }
+
+        String[] tokens = inclusiveNamespaces.split("\\s");
+        for (String prefix : tokens) {
+            if (prefix.equals("#default")) {
+                prefixes.add("xmlns");
+            } else {
+                prefixes.add(prefix);
+            }
+        }
+
+        return prefixes;
+    }
+
+    /**
+     * Method getBaseNamespace
+     *
+     * @inheritDoc
+     */
+    public String getBaseNamespace() {
+        return InclusiveNamespaces.ExclusiveCanonicalizationNamespace;
+    }
+
+    /**
+     * Method getBaseLocalName
+     *
+     * @inheritDoc
+     */
+    public String getBaseLocalName() {
+        return InclusiveNamespaces._TAG_EC_INCLUSIVENAMESPACES;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/InvalidCanonicalizerException.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/InvalidCanonicalizerException.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/InvalidCanonicalizerException.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/InvalidCanonicalizerException.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+
+public class InvalidCanonicalizerException extends Exception {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Constructor InvalidCanonicalizerException
+     *
+     */
+    public InvalidCanonicalizerException() {
+        super();
+    }
+
+    /**
+     * Constructor InvalidCanonicalizerException
+     *
+     * @param message
+     */
+    public InvalidCanonicalizerException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor InvalidCanonicalizerException
+     *
+     * @param message
+     * @param exArgs
+     */
+    public InvalidCanonicalizerException(String message, Object... exArgs) {
+        super(C14nHelper.getErrorMessage(message, exArgs));
+    }
+
+    /**
+     * Constructor InvalidCanonicalizerException
+     *
+     * @param msgID
+     * @param originalException
+     */
+    public InvalidCanonicalizerException(String message, Exception rootCause) {
+        super(message, rootCause);
+    }
+
+    /**
+     * Constructor InvalidCanonicalizerException
+     *
+     * @param msgID
+     * @param exArgs
+     * @param originalException
+     */
+    public InvalidCanonicalizerException(String message, Exception rootCause, Object... exArgs) {
+        super(C14nHelper.getErrorMessage(message, exArgs), rootCause);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/NameSpaceSymbTable.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/NameSpaceSymbTable.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/NameSpaceSymbTable.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/NameSpaceSymbTable.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,402 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Node;
+
+/**
+ * A stack based Symbol Table.
+ *<br>For speed reasons all the symbols are introduced in the same map,
+ * and at the same time in a list so it can be removed when the frame is pop back.
+ * @author Raul Benito
+ */
+public class NameSpaceSymbTable {
+
+    private static final String XMLNS = "xmlns";
+    private static final SymbMap initialMap = new SymbMap();
+    
+    static {
+        NameSpaceSymbEntry ne = new NameSpaceSymbEntry("", null, true, XMLNS);
+        ne.lastrendered = "";		
+        initialMap.put(XMLNS, ne);
+    }
+    
+    /**The map betwen prefix-> entry table. */
+    private SymbMap symb;
+    
+    /**The stacks for removing the definitions when doing pop.*/
+    private List<SymbMap> level;
+    private boolean cloned = true;
+    
+    /**
+     * Default constractor
+     **/		
+    public NameSpaceSymbTable() {    	
+        level = new ArrayList<SymbMap>();
+        //Insert the default binding for xmlns.    	
+        symb = (SymbMap) initialMap.clone();
+    }
+
+    /**
+     * Get all the unrendered nodes in the name space.
+     * For Inclusive rendering
+     * @param result the list where to fill the unrendered xmlns definitions.
+     **/       
+    public void getUnrenderedNodes(Collection<Attr> result) {		
+        Iterator<NameSpaceSymbEntry> it = symb.entrySet().iterator();
+        while (it.hasNext()) {	   	   
+            NameSpaceSymbEntry n = it.next();
+            //put them rendered?
+            if ((!n.rendered) && (n.n != null)) {
+                n = (NameSpaceSymbEntry) n.clone();
+                needsClone();
+                symb.put(n.prefix, n);         
+                n.lastrendered = n.uri;
+                n.rendered = true;
+
+                result.add(n.n);
+            }
+        }	   
+    }
+
+    /**
+     * Push a frame for visible namespace. 
+     * For Inclusive rendering.
+     **/
+    public void outputNodePush() {
+        push();
+    }
+
+    /**
+     * Pop a frame for visible namespace.
+     **/
+    public void outputNodePop() {
+        pop();
+    }
+
+    /**
+     * Push a frame for a node.
+     * Inclusive or Exclusive.
+     **/
+    public void push() {		
+        //Put the number of namespace definitions in the stack.
+        level.add(null);
+        cloned = false;
+    }
+
+    /**
+     * Pop a frame.
+     * Inclusive or Exclusive.
+     **/
+    public void pop() {
+        int size = level.size() - 1;
+        Object ob = level.remove(size);
+        if (ob != null) {
+            symb = (SymbMap)ob;
+            if (size == 0) {
+                cloned = false;   
+            } else {
+                cloned = (level.get(size - 1) != symb);
+            }
+        } else {
+            cloned = false;
+        }
+    }
+
+    final void needsClone() {
+        if (!cloned) {			
+            level.set(level.size() - 1, symb);
+            symb = (SymbMap) symb.clone();
+            cloned = true;
+        }
+    }
+
+
+    /**
+     * Gets the attribute node that defines the binding for the prefix.      
+     * @param prefix the prefix to obtain the attribute.
+     * @return null if there is no need to render the prefix. Otherwise the node of
+     * definition.
+     **/
+    public Attr getMapping(String prefix) {					
+        NameSpaceSymbEntry entry = symb.get(prefix);
+        if (entry == null) {
+            //There is no definition for the prefix(a bug?).
+            return null;
+        }
+        if (entry.rendered) {		
+            //No need to render an entry already rendered.
+            return null;		
+        }
+        // Mark this entry as render.
+        entry = (NameSpaceSymbEntry) entry.clone();
+        needsClone();
+        symb.put(prefix, entry);
+        entry.rendered = true;
+        entry.lastrendered = entry.uri;				
+        // Return the node for outputing.
+        return entry.n;
+    }
+
+    /**
+     * Gets a definition without mark it as render. 
+     * For render in exclusive c14n the namespaces in the include prefixes.
+     * @param prefix The prefix whose definition is neaded.
+     * @return the attr to render, null if there is no need to render
+     **/
+    public Attr getMappingWithoutRendered(String prefix) {					
+        NameSpaceSymbEntry entry = symb.get(prefix);
+        if (entry == null) {		   
+            return null;
+        }
+        if (entry.rendered) {		
+            return null;		
+        }
+        return entry.n;
+    }
+
+    /**
+     * Adds the mapping for a prefix.
+     * @param prefix the prefix of definition
+     * @param uri the Uri of the definition
+     * @param n the attribute that have the definition
+     * @return true if there is already defined.
+     **/
+    public boolean addMapping(String prefix, String uri, Attr n) {						
+        NameSpaceSymbEntry ob = symb.get(prefix);		
+        if ((ob != null) && uri.equals(ob.uri)) {
+            //If we have it previously defined. Don't keep working.
+            return false;
+        }			
+        //Creates and entry in the table for this new definition.
+        NameSpaceSymbEntry ne = new NameSpaceSymbEntry(uri, n, false, prefix);		
+        needsClone();
+        symb.put(prefix, ne);
+        if (ob != null) {
+            //We have a previous definition store it for the pop.			
+            //Check if a previous definition(not the inmidiatly one) has been rendered.			
+            ne.lastrendered = ob.lastrendered;			
+            if ((ob.lastrendered != null) && (ob.lastrendered.equals(uri))) {
+                //Yes it is. Mark as rendered.
+                ne.rendered = true;
+            }			
+        } 			
+        return true;
+    }
+
+    /**
+     * Adds a definition and mark it as render.
+     * For inclusive c14n.
+     * @param prefix the prefix of definition
+     * @param uri the Uri of the definition
+     * @param n the attribute that have the definition
+     * @return the attr to render, null if there is no need to render
+     **/
+    public Node addMappingAndRender(String prefix, String uri, Attr n) {                     
+        NameSpaceSymbEntry ob = symb.get(prefix);
+
+        if ((ob != null) && uri.equals(ob.uri)) {
+            if (!ob.rendered) {                 
+                ob = (NameSpaceSymbEntry) ob.clone();
+                needsClone();
+                symb.put(prefix, ob);         
+                ob.lastrendered = uri;
+                ob.rendered = true;
+                return ob.n;
+            }           
+            return null;
+        }   
+
+        NameSpaceSymbEntry ne = new NameSpaceSymbEntry(uri,n,true,prefix);
+        ne.lastrendered = uri;
+        needsClone();
+        symb.put(prefix, ne);
+        if ((ob != null) && (ob.lastrendered != null) && (ob.lastrendered.equals(uri))) {
+            ne.rendered = true;
+            return null;
+        }
+        return ne.n;
+    }
+
+    public int getLevel() {
+        return level.size();
+    }
+
+    public void removeMapping(String prefix) {
+        NameSpaceSymbEntry ob = symb.get(prefix);
+
+        if (ob != null) {
+            needsClone();
+            symb.put(prefix, null);         
+        }
+    }
+
+    public void removeMappingIfNotRender(String prefix) {
+        NameSpaceSymbEntry ob = symb.get(prefix);
+
+        if (ob != null && !ob.rendered) {
+            needsClone();
+            symb.put(prefix, null);         
+        }
+    }
+
+    public boolean removeMappingIfRender(String prefix) {
+        NameSpaceSymbEntry ob = symb.get(prefix);
+
+        if (ob != null && ob.rendered) {
+            needsClone();
+            symb.put(prefix, null);         
+        }
+        return false;
+    }
+}
+
+/**
+ * The internal structure of NameSpaceSymbTable.
+ **/
+class NameSpaceSymbEntry implements Cloneable {
+    
+    String prefix;
+    
+    /**The URI that the prefix defines */
+    String uri;
+    
+    /**The last output in the URI for this prefix (This for speed reason).*/
+    String lastrendered = null;
+    
+    /**This prefix-URI has been already render or not.*/
+    boolean rendered = false;
+    
+    /**The attribute to include.*/
+    Attr n;     
+    
+    NameSpaceSymbEntry(String name, Attr n, boolean rendered, String prefix) {
+        this.uri = name;          
+        this.rendered = rendered;
+        this.n = n;            
+        this.prefix = prefix;
+    }
+    
+    /** @inheritDoc */
+    public Object clone() {         
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException e) {
+            return null;
+        }
+    }
+}
+
+class SymbMap implements Cloneable {
+    int free = 23;
+    NameSpaceSymbEntry[] entries;
+    String[] keys;
+    
+    SymbMap() {
+        entries = new NameSpaceSymbEntry[free];
+        keys = new String[free];
+    }
+    
+    void put(String key, NameSpaceSymbEntry value) {		
+        int index = index(key);
+        Object oldKey = keys[index];
+        keys[index] = key;
+        entries[index] = value;
+        if ((oldKey == null || !oldKey.equals(key)) && (--free == 0)) {	        	        
+            free = entries.length;
+            int newCapacity = free << 2;				
+            rehash(newCapacity);			
+        }
+    }
+
+    List<NameSpaceSymbEntry> entrySet() {
+        List<NameSpaceSymbEntry> a = new ArrayList<NameSpaceSymbEntry>();
+        for (int i = 0;i < entries.length;i++) {
+            if ((entries[i] != null) && !("".equals(entries[i].uri))) {
+                a.add(entries[i]);
+            }
+        }
+        return a;		
+    }
+
+    protected int index(Object obj) {		
+        Object[] set = keys;
+        int length = set.length;
+        //abs of index
+        int index = (obj.hashCode() & 0x7fffffff) % length;
+        Object cur = set[index];
+
+        if (cur == null || (cur.equals(obj))) {
+            return index;
+        }
+        length--;
+        do {
+            index = index == length ? 0 : ++index;
+            cur = set[index];
+        } while (cur != null && (!cur.equals(obj)));       
+        return index;
+    }
+
+    /**
+     * rehashes the map to the new capacity.
+     *
+     * @param newCapacity an <code>int</code> value
+     */
+    protected void rehash(int newCapacity) {
+        int oldCapacity = keys.length;
+        String oldKeys[] = keys;
+        NameSpaceSymbEntry oldVals[] = entries;
+
+        keys = new String[newCapacity];        
+        entries = new NameSpaceSymbEntry[newCapacity];
+
+        for (int i = oldCapacity; i-- > 0;) {
+            if (oldKeys[i] != null) {
+                String o = oldKeys[i];
+                int index = index(o);
+                keys[index] = o;
+                entries[index] = oldVals[i];
+            }
+        }
+    }
+
+    NameSpaceSymbEntry get(String key) {
+        return entries[index(key)];
+    }
+
+    protected Object clone()  {
+        try {
+            SymbMap copy = (SymbMap) super.clone();
+            copy.entries = new NameSpaceSymbEntry[entries.length];
+            System.arraycopy(entries, 0, copy.entries, 0, entries.length);
+            copy.keys = new String[keys.length];
+            System.arraycopy(keys, 0, copy.keys, 0, keys.length);
+
+            return copy;
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/NodeFilter.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/NodeFilter.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/NodeFilter.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/NodeFilter.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import org.w3c.dom.Node;
+
+/**
+ * An interface to tell to the c14n if a node is included or not in the output
+ */
+public interface NodeFilter {
+    
+    /**
+     * Tells if a node must be output in c14n.
+     * @param n
+     * @return 1 if the node should be output.
+     * 		   0 if node must not be output, 
+     * 		  -1 if the node and all it's child must not be output.
+     * 			
+     */
+    int isNodeInclude(Node n);
+    
+    /**
+     * Tells if a node must be output in a c14n.
+     * The caller must assured that this method is always call
+     * in document order. The implementations can use this 
+     * restriction to optimize the transformation.
+     * @param n
+     * @param level the relative level in the tree
+     * @return 1 if the node should be output.
+     * 		   0 if node must not be output, 
+     * 		  -1 if the node and all it's child must not be output.
+     */
+    int isNodeIncludeDO(Node n, int level);
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/UtfHelpper.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/UtfHelpper.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/UtfHelpper.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/UtfHelpper.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,179 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+
+public final class UtfHelpper {
+    
+    private UtfHelpper() {
+        // complete
+    }
+
+    public static void writeByte(
+        final String str,
+        final OutputStream out,
+        Map<String, byte[]> cache
+    ) throws IOException {
+        byte[] result = cache.get(str);	 
+        if (result == null) {
+            result = getStringInUtf8(str);
+            cache.put(str, result);
+        }
+
+        out.write(result);
+    }
+
+    public static void writeCharToUtf8(final char c, final OutputStream out) throws IOException {   	
+        if (c < 0x80) {
+            out.write(c);
+            return;
+        }
+        if ((c >= 0xD800 && c <= 0xDBFF) || (c >= 0xDC00 && c <= 0xDFFF)) {
+            //No Surrogates in sun java
+            out.write(0x3f);
+            return;
+        }
+        int bias;
+        int write;
+        char ch;
+        if (c > 0x07FF) {
+            ch = (char)(c>>>12);      
+            write = 0xE0;
+            if (ch > 0) {
+                write |= (ch & 0x0F);
+            } 
+            out.write(write);
+            write = 0x80;
+            bias = 0x3F;        
+        } else {
+            write = 0xC0;
+            bias = 0x1F;
+        }
+        ch = (char)(c>>>6);
+        if (ch > 0) {
+            write |= (ch & bias);
+        } 
+        out.write(write);
+        out.write(0x80 | ((c) & 0x3F));    
+
+    }
+
+    public static void writeStringToUtf8(
+        final String str,
+        final OutputStream out
+    ) throws IOException{	   
+        final int length = str.length();
+        int i = 0;
+        char c;    
+        while (i < length) {
+            c = str.charAt(i++);        
+            if (c < 0x80)  {
+                out.write(c);
+                continue;
+            }
+            if ((c >= 0xD800 && c <= 0xDBFF) || (c >= 0xDC00 && c <= 0xDFFF)) {
+                //No Surrogates in sun java
+                out.write(0x3f);
+                continue;
+            }
+            char ch;
+            int bias;
+            int write;
+            if (c > 0x07FF) {
+                ch = (char)(c>>>12);      
+                write = 0xE0;
+                if (ch > 0) {
+                    write |= (ch & 0x0F);
+                } 
+                out.write(write);
+                write = 0x80;
+                bias = 0x3F;        
+            } else {
+                write = 0xC0;
+                bias = 0x1F;
+            }
+            ch = (char)(c>>>6);
+            if (ch > 0) {
+                write |= (ch & bias);
+            } 
+            out.write(write);
+            out.write(0x80 | ((c) & 0x3F));       
+
+        }
+
+    }
+    
+    public static byte[] getStringInUtf8(final String str) {
+        final int length = str.length();
+        boolean expanded = false;
+        byte[] result = new byte[length];
+        int i = 0;
+        int out = 0;
+        char c;    
+        while (i < length) {
+            c = str.charAt(i++);        
+            if (c < 0x80) {
+                result[out++] = (byte)c;
+                continue;
+            }
+            if ((c >= 0xD800 && c <= 0xDBFF) || (c >= 0xDC00 && c <= 0xDFFF)) {  	
+                //No Surrogates in sun java
+                result[out++] = 0x3f;
+                continue;
+            }
+            if (!expanded) {
+                byte newResult[] = new byte[3*length];
+                System.arraycopy(result, 0, newResult, 0, out);				   	    	
+                result = newResult;
+                expanded = true;
+            } 
+            char ch;
+            int bias;
+            byte write;
+            if (c > 0x07FF) {
+                ch = (char)(c>>>12);      
+                write = (byte)0xE0;
+                if (ch > 0) {
+                    write |= (ch & 0x0F);
+                } 
+                result[out++] = write;
+                write = (byte)0x80;
+                bias = 0x3F;        
+            } else {
+                write = (byte)0xC0;
+                bias = 0x1F;
+            }
+            ch = (char)(c>>>6);
+            if (ch > 0) {
+                write |= (ch & bias);
+            } 
+            result[out++] = write;
+            result[out++] = (byte)(0x80 | ((c) & 0x3F)); 
+        }
+        if (expanded) {
+            byte newResult[] = new byte[out];
+            System.arraycopy(result, 0, newResult, 0, out);
+            result = newResult;
+        }
+        return result;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/c14n/XMLUtils.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/XMLUtils.java
--- 1.8.4-1/ext/java/nokogiri/internals/c14n/XMLUtils.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/c14n/XMLUtils.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,507 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package nokogiri.internals.c14n;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+
+/**
+ * DOM and XML accessibility and comfort functions.
+ *
+ * @author Christian Geuer-Pollmann
+ */
+public class XMLUtils {
+
+    /**
+     * Constructor XMLUtils
+     *
+     */
+    private XMLUtils() {
+        // we don't allow instantiation
+    }
+
+    /**
+     * Method getFullTextChildrenFromElement
+     *
+     * @param element
+     * @return the string of children
+     */
+    public static String getFullTextChildrenFromElement(Element element) {
+        StringBuilder sb = new StringBuilder();
+
+        Node child = element.getFirstChild();
+        while (child != null) {
+            if (child.getNodeType() == Node.TEXT_NODE) {
+                sb.append(((Text)child).getData());
+            }
+            child = child.getNextSibling();
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * This method returns the owner document of a particular node.
+     * This method is necessary because it <I>always</I> returns a
+     * {@link Document}. {@link Node#getOwnerDocument} returns <CODE>null</CODE>
+     * if the {@link Node} is a {@link Document}.
+     *
+     * @param node
+     * @return the owner document of the node
+     */
+    public static Document getOwnerDocument(Node node) {
+        if (node.getNodeType() == Node.DOCUMENT_NODE) {
+            return (Document) node;
+        }
+        try {
+            return node.getOwnerDocument();
+        } catch (NullPointerException npe) {
+            throw new NullPointerException(npe.getMessage());
+        }
+    }
+
+    /**
+     * This method returns the first non-null owner document of the Nodes in this Set.
+     * This method is necessary because it <I>always</I> returns a
+     * {@link Document}. {@link Node#getOwnerDocument} returns <CODE>null</CODE>
+     * if the {@link Node} is a {@link Document}.
+     *
+     * @param xpathNodeSet
+     * @return the owner document
+     */
+    public static Document getOwnerDocument(Set<Node> xpathNodeSet) {
+        NullPointerException npe = null;
+        for (Node node : xpathNodeSet) {
+            int nodeType = node.getNodeType();
+            if (nodeType == Node.DOCUMENT_NODE) {
+                return (Document) node;
+            }
+            try {
+                if (nodeType == Node.ATTRIBUTE_NODE) {
+                    return ((Attr)node).getOwnerElement().getOwnerDocument();
+                }
+                return node.getOwnerDocument();
+            } catch (NullPointerException e) {
+                npe = e;
+            }
+        }
+
+        throw new NullPointerException(npe.getMessage());
+    }
+
+    /**
+     * Method convertNodelistToSet
+     *
+     * @param xpathNodeSet
+     * @return the set with the nodelist
+     */
+    public static Set<Node> convertNodelistToSet(NodeList xpathNodeSet) {
+        if (xpathNodeSet == null) {
+            return new HashSet<Node>();
+        }
+
+        int length = xpathNodeSet.getLength();
+        Set<Node> set = new HashSet<Node>(length);
+
+        for (int i = 0; i < length; i++) {
+            set.add(xpathNodeSet.item(i));
+        }
+
+        return set;
+    }
+
+    /**
+     * This method spreads all namespace attributes in a DOM document to their
+     * children. This is needed because the XML Signature XPath transform
+     * must evaluate the XPath against all nodes in the input, even against
+     * XPath namespace nodes. Through a bug in XalanJ2, the namespace nodes are
+     * not fully visible in the Xalan XPath model, so we have to do this by
+     * hand in DOM spaces so that the nodes become visible in XPath space.
+     *
+     * @param doc
+     * @see <A HREF="http://nagoya.apache.org/bugzilla/show_bug.cgi?id=2650">
+     * Namespace axis resolution is not XPath compliant </A>
+     */
+    public static void circumventBug2650(Document doc) {
+
+        Element documentElement = doc.getDocumentElement();
+
+        // if the document element has no xmlns definition, we add xmlns=""
+        Attr xmlnsAttr =
+            documentElement.getAttributeNodeNS(Constants.NamespaceSpecNS, "xmlns");
+
+        if (xmlnsAttr == null) {
+            documentElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", "");
+        }
+
+        XMLUtils.circumventBug2650internal(doc);
+    }
+
+    /**
+     * This is the work horse for {@link #circumventBug2650}.
+     *
+     * @param node
+     * @see <A HREF="http://nagoya.apache.org/bugzilla/show_bug.cgi?id=2650">
+     * Namespace axis resolution is not XPath compliant </A>
+     */
+    @SuppressWarnings("fallthrough")
+    private static void circumventBug2650internal(Node node) {
+        Node parent = null;
+        Node sibling = null;
+        final String namespaceNs = Constants.NamespaceSpecNS;
+        do {
+            switch (node.getNodeType()) {
+            case Node.ELEMENT_NODE :
+                Element element = (Element) node;
+                if (!element.hasChildNodes()) {
+                    break;
+                }
+                if (element.hasAttributes()) {
+                    NamedNodeMap attributes = element.getAttributes();
+                    int attributesLength = attributes.getLength();
+
+                    for (Node child = element.getFirstChild(); child!=null;
+                        child = child.getNextSibling()) {
+
+                        if (child.getNodeType() != Node.ELEMENT_NODE) {
+                            continue;
+                        }
+                        Element childElement = (Element) child;
+
+                        for (int i = 0; i < attributesLength; i++) {
+                            Attr currentAttr = (Attr) attributes.item(i);
+                            if (!namespaceNs.equals(currentAttr.getNamespaceURI())) {
+                                continue;
+                            }
+                            if (childElement.hasAttributeNS(namespaceNs,
+                                                            currentAttr.getLocalName())) {
+                                continue;
+                            }
+                            childElement.setAttributeNS(namespaceNs,
+                                                        currentAttr.getName(),
+                                                        currentAttr.getNodeValue());
+                        }
+                    }
+                }
+            case Node.ENTITY_REFERENCE_NODE :
+            case Node.DOCUMENT_NODE :
+                parent = node;
+                sibling = node.getFirstChild();
+                break;
+            }
+            while ((sibling == null) && (parent != null)) {
+                sibling = parent.getNextSibling();
+                parent = parent.getParentNode();
+            }
+            if (sibling == null) {
+                return;
+            }
+
+            node = sibling;
+            sibling = node.getNextSibling();
+        } while (true);
+    }
+
+    /**
+     * @param sibling
+     * @param uri
+     * @param nodeName
+     * @param number
+     * @return nodes with the constrain
+     */
+    public static Text selectNodeText(Node sibling, String uri, String nodeName, int number) {
+        Node n = selectNode(sibling,uri,nodeName,number);
+        if (n == null) {
+            return null;
+        }
+        n = n.getFirstChild();
+        while (n != null && n.getNodeType() != Node.TEXT_NODE) {
+            n = n.getNextSibling();
+        }
+        return (Text)n;
+    }
+
+    /**
+     * @param sibling
+     * @param uri
+     * @param nodeName
+     * @param number
+     * @return nodes with the constrain
+     */
+    public static Element selectNode(Node sibling, String uri, String nodeName, int number) {
+        while (sibling != null) {
+            if (sibling.getNamespaceURI() != null && sibling.getNamespaceURI().equals(uri)
+                && sibling.getLocalName().equals(nodeName)) {
+                if (number == 0){
+                    return (Element)sibling;
+                }
+                number--;
+            }
+            sibling = sibling.getNextSibling();
+        }
+        return null;
+    }
+
+    /**
+     * @param sibling
+     * @param uri
+     * @param nodeName
+     * @return nodes with the constraint
+     */
+    public static Element[] selectNodes(Node sibling, String uri, String nodeName) {
+        List<Element> list = new ArrayList<Element>();
+        while (sibling != null) {
+            if (sibling.getNamespaceURI() != null && sibling.getNamespaceURI().equals(uri)
+                && sibling.getLocalName().equals(nodeName)) {
+                list.add((Element)sibling);
+            }
+            sibling = sibling.getNextSibling();
+        }
+        return list.toArray(new Element[list.size()]);
+    }
+
+    /**
+     * @param signatureElement
+     * @param inputSet
+     * @return nodes with the constrain
+     */
+    public static Set<Node> excludeNodeFromSet(Node signatureElement, Set<Node> inputSet) {
+        Set<Node> resultSet = new HashSet<Node>();
+        Iterator<Node> iterator = inputSet.iterator();
+
+        while (iterator.hasNext()) {
+            Node inputNode = iterator.next();
+
+            if (!XMLUtils.isDescendantOrSelf(signatureElement, inputNode)) {
+                resultSet.add(inputNode);
+            }
+        }
+        return resultSet;
+    }
+
+    /**
+     * Method getStrFromNode
+     *
+     * @param xpathnode
+     * @return the string for the node.
+     */
+    public static String getStrFromNode(Node xpathnode) {
+        if (xpathnode.getNodeType() == Node.TEXT_NODE) {
+            // we iterate over all siblings of the context node because eventually,
+            // the text is "polluted" with pi's or comments
+            StringBuilder sb = new StringBuilder();
+
+            for (Node currentSibling = xpathnode.getParentNode().getFirstChild();
+                currentSibling != null;
+                currentSibling = currentSibling.getNextSibling()) {
+                if (currentSibling.getNodeType() == Node.TEXT_NODE) {
+                    sb.append(((Text) currentSibling).getData());
+                }
+            }
+
+            return sb.toString();
+        } else if (xpathnode.getNodeType() == Node.ATTRIBUTE_NODE) {
+            return ((Attr) xpathnode).getNodeValue();
+        } else if (xpathnode.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
+            return ((ProcessingInstruction) xpathnode).getNodeValue();
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns true if the descendantOrSelf is on the descendant-or-self axis
+     * of the context node.
+     *
+     * @param ctx
+     * @param descendantOrSelf
+     * @return true if the node is descendant
+     */
+    public static boolean isDescendantOrSelf(Node ctx, Node descendantOrSelf) {
+        if (ctx == descendantOrSelf) {
+            return true;
+        }
+
+        Node parent = descendantOrSelf;
+
+        while (true) {
+            if (parent == null) {
+                return false;
+            }
+
+            if (parent == ctx) {
+                return true;
+            }
+
+            if (parent.getNodeType() == Node.ATTRIBUTE_NODE) {
+                parent = ((Attr) parent).getOwnerElement();
+            } else {
+                parent = parent.getParentNode();
+            }
+        }
+    }
+
+    /**
+     * Returns the attribute value for the attribute with the specified name.
+     * Returns null if there is no such attribute, or
+     * the empty string if the attribute value is empty.
+     *
+     * <p>This works around a limitation of the DOM
+     * <code>Element.getAttributeNode</code> method, which does not distinguish
+     * between an unspecified attribute and an attribute with a value of
+     * "" (it returns "" for both cases).
+     *
+     * @param elem the element containing the attribute
+     * @param name the name of the attribute
+     * @return the attribute value (may be null if unspecified)
+     */
+    public static String getAttributeValue(Element elem, String name) {
+        Attr attr = elem.getAttributeNodeNS(null, name);
+        return (attr == null) ? null : attr.getValue();
+    }
+
+    /**
+     * This method is a tree-search to help prevent against wrapping attacks. It checks that no
+     * two Elements have ID Attributes that match the "value" argument, if this is the case then
+     * "false" is returned. Note that a return value of "true" does not necessarily mean that
+     * a matching Element has been found, just that no wrapping attack has been detected.
+     */
+    public static boolean protectAgainstWrappingAttack(Node startNode, String value) {
+        Node startParent = startNode.getParentNode();
+        Node processedNode;
+        Element foundElement = null;
+
+        String id = value.trim();
+        if (id.charAt(0) == '#') {
+            id = id.substring(1);
+        }
+
+        while (startNode != null) {
+            if (startNode.getNodeType() == Node.ELEMENT_NODE) {
+                Element se = (Element) startNode;
+
+                NamedNodeMap attributes = se.getAttributes();
+                if (attributes != null) {
+                    for (int i = 0; i < attributes.getLength(); i++) {
+                        Attr attr = (Attr)attributes.item(i);
+                        if (attr.isId() && id.equals(attr.getValue())) {
+                            if (foundElement == null) {
+                                // Continue searching to find duplicates
+                                foundElement = attr.getOwnerElement();
+                            } else {
+                                //log.debug("Multiple elements with the same 'Id' attribute value!");
+                                return false;
+                            }
+                        }
+                    }
+                }
+            }
+
+            processedNode = startNode;
+            startNode = startNode.getFirstChild();
+
+            // no child, this node is done.
+            if (startNode == null) {
+                // close node processing, get sibling
+                startNode = processedNode.getNextSibling();
+            }
+
+            // no more siblings, get parent, all children
+            // of parent are processed.
+            while (startNode == null) {
+                processedNode = processedNode.getParentNode();
+                if (processedNode == startParent) {
+                    return true;
+                }
+                // close parent node processing (processed node now)
+                startNode = processedNode.getNextSibling();
+            }
+        }
+        return true;
+    }
+
+    /**
+     * This method is a tree-search to help prevent against wrapping attacks. It checks that no other
+     * Element than the given "knownElement" argument has an ID attribute that matches the "value"
+     * argument, which is the ID value of "knownElement". If this is the case then "false" is returned.
+     */
+    public static boolean protectAgainstWrappingAttack(
+        Node startNode, Element knownElement, String value
+    ) {
+        Node startParent = startNode.getParentNode();
+        Node processedNode;
+
+        String id = value.trim();
+        if (id.charAt(0) == '#') {
+            id = id.substring(1);
+        }
+
+        while (startNode != null) {
+            if (startNode.getNodeType() == Node.ELEMENT_NODE) {
+                Element se = (Element) startNode;
+
+                NamedNodeMap attributes = se.getAttributes();
+                if (attributes != null) {
+                    for (int i = 0; i < attributes.getLength(); i++) {
+                        Attr attr = (Attr)attributes.item(i);
+                        if (attr.isId() && id.equals(attr.getValue()) && se != knownElement) {
+                            //log.debug("Multiple elements with the same 'Id' attribute value!");
+                            return false;
+                        }
+                    }
+                }
+            }
+
+            processedNode = startNode;
+            startNode = startNode.getFirstChild();
+
+            // no child, this node is done.
+            if (startNode == null) {
+                // close node processing, get sibling
+                startNode = processedNode.getNextSibling();
+            }
+
+            // no more siblings, get parent, all children
+            // of parent are processed.
+            while (startNode == null) {
+                processedNode = processedNode.getParentNode();
+                if (processedNode == startParent) {
+                    return true;
+                }
+                // close parent node processing (processed node now)
+                startNode = processedNode.getNextSibling();
+            }
+        }
+        return true;
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/ClosedStreamException.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/ClosedStreamException.java
--- 1.8.4-1/ext/java/nokogiri/internals/ClosedStreamException.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/ClosedStreamException.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,10 @@
+package nokogiri.internals;
+
+@SuppressWarnings("serial")
+public class ClosedStreamException extends Exception {
+
+  public ClosedStreamException(String message) {
+    super(message);
+  }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/dom2dtm/DOM2DTMdefaultNamespaceDeclarationNode.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/dom2dtm/DOM2DTMdefaultNamespaceDeclarationNode.java
--- 1.8.4-1/ext/java/nokogiri/internals/dom2dtm/DOM2DTMdefaultNamespaceDeclarationNode.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/dom2dtm/DOM2DTMdefaultNamespaceDeclarationNode.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,685 @@
+/*
+ * reserved comment block
+ * DO NOT REMOVE OR ALTER!
+ */
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * $Id: DOM2DTMdefaultNamespaceDeclarationNode.java,v 1.2.4.1 2005/09/15 08:15:11 suresh_emailid Exp $
+ */
+
+// This is copied directly out of the original
+// com.sun.org.apache.xml.internal.dtm.ref.dom2dtm path to allow modifying
+// DOM2DTM and working around package private constructors.  Other than the
+// package declaration, this class is unmodified
+
+package nokogiri.internals.dom2dtm;
+
+import org.apache.xml.dtm.DTMException;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.TypeInfo;
+import org.w3c.dom.UserDataHandler;
+import org.w3c.dom.DOMException;
+
+/** This is a kluge to let us shove a declaration for xml: into the
+ * DOM2DTM model.  Basically, it creates a proxy node in DOM space to
+ * carry the additional information. This is _NOT_ a full DOM
+ * implementation, and shouldn't be one since it sits alongside the
+ * DOM rather than becoming part of the DOM model.
+ *
+ * (This used to be an internal class within DOM2DTM. Moved out because
+ * I need to perform an instanceof operation on it to support a temporary
+ * workaround in DTMManagerDefault.)
+ *
+ * %REVIEW% What if the DOM2DTM was built around a DocumentFragment and
+ * there isn't a single root element? I think this fails that case...
+ *
+ * %REVIEW% An alternative solution would be to create the node _only_
+ * in DTM space, but given how DOM2DTM is currently written I think
+ * this is simplest.
+ * */
+public class DOM2DTMdefaultNamespaceDeclarationNode implements Attr,TypeInfo
+{
+  final String NOT_SUPPORTED_ERR="Unsupported operation on pseudonode";
+
+  Element pseudoparent;
+  String prefix,uri,nodename;
+  int handle;
+  DOM2DTMdefaultNamespaceDeclarationNode(Element pseudoparent,String prefix,String uri,int handle)
+  {
+    this.pseudoparent=pseudoparent;
+    this.prefix=prefix;
+    this.uri=uri;
+    this.handle=handle;
+    this.nodename="xmlns:"+prefix;
+  }
+  public String getNodeName() {return nodename;}
+  public String getName() {return nodename;}
+  public String getNamespaceURI() {return "http://www.w3.org/2000/xmlns/";}
+  public String getPrefix() {return prefix;}
+  public String getLocalName() {return prefix;}
+  public String getNodeValue() {return uri;}
+  public String getValue() {return uri;}
+  public Element getOwnerElement() {return pseudoparent;}
+
+  public boolean isSupported(String feature, String version) {return false;}
+  public boolean hasChildNodes() {return false;}
+  public boolean hasAttributes() {return false;}
+  public Node getParentNode() {return null;}
+  public Node getFirstChild() {return null;}
+  public Node getLastChild() {return null;}
+  public Node getPreviousSibling() {return null;}
+  public Node getNextSibling() {return null;}
+  public boolean getSpecified() {return false;}
+  public void normalize() {return;}
+  public NodeList getChildNodes() {return null;}
+  public NamedNodeMap getAttributes() {return null;}
+  public short getNodeType() {return Node.ATTRIBUTE_NODE;}
+  public void setNodeValue(String value) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public void setValue(String value) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public void setPrefix(String value) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public Node insertBefore(Node a, Node b) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public Node replaceChild(Node a, Node b) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public Node appendChild(Node a) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public Node removeChild(Node a) {throw new DTMException(NOT_SUPPORTED_ERR);}
+  public Document getOwnerDocument() {return pseudoparent.getOwnerDocument();}
+  public Node cloneNode(boolean deep) {throw new DTMException(NOT_SUPPORTED_ERR);}
+
+    /** Non-DOM method, part of the temporary kluge
+     * %REVIEW% This would be a pruning problem, but since it will always be
+     * added to the root element and we prune on elements, we shouldn't have
+     * to worry.
+     */
+    public int getHandleOfNode()
+    {
+        return handle;
+    }
+
+    //RAMESH: PENDING=> Add proper implementation for the below DOM L3 additions
+
+    /**
+     * @see org.w3c.dom.TypeInfo#getTypeName()
+     */
+    public String getTypeName() {return null; }
+
+    /**
+     * @see org.w3c.dom.TypeInfo#getTypeNamespace()
+     */
+    public String getTypeNamespace() { return null;}
+
+    /**
+     * @see or.gw3c.dom.TypeInfo#isDerivedFrom(String,String,int)
+     */
+    public boolean isDerivedFrom( String ns, String localName, int derivationMethod ) {
+        return false;
+    }
+
+    public TypeInfo getSchemaTypeInfo() { return this; }
+
+    public boolean isId( ) { return false; }
+
+    /**
+     * Associate an object to a key on this node. The object can later be
+     * retrieved from this node by calling <code>getUserData</code> with the
+     * same key.
+     * @param key The key to associate the object to.
+     * @param data The object to associate to the given key, or
+     *   <code>null</code> to remove any existing association to that key.
+     * @param handler The handler to associate to that key, or
+     *   <code>null</code>.
+     * @return Returns the <code>DOMObject</code> previously associated to
+     *   the given key on this node, or <code>null</code> if there was none.
+     * @since DOM Level 3
+     */
+    public Object setUserData(String key,
+                              Object data,
+                              UserDataHandler handler) {
+        return getOwnerDocument().setUserData( key, data, handler);
+    }
+
+    /**
+     * Retrieves the object associated to a key on a this node. The object
+     * must first have been set to this node by calling
+     * <code>setUserData</code> with the same key.
+     * @param key The key the object is associated to.
+     * @return Returns the <code>DOMObject</code> associated to the given key
+     *   on this node, or <code>null</code> if there was none.
+     * @since DOM Level 3
+     */
+    public Object getUserData(String key) {
+        return getOwnerDocument().getUserData( key);
+    }
+
+    /**
+     *  This method returns a specialized object which implements the
+     * specialized APIs of the specified feature and version. The
+     * specialized object may also be obtained by using binding-specific
+     * casting methods but is not necessarily expected to, as discussed in Mixed DOM implementations.
+     * @param feature The name of the feature requested (case-insensitive).
+     * @param version  This is the version number of the feature to test. If
+     *   the version is <code>null</code> or the empty string, supporting
+     *   any version of the feature will cause the method to return an
+     *   object that supports at least one version of the feature.
+     * @return  Returns an object which implements the specialized APIs of
+     *   the specified feature and version, if any, or <code>null</code> if
+     *   there is no object which implements interfaces associated with that
+     *   feature. If the <code>DOMObject</code> returned by this method
+     *   implements the <code>Node</code> interface, it must delegate to the
+     *   primary core <code>Node</code> and not return results inconsistent
+     *   with the primary core <code>Node</code> such as attributes,
+     *   childNodes, etc.
+     * @since DOM Level 3
+     */
+    public Object getFeature(String feature, String version) {
+        // we don't have any alternate node, either this node does the job
+        // or we don't have anything that does
+        return isSupported(feature, version) ? this : null;
+    }
+
+    /**
+     * Tests whether two nodes are equal.
+     * <br>This method tests for equality of nodes, not sameness (i.e.,
+     * whether the two nodes are references to the same object) which can be
+     * tested with <code>Node.isSameNode</code>. All nodes that are the same
+     * will also be equal, though the reverse may not be true.
+     * <br>Two nodes are equal if and only if the following conditions are
+     * satisfied: The two nodes are of the same type.The following string
+     * attributes are equal: <code>nodeName</code>, <code>localName</code>,
+     * <code>namespaceURI</code>, <code>prefix</code>, <code>nodeValue</code>
+     * , <code>baseURI</code>. This is: they are both <code>null</code>, or
+     * they have the same length and are character for character identical.
+     * The <code>attributes</code> <code>NamedNodeMaps</code> are equal.
+     * This is: they are both <code>null</code>, or they have the same
+     * length and for each node that exists in one map there is a node that
+     * exists in the other map and is equal, although not necessarily at the
+     * same index.The <code>childNodes</code> <code>NodeLists</code> are
+     * equal. This is: they are both <code>null</code>, or they have the
+     * same length and contain equal nodes at the same index. This is true
+     * for <code>Attr</code> nodes as for any other type of node. Note that
+     * normalization can affect equality; to avoid this, nodes should be
+     * normalized before being compared.
+     * <br>For two <code>DocumentType</code> nodes to be equal, the following
+     * conditions must also be satisfied: The following string attributes
+     * are equal: <code>publicId</code>, <code>systemId</code>,
+     * <code>internalSubset</code>.The <code>entities</code>
+     * <code>NamedNodeMaps</code> are equal.The <code>notations</code>
+     * <code>NamedNodeMaps</code> are equal.
+     * <br>On the other hand, the following do not affect equality: the
+     * <code>ownerDocument</code> attribute, the <code>specified</code>
+     * attribute for <code>Attr</code> nodes, the
+     * <code>isWhitespaceInElementContent</code> attribute for
+     * <code>Text</code> nodes, as well as any user data or event listeners
+     * registered on the nodes.
+     * @param arg The node to compare equality with.
+     * @param deep If <code>true</code>, recursively compare the subtrees; if
+     *   <code>false</code>, compare only the nodes themselves (and its
+     *   attributes, if it is an <code>Element</code>).
+     * @return If the nodes, and possibly subtrees are equal,
+     *   <code>true</code> otherwise <code>false</code>.
+     * @since DOM Level 3
+     */
+    public boolean isEqualNode(Node arg) {
+        if (arg == this) {
+            return true;
+        }
+        if (arg.getNodeType() != getNodeType()) {
+            return false;
+        }
+        // in theory nodeName can't be null but better be careful
+        // who knows what other implementations may be doing?...
+        if (getNodeName() == null) {
+            if (arg.getNodeName() != null) {
+                return false;
+            }
+        }
+        else if (!getNodeName().equals(arg.getNodeName())) {
+            return false;
+        }
+
+        if (getLocalName() == null) {
+            if (arg.getLocalName() != null) {
+                return false;
+            }
+        }
+        else if (!getLocalName().equals(arg.getLocalName())) {
+            return false;
+        }
+
+        if (getNamespaceURI() == null) {
+            if (arg.getNamespaceURI() != null) {
+                return false;
+            }
+        }
+        else if (!getNamespaceURI().equals(arg.getNamespaceURI())) {
+            return false;
+        }
+
+        if (getPrefix() == null) {
+            if (arg.getPrefix() != null) {
+                return false;
+            }
+        }
+        else if (!getPrefix().equals(arg.getPrefix())) {
+            return false;
+        }
+
+        if (getNodeValue() == null) {
+            if (arg.getNodeValue() != null) {
+                return false;
+            }
+        }
+        else if (!getNodeValue().equals(arg.getNodeValue())) {
+            return false;
+        }
+    /*
+        if (getBaseURI() == null) {
+            if (((NodeImpl) arg).getBaseURI() != null) {
+                return false;
+            }
+        }
+        else if (!getBaseURI().equals(((NodeImpl) arg).getBaseURI())) {
+            return false;
+        }
+*/
+
+             return true;
+    }
+
+    /**
+     * DOM Level 3 - Experimental:
+     * Look up the namespace URI associated to the given prefix, starting from this node.
+     * Use lookupNamespaceURI(null) to lookup the default namespace
+     *
+     * @param namespaceURI
+     * @return th URI for the namespace
+     * @since DOM Level 3
+     */
+    public String lookupNamespaceURI(String specifiedPrefix) {
+        short type = this.getNodeType();
+        switch (type) {
+        case Node.ELEMENT_NODE : {
+
+                String namespace = this.getNamespaceURI();
+                String prefix = this.getPrefix();
+                if (namespace !=null) {
+                    // REVISIT: is it possible that prefix is empty string?
+                    if (specifiedPrefix== null && prefix==specifiedPrefix) {
+                        // looking for default namespace
+                        return namespace;
+                    } else if (prefix != null && prefix.equals(specifiedPrefix)) {
+                        // non default namespace
+                        return namespace;
+                    }
+                }
+                if (this.hasAttributes()) {
+                    NamedNodeMap map = this.getAttributes();
+                    int length = map.getLength();
+                    for (int i=0;i<length;i++) {
+                        Node attr = map.item(i);
+                        String attrPrefix = attr.getPrefix();
+                        String value = attr.getNodeValue();
+                        namespace = attr.getNamespaceURI();
+                        if (namespace !=null && namespace.equals("http://www.w3.org/2000/xmlns/")) {
+                            // at this point we are dealing with DOM Level 2 nodes only
+                            if (specifiedPrefix == null &&
+                                attr.getNodeName().equals("xmlns")) {
+                                // default namespace
+                                return value;
+                            } else if (attrPrefix !=null &&
+                                       attrPrefix.equals("xmlns") &&
+                                       attr.getLocalName().equals(specifiedPrefix)) {
+                 // non default namespace
+                                return value;
+                            }
+                        }
+                    }
+                }
+                /*
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.lookupNamespaceURI(specifiedPrefix);
+                }
+                */
+
+                return null;
+
+
+            }
+/*
+        case Node.DOCUMENT_NODE : {
+                return((NodeImpl)((Document)this).getDocumentElement()).lookupNamespaceURI(specifiedPrefix) ;
+            }
+*/
+        case Node.ENTITY_NODE :
+        case Node.NOTATION_NODE:
+        case Node.DOCUMENT_FRAGMENT_NODE:
+        case Node.DOCUMENT_TYPE_NODE:
+            // type is unknown
+            return null;
+        case Node.ATTRIBUTE_NODE:{
+                if (this.getOwnerElement().getNodeType() == Node.ELEMENT_NODE) {
+                    return getOwnerElement().lookupNamespaceURI(specifiedPrefix);
+
+                }
+                return null;
+            }
+        default:{
+           /*
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.lookupNamespaceURI(specifiedPrefix);
+                }
+             */
+                return null;
+            }
+
+        }
+    }
+
+    /**
+     *  DOM Level 3: Experimental
+     *  This method checks if the specified <code>namespaceURI</code> is the
+     *  default namespace or not.
+     *  @param namespaceURI The namespace URI to look for.
+     *  @return  <code>true</code> if the specified <code>namespaceURI</code>
+     *   is the default namespace, <code>false</code> otherwise.
+     * @since DOM Level 3
+     */
+    public boolean isDefaultNamespace(String namespaceURI){
+       /*
+        // REVISIT: remove casts when DOM L3 becomes REC.
+        short type = this.getNodeType();
+        switch (type) {
+        case Node.ELEMENT_NODE: {
+            String namespace = this.getNamespaceURI();
+            String prefix = this.getPrefix();
+
+            // REVISIT: is it possible that prefix is empty string?
+            if (prefix == null || prefix.length() == 0) {
+                if (namespaceURI == null) {
+                    return (namespace == namespaceURI);
+                }
+                return namespaceURI.equals(namespace);
+            }
+            if (this.hasAttributes()) {
+                ElementImpl elem = (ElementImpl)this;
+                NodeImpl attr = (NodeImpl)elem.getAttributeNodeNS("http://www.w3.org/2000/xmlns/", "xmlns");
+                if (attr != null) {
+                    String value = attr.getNodeValue();
+                    if (namespaceURI == null) {
+                        return (namespace == value);
+                    }
+                    return namespaceURI.equals(value);
+                }
+            }
+
+            NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+            if (ancestor != null) {
+                return ancestor.isDefaultNamespace(namespaceURI);
+            }
+            return false;
+        }
+        case Node.DOCUMENT_NODE:{
+                return((NodeImpl)((Document)this).getDocumentElement()).isDefaultNamespace(namespaceURI);
+            }
+
+        case Node.ENTITY_NODE :
+          case Node.NOTATION_NODE:
+        case Node.DOCUMENT_FRAGMENT_NODE:
+        case Node.DOCUMENT_TYPE_NODE:
+            // type is unknown
+            return false;
+        case Node.ATTRIBUTE_NODE:{
+                if (this.ownerNode.getNodeType() == Node.ELEMENT_NODE) {
+                    return ownerNode.isDefaultNamespace(namespaceURI);
+
+                }
+                return false;
+            }
+        default:{
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.isDefaultNamespace(namespaceURI);
+                }
+                return false;
+            }
+
+        }
+*/
+        return false;
+
+
+    }
+
+    /**
+     *
+     * DOM Level 3 - Experimental:
+     * Look up the prefix associated to the given namespace URI, starting from this node.
+     *
+     * @param namespaceURI
+     * @return the prefix for the namespace
+     */
+    public String lookupPrefix(String namespaceURI){
+
+        // REVISIT: When Namespaces 1.1 comes out this may not be true
+        // Prefix can't be bound to null namespace
+        if (namespaceURI == null) {
+            return null;
+        }
+
+        short type = this.getNodeType();
+
+        switch (type) {
+/*
+        case Node.ELEMENT_NODE: {
+
+                String namespace = this.getNamespaceURI(); // to flip out children
+                return lookupNamespacePrefix(namespaceURI, (ElementImpl)this);
+            }
+
+        case Node.DOCUMENT_NODE:{
+                return((NodeImpl)((Document)this).getDocumentElement()).lookupPrefix(namespaceURI);
+            }
+*/
+        case Node.ENTITY_NODE :
+        case Node.NOTATION_NODE:
+        case Node.DOCUMENT_FRAGMENT_NODE:
+        case Node.DOCUMENT_TYPE_NODE:
+            // type is unknown
+            return null;
+        case Node.ATTRIBUTE_NODE:{
+                if (this.getOwnerElement().getNodeType() == Node.ELEMENT_NODE) {
+                    return getOwnerElement().lookupPrefix(namespaceURI);
+
+                }
+                return null;
+            }
+        default:{
+/*
+                NodeImpl ancestor = (NodeImpl)getElementAncestor(this);
+                if (ancestor != null) {
+                    return ancestor.lookupPrefix(namespaceURI);
+                }
+*/
+                return null;
+            }
+         }
+    }
+
+    /**
+     * Returns whether this node is the same node as the given one.
+     * <br>This method provides a way to determine whether two
+     * <code>Node</code> references returned by the implementation reference
+     * the same object. When two <code>Node</code> references are references
+     * to the same object, even if through a proxy, the references may be
+     * used completely interchangably, such that all attributes have the
+     * same values and calling the same DOM method on either reference
+     * always has exactly the same effect.
+     * @param other The node to test against.
+     * @return Returns <code>true</code> if the nodes are the same,
+     *   <code>false</code> otherwise.
+     * @since DOM Level 3
+     */
+    public boolean isSameNode(Node other) {
+        // we do not use any wrapper so the answer is obvious
+        return this == other;
+    }
+
+    /**
+     * This attribute returns the text content of this node and its
+     * descendants. When it is defined to be null, setting it has no effect.
+     * When set, any possible children this node may have are removed and
+     * replaced by a single <code>Text</code> node containing the string
+     * this attribute is set to. On getting, no serialization is performed,
+     * the returned string does not contain any markup. No whitespace
+     * normalization is performed, the returned string does not contain the
+     * element content whitespaces . Similarly, on setting, no parsing is
+     * performed either, the input string is taken as pure textual content.
+     * <br>The string returned is made of the text content of this node
+     * depending on its type, as defined below:
+     * <table border='1'>
+     * <tr>
+     * <th>Node type</th>
+     * <th>Content</th>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * ELEMENT_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE,
+     * DOCUMENT_FRAGMENT_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>concatenation of the <code>textContent</code>
+     * attribute value of every child node, excluding COMMENT_NODE and
+     * PROCESSING_INSTRUCTION_NODE nodes</td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>ATTRIBUTE_NODE, TEXT_NODE,
+     * CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * <code>nodeValue</code></td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * null</td>
+     * </tr>
+     * </table>
+     * @exception DOMException
+     *   NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
+     * @exception DOMException
+     *   DOMSTRING_SIZE_ERR: Raised when it would return more characters than
+     *   fit in a <code>DOMString</code> variable on the implementation
+     *   platform.
+     * @since DOM Level 3
+     */
+    public void setTextContent(String textContent)
+        throws DOMException {
+        setNodeValue(textContent);
+    }
+
+    /**
+     * This attribute returns the text content of this node and its
+     * descendants. When it is defined to be null, setting it has no effect.
+     * When set, any possible children this node may have are removed and
+     * replaced by a single <code>Text</code> node containing the string
+     * this attribute is set to. On getting, no serialization is performed,
+     * the returned string does not contain any markup. No whitespace
+     * normalization is performed, the returned string does not contain the
+     * element content whitespaces . Similarly, on setting, no parsing is
+     * performed either, the input string is taken as pure textual content.
+     * <br>The string returned is made of the text content of this node
+     * depending on its type, as defined below:
+     * <table border='1'>
+     * <tr>
+     * <th>Node type</th>
+     * <th>Content</th>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * ELEMENT_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE,
+     * DOCUMENT_FRAGMENT_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>concatenation of the <code>textContent</code>
+     * attribute value of every child node, excluding COMMENT_NODE and
+     * PROCESSING_INSTRUCTION_NODE nodes</td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>ATTRIBUTE_NODE, TEXT_NODE,
+     * CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * <code>nodeValue</code></td>
+     * </tr>
+     * <tr>
+     * <td valign='top' rowspan='1' colspan='1'>DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE</td>
+     * <td valign='top' rowspan='1' colspan='1'>
+     * null</td>
+     * </tr>
+     * </table>
+     * @exception DOMException
+     *   NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
+     * @exception DOMException
+     *   DOMSTRING_SIZE_ERR: Raised when it would return more characters than
+     *   fit in a <code>DOMString</code> variable on the implementation
+     *   platform.
+     * @since DOM Level 3
+     */
+    public String getTextContent() throws DOMException {
+        return getNodeValue();  // overriden in some subclasses
+    }
+
+    /**
+     * Compares a node with this node with regard to their position in the
+     * document.
+     * @param other The node to compare against this node.
+     * @return Returns how the given node is positioned relatively to this
+     *   node.
+     * @since DOM Level 3
+     */
+    public short compareDocumentPosition(Node other) throws DOMException {
+        return 0;
+    }
+
+    /**
+     * The absolute base URI of this node or <code>null</code> if undefined.
+     * This value is computed according to . However, when the
+     * <code>Document</code> supports the feature "HTML" , the base URI is
+     * computed using first the value of the href attribute of the HTML BASE
+     * element if any, and the value of the <code>documentURI</code>
+     * attribute from the <code>Document</code> interface otherwise.
+     * <br> When the node is an <code>Element</code>, a <code>Document</code>
+     * or a a <code>ProcessingInstruction</code>, this attribute represents
+     * the properties [base URI] defined in . When the node is a
+     * <code>Notation</code>, an <code>Entity</code>, or an
+     * <code>EntityReference</code>, this attribute represents the
+     * properties [declaration base URI] in the . How will this be affected
+     * by resolution of relative namespace URIs issue?It's not.Should this
+     * only be on Document, Element, ProcessingInstruction, Entity, and
+     * Notation nodes, according to the infoset? If not, what is it equal to
+     * on other nodes? Null? An empty string? I think it should be the
+     * parent's.No.Should this be read-only and computed or and actual
+     * read-write attribute?Read-only and computed (F2F 19 Jun 2000 and
+     * teleconference 30 May 2001).If the base HTML element is not yet
+     * attached to a document, does the insert change the Document.baseURI?
+     * Yes. (F2F 26 Sep 2001)
+     * @since DOM Level 3
+     */
+    public String getBaseURI() {
+        return null;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/dom2dtm/DOM2DTM.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/dom2dtm/DOM2DTM.java
--- 1.8.4-1/ext/java/nokogiri/internals/dom2dtm/DOM2DTM.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/dom2dtm/DOM2DTM.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,1745 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package nokogiri.internals.dom2dtm;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.dom.DOMSource;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMManager;
+import org.apache.xml.dtm.DTMWSFilter;
+import org.apache.xml.dtm.ref.DTMDefaultBaseIterators;
+import org.apache.xml.dtm.ref.DTMManagerDefault;
+import org.apache.xml.dtm.ref.ExpandedNameTable;
+import org.apache.xml.dtm.ref.IncrementalSAXSource;
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+import org.apache.xml.utils.FastStringBuffer;
+import org.apache.xml.utils.QName;
+import org.apache.xml.utils.StringBufferPool;
+import org.apache.xml.utils.TreeWalker;
+import org.apache.xml.utils.XMLCharacterRecognizer;
+import org.apache.xml.utils.XMLString;
+import org.apache.xml.utils.XMLStringFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.Entity;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.xml.sax.ContentHandler;
+
+/** The <code>DOM2DTM</code> class serves up a DOM's contents via the
+ * DTM API.
+ *
+ * Note that it doesn't necessarily represent a full Document
+ * tree. You can wrap a DOM2DTM around a specific node and its subtree
+ * and the right things should happen. (I don't _think_ we currently
+ * support DocumentFrgment nodes as roots, though that might be worth
+ * considering.)
+ *
+ * Note too that we do not currently attempt to track document
+ * mutation. If you alter the DOM after wrapping DOM2DTM around it,
+ * all bets are off.
+ * */
+public class DOM2DTM extends DTMDefaultBaseIterators
+{
+//    static final boolean JJK_DEBUG=false;
+//    static final boolean JJK_NEWCODE=true;
+
+    /** Manefest constant
+     */
+    static final String NAMESPACE_DECL_NS="http://www.w3.org/XML/1998/namespace";
+
+    /** The current position in the DOM tree. Last node examined for
+     * possible copying to DTM. */
+    transient private Node m_pos;
+    /** The current position in the DTM tree. Who children get appended to. */
+    private int m_last_parent=0;
+    /** The current position in the DTM tree. Who children reference as their
+     * previous sib. */
+    private int m_last_kid=NULL;
+
+    /** The top of the subtree.
+     * %REVIEW%: 'may not be the same as m_context if "//foo" pattern.'
+     * */
+    transient private Node m_root;
+
+    /** True iff the first element has been processed. This is used to control
+     synthesis of the implied xml: namespace declaration node. */
+    boolean m_processedFirstElement=false;
+
+    /** true if ALL the nodes in the m_root subtree have been processed;
+     * false if our incremental build has not yet finished scanning the
+     * DOM tree.  */
+    transient private boolean m_nodesAreProcessed;
+
+    /** The node objects.  The instance part of the handle indexes
+     * directly into this vector.  Each DTM node may actually be
+     * composed of several DOM nodes (for example, if logically-adjacent
+     * Text/CDATASection nodes in the DOM have been coalesced into a
+     * single DTM Text node); this table points only to the first in
+     * that sequence. */
+    protected final java.util.List<Node> m_nodes = new java.util.ArrayList<Node>(12);
+
+    /**
+     * Construct a DOM2DTM object from a DOM node.
+     *
+     * @param mgr The DTMManager who owns this DTM.
+     * @param domSource the DOM source that this DTM will wrap.
+     * @param dtmIdentity The DTM identity ID for this DTM.
+     * @param whiteSpaceFilter The white space filter for this DTM, which may
+     *                         be null.
+     * @param xstringfactory XMLString factory for creating character content.
+     * @param doIndexing true if the caller considers it worth it to use
+     *                   indexing schemes.
+     */
+    public DOM2DTM(DTMManager mgr, DOMSource domSource,
+                   int dtmIdentity, DTMWSFilter whiteSpaceFilter,
+                   XMLStringFactory xstringfactory,
+                   boolean doIndexing)
+    {
+        super(mgr, domSource, dtmIdentity, whiteSpaceFilter,
+            xstringfactory, doIndexing);
+
+        // Initialize DOM navigation
+        m_pos=m_root = domSource.getNode();
+        // Initialize DTM navigation
+        m_last_parent=m_last_kid=NULL;
+        m_last_kid=addNode(m_root, m_last_parent,m_last_kid, NULL);
+
+        // Apparently the domSource root may not actually be the
+        // Document node. If it's an Element node, we need to immediately
+        // add its attributes. Adapted from nextNode().
+        // %REVIEW% Move this logic into addNode and recurse? Cleaner!
+        //
+        // (If it's an EntityReference node, we're probably in
+        // seriously bad trouble. For now
+        // I'm just hoping nobody is ever quite that foolish... %REVIEW%)
+        //
+        // %ISSUE% What about inherited namespaces in this case?
+        // Do we need to special-case initialize them into the DTM model?
+        if(ELEMENT_NODE == m_root.getNodeType())
+        {
+            NamedNodeMap attrs=m_root.getAttributes();
+            int attrsize=(attrs==null) ? 0 : attrs.getLength();
+            if(attrsize>0)
+            {
+                int attrIndex=NULL; // start with no previous sib
+                for(int i=0;i<attrsize;++i)
+                {
+                    // No need to force nodetype in this case;
+                    // addNode() will take care of switching it from
+                    // Attr to Namespace if necessary.
+                    attrIndex=addNode(attrs.item(i),0,attrIndex,NULL);
+                    m_firstch.setElementAt(DTM.NULL,attrIndex);
+                }
+                // Terminate list of attrs, and make sure they aren't
+                // considered children of the element
+                m_nextsib.setElementAt(DTM.NULL,attrIndex);
+
+                // IMPORTANT: This does NOT change m_last_parent or m_last_kid!
+            } // if attrs exist
+        } //if(ELEMENT_NODE)
+
+        // Initialize DTM-completed status
+        m_nodesAreProcessed = false;
+    }
+
+    /**
+     * Construct the node map from the node.
+     *
+     * @param node The node that is to be added to the DTM.
+     * @param parentIndex The current parent index.
+     * @param previousSibling The previous sibling index.
+     * @param forceNodeType If not DTM.NULL, overrides the DOM node type.
+     *	Used to force nodes to Text rather than CDATASection when their
+     *	coalesced value includes ordinary Text nodes (current DTM behavior).
+     *
+     * @return The index identity of the node that was added.
+     */
+    protected int addNode(Node node, int parentIndex,
+                          int previousSibling, int forceNodeType)
+    {
+        int nodeIndex = m_nodes.size();
+
+        // Have we overflowed a DTM Identity's addressing range?
+        if(m_dtmIdent.size() == (nodeIndex>>>DTMManager.IDENT_DTM_NODE_BITS))
+        {
+            try
+            {
+                if(m_mgr==null)
+                    throw new ClassCastException();
+
+                // Handle as Extended Addressing
+                DTMManagerDefault mgrD=(DTMManagerDefault)m_mgr;
+                int id=mgrD.getFirstFreeDTMID();
+                mgrD.addDTM(this,id,nodeIndex);
+                m_dtmIdent.addElement(id<<DTMManager.IDENT_DTM_NODE_BITS);
+            }
+            catch(ClassCastException e)
+            {
+                // %REVIEW% Wrong error message, but I've been told we're trying
+                // not to add messages right not for I18N reasons.
+                // %REVIEW% Should this be a Fatal Error?
+                error(XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_DTMIDS_AVAIL, null));//"No more DTM IDs are available";
+            }
+        }
+
+        m_size++;
+        // ensureSize(nodeIndex);
+
+        int type;
+        if(NULL==forceNodeType)
+            type = node.getNodeType();
+        else
+            type=forceNodeType;
+
+        // %REVIEW% The Namespace Spec currently says that Namespaces are
+        // processed in a non-namespace-aware manner, by matching the
+        // QName, even though there is in fact a namespace assigned to
+        // these nodes in the DOM. If and when that changes, we will have
+        // to consider whether we check the namespace-for-namespaces
+        // rather than the node name.
+        //
+        // %TBD% Note that the DOM does not necessarily explicitly declare
+        // all the namespaces it uses. DOM Level 3 will introduce a
+        // namespace-normalization operation which reconciles that, and we
+        // can request that users invoke it or otherwise ensure that the
+        // tree is namespace-well-formed before passing the DOM to Xalan.
+        // But if they don't, what should we do about it? We probably
+        // don't want to alter the source DOM (and may not be able to do
+        // so if it's read-only). The best available answer might be to
+        // synthesize additional DTM Namespace Nodes that don't correspond
+        // to DOM Attr Nodes.
+        if (Node.ATTRIBUTE_NODE == type)
+        {
+            String name = node.getNodeName();
+
+            if (name.startsWith("xmlns:") || name.equals("xmlns"))
+            {
+                type = DTM.NAMESPACE_NODE;
+            }
+        }
+
+        m_nodes.add(node);
+
+        m_firstch.setElementAt(NOTPROCESSED,nodeIndex);
+        m_nextsib.setElementAt(NOTPROCESSED,nodeIndex);
+        m_prevsib.setElementAt(previousSibling,nodeIndex);
+        m_parent.setElementAt(parentIndex,nodeIndex);
+
+        if(DTM.NULL != parentIndex &&
+            type != DTM.ATTRIBUTE_NODE &&
+            type != DTM.NAMESPACE_NODE)
+        {
+            // If the DTM parent had no children, this becomes its first child.
+            if(NOTPROCESSED == m_firstch.elementAt(parentIndex))
+                m_firstch.setElementAt(nodeIndex,parentIndex);
+        }
+
+        String nsURI = node.getNamespaceURI();
+
+        // Deal with the difference between Namespace spec and XSLT
+        // definitions of local name. (The former says PIs don't have
+        // localnames; the latter says they do.)
+        String localName =  (type == Node.PROCESSING_INSTRUCTION_NODE) ?
+            node.getNodeName() :
+            node.getLocalName();
+
+        // Hack to make DOM1 sort of work...
+        if(((type == Node.ELEMENT_NODE) || (type == Node.ATTRIBUTE_NODE))
+            && null == localName)
+            localName = node.getNodeName(); // -sb
+
+        ExpandedNameTable exnt = m_expandedNameTable;
+
+        // %TBD% Nodes created with the old non-namespace-aware DOM
+        // calls createElement() and createAttribute() will never have a
+        // localname. That will cause their expandedNameID to be just the
+        // nodeType... which will keep them from being matched
+        // successfully by name. Since the DOM makes no promise that
+        // those will participate in namespace processing, this is
+        // officially accepted as Not Our Fault. But it might be nice to
+        // issue a diagnostic message!
+        if(node.getLocalName()==null &&
+            (type==Node.ELEMENT_NODE || type==Node.ATTRIBUTE_NODE))
+        {
+            // warning("DOM 'level 1' node "+node.getNodeName()+" won't be mapped properly in DOM2DTM.");
+        }
+
+        int expandedNameID = (null != localName)
+            ? exnt.getExpandedTypeID(nsURI, localName, type) :
+            exnt.getExpandedTypeID(type);
+
+        m_exptype.setElementAt(expandedNameID,nodeIndex);
+
+        indexNode(expandedNameID, nodeIndex);
+
+        if (DTM.NULL != previousSibling)
+            m_nextsib.setElementAt(nodeIndex,previousSibling);
+
+        // This should be done after m_exptype has been set, and probably should
+        // always be the last thing we do
+        if (type == DTM.NAMESPACE_NODE)
+            declareNamespaceInContext(parentIndex,nodeIndex);
+
+        return nodeIndex;
+    }
+
+    /**
+     * Get the number of nodes that have been added.
+     */
+    public int getNumberOfNodes()
+    {
+        return m_nodes.size();
+    }
+
+    /**
+     * This method iterates to the next node that will be added to the table.
+     * Each call to this method adds a new node to the table, unless the end
+     * is reached, in which case it returns null.
+     *
+     * @return The true if a next node is found or false if
+     *         there are no more nodes.
+     */
+    protected boolean nextNode()
+    {
+        // Non-recursive one-fetch-at-a-time depth-first traversal with
+        // attribute/namespace nodes and white-space stripping.
+        // Navigating the DOM is simple, navigating the DTM is simple;
+        // keeping track of both at once is a trifle baroque but at least
+        // we've avoided most of the special cases.
+        if (m_nodesAreProcessed)
+            return false;
+
+        // %REVIEW% Is this local copy Really Useful from a performance
+        // point of view?  Or is this a false microoptimization?
+        Node pos=m_pos;
+        Node next=null;
+        int nexttype=NULL;
+
+        // Navigate DOM tree
+        do
+        {
+            // Look down to first child.
+            if (pos.hasChildNodes())
+            {
+                next = pos.getFirstChild();
+
+                // %REVIEW% There's probably a more elegant way to skip
+                // the doctype. (Just let it go and Suppress it?
+                if(next!=null && DOCUMENT_TYPE_NODE==next.getNodeType())
+                    next=next.getNextSibling();
+
+                // Push DTM context -- except for children of Entity References,
+                // which have no DTM equivalent and cause no DTM navigation.
+                if(ENTITY_REFERENCE_NODE!=pos.getNodeType())
+                {
+                    m_last_parent=m_last_kid;
+                    m_last_kid=NULL;
+                    // Whitespace-handler context stacking
+                    if(null != m_wsfilter)
+                    {
+                        short wsv =
+                            m_wsfilter.getShouldStripSpace(makeNodeHandle(m_last_parent),this);
+                        boolean shouldStrip = (DTMWSFilter.INHERIT == wsv)
+                            ? getShouldStripWhitespace()
+                            : (DTMWSFilter.STRIP == wsv);
+                        pushShouldStripWhitespace(shouldStrip);
+                    } // if(m_wsfilter)
+                }
+            }
+
+            // If that fails, look up and right (but not past root!)
+            else
+            {
+                if(m_last_kid!=NULL)
+                {
+                    // Last node posted at this level had no more children
+                    // If it has _no_ children, we need to record that.
+                    if(m_firstch.elementAt(m_last_kid)==NOTPROCESSED)
+                        m_firstch.setElementAt(NULL,m_last_kid);
+                }
+
+                while(m_last_parent != NULL)
+                {
+                    // %REVIEW% There's probably a more elegant way to
+                    // skip the doctype. (Just let it go and Suppress it?
+                    next = pos.getNextSibling();
+                    if(next!=null && DOCUMENT_TYPE_NODE==next.getNodeType())
+                        next=next.getNextSibling();
+
+                    if(next!=null)
+                        break; // Found it!
+
+                    // No next-sibling found. Pop the DOM.
+                    pos=pos.getParentNode();
+//                    if(pos==null)
+//                    {
+//                        // %TBD% Should never arise, but I want to be sure of that...
+//                        if(JJK_DEBUG)
+//                        {
+//                            System.out.println("***** DOM2DTM Pop Control Flow problem");
+//                            for(;;); // Freeze right here!
+//                        }
+//                    }
+
+                    // The only parents in the DTM are Elements.  However,
+                    // the DOM could contain EntityReferences.  If we
+                    // encounter one, pop it _without_ popping DTM.
+                    if(pos!=null && ENTITY_REFERENCE_NODE == pos.getNodeType())
+                    {
+//                        // Nothing needs doing
+//                        if(JJK_DEBUG) System.out.println("***** DOM2DTM popping EntRef");
+                    }
+                    else
+                    {
+                        popShouldStripWhitespace();
+                        // Fix and pop DTM
+                        if(m_last_kid==NULL)
+                            m_firstch.setElementAt(NULL,m_last_parent); // Popping from an element
+                        else
+                            m_nextsib.setElementAt(NULL,m_last_kid); // Popping from anything else
+                        m_last_parent=m_parent.elementAt(m_last_kid=m_last_parent);
+                    }
+                }
+                if(m_last_parent==NULL) next=null;
+            }
+
+            if(next!=null) nexttype=next.getNodeType();
+
+            // If it's an entity ref, advance past it.
+            //
+            // %REVIEW% Should we let this out the door and just suppress it?
+            // More work, but simpler code, more likely to be correct, and
+            // it doesn't happen very often. We'd get rid of the loop too.
+            if (ENTITY_REFERENCE_NODE == nexttype) pos=next;
+        }
+        while (ENTITY_REFERENCE_NODE == nexttype);
+
+        // Did we run out of the tree?
+        if(next==null)
+        {
+            m_nextsib.setElementAt(NULL,0);
+            m_nodesAreProcessed = true;
+            m_pos=null;
+
+//            if(JJK_DEBUG)
+//            {
+//                System.out.println("***** DOM2DTM Crosscheck:");
+//                for(int i=0;i<m_nodes.size();++i)
+//                    System.out.println(i+":\t"+m_firstch.elementAt(i)+"\t"+m_nextsib.elementAt(i));
+//            }
+
+            return false;
+        }
+
+        // Text needs some special handling:
+        //
+        // DTM may skip whitespace. This is handled by the suppressNode flag, which
+        // when true will keep the DTM node from being created.
+        //
+        // DTM only directly records the first DOM node of any logically-contiguous
+        // sequence. The lastTextNode value will be set to the last node in the
+        // contiguous sequence, and -- AFTER the DTM addNode -- can be used to
+        // advance next over this whole block. Should be simpler than special-casing
+        // the above loop for "Was the logically-preceeding sibling a text node".
+        //
+        // Finally, a DTM node should be considered a CDATASection only if all the
+        // contiguous text it covers is CDATASections. The first Text should
+        // force DTM to Text.
+
+        boolean suppressNode=false;
+        Node lastTextNode=null;
+
+        nexttype=next.getNodeType();
+
+        // nexttype=pos.getNodeType();
+        if(TEXT_NODE == nexttype || CDATA_SECTION_NODE == nexttype)
+        {
+            // If filtering, initially assume we're going to suppress the node
+            suppressNode=((null != m_wsfilter) && getShouldStripWhitespace());
+
+            // Scan logically contiguous text (siblings, plus "flattening"
+            // of entity reference boundaries).
+            if (suppressNode) {
+                Node n = next;
+                while (n != null) {
+                    lastTextNode=n;
+                    // Any Text node means DTM considers it all Text
+                    if (TEXT_NODE == n.getNodeType()) nexttype = TEXT_NODE;
+                    // Any non-whitespace in this sequence blocks whitespace
+                    // suppression
+                    suppressNode &= XMLCharacterRecognizer.isWhiteSpace(n.getNodeValue());
+
+                    n = logicalNextDOMTextNode(n);
+                }
+            }
+        }
+
+        // Special handling for PIs: Some DOMs represent the XML
+        // Declaration as a PI. This is officially incorrect, per the DOM
+        // spec, but is considered a "wrong but tolerable" temporary
+        // workaround pending proper handling of these fields in DOM Level
+        // 3. We want to recognize and reject that case.
+        else if(PROCESSING_INSTRUCTION_NODE==nexttype)
+        {
+            suppressNode = (pos.getNodeName().toLowerCase().equals("xml"));
+        }
+
+
+        if(!suppressNode)
+        {
+            // Inserting next. NOTE that we force the node type; for
+            // coalesced Text, this records CDATASections adjacent to
+            // ordinary Text as Text.
+            int nextindex=addNode(next, m_last_parent, m_last_kid, nexttype);
+
+            m_last_kid=nextindex;
+
+            if(ELEMENT_NODE == nexttype)
+            {
+                int attrIndex=NULL; // start with no previous sib
+                // Process attributes _now_, rather than waiting.
+                // Simpler control flow, makes NS cache available immediately.
+                NamedNodeMap attrs=next.getAttributes();
+                int attrsize=(attrs==null) ? 0 : attrs.getLength();
+                if(attrsize>0)
+                {
+                    for(int i=0;i<attrsize;++i)
+                    {
+                        // No need to force nodetype in this case;
+                        // addNode() will take care of switching it from
+                        // Attr to Namespace if necessary.
+                        attrIndex=addNode(attrs.item(i), nextindex, attrIndex, NULL);
+                        m_firstch.setElementAt(DTM.NULL,attrIndex);
+
+                        // If the xml: prefix is explicitly declared
+                        // we don't need to synthesize one.
+                        //
+                        // NOTE that XML Namespaces were not originally
+                        // defined as being namespace-aware (grrr), and
+                        // while the W3C is planning to fix this it's
+                        // safer for now to test the QName and trust the
+                        // parsers to prevent anyone from redefining the
+                        // reserved xmlns: prefix
+                        if(!m_processedFirstElement
+                            && "xmlns:xml".equals(attrs.item(i).getNodeName()))
+                            m_processedFirstElement=true;
+                    }
+                    // Terminate list of attrs, and make sure they aren't
+                    // considered children of the element
+                } // if attrs exist
+                if(!m_processedFirstElement)
+                {
+                    // The DOM might not have an explicit declaration for the
+                    // implicit "xml:" prefix, but the XPath data model
+                    // requires that this appear as a Namespace Node so we
+                    // have to synthesize one. You can think of this as
+                    // being a default attribute defined by the XML
+                    // Namespaces spec rather than by the DTD.
+                    attrIndex=addNode(new DOM2DTMdefaultNamespaceDeclarationNode(
+                            (Element)next,"xml",NAMESPACE_DECL_NS,
+                            makeNodeHandle(((attrIndex==NULL)?nextindex:attrIndex)+1)
+                        ),
+                        nextindex,attrIndex,NULL);
+                    m_firstch.setElementAt(DTM.NULL,attrIndex);
+                    m_processedFirstElement=true;
+                }
+                if(attrIndex!=NULL) m_nextsib.setElementAt(DTM.NULL,attrIndex);
+            } //if(ELEMENT_NODE)
+        } // (if !suppressNode)
+
+        // Text postprocessing: Act on values stored above
+        //if(TEXT_NODE == nexttype || CDATA_SECTION_NODE == nexttype)
+        //{
+            // %TBD% If nexttype was forced to TEXT, patch the DTM node
+            if (lastTextNode != null) next=lastTextNode;      // Advance the DOM cursor over contiguous text
+        //}
+
+        // Remember where we left off.
+        m_pos=next;
+        return true;
+    }
+
+
+    /**
+     * Return an DOM node for the given node.
+     *
+     * @param nodeHandle The node ID.
+     *
+     * @return A node representation of the DTM node.
+     */
+    public Node getNode(int nodeHandle)
+    {
+
+        int identity = makeNodeIdentity(nodeHandle);
+
+        return (Node) m_nodes.get(identity);
+    }
+
+    /**
+     * Get a Node from an identity index.
+     *
+     * NEEDSDOC @param nodeIdentity
+     *
+     * NEEDSDOC ($objectName$) @return
+     */
+    protected Node lookupNode(int nodeIdentity)
+    {
+        return (Node) m_nodes.get(nodeIdentity);
+    }
+
+    /**
+     * Get the next node identity value in the list, and call the iterator
+     * if it hasn't been added yet.
+     *
+     * @param identity The node identity (index).
+     * @return identity+1, or DTM.NULL.
+     */
+    protected int getNextNodeIdentity(int identity)
+    {
+
+        identity += 1;
+
+        if (identity >= m_nodes.size())
+        {
+            if (!nextNode())
+                identity = DTM.NULL;
+        }
+
+        return identity;
+    }
+
+    /**
+     * Get the handle from a Node.
+     * <p>%OPT% This will be pretty slow.</p>
+     *
+     * <p>%OPT% An XPath-like search (walk up DOM to root, tracking path;
+     * walk down DTM reconstructing path) might be considerably faster
+     * on later nodes in large documents. That might also imply improving
+     * this call to handle nodes which would be in this DTM but
+     * have not yet been built, which might or might not be a Good Thing.</p>
+     *
+     * %REVIEW% This relies on being able to test node-identity via
+     * object-identity. DTM2DOM proxying is a great example of a case where
+     * that doesn't work. DOM Level 3 will provide the isSameNode() method
+     * to fix that, but until then this is going to be flaky.
+     *
+     * @param node A node, which may be null.
+     *
+     * @return The node handle or <code>DTM.NULL</code>.
+     */
+    public int getHandleFromNode(Node node)
+    {
+        if (null != node)
+        {
+            int len = m_nodes.size();
+            boolean isMore;
+            int i = 0;
+            do
+            {
+                for (; i < len; i++)
+                {
+                    if (m_nodes.get(i) == node)
+                        return makeNodeHandle(i);
+                }
+
+                isMore = nextNode();
+
+                len = m_nodes.size();
+
+            }
+            while(isMore || i < len);
+        }
+
+        return DTM.NULL;
+    }
+
+    /** Get the handle from a Node. This is a more robust version of
+     * getHandleFromNode, intended to be usable by the public.
+     *
+     * <p>%OPT% This will be pretty slow.</p>
+     *
+     * %REVIEW% This relies on being able to test node-identity via
+     * object-identity. DTM2DOM proxying is a great example of a case where
+     * that doesn't work. DOM Level 3 will provide the isSameNode() method
+     * to fix that, but until then this is going to be flaky.
+     *
+     * @param node A node, which may be null.
+     *
+     * @return The node handle or <code>DTM.NULL</code>.  */
+    public int getHandleOfNode(Node node)
+    {
+        if (null != node)
+        {
+            // Is Node actually within the same document? If not, don't search!
+            // This would be easier if m_root was always the Document node, but
+            // we decided to allow wrapping a DTM around a subtree.
+            if((m_root==node) ||
+                (m_root.getNodeType()==DOCUMENT_NODE &&
+                    m_root==node.getOwnerDocument()) ||
+                (m_root.getNodeType()!=DOCUMENT_NODE &&
+                    m_root.getOwnerDocument()==node.getOwnerDocument())
+                )
+            {
+                // If node _is_ in m_root's tree, find its handle
+                //
+                // %OPT% This check may be improved significantly when DOM
+                // Level 3 nodeKey and relative-order tests become
+                // available!
+                for(Node cursor=node;
+                    cursor!=null;
+                    cursor=
+                        (cursor.getNodeType()!=ATTRIBUTE_NODE)
+                            ? cursor.getParentNode()
+                            : ((org.w3c.dom.Attr)cursor).getOwnerElement())
+                {
+                    if(cursor==m_root)
+                        // We know this node; find its handle.
+                        return getHandleFromNode(node);
+                } // for ancestors of node
+            } // if node and m_root in same Document
+        } // if node!=null
+
+        return DTM.NULL;
+    }
+
+    /**
+     * Retrieves an attribute node by by qualified name and namespace URI.
+     *
+     * @param nodeHandle int Handle of the node upon which to look up this attribute..
+     * @param namespaceURI The namespace URI of the attribute to
+     *   retrieve, or null.
+     * @param name The local name of the attribute to
+     *   retrieve.
+     * @return The attribute node handle with the specified name (
+     *   <code>nodeName</code>) or <code>DTM.NULL</code> if there is no such
+     *   attribute.
+     */
+    public int getAttributeNode(int nodeHandle, String namespaceURI,
+                                String name)
+    {
+
+        // %OPT% This is probably slower than it needs to be.
+        if (null == namespaceURI)
+            namespaceURI = "";
+
+        int type = getNodeType(nodeHandle);
+
+        if (DTM.ELEMENT_NODE == type)
+        {
+
+            // Assume that attributes immediately follow the element.
+            int identity = makeNodeIdentity(nodeHandle);
+
+            while (DTM.NULL != (identity = getNextNodeIdentity(identity)))
+            {
+                // Assume this can not be null.
+                type = _type(identity);
+
+                // %REVIEW%
+                // Should namespace nodes be retrievable DOM-style as attrs?
+                // If not we need a separate function... which may be desirable
+                // architecturally, but which is ugly from a code point of view.
+                // (If we REALLY insist on it, this code should become a subroutine
+                // of both -- retrieve the node, then test if the type matches
+                // what you're looking for.)
+                if (type == DTM.ATTRIBUTE_NODE || type==DTM.NAMESPACE_NODE)
+                {
+                    Node node = lookupNode(identity);
+                    String nodeuri = node.getNamespaceURI();
+
+                    if (null == nodeuri)
+                        nodeuri = "";
+
+                    String nodelocalname = node.getLocalName();
+
+                    if (nodeuri.equals(namespaceURI) && name.equals(nodelocalname))
+                        return makeNodeHandle(identity);
+                }
+
+                else // if (DTM.NAMESPACE_NODE != type)
+                {
+                    break;
+                }
+            }
+        }
+
+        return DTM.NULL;
+    }
+
+    /**
+     * Get the string-value of a node as a String object
+     * (see http://www.w3.org/TR/xpath#data-model
+     * for the definition of a node's string-value).
+     *
+     * @param nodeHandle The node ID.
+     *
+     * @return A string object that represents the string-value of the given node.
+     */
+    public XMLString getStringValue(int nodeHandle)
+    {
+
+        int type = getNodeType(nodeHandle);
+        Node node = getNode(nodeHandle);
+        // %TBD% If an element only has one text node, we should just use it
+        // directly.
+        if(DTM.ELEMENT_NODE == type || DTM.DOCUMENT_NODE == type
+            || DTM.DOCUMENT_FRAGMENT_NODE == type)
+        {
+            FastStringBuffer buf = StringBufferPool.get();
+            String s;
+
+            try
+            {
+                getNodeData(node, buf);
+
+                s = (buf.length() > 0) ? buf.toString() : "";
+            }
+            finally
+            {
+                StringBufferPool.free(buf);
+            }
+
+            return m_xstrf.newstr( s );
+        }
+        else if(TEXT_NODE == type || CDATA_SECTION_NODE == type)
+        {
+            // If this is a DTM text node, it may be made of multiple DOM text
+            // nodes -- including navigating into Entity References. DOM2DTM
+            // records the first node in the sequence and requires that we
+            // pick up the others when we retrieve the DTM node's value.
+            //
+            // %REVIEW% DOM Level 3 is expected to add a "whole text"
+            // retrieval method which performs this function for us.
+            FastStringBuffer buf = StringBufferPool.get();
+            while(node!=null)
+            {
+                buf.append(node.getNodeValue());
+                node=logicalNextDOMTextNode(node);
+            }
+            String s=(buf.length() > 0) ? buf.toString() : "";
+            StringBufferPool.free(buf);
+            return m_xstrf.newstr( s );
+        }
+        else
+            return m_xstrf.newstr( node.getNodeValue() );
+    }
+
+    /**
+     * Determine if the string-value of a node is whitespace
+     *
+     * @param nodeHandle The node Handle.
+     *
+     * @return Return true if the given node is whitespace.
+     */
+    public boolean isWhitespace(int nodeHandle)
+    {
+        int type = getNodeType(nodeHandle);
+        Node node = getNode(nodeHandle);
+        if(TEXT_NODE == type || CDATA_SECTION_NODE == type)
+        {
+            // If this is a DTM text node, it may be made of multiple DOM text
+            // nodes -- including navigating into Entity References. DOM2DTM
+            // records the first node in the sequence and requires that we
+            // pick up the others when we retrieve the DTM node's value.
+            //
+            // %REVIEW% DOM Level 3 is expected to add a "whole text"
+            // retrieval method which performs this function for us.
+            FastStringBuffer buf = StringBufferPool.get();
+            while(node!=null)
+            {
+                buf.append(node.getNodeValue());
+                node=logicalNextDOMTextNode(node);
+            }
+            boolean b = buf.isWhitespace(0, buf.length());
+            StringBufferPool.free(buf);
+            return b;
+        }
+        return false;
+    }
+
+    /**
+     * Retrieve the text content of a DOM subtree, appending it into a
+     * user-supplied FastStringBuffer object. Note that attributes are
+     * not considered part of the content of an element.
+     * <p>
+     * There are open questions regarding whitespace stripping.
+     * Currently we make no special effort in that regard, since the standard
+     * DOM doesn't yet provide DTD-based information to distinguish
+     * whitespace-in-element-context from genuine #PCDATA. Note that we
+     * should probably also consider xml:space if/when we address this.
+     * DOM Level 3 may solve the problem for us.
+     * <p>
+     * %REVIEW% Actually, since this method operates on the DOM side of the
+     * fence rather than the DTM side, it SHOULDN'T do
+     * any special handling. The DOM does what the DOM does; if you want
+     * DTM-level abstractions, use DTM-level methods.
+     *
+     * @param node Node whose subtree is to be walked, gathering the
+     * contents of all Text or CDATASection nodes.
+     * @param buf FastStringBuffer into which the contents of the text
+     * nodes are to be concatenated.
+     */
+    protected static void getNodeData(Node node, FastStringBuffer buf)
+    {
+
+        switch (node.getNodeType())
+        {
+            case Node.DOCUMENT_FRAGMENT_NODE :
+            case Node.DOCUMENT_NODE :
+            case Node.ELEMENT_NODE :
+            {
+                for (Node child = node.getFirstChild(); null != child;
+                     child = child.getNextSibling())
+                {
+                    getNodeData(child, buf);
+                }
+            }
+            break;
+            case Node.TEXT_NODE :
+            case Node.CDATA_SECTION_NODE :
+            case Node.ATTRIBUTE_NODE :	// Never a child but might be our starting node
+                buf.append(node.getNodeValue());
+                break;
+            case Node.PROCESSING_INSTRUCTION_NODE :
+                // warning(XPATHErrorResources.WG_PARSING_AND_PREPARING);
+                break;
+            default :
+                // ignore
+                break;
+        }
+    }
+
+    /**
+     * Given a node handle, return its DOM-style node name. This will
+     * include names such as #text or #document.
+     *
+     * @param nodeHandle the id of the node.
+     * @return String Name of this node, which may be an empty string.
+     * %REVIEW% Document when empty string is possible...
+     * %REVIEW-COMMENT% It should never be empty, should it?
+     */
+    public String getNodeName(int nodeHandle)
+    {
+
+        Node node = getNode(nodeHandle);
+
+        // Assume non-null.
+        return node.getNodeName();
+    }
+
+    /**
+     * Given a node handle, return the XPath node name.  This should be
+     * the name as described by the XPath data model, NOT the DOM-style
+     * name.
+     *
+     * @param nodeHandle the id of the node.
+     * @return String Name of this node, which may be an empty string.
+     */
+    public String getNodeNameX(int nodeHandle)
+    {
+
+        String name;
+        short type = getNodeType(nodeHandle);
+
+        switch (type)
+        {
+            case DTM.NAMESPACE_NODE :
+            {
+                Node node = getNode(nodeHandle);
+
+                // assume not null.
+                name = node.getNodeName();
+                if(name.startsWith("xmlns:"))
+                {
+                    name = QName.getLocalPart(name);
+                }
+                else if(name.equals("xmlns"))
+                {
+                    name = "";
+                }
+            }
+            break;
+            case DTM.ATTRIBUTE_NODE :
+            case DTM.ELEMENT_NODE :
+            case DTM.ENTITY_REFERENCE_NODE :
+            case DTM.PROCESSING_INSTRUCTION_NODE :
+            {
+                Node node = getNode(nodeHandle);
+
+                // assume not null.
+                name = node.getNodeName();
+            }
+            break;
+            default :
+                name = "";
+        }
+
+        return name;
+    }
+
+    /**
+     * Given a node handle, return its XPath-style localname.
+     * (As defined in Namespaces, this is the portion of the name after any
+     * colon character).
+     *
+     * @param nodeHandle the id of the node.
+     * @return String Local name of this node.
+     */
+    public String getLocalName(int nodeHandle)
+    {
+//        if(JJK_NEWCODE)
+//        {
+            int id=makeNodeIdentity(nodeHandle);
+            if(NULL==id) return null;
+            Node newnode=(Node)m_nodes.get(id);
+            String newname=newnode.getLocalName();
+            if (null == newname)
+            {
+                // XSLT treats PIs, and possibly other things, as having QNames.
+                String qname = newnode.getNodeName();
+                if('#'==qname.charAt(0))
+                {
+                    //  Match old default for this function
+                    // This conversion may or may not be necessary
+                    newname="";
+                }
+                else
+                {
+                    int index = qname.indexOf(':');
+                    newname = (index < 0) ? qname : qname.substring(index + 1);
+                }
+            }
+            return newname;
+//        }
+//        else
+//        {
+//            String name;
+//            short type = getNodeType(nodeHandle);
+//            switch (type)
+//            {
+//                case DTM.ATTRIBUTE_NODE :
+//                case DTM.ELEMENT_NODE :
+//                case DTM.ENTITY_REFERENCE_NODE :
+//                case DTM.NAMESPACE_NODE :
+//                case DTM.PROCESSING_INSTRUCTION_NODE :
+//                {
+//                    Node node = getNode(nodeHandle);
+//
+//                    // assume not null.
+//                    name = node.getLocalName();
+//
+//                    if (null == name)
+//                    {
+//                        String qname = node.getNodeName();
+//                        int index = qname.indexOf(':');
+//
+//                        name = (index < 0) ? qname : qname.substring(index + 1);
+//                    }
+//                }
+//                break;
+//                default :
+//                    name = "";
+//            }
+//            return name;
+//        }
+    }
+
+    /**
+     * Given a namespace handle, return the prefix that the namespace decl is
+     * mapping.
+     * Given a node handle, return the prefix used to map to the namespace.
+     *
+     * <p> %REVIEW% Are you sure you want "" for no prefix?  </p>
+     * <p> %REVIEW-COMMENT% I think so... not totally sure. -sb  </p>
+     *
+     * @param nodeHandle the id of the node.
+     * @return String prefix of this node's name, or "" if no explicit
+     * namespace prefix was given.
+     */
+    public String getPrefix(int nodeHandle)
+    {
+
+        String prefix;
+        short type = getNodeType(nodeHandle);
+
+        switch (type)
+        {
+            case DTM.NAMESPACE_NODE :
+            {
+                Node node = getNode(nodeHandle);
+
+                // assume not null.
+                String qname = node.getNodeName();
+                int index = qname.indexOf(':');
+
+                prefix = (index < 0) ? "" : qname.substring(index + 1);
+            }
+            break;
+            case DTM.ATTRIBUTE_NODE :
+            case DTM.ELEMENT_NODE :
+            {
+                Node node = getNode(nodeHandle);
+
+                // assume not null.
+                String qname = node.getNodeName();
+                int index = qname.indexOf(':');
+
+                prefix = (index < 0) ? "" : qname.substring(0, index);
+            }
+            break;
+            default :
+                prefix = "";
+        }
+
+        return prefix;
+    }
+
+    /**
+     * Given a node handle, return its DOM-style namespace URI
+     * (As defined in Namespaces, this is the declared URI which this node's
+     * prefix -- or default in lieu thereof -- was mapped to.)
+     *
+     * <p>%REVIEW% Null or ""? -sb</p>
+     *
+     * @param nodeHandle the id of the node.
+     * @return String URI value of this node's namespace, or null if no
+     * namespace was resolved.
+     */
+    public String getNamespaceURI(int nodeHandle)
+    {
+//        if(JJK_NEWCODE)
+//        {
+            int id=makeNodeIdentity(nodeHandle);
+            if(id==NULL) return null;
+            Node node=(Node)m_nodes.get(id);
+            return node.getNamespaceURI();
+//        }
+//        else
+//        {
+//            String nsuri;
+//            short type = getNodeType(nodeHandle);
+//
+//            switch (type)
+//            {
+//                case DTM.ATTRIBUTE_NODE :
+//                case DTM.ELEMENT_NODE :
+//                case DTM.ENTITY_REFERENCE_NODE :
+//                case DTM.NAMESPACE_NODE :
+//                case DTM.PROCESSING_INSTRUCTION_NODE :
+//                {
+//                    Node node = getNode(nodeHandle);
+//
+//                    // assume not null.
+//                    nsuri = node.getNamespaceURI();
+//
+//                    // %TBD% Handle DOM1?
+//                }
+//                break;
+//                default :
+//                    nsuri = null;
+//            }
+//
+//            return nsuri;
+//        }
+    }
+
+    /** Utility function: Given a DOM Text node, determine whether it is
+     * logically followed by another Text or CDATASection node. This may
+     * involve traversing into Entity References.
+     *
+     * %REVIEW% DOM Level 3 is expected to add functionality which may
+     * allow us to retire this.
+     */
+    private Node logicalNextDOMTextNode(Node n)
+    {
+        Node p=n.getNextSibling();
+        if(p==null)
+        {
+            // Walk out of any EntityReferenceNodes that ended with text
+            for(n=n.getParentNode();
+                n!=null && ENTITY_REFERENCE_NODE == n.getNodeType();
+                n=n.getParentNode())
+            {
+                p=n.getNextSibling();
+                if(p!=null)
+                    break;
+            }
+        }
+        n=p;
+        while(n!=null && ENTITY_REFERENCE_NODE == n.getNodeType())
+        {
+            // Walk into any EntityReferenceNodes that start with text
+            if(n.hasChildNodes())
+                n=n.getFirstChild();
+            else
+                n=n.getNextSibling();
+        }
+        if(n!=null)
+        {
+            // Found a logical next sibling. Is it text?
+            int ntype=n.getNodeType();
+            if(TEXT_NODE != ntype && CDATA_SECTION_NODE != ntype)
+                n=null;
+        }
+        return n;
+    }
+
+    /**
+     * Given a node handle, return its node value. This is mostly
+     * as defined by the DOM, but may ignore some conveniences.
+     * <p>
+     *
+     * @param nodeHandle The node id.
+     * @return String Value of this node, or null if not
+     * meaningful for this node type.
+     */
+    public String getNodeValue(int nodeHandle)
+    {
+        // The _type(nodeHandle) call was taking the lion's share of our
+        // time, and was wrong anyway since it wasn't coverting handle to
+        // identity. Inlined it.
+        int type = _exptype(makeNodeIdentity(nodeHandle));
+        type=(NULL != type) ? getNodeType(nodeHandle) : NULL;
+
+        if(TEXT_NODE!=type && CDATA_SECTION_NODE!=type)
+            return getNode(nodeHandle).getNodeValue();
+
+        // If this is a DTM text node, it may be made of multiple DOM text
+        // nodes -- including navigating into Entity References. DOM2DTM
+        // records the first node in the sequence and requires that we
+        // pick up the others when we retrieve the DTM node's value.
+        //
+        // %REVIEW% DOM Level 3 is expected to add a "whole text"
+        // retrieval method which performs this function for us.
+        Node node = getNode(nodeHandle);
+        Node n=logicalNextDOMTextNode(node);
+        if(n==null)
+            return node.getNodeValue();
+
+        FastStringBuffer buf = StringBufferPool.get();
+        buf.append(node.getNodeValue());
+        while(n!=null)
+        {
+            buf.append(n.getNodeValue());
+            n=logicalNextDOMTextNode(n);
+        }
+        String s = (buf.length() > 0) ? buf.toString() : "";
+        StringBufferPool.free(buf);
+        return s;
+    }
+
+    /**
+     *   A document type declaration information item has the following properties:
+     *
+     *     1. [system identifier] The system identifier of the external subset, if
+     *        it exists. Otherwise this property has no value.
+     *
+     * @return the system identifier String object, or null if there is none.
+     */
+    public String getDocumentTypeDeclarationSystemIdentifier()
+    {
+
+        Document doc;
+
+        if (m_root.getNodeType() == Node.DOCUMENT_NODE)
+            doc = (Document) m_root;
+        else
+            doc = m_root.getOwnerDocument();
+
+        if (null != doc)
+        {
+            DocumentType dtd = doc.getDoctype();
+
+            if (null != dtd)
+            {
+                return dtd.getSystemId();
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Return the public identifier of the external subset,
+     * normalized as described in 4.2.2 External Entities [XML]. If there is
+     * no external subset or if it has no public identifier, this property
+     * has no value.
+     *
+     * @return the public identifier String object, or null if there is none.
+     */
+    public String getDocumentTypeDeclarationPublicIdentifier()
+    {
+
+        Document doc;
+
+        if (m_root.getNodeType() == Node.DOCUMENT_NODE)
+            doc = (Document) m_root;
+        else
+            doc = m_root.getOwnerDocument();
+
+        if (null != doc)
+        {
+            DocumentType dtd = doc.getDoctype();
+
+            if (null != dtd)
+            {
+                return dtd.getPublicId();
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the <code>Element</code> whose <code>ID</code> is given by
+     * <code>elementId</code>. If no such element exists, returns
+     * <code>DTM.NULL</code>. Behavior is not defined if more than one element
+     * has this <code>ID</code>. Attributes (including those
+     * with the name "ID") are not of type ID unless so defined by DTD/Schema
+     * information available to the DTM implementation.
+     * Implementations that do not know whether attributes are of type ID or
+     * not are expected to return <code>DTM.NULL</code>.
+     *
+     * <p>%REVIEW% Presumably IDs are still scoped to a single document,
+     * and this operation searches only within a single document, right?
+     * Wouldn't want collisions between DTMs in the same process.</p>
+     *
+     * @param elementId The unique <code>id</code> value for an element.
+     * @return The handle of the matching element.
+     */
+    public int getElementById(String elementId)
+    {
+
+        Document doc = (m_root.getNodeType() == Node.DOCUMENT_NODE)
+            ? (Document) m_root : m_root.getOwnerDocument();
+
+        if(null != doc)
+        {
+            Node elem = doc.getElementById(elementId);
+            if(null != elem)
+            {
+                int elemHandle = getHandleFromNode(elem);
+
+                if(DTM.NULL == elemHandle)
+                {
+                    int identity = m_nodes.size()-1;
+                    while (DTM.NULL != (identity = getNextNodeIdentity(identity)))
+                    {
+                        Node node = getNode(identity);
+                        if(node == elem)
+                        {
+                            elemHandle = getHandleFromNode(elem);
+                            break;
+                        }
+                    }
+                }
+
+                return elemHandle;
+            }
+
+        }
+        return DTM.NULL;
+    }
+
+    /**
+     * The getUnparsedEntityURI function returns the URI of the unparsed
+     * entity with the specified name in the same document as the context
+     * node (see [3.3 Unparsed Entities]). It returns the empty string if
+     * there is no such entity.
+     * <p>
+     * XML processors may choose to use the System Identifier (if one
+     * is provided) to resolve the entity, rather than the URI in the
+     * Public Identifier. The details are dependent on the processor, and
+     * we would have to support some form of plug-in resolver to handle
+     * this properly. Currently, we simply return the System Identifier if
+     * present, and hope that it a usable URI or that our caller can
+     * map it to one.
+     * TODO: Resolve Public Identifiers... or consider changing function name.
+     * <p>
+     * If we find a relative URI
+     * reference, XML expects it to be resolved in terms of the base URI
+     * of the document. The DOM doesn't do that for us, and it isn't
+     * entirely clear whether that should be done here; currently that's
+     * pushed up to a higher level of our application. (Note that DOM Level
+     * 1 didn't store the document's base URI.)
+     * TODO: Consider resolving Relative URIs.
+     * <p>
+     * (The DOM's statement that "An XML processor may choose to
+     * completely expand entities before the structure model is passed
+     * to the DOM" refers only to parsed entities, not unparsed, and hence
+     * doesn't affect this function.)
+     *
+     * @param name A string containing the Entity Name of the unparsed
+     * entity.
+     *
+     * @return String containing the URI of the Unparsed Entity, or an
+     * empty string if no such entity exists.
+     */
+    public String getUnparsedEntityURI(String name)
+    {
+
+        String url = "";
+        Document doc = (m_root.getNodeType() == Node.DOCUMENT_NODE)
+            ? (Document) m_root : m_root.getOwnerDocument();
+
+        if (null != doc)
+        {
+            DocumentType doctype = doc.getDoctype();
+
+            if (null != doctype)
+            {
+                NamedNodeMap entities = doctype.getEntities();
+                if(null == entities)
+                    return url;
+                Entity entity = (Entity) entities.getNamedItem(name);
+                if(null == entity)
+                    return url;
+
+                String notationName = entity.getNotationName();
+
+                if (null != notationName)  // then it's unparsed
+                {
+                    // The draft says: "The XSLT processor may use the public
+                    // identifier to generate a URI for the entity instead of the URI
+                    // specified in the system identifier. If the XSLT processor does
+                    // not use the public identifier to generate the URI, it must use
+                    // the system identifier; if the system identifier is a relative
+                    // URI, it must be resolved into an absolute URI using the URI of
+                    // the resource containing the entity declaration as the base
+                    // URI [RFC2396]."
+                    // So I'm falling a bit short here.
+                    url = entity.getSystemId();
+
+                    if (null == url)
+                    {
+                        url = entity.getPublicId();
+                    }
+                    else
+                    {
+                        // This should be resolved to an absolute URL, but that's hard
+                        // to do from here.
+                    }
+                }
+            }
+        }
+
+        return url;
+    }
+
+    /**
+     *     5. [specified] A flag indicating whether this attribute was actually
+     *        specified in the start-tag of its element, or was defaulted from the
+     *        DTD.
+     *
+     * @param attributeHandle the attribute handle
+     * @return <code>true</code> if the attribute was specified;
+     *         <code>false</code> if it was defaulted.
+     */
+    public boolean isAttributeSpecified(int attributeHandle)
+    {
+        int type = getNodeType(attributeHandle);
+
+        if (DTM.ATTRIBUTE_NODE == type)
+        {
+            Attr attr = (Attr)getNode(attributeHandle);
+            return attr.getSpecified();
+        }
+        return false;
+    }
+
+    /** Bind an IncrementalSAXSource to this DTM. NOT RELEVANT for DOM2DTM, since
+     * we're wrapped around an existing DOM.
+     *
+     * @param source The IncrementalSAXSource that we want to recieve events from
+     * on demand.
+     */
+    public void setIncrementalSAXSource(IncrementalSAXSource source)
+    {
+    }
+
+    /** getContentHandler returns "our SAX builder" -- the thing that
+     * someone else should send SAX events to in order to extend this
+     * DTM model.
+     *
+     * @return null if this model doesn't respond to SAX events,
+     * "this" if the DTM object has a built-in SAX ContentHandler,
+     * the IncrmentalSAXSource if we're bound to one and should receive
+     * the SAX stream via it for incremental build purposes...
+     * */
+    public org.xml.sax.ContentHandler getContentHandler()
+    {
+        return null;
+    }
+
+    /**
+     * Return this DTM's lexical handler.
+     *
+     * %REVIEW% Should this return null if constrution already done/begun?
+     *
+     * @return null if this model doesn't respond to lexical SAX events,
+     * "this" if the DTM object has a built-in SAX ContentHandler,
+     * the IncrementalSAXSource if we're bound to one and should receive
+     * the SAX stream via it for incremental build purposes...
+     */
+    public org.xml.sax.ext.LexicalHandler getLexicalHandler()
+    {
+
+        return null;
+    }
+
+
+    /**
+     * Return this DTM's EntityResolver.
+     *
+     * @return null if this model doesn't respond to SAX entity ref events.
+     */
+    public org.xml.sax.EntityResolver getEntityResolver()
+    {
+
+        return null;
+    }
+
+    /**
+     * Return this DTM's DTDHandler.
+     *
+     * @return null if this model doesn't respond to SAX dtd events.
+     */
+    public org.xml.sax.DTDHandler getDTDHandler()
+    {
+
+        return null;
+    }
+
+    /**
+     * Return this DTM's ErrorHandler.
+     *
+     * @return null if this model doesn't respond to SAX error events.
+     */
+    public org.xml.sax.ErrorHandler getErrorHandler()
+    {
+
+        return null;
+    }
+
+    /**
+     * Return this DTM's DeclHandler.
+     *
+     * @return null if this model doesn't respond to SAX Decl events.
+     */
+    public org.xml.sax.ext.DeclHandler getDeclHandler()
+    {
+
+        return null;
+    }
+
+    /** @return true iff we're building this model incrementally (eg
+     * we're partnered with a IncrementalSAXSource) and thus require that the
+     * transformation and the parse run simultaneously. Guidance to the
+     * DTMManager.
+     * */
+    public boolean needsTwoThreads()
+    {
+        return false;
+    }
+
+    // ========== Direct SAX Dispatch, for optimization purposes ========
+
+    /**
+     * Returns whether the specified <var>ch</var> conforms to the XML 1.0 definition
+     * of whitespace.  Refer to <A href="http://www.w3.org/TR/1998/REC-xml-19980210#NT-S">
+     * the definition of <CODE>S</CODE></A> for details.
+     * @param   ch      Character to check as XML whitespace.
+     * @return          =true if <var>ch</var> is XML whitespace; otherwise =false.
+     */
+    private static boolean isSpace(char ch)
+    {
+        return XMLCharacterRecognizer.isWhiteSpace(ch);  // Take the easy way out for now.
+    }
+
+    /**
+     * Directly call the
+     * characters method on the passed ContentHandler for the
+     * string-value of the given node (see http://www.w3.org/TR/xpath#data-model
+     * for the definition of a node's string-value). Multiple calls to the
+     * ContentHandler's characters methods may well occur for a single call to
+     * this method.
+     *
+     * @param nodeHandle The node ID.
+     * @param ch A non-null reference to a ContentHandler.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void dispatchCharactersEvents(
+        int nodeHandle, org.xml.sax.ContentHandler ch,
+        boolean normalize)
+        throws org.xml.sax.SAXException
+    {
+        if(normalize)
+        {
+            XMLString str = getStringValue(nodeHandle);
+            str = str.fixWhiteSpace(true, true, false);
+            str.dispatchCharactersEvents(ch);
+        }
+        else
+        {
+            int type = getNodeType(nodeHandle);
+            Node node = getNode(nodeHandle);
+            dispatchNodeData(node, ch, 0);
+            // Text coalition -- a DTM text node may represent multiple
+            // DOM nodes.
+            if(TEXT_NODE == type || CDATA_SECTION_NODE == type)
+            {
+                while( null != (node=logicalNextDOMTextNode(node)) )
+                {
+                    dispatchNodeData(node, ch, 0);
+                }
+            }
+        }
+    }
+
+    /**
+     * Retrieve the text content of a DOM subtree, appending it into a
+     * user-supplied FastStringBuffer object. Note that attributes are
+     * not considered part of the content of an element.
+     * <p>
+     * There are open questions regarding whitespace stripping.
+     * Currently we make no special effort in that regard, since the standard
+     * DOM doesn't yet provide DTD-based information to distinguish
+     * whitespace-in-element-context from genuine #PCDATA. Note that we
+     * should probably also consider xml:space if/when we address this.
+     * DOM Level 3 may solve the problem for us.
+     * <p>
+     * %REVIEW% Note that as a DOM-level operation, it can be argued that this
+     * routine _shouldn't_ perform any processing beyond what the DOM already
+     * does, and that whitespace stripping and so on belong at the DTM level.
+     * If you want a stripped DOM view, wrap DTM2DOM around DOM2DTM.
+     *
+     * @param node Node whose subtree is to be walked, gathering the
+     * contents of all Text or CDATASection nodes.
+     */
+    protected static void dispatchNodeData(Node node,
+                                           org.xml.sax.ContentHandler ch,
+                                           int depth)
+        throws org.xml.sax.SAXException
+    {
+
+        switch (node.getNodeType())
+        {
+            case Node.DOCUMENT_FRAGMENT_NODE :
+            case Node.DOCUMENT_NODE :
+            case Node.ELEMENT_NODE :
+            {
+                for (Node child = node.getFirstChild(); null != child;
+                     child = child.getNextSibling())
+                {
+                    dispatchNodeData(child, ch, depth+1);
+                }
+            }
+            break;
+            case Node.PROCESSING_INSTRUCTION_NODE : // %REVIEW%
+            case Node.COMMENT_NODE :
+                if(0 != depth)
+                    break;
+                // NOTE: Because this operation works in the DOM space, it does _not_ attempt
+                // to perform Text Coalition. That should only be done in DTM space.
+            case Node.TEXT_NODE :
+            case Node.CDATA_SECTION_NODE :
+            case Node.ATTRIBUTE_NODE :
+                String str = node.getNodeValue();
+                if(ch instanceof org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler)
+                {
+                    ((org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler)ch).characters(node);
+                }
+                else
+                {
+                    ch.characters(str.toCharArray(), 0, str.length());
+                }
+                break;
+//    /* case Node.PROCESSING_INSTRUCTION_NODE :
+//      // warning(XPATHErrorResources.WG_PARSING_AND_PREPARING);
+//      break; */
+            default :
+                // ignore
+                break;
+        }
+    }
+
+    TreeWalker m_walker = new TreeWalker(null);
+
+    /**
+     * Directly create SAX parser events from a subtree.
+     *
+     * @param nodeHandle The node ID.
+     * @param ch A non-null reference to a ContentHandler.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void dispatchToEvents(int nodeHandle, org.xml.sax.ContentHandler ch)
+        throws org.xml.sax.SAXException
+    {
+        TreeWalker treeWalker = m_walker;
+        ContentHandler prevCH = treeWalker.getContentHandler();
+
+        if(null != prevCH)
+        {
+            treeWalker = new TreeWalker(null);
+        }
+        treeWalker.setContentHandler(ch);
+
+        try
+        {
+            Node node = getNode(nodeHandle);
+            treeWalker.traverseFragment(node);
+        }
+        finally
+        {
+            treeWalker.setContentHandler(null);
+        }
+    }
+
+    /**
+     * For the moment all the run time properties are ignored by this
+     * class.
+     *
+     * @param property a <code>String</code> value
+     * @param value an <code>Object</code> value
+     */
+    public void setProperty(String property, Object value)
+    {
+    }
+
+    /**
+     * No source information is available for DOM2DTM, so return
+     * <code>null</code> here.
+     *
+     * @param node an <code>int</code> value
+     * @return null
+     */
+    public SourceLocator getSourceLocatorFor(int node)
+    {
+        return null;
+    }
+
+}
+
+
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/HtmlDomParserContext.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/HtmlDomParserContext.java
--- 1.8.4-1/ext/java/nokogiri/internals/HtmlDomParserContext.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/HtmlDomParserContext.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,246 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.isNamespace;
+import static nokogiri.internals.NokogiriHelpers.stringOrNil;
+import nokogiri.HtmlDocument;
+import nokogiri.NokogiriService;
+import nokogiri.XmlDocument;
+
+import org.apache.xerces.xni.Augmentations;
+import org.apache.xerces.xni.QName;
+import org.apache.xerces.xni.XMLAttributes;
+import org.apache.xerces.xni.XNIException;
+import org.apache.xerces.xni.parser.XMLDocumentFilter;
+import org.apache.xerces.xni.parser.XMLParserConfiguration;
+import org.cyberneko.html.HTMLConfiguration;
+import org.cyberneko.html.filters.DefaultFilter;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Parser for HtmlDocument. This class actually parses HtmlDocument using NekoHtml.
+ * 
+ * @author sergio
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class HtmlDomParserContext extends XmlDomParserContext {
+
+	public HtmlDomParserContext(Ruby runtime, IRubyObject options) {
+        super(runtime, options);
+    }
+    
+    public HtmlDomParserContext(Ruby runtime, IRubyObject encoding, IRubyObject options) {
+        super(runtime, encoding, options);
+    }
+
+    @Override
+    protected void initErrorHandler() {
+        if (options.strict) {
+            errorHandler = new NokogiriStrictErrorHandler(options.noError, options.noWarning);
+        } else {
+            errorHandler = new NokogiriNonStrictErrorHandler4NekoHtml(options.noError, options.noWarning);
+        }
+    }
+
+    @Override
+    protected void initParser(Ruby runtime) {
+        XMLParserConfiguration config = new HTMLConfiguration();
+        //XMLDocumentFilter removeNSAttrsFilter = new RemoveNSAttrsFilter();
+        XMLDocumentFilter elementValidityCheckFilter = new ElementValidityCheckFilter(errorHandler);
+        //XMLDocumentFilter[] filters = { removeNSAttrsFilter,  elementValidityCheckFilter};
+        XMLDocumentFilter[] filters = { elementValidityCheckFilter};
+
+        config.setErrorHandler(this.errorHandler);
+
+        parser = new NokogiriDomParser(config);
+
+        // see http://nekohtml.sourceforge.net/settings.html for details
+        setProperty("http://cyberneko.org/html/properties/default-encoding", java_encoding);
+        setProperty("http://cyberneko.org/html/properties/names/elems", "lower");
+        setProperty("http://cyberneko.org/html/properties/names/attrs", "lower");
+        setProperty("http://cyberneko.org/html/properties/filters", filters);
+        setFeature("http://cyberneko.org/html/features/report-errors", true);
+        setFeature("http://xml.org/sax/features/namespaces", false);
+    }
+    
+    @Override
+    public void setEncoding(String encoding) {
+		super.setEncoding(encoding);
+    }
+
+    /**
+     * Enable NekoHTML feature for balancing tags in a document fragment.
+     * 
+     * This method is used in XmlNode#in_context method.
+     */
+    public void enableDocumentFragment() {
+        setFeature("http://cyberneko.org/html/features/balance-tags/document-fragment", true);
+    }
+
+    @Override
+    protected XmlDocument getNewEmptyDocument(ThreadContext context) {
+        IRubyObject[] args = IRubyObject.NULL_ARRAY;
+        return (XmlDocument) XmlDocument.rbNew(context, getNokogiriClass(context.getRuntime(), "Nokogiri::HTML::Document"), args);
+    }
+
+    @Override
+    protected XmlDocument wrapDocument(ThreadContext context, RubyClass klazz, Document document) {
+        HtmlDocument htmlDocument = (HtmlDocument) NokogiriService.HTML_DOCUMENT_ALLOCATOR.allocate(context.getRuntime(), klazz);
+        htmlDocument.setDocumentNode(context, document);
+        if (ruby_encoding.isNil()) {
+            // ruby_encoding might have detected by HtmlDocument::EncodingReader
+            if (detected_encoding != null && !detected_encoding.isNil()) {
+                ruby_encoding = detected_encoding;
+            } else {
+                // no encoding given & no encoding detected, then try to get it
+                String charset = tryGetCharsetFromHtml5MetaTag(document);
+                ruby_encoding = stringOrNil(context.getRuntime(), charset);
+            }
+        }
+        htmlDocument.setEncoding(ruby_encoding);
+        htmlDocument.setParsedEncoding(java_encoding);
+        return htmlDocument;
+    }
+    
+    // NekoHtml doesn't understand HTML5 meta tag format. This fails to detect charset
+    // from an HTML5 style meta tag. Luckily, the meta tag and charset exists in DOM tree
+    // so, this method attempts to find the charset.
+    private static String tryGetCharsetFromHtml5MetaTag(Document document) {
+        if (!"html".equalsIgnoreCase(document.getDocumentElement().getNodeName())) return null;
+        NodeList list = document.getDocumentElement().getChildNodes(); Node item;
+        for (int i = 0; i < list.getLength(); i++) {
+            if ("head".equalsIgnoreCase((item = list.item(i)).getNodeName())) {
+                NodeList headers = item.getChildNodes();
+                for (int j = 0; j < headers.getLength(); j++) {
+                    if ("meta".equalsIgnoreCase((item = headers.item(j)).getNodeName())) {
+                        NamedNodeMap nodeMap = item.getAttributes();
+                        for (int k = 0; k < nodeMap.getLength(); k++) {
+                            if ("charset".equalsIgnoreCase((item = nodeMap.item(k)).getNodeName())) {
+                                return item.getNodeValue();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Filter to strip out attributes that pertain to XML namespaces.
+     */
+    public static class RemoveNSAttrsFilter extends DefaultFilter {
+        @Override
+        public void startElement(QName element, XMLAttributes attrs,
+                                 Augmentations augs) throws XNIException {
+            int i;
+            for (i = 0; i < attrs.getLength(); ++i) {
+                if (isNamespace(attrs.getQName(i))) {
+                    attrs.removeAttributeAt(i);
+                    --i;
+                }
+            }
+
+            element.uri = null;
+            super.startElement(element, attrs, augs);
+        }
+    }
+    
+    public static class ElementValidityCheckFilter extends DefaultFilter {
+        private NokogiriErrorHandler errorHandler;
+        
+        private ElementValidityCheckFilter(NokogiriErrorHandler errorHandler) {
+            this.errorHandler = errorHandler;
+        }
+        
+        // element names from xhtml1-strict.dtd
+        private static String[][] element_names = {
+                {"a", "abbr", "acronym", "address", "area"},
+                {"b", "base", "basefont", "bdo", "big", "blockquote", "body", "br", "button"},
+                {"caption", "cite", "code", "col", "colgroup"},
+                {"dd", "del", "dfn", "div", "dl", "dt"},
+                {"em"},
+                {"fieldset", "font", "form", "frame", "frameset"},
+                {}, // g
+                {"h1", "h2", "h3", "h4", "h5", "h6", "head", "hr", "html"},
+                {"i", "iframe", "img", "input", "ins"},
+                {}, // j
+                {"kbd"},
+                {"label", "legend", "li", "link"},
+                {"map", "meta"},
+                {"noframes", "noscript"},
+                {"object", "ol", "optgroup", "option"},
+                {"p", "param", "pre"},
+                {"q"},
+                {}, // r
+                {"s", "samp", "script", "select", "small", "span", "strike", "strong", "style", "sub", "sup"},
+                {"table", "tbody", "td", "textarea", "tfoot", "th", "thead", "title", "tr", "tt"},
+                {"u", "ul"},
+                {"var"},
+                {}, // w
+                {}, // x
+                {}, // y
+                {}  // z
+        };
+        
+        private static boolean isValid(final String name) {
+            int index = name.charAt(0) - 97;
+            if (index >= element_names.length) return false;
+            String[] elementNames = element_names[index];
+            for (int i=0; i<elementNames.length; i++) {
+                if (name.equals(elementNames[i])) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        
+        @Override
+        public void startElement(QName name, XMLAttributes attrs, Augmentations augs) throws XNIException {
+            if (!isValid(name.rawname)) {
+                errorHandler.addError(new Exception("Tag " + name.rawname + " invalid"));
+            }
+            super.startElement(name, attrs, augs);
+        }
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/IgnoreSchemaErrorsErrorHandler.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/IgnoreSchemaErrorsErrorHandler.java
--- 1.8.4-1/ext/java/nokogiri/internals/IgnoreSchemaErrorsErrorHandler.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/IgnoreSchemaErrorsErrorHandler.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,20 @@
+package nokogiri.internals;
+
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+public class IgnoreSchemaErrorsErrorHandler implements ErrorHandler {
+
+    @Override
+    public void warning(SAXParseException exception) throws SAXException {
+    }
+
+    @Override
+    public void error(SAXParseException exception) throws SAXException {
+    }
+
+    @Override
+    public void fatalError(SAXParseException exception) throws SAXException {
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriBlockingQueueInputStream.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriBlockingQueueInputStream.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriBlockingQueueInputStream.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriBlockingQueueInputStream.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,151 @@
+/**
+ *
+ */
+package nokogiri.internals;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import nokogiri.XmlSaxPushParser;
+
+/**
+ * A smart input stream that signals the caller when a chunk of data is consumed
+ * from the stream. The main use of this stream is to synchronize the
+ * {@link XmlSaxPushParser} and the {@link XmlSaxParser} which runs in a
+ * different thread.
+ *
+ * @author John Shahid <jvshahid@gmail.com>
+ */
+public class NokogiriBlockingQueueInputStream extends InputStream {
+  private final LinkedBlockingQueue<Task>  queue;
+  protected Task                           currentTask;
+  protected ByteArrayInputStream           currentStream;
+  protected int                            position;
+  protected boolean                        closed = false;
+
+  public static final ByteArrayInputStream END    = new ByteArrayInputStream(new byte[0]);
+
+  private static class Task extends FutureTask<Void> {
+    private final ByteArrayInputStream stream;
+
+    public Task(ByteArrayInputStream stream) {
+      super(new Callable<Void>() {
+        @Override
+        public Void call() throws Exception {
+          // TODO Auto-generated method stub
+          return null;
+        }
+      });
+      this.stream = stream;
+    }
+
+    public ByteArrayInputStream getStream() {
+      return stream;
+    }
+
+    @Override
+    public void run() {
+      // don't do anything
+    }
+
+    @Override
+    public boolean runAndReset() {
+      // don't do anything
+      return true;
+    }
+
+    @Override
+    public void set(Void v) {
+      super.set(v);
+    }
+  }
+
+  public NokogiriBlockingQueueInputStream() {
+    queue = new LinkedBlockingQueue<Task>();
+  }
+
+  /**
+   * This method shouldn't be called unless the parser has finished parsing or
+   * threw an exception while doing so, otherwise, there'll be the protential
+   * that the read method will block indefinitely.
+   */
+  @Override
+  public synchronized void close() {
+    closed = true;
+    List<Task> tasks = new LinkedList<Task>();
+    queue.drainTo(tasks);
+    tasks.add(currentTask);
+    for (Task task : tasks) {
+      task.set(null);
+    }
+  }
+
+  /**
+   * Add @param stream to the end of the queue of data that will be returned by
+   * {@link #read()} and its variants. The method will @return a future whose
+   * {@link Future#get()} will block until the data in @param stream is read.
+   *
+   * Passing the special stream {@link #END} to this method, will cause
+   * {@link #read()} to return an eof indicator (i.e. -1) to the caller, after
+   * all the data inserted before {@link #END} is processed.
+   *
+   * @return
+   */
+  public synchronized Future<Void> addChunk(ByteArrayInputStream stream) throws ClosedStreamException {
+    if (closed)
+      throw new ClosedStreamException("Cannot add a chunk to a closed stream");
+    Task task = new Task(stream);
+    queue.add(task);
+    return task;
+  }
+
+  /*
+   * (non-Javadoc)
+   *
+   * @see java.io.InputStream#read()
+   */
+  @Override
+  public int read() throws IOException {
+    if (currentTask == null || currentStream.available() == 0)
+      if (getNextTask() == -1)
+        return -1;
+    return currentStream.read();
+  }
+
+  /*
+   * (non-Javadoc)
+   *
+   * @see java.io.InputStream#read(byte[], int, int)
+   */
+  @Override
+  public int read(byte[] bytes, int off, int len) {
+    if (currentTask == null || currentStream.available() == 0) {
+      if (getNextTask() == -1) {
+        currentTask.set(null);
+        return -1;
+      }
+    }
+    return currentStream.read(bytes, off, len);
+  }
+
+  protected int getNextTask() {
+    while (true) {
+      try {
+        if (currentTask != null)
+          currentTask.set(null);
+        currentTask = queue.take();
+        currentStream = currentTask.getStream();
+        return currentStream.available() == 0 ? -1 : currentStream.available();
+      } catch (InterruptedException ex) {
+        // keep retrying to read
+      }
+    }
+  }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriDomParser.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriDomParser.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriDomParser.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriDomParser.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,116 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import java.io.IOException;
+
+import nokogiri.XmlDocument;
+
+import org.apache.xerces.parsers.DOMParser;
+import org.apache.xerces.parsers.XIncludeParserConfiguration;
+import org.apache.xerces.xni.parser.XMLParserConfiguration;
+import org.cyberneko.dtd.DTDConfiguration;
+import org.w3c.dom.Document;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+/**
+ * Sets up a Xerces/XNI DOM Parser for use with Nokogiri.  Uses
+ * NekoDTD to parse the DTD into a tree of Nodes.
+ *
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ */
+public class NokogiriDomParser extends DOMParser {
+    protected DOMParser dtd;
+    protected boolean xInclude;
+    protected XMLParserConfiguration config;
+
+    public NokogiriDomParser(XMLParserConfiguration config) {
+        super(config);
+        this.config = config;
+        initialize();
+    }
+
+    public NokogiriDomParser(ParserContext.Options options) {
+        xInclude = options.xInclude;
+        initialize();
+    }
+
+    protected void initialize() {
+        if (config == null) {
+            if (xInclude) {
+                config = new XIncludeParserConfiguration();
+            } else {
+                config = getXMLParserConfiguration();
+            }
+        }
+
+        DTDConfiguration dtdConfig = new DTDConfiguration();
+        dtd = new DOMParser(dtdConfig);
+
+        config.setDTDHandler(dtdConfig);
+        config.setDTDContentModelHandler(dtdConfig);
+    }
+
+    @Override
+    public void parse(InputSource source) throws SAXException, IOException {
+        dtd.reset();
+        if (xInclude) {
+            setEntityResolver(new NokogiriXInlcudeEntityResolver(source));
+        }
+        super.parse(source);
+        Document doc = getDocument();
+        if (doc == null)
+            throw new RuntimeException("null document");
+
+        doc.setUserData(XmlDocument.DTD_RAW_DOCUMENT, dtd.getDocument(), null);
+    }
+
+    private static class NokogiriXInlcudeEntityResolver implements org.xml.sax.EntityResolver {
+        InputSource source;
+        private NokogiriXInlcudeEntityResolver(InputSource source) {
+            this.source = source;
+        }
+
+        @Override
+        public InputSource resolveEntity(String publicId, String systemId)
+                throws SAXException, IOException {
+            if (systemId != null) source.setSystemId(systemId);
+            if (publicId != null) source.setPublicId(publicId);
+            return source;
+        }
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriEncodingReaderWrapper.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriEncodingReaderWrapper.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriEncodingReaderWrapper.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriEncodingReaderWrapper.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,109 @@
+package nokogiri.internals;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.jruby.Ruby;
+import org.jruby.RubyObject;
+import org.jruby.RubyProcess.Sys;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.javasupport.util.RuntimeHelpers;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+
+/**
+ * This class wraps the EncodingReader which act like a rewinding input stream,
+ * it tries to read the first 1K of data to detect the encoding, but save
+ * this data in a buffer for the subsequent read. Unfortunately, the EncodingReader
+ * will behave as expected only if encoding was detected, otherwise, the read data
+ * won't be stored and EncodingReader will fallback to read directory from the io stream.
+ * this is kind of lame, since we need to have similar logic in both layers. The alternative
+ * is to implement the encoding detection similar to the way C-Nokogiri does it; it starts
+ * parsing assuming encoding is unknown and if encoding is detected it will throw an exception
+ * causing parsing to stop, in which case we have to intercept the exception and set the encoding.
+ * Also in this case we don't have to restart the parsing since html/document.rb does that for us.
+ *
+ * @author John Shahid <jvshahid@gmail.com>
+ *
+ */
+public class NokogiriEncodingReaderWrapper extends InputStream {
+  private final ThreadContext context;
+  private final IRubyObject   encodingReader;
+  private final Ruby          ruby;
+  private IRubyObject         detectedEncoding;
+  private final byte[]        firstChunk       = new byte[1024];
+  private int                 firstChunkOff    = 0;
+  private int                 firstChunkLength = 0;
+
+  public NokogiriEncodingReaderWrapper(ThreadContext context, RubyObject encodingReader) {
+    this.context = context;
+    this.encodingReader = encodingReader;
+    this.ruby = context.getRuntime();
+
+    if (!RuntimeHelpers.invoke(context, encodingReader, "respond_to?", ruby.newSymbol("read")).isTrue()
+        || encodingReader.getInstanceVariable("@io") == null) {
+      throw ruby.newArgumentError("Argument doesn't respond to read or doesn't have instance variable @io");
+    }
+  }
+
+  public boolean detectEncoding() {
+    try {
+      firstChunkLength = read(firstChunk);
+    } catch (RaiseException e) {
+      detectedEncoding = e.getException().getInstanceVariable("@found_encoding");
+      return true;
+    }
+    detectedEncoding = context.nil;
+    return false;
+  }
+
+  public IRubyObject getEncoding() {
+    return detectedEncoding;
+  }
+
+  @Override
+  public int read(byte b[]) {
+    return read(b, 0, b.length);
+  }
+
+  @Override
+  public int read(byte b[], int off, int len) {
+    if (b == null) {
+      throw new NullPointerException();
+    } else if (off < 0 || len < 0 || len > b.length - off) {
+      throw new IndexOutOfBoundsException();
+    } else if (len == 0) {
+      return 0;
+    }
+
+    int copyLength = Math.min(firstChunkLength - firstChunkOff, len);
+    if (copyLength > 0) {
+      System.arraycopy(firstChunk, firstChunkOff, b, off, copyLength);
+      len -= copyLength;
+      firstChunkOff += copyLength;
+    }
+
+    if (len <= 0)
+      return copyLength;
+
+    IRubyObject returnValue = encodingReader.callMethod(context, "read", ruby.newFixnum(len));
+    if (returnValue.isNil())
+      return -1;
+
+    ByteList bytes = returnValue.asString().getByteList();
+    int length = bytes.length();
+    System.arraycopy(bytes.unsafeBytes(), bytes.getBegin(), b, off + copyLength, length);
+    return length + copyLength;
+  }
+
+  @Override
+  public int read() {
+    byte[] bytes = new byte[1];
+    int count = read(bytes, 0, 1);
+    if (count < 1)
+      return count;
+    return bytes[0];
+  }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriEntityResolver.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriEntityResolver.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriEntityResolver.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriEntityResolver.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,121 @@
+package nokogiri.internals;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+
+import nokogiri.internals.ParserContext.Options;
+
+import org.jruby.Ruby;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.EntityResolver2;
+
+/**
+ * An entity resolver aware of the fact that the Ruby runtime can
+ * change directory but the JVM cannot.  Thus any file based
+ * entity resolution that uses relative paths must be translated
+ * to be relative to the current directory of the Ruby runtime.
+ */
+public class NokogiriEntityResolver implements EntityResolver2 {
+    protected final Ruby runtime;
+    private final NokogiriErrorHandler handler;
+    private final Options options;
+
+    public NokogiriEntityResolver(Ruby runtime, NokogiriErrorHandler handler, Options options) {
+        super();
+        this.runtime = runtime;
+        this.handler = handler;
+        this.options = options;
+    }
+
+    @Override
+    public InputSource getExternalSubset(String name, String baseURI)
+        throws SAXException, IOException {
+        return null;
+    }
+
+    @Override
+    public InputSource resolveEntity(String publicId, String systemId)
+        throws SAXException, IOException {
+        return resolveEntity(runtime, null, publicId, null, systemId);
+    }
+
+    @Override
+    public InputSource resolveEntity(String name,
+                                     String publicId,
+                                     String baseURI,
+                                     String systemId)
+        throws SAXException, IOException {
+        return resolveEntity(runtime, name, publicId, baseURI, systemId);
+    }
+
+    private static File join(String parent, String child) {
+        if (new File(parent).isFile()) {
+            parent = new File(parent).getParent();
+        }
+        return new File(parent, child);
+    }
+
+    private static InputSource emptyInputSource(InputSource source) {
+        source.setByteStream(new ByteArrayInputStream(new byte[0]));
+        return source;
+    }
+
+    private boolean shouldLoadDtd() {
+      return options.dtdLoad || options.dtdValid;
+    }
+
+    private void addError(String errorMessage) {
+        if (handler != null) handler.errors.add(new Exception(errorMessage));
+    }
+
+    /**
+     * Create a file base input source taking into account the current
+     * directory of <code>runtime</code>.
+     * @throws SAXException
+     */
+    protected InputSource resolveEntity(Ruby runtime, String name, String publicId, String baseURI, String systemId)
+        throws IOException, SAXException {
+        InputSource s = new InputSource();
+        if (name.equals("[dtd]") && !shouldLoadDtd()) {
+          return emptyInputSource(s);
+        } else if (!name.equals("[dtd]") && !options.noEnt) {
+          return emptyInputSource(s);
+        }
+        String adjustedSystemId;
+        URI uri = URI.create(systemId);
+        if (options.noNet && uri.getHost() != null) {
+          addError("Attempt to load network entity " + systemId);
+          return emptyInputSource(s);
+        }
+        // if this is a url or absolute file name then use it
+        if (uri.isAbsolute() && !uri.isOpaque()) {
+          adjustedSystemId = uri.toURL().toString();
+        } else if (new File(uri.getPath()).isAbsolute()) {
+          adjustedSystemId = uri.getPath();
+        } else if (baseURI != null) {
+          URI baseuri = URI.create(baseURI);
+          if (options.noNet && baseuri.getHost() != null) {
+            addError("Attempt to load network entity " + systemId);
+            return emptyInputSource(s);
+          }
+          if (baseuri.getHost() == null) {
+            // this is a local file
+            adjustedSystemId = join(baseuri.getPath(), uri.getPath()).getCanonicalPath();
+          } else {
+            // this is a url, then resolve uri using baseuri
+            adjustedSystemId = baseuri.resolve(systemId).toURL().toString();
+          }
+        } else {
+          // baseURI is null we have to use the current working directory to resolve the entity
+          String pwd = runtime.getCurrentDirectory();
+          adjustedSystemId = join(pwd, uri.getPath()).getCanonicalPath();
+        }
+        s.setSystemId(adjustedSystemId);
+        s.setPublicId(publicId);
+        return s;
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriErrorHandler.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriErrorHandler.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriErrorHandler.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriErrorHandler.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,69 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.xerces.xni.parser.XMLErrorHandler;
+import org.xml.sax.ErrorHandler;
+
+/**
+ * Super class of error handlers.
+ * 
+ * XMLErrorHandler is used by nokogiri.internals.HtmlDomParserContext since NekoHtml
+ * uses this type of the error handler.
+ * 
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public abstract class NokogiriErrorHandler implements ErrorHandler, XMLErrorHandler {
+    protected final List<Exception> errors;
+    protected boolean noerror;
+    protected boolean nowarning;
+
+    public NokogiriErrorHandler(boolean noerror, boolean nowarning) {
+        this.errors = new ArrayList<Exception>(4);
+        this.noerror = noerror;
+        this.nowarning = nowarning;
+    }
+
+    List<Exception> getErrors() { return errors; }
+
+    public void addError(Exception ex) { errors.add(ex); }
+
+    protected boolean usesNekoHtml(String domain) {
+        return "http://cyberneko.org/html".equals(domain);
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriHandler.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriHandler.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriHandler.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriHandler.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,330 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import static nokogiri.internals.NokogiriHelpers.getLocalPart;
+import static nokogiri.internals.NokogiriHelpers.getPrefix;
+import static nokogiri.internals.NokogiriHelpers.isNamespace;
+import static nokogiri.internals.NokogiriHelpers.stringOrNil;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Set;
+
+import nokogiri.XmlSyntaxError;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyObject;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.javasupport.util.RuntimeHelpers;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.ext.DefaultHandler2;
+
+/**
+ * A handler for SAX parsing.
+ *
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class NokogiriHandler extends DefaultHandler2 implements XmlDeclHandler {
+
+    StringBuilder charactersBuilder;
+    private final Ruby runtime;
+    private final RubyClass attrClass;
+    private final IRubyObject object;
+
+    /**
+     * Stores parse errors with the most-recent error last.
+     *
+     * TODO: should these be stored in the document 'errors' array?
+     * Currently only string messages are stored there.
+     */
+    private final LinkedList<RaiseException> errors = new LinkedList<RaiseException>();
+
+    private Locator locator;
+    private boolean needEmptyAttrCheck;
+
+    public NokogiriHandler(Ruby runtime, IRubyObject object) {
+        assert object != null;
+        this.runtime = runtime;
+        this.attrClass = (RubyClass) runtime.getClassFromPath("Nokogiri::XML::SAX::Parser::Attribute");
+        this.object = object;
+        charactersBuilder = new StringBuilder();
+        String objectName = object.getMetaClass().getName();
+        if ("Nokogiri::HTML::SAX::Parser".equals(objectName)) needEmptyAttrCheck = true;
+    }
+
+    @Override
+    public void skippedEntity(String skippedEntity) {
+        call("error", runtime.newString("Entity '" + skippedEntity + "' not defined\n"));
+    }
+
+    @Override
+    public void setDocumentLocator(Locator locator) {
+        this.locator = locator;
+    }
+
+    @Override
+    public void startDocument() {
+        call("start_document");
+    }
+
+    @Override
+    public void xmlDecl(String version, String encoding, String standalone) {
+        call("xmldecl", stringOrNil(runtime, version), stringOrNil(runtime, encoding), stringOrNil(runtime, standalone));
+    }
+
+    @Override
+    public void endDocument() {
+        populateCharacters();
+        call("end_document");
+    }
+
+    @Override
+    public void processingInstruction(String target, String data) {
+        call("processing_instruction", runtime.newString(target), runtime.newString(data));
+    }
+
+    /*
+     * This calls "start_element_namespace".
+     *
+     * Attributes that define namespaces are passed in a separate
+     * array of <code>[:prefix, :uri]</code> arrays and are not
+     * passed with the other attributes.
+     */
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
+        final Ruby runtime = this.runtime;
+        final ThreadContext context = runtime.getCurrentContext();
+
+        // for attributes other than namespace attrs
+        RubyArray rubyAttr = RubyArray.newArray(runtime);
+        // for namespace defining attributes
+        RubyArray rubyNSAttr = RubyArray.newArray(runtime);
+
+        boolean fromFragmentHandler = false; // isFromFragmentHandler();
+
+        for (int i = 0; i < attrs.getLength(); i++) {
+            String u = attrs.getURI(i);
+            String qn = attrs.getQName(i);
+            String ln = attrs.getLocalName(i);
+            String val = attrs.getValue(i);
+            String pre;
+
+            pre = getPrefix(qn);
+            if (ln == null || ln.isEmpty()) ln = getLocalPart(qn);
+
+            if (isNamespace(qn) && !fromFragmentHandler) {
+                // I haven't figured the reason out yet, but, in somewhere,
+                // namespace is converted to array in array and cause
+                // TypeError at line 45 in fragment_handler.rb
+                if (ln.equals("xmlns")) ln = null;
+                rubyNSAttr.append( runtime.newArray( stringOrNil(runtime, ln), runtime.newString(val) ) );
+            } else {
+                IRubyObject[] args = null;
+                if (needEmptyAttrCheck) {
+                    if (isEmptyAttr(ln)) {
+                        args = new IRubyObject[] {
+                            stringOrNil(runtime, ln),
+                            stringOrNil(runtime, pre),
+                            stringOrNil(runtime, u)
+                        };
+                    }
+                }
+                if (args == null) {
+                    args = new IRubyObject[] {
+                        stringOrNil(runtime, ln),
+                        stringOrNil(runtime, pre),
+                        stringOrNil(runtime, u),
+                        stringOrNil(runtime, val)
+                    };
+                }
+
+                rubyAttr.append( RuntimeHelpers.invoke(context, attrClass, "new", args) );
+            }
+        }
+
+        if (localName == null || localName.isEmpty()) localName = getLocalPart(qName);
+        populateCharacters();
+        call("start_element_namespace",
+             stringOrNil(runtime, localName),
+             rubyAttr,
+             stringOrNil(runtime, getPrefix(qName)),
+             stringOrNil(runtime, uri),
+             rubyNSAttr);
+    }
+
+    static final Set<String> EMPTY_ATTRS;
+    static {
+        final String[] emptyAttrs = {
+            "checked", "compact", "declare", "defer", "disabled", "ismap", "multiple",
+            "noresize", "nohref", "noshade", "nowrap", "readonly", "selected"
+        };
+        EMPTY_ATTRS = new HashSet<String>(Arrays.asList(emptyAttrs));
+    }
+    
+    private static boolean isEmptyAttr(String name) {
+        return EMPTY_ATTRS.contains(name);
+    }
+    
+    public final Integer getLine() { // -1 if none is available
+        final int line = locator.getLineNumber();
+        return line == -1 ? null : line;
+    }
+    
+    public final Integer getColumn() { // -1 if none is available
+        final int column = locator.getColumnNumber();
+        return column == -1 ? null : column - 1;
+    }
+
+    @Override
+    public void endElement(String uri, String localName, String qName) {
+        populateCharacters();
+        call("end_element_namespace",
+             stringOrNil(runtime, localName),
+             stringOrNil(runtime, getPrefix(qName)),
+             stringOrNil(runtime, uri));
+    }
+
+    @Override
+    public void characters(char[] ch, int start, int length) {
+        charactersBuilder.append(ch, start, length);
+    }
+
+    @Override
+    public void comment(char[] ch, int start, int length) {
+        populateCharacters();
+        call("comment", runtime.newString(new String(ch, start, length)));
+    }
+
+    @Override
+    public void startCDATA() {
+        populateCharacters();
+    }
+
+    @Override
+    public void endCDATA() {
+        call("cdata_block", runtime.newString(charactersBuilder.toString()));
+        charactersBuilder.setLength(0);
+    }
+
+    @Override
+    public void error(SAXParseException ex) {
+        try {
+            final String msg = ex.getMessage();
+            call("error", runtime.newString(msg == null ? "" : msg));
+            addError(new RaiseException(XmlSyntaxError.createError(runtime, ex), true));
+        } catch(RaiseException e) {
+            addError(e);
+        }
+    }
+
+    @Override
+    public void fatalError(SAXParseException ex) {
+        try {
+            final String msg = ex.getMessage();
+            call("error", runtime.newString(msg == null ? "" : msg));
+            addError(new RaiseException(XmlSyntaxError.createFatalError(runtime, ex), true));
+
+        } catch(RaiseException e) {
+            addError(e);
+        }
+    }
+
+    @Override
+    public void warning(SAXParseException ex) {
+        final String msg = ex.getMessage();
+        call("warning", runtime.newString(msg == null ? "" : msg));
+    }
+
+    protected synchronized void addError(RaiseException e) {
+        errors.add(e);
+    }
+
+    public synchronized int getErrorCount() {
+        return errors.size();
+    }
+
+    public synchronized RaiseException getLastError() {
+        return errors.getLast();
+    }
+
+    private void call(String methodName) {
+        ThreadContext context = runtime.getCurrentContext();
+        RuntimeHelpers.invoke(context, document(context), methodName);
+    }
+
+    private void call(String methodName, IRubyObject argument) {
+        ThreadContext context = runtime.getCurrentContext();
+        RuntimeHelpers.invoke(context, document(context), methodName, argument);
+    }
+
+    private void call(String methodName, IRubyObject arg1, IRubyObject arg2) {
+        ThreadContext context = runtime.getCurrentContext();
+        RuntimeHelpers.invoke(context, document(context), methodName, arg1, arg2);
+    }
+
+    private void call(String methodName, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3) {
+        ThreadContext context = runtime.getCurrentContext();
+        RuntimeHelpers.invoke(context, document(context), methodName, arg1, arg2, arg3);
+    }
+
+    private void call(String methodName,
+                      IRubyObject arg0,
+                      IRubyObject arg1,
+                      IRubyObject arg2,
+                      IRubyObject arg3,
+                      IRubyObject arg4) {
+        ThreadContext context = runtime.getCurrentContext();
+        RuntimeHelpers.invoke(context, document(context), methodName, arg0, arg1, arg2, arg3, arg4);
+    }
+
+    private IRubyObject document(ThreadContext context) {
+        return object.getInstanceVariables().getInstanceVariable("@document");
+    }
+
+    protected void populateCharacters() {
+        if (charactersBuilder.length() > 0) {
+            call("characters", runtime.newString(charactersBuilder.toString()));
+            charactersBuilder.setLength(0);
+        }
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriHelpers.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriHelpers.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriHelpers.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriHelpers.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,798 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2014:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyString;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+import org.w3c.dom.Attr;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import nokogiri.HtmlDocument;
+import nokogiri.NokogiriService;
+import nokogiri.XmlAttr;
+import nokogiri.XmlCdata;
+import nokogiri.XmlComment;
+import nokogiri.XmlDocument;
+import nokogiri.XmlDtd;
+import nokogiri.XmlElement;
+import nokogiri.XmlEntityReference;
+import nokogiri.XmlNamespace;
+import nokogiri.XmlNode;
+import nokogiri.XmlProcessingInstruction;
+import nokogiri.XmlText;
+import nokogiri.XmlXpathContext;
+
+/**
+ * A class for various utility methods.
+ * 
+ * @author serabe
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class NokogiriHelpers {
+    public static final String CACHED_NODE = "NOKOGIRI_CACHED_NODE";
+    public static final String VALID_ROOT_NODE = "NOKOGIRI_VALIDE_ROOT_NODE";
+    public static final String ENCODED_STRING = "NOKOGIRI_ENCODED_STRING";
+
+    public static XmlNode getCachedNode(Node node) {
+        return (XmlNode) node.getUserData(CACHED_NODE);
+    }
+
+    public static void clearCachedNode(Node node) {
+        node.setUserData(CACHED_NODE, null, null);
+    }
+
+    public static void clearXpathContext(Node node) {
+        if (node == null) return;
+
+        Node ownerDocument = node.getOwnerDocument();
+        if (ownerDocument == null) {
+            ownerDocument = node;
+        }
+        ownerDocument.setUserData(XmlXpathContext.XPATH_CONTEXT, null, null);
+    }
+
+    /**
+     * Get the XmlNode associated with the underlying
+     * <code>node</code>. Creates a new XmlNode (or appropriate subclass)
+     * or XmlNamespace wrapping <code>node</code> if there is no cached
+     * value.
+     */
+    public static IRubyObject getCachedNodeOrCreate(Ruby ruby, Node node) {
+        if(node == null) return ruby.getNil();
+        if (node.getNodeType() == Node.ATTRIBUTE_NODE && isNamespace(node.getNodeName())) {
+            XmlDocument xmlDocument = (XmlDocument)node.getOwnerDocument().getUserData(CACHED_NODE);
+            if (!(xmlDocument instanceof HtmlDocument)) {
+                String prefix = getLocalNameForNamespace(((Attr)node).getName());
+                prefix = prefix != null ? prefix : "";
+                String href = ((Attr)node).getValue();
+                XmlNamespace xmlNamespace = xmlDocument.getNamespaceCache().get(prefix, href);
+                if (xmlNamespace != null) return xmlNamespace;
+                else return XmlNamespace.createFromAttr(ruby, (Attr)node);
+            }
+        }
+        XmlNode xmlNode = getCachedNode(node);
+        if(xmlNode == null) {
+            xmlNode = (XmlNode)constructNode(ruby, node);
+            node.setUserData(CACHED_NODE, xmlNode, null);
+        }
+        return xmlNode;
+    }
+
+    /**
+     * Construct a new XmlNode wrapping <code>node</code>.  The proper
+     * subclass of XmlNode is chosen based on the type of
+     * <code>node</code>.
+     */
+    public static IRubyObject constructNode(Ruby runtime, Node node) {
+        if (node == null) return runtime.getNil();
+        // this is slow; need a way to cache nokogiri classes/modules somewhere
+        switch (node.getNodeType()) {
+            case Node.ELEMENT_NODE:
+                XmlElement xmlElement = (XmlElement) NokogiriService.XML_ELEMENT_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Element"));
+                xmlElement.setNode(runtime.getCurrentContext(), node);
+                return xmlElement;
+            case Node.ATTRIBUTE_NODE:
+                XmlAttr xmlAttr = (XmlAttr) NokogiriService.XML_ATTR_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Attr"));
+                xmlAttr.setNode(runtime.getCurrentContext(), node);
+                return xmlAttr;
+            case Node.TEXT_NODE:
+                XmlText xmlText = (XmlText) NokogiriService.XML_TEXT_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Text"));
+                xmlText.setNode(runtime.getCurrentContext(), node);
+                return xmlText;
+            case Node.COMMENT_NODE:
+                XmlComment xmlComment = (XmlComment) NokogiriService.XML_COMMENT_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Comment"));
+                xmlComment.setNode(runtime.getCurrentContext(), node);
+                return xmlComment;
+            case Node.ENTITY_NODE:
+                return new XmlNode(runtime, getNokogiriClass(runtime, "Nokogiri::XML::EntityDecl"), node);
+            case Node.ENTITY_REFERENCE_NODE:
+                XmlEntityReference xmlEntityRef = (XmlEntityReference) NokogiriService.XML_ENTITY_REFERENCE_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::EntityReference"));
+                xmlEntityRef.setNode(runtime.getCurrentContext(), node);
+                return xmlEntityRef;
+            case Node.PROCESSING_INSTRUCTION_NODE:
+                XmlProcessingInstruction xmlProcessingInstruction = (XmlProcessingInstruction) NokogiriService.XML_PROCESSING_INSTRUCTION_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::ProcessingInstruction"));
+                xmlProcessingInstruction.setNode(runtime.getCurrentContext(), node);
+                return xmlProcessingInstruction;
+            case Node.CDATA_SECTION_NODE:
+                XmlCdata xmlCdata = (XmlCdata) NokogiriService.XML_CDATA_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::CDATA"));
+                xmlCdata.setNode(runtime.getCurrentContext(), node);
+                return xmlCdata;
+            case Node.DOCUMENT_NODE:
+                XmlDocument xmlDocument = (XmlDocument) NokogiriService.XML_DOCUMENT_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Document"));
+                xmlDocument.setDocumentNode(runtime.getCurrentContext(), node);
+                return xmlDocument;
+            case Node.DOCUMENT_TYPE_NODE:
+                XmlDtd xmlDtd = (XmlDtd) NokogiriService.XML_DTD_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::DTD"));
+                xmlDtd.setNode(runtime, node);
+                return xmlDtd;
+            default:
+                XmlNode xmlNode = (XmlNode) NokogiriService.XML_NODE_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Node"));
+                xmlNode.setNode(runtime.getCurrentContext(), node);
+                return xmlNode;
+        }
+    }
+    
+    public static RubyClass getNokogiriClass(Ruby ruby, String name) {
+        return NokogiriService.getNokogiriClassCache(ruby).get(name);
+    }
+
+    public static IRubyObject stringOrNil(Ruby runtime, String str) {
+        return str == null ? runtime.getNil() : convertString(runtime, str);
+    }
+
+    public static IRubyObject stringOrNil(Ruby runtime, CharSequence str) {
+        return str == null ? runtime.getNil() : convertString(runtime, str);
+    }
+
+    public static IRubyObject stringOrNil(Ruby runtime, byte[] bytes) {
+        return bytes == null ? runtime.getNil() : RubyString.newString(runtime, bytes);
+    }
+
+    public static IRubyObject stringOrBlank(Ruby runtime, String str) {
+        return str == null ? runtime.newString() : convertString(runtime, str);
+    }
+    
+    public static RubyString convertString(Ruby runtime, String str) {
+        return RubyString.newUTF8String(runtime, str);
+    }
+
+    public static RubyString convertString(Ruby runtime, CharSequence str) {
+        return RubyString.newUTF8String(runtime, str);
+    }
+
+    /**
+     * Convert <code>s</code> to a RubyString, or if s is null or
+     * empty return RubyNil.
+     */
+    public static IRubyObject nonEmptyStringOrNil(Ruby runtime, String s) {
+        if (s == null || s.length() == 0) return runtime.getNil();
+        return RubyString.newString(runtime, s);
+    }
+
+    /**
+     * Return the prefix of a qualified name like "prefix:local".
+     * Returns null if there is no prefix.
+     */
+    public static String getPrefix(String qName) {
+        if (qName == null) return null;
+
+        final int pos = qName.indexOf(':');
+        return pos > 0 ? qName.substring(0, pos) : null;
+    }
+
+    /**
+     * Return the local part of a qualified name like "prefix:local".
+     * Returns <code>qName</code> if there is no prefix.
+     */
+    public static String getLocalPart(String qName) {
+        if (qName == null) return null;
+
+        final int pos = qName.indexOf(':');
+        return pos > 0 ? qName.substring(pos + 1) : qName;
+    }
+
+    public static String getLocalNameForNamespace(String name) {
+        String localName = getLocalPart(name);
+        return ("xmlns".equals(localName)) ? null : localName;
+    }
+
+    private static final Charset UTF8 = Charset.forName("UTF-8");
+
+    /**
+     * Converts a RubyString in to a Java String.  Assumes the
+     * RubyString is encoded as UTF-8.  This is generally the case for
+     * RubyStrings created with getRuntime().newString("java string").
+     * It also seems to be the case for strings created within Ruby
+     * where $KCODE has not been set.
+     *
+     * Note that RubyString#toString() decodes the string data as
+     * ISO-8859-1 (See org.jruby.util.ByteList.java).  This is not
+     * what you want if you have any multibyte characters in your
+     * UTF-8 string.
+     *
+     * FIXME: This really needs to be more robust in terms of
+     * detecting the encoding and properly converting to a Java
+     * String.  It's unfortunate that RubyString#toString() doesn't do
+     * this for us.
+     */
+    public static String rubyStringToString(IRubyObject str) {
+        if (str.isNil()) return null;
+        //return rubyStringToString(str.convertToString());
+        return toJavaString(str.convertToString());
+    }
+    
+    private static String toJavaString(RubyString str) {
+        return str.decodeString(); // toString()
+    }
+
+    public static String rubyStringToString(RubyString str) {
+        ByteList byteList = str.getByteList();
+        byte[] data = byteList.unsafeBytes();
+        int offset = byteList.begin();
+        int len = byteList.length();
+        ByteBuffer buf = ByteBuffer.wrap(data, offset, len);
+        return UTF8.decode(buf).toString();
+    }
+
+    public static ByteArrayInputStream stringBytesToStream(final IRubyObject str) {
+        if (str instanceof RubyString || str.respondsTo("to_str")) {
+            final ByteList bytes = str.convertToString().getByteList();
+            return new ByteArrayInputStream(bytes.unsafeBytes(), bytes.begin(), bytes.length());
+        }
+        return null;
+    }
+
+    public static String getNodeCompletePath(Node node) {
+
+        Node cur, tmp, next;
+
+        // TODO: Rename buffer to path.
+        String buffer = "";
+
+        cur = node;
+
+        do {
+            String name = "";
+            String sep = "?";
+            int occur = 0;
+            boolean generic = false;
+
+            if(cur.getNodeType() == Node.DOCUMENT_NODE) {
+                if(buffer.startsWith("/")) break;
+
+                sep = "/";
+                next = null;
+            } else if(cur.getNodeType() == Node.ELEMENT_NODE) {
+                generic = false;
+                sep = "/";
+
+                name = cur.getLocalName();
+                if (name == null) name = cur.getNodeName();
+                if(cur.getNamespaceURI() != null) {
+                    if(cur.getPrefix() != null) {
+                        name = cur.getPrefix() + ":" + name;
+                    } else {
+                        generic = true;
+                        name = "*";
+                    }
+                }
+
+                next = cur.getParentNode();
+
+                /*
+                 * Thumbler index computation
+                 */
+
+                tmp = cur.getPreviousSibling();
+
+                while(tmp != null) {
+                    if((tmp.getNodeType() == Node.ELEMENT_NODE) &&
+                       (generic || fullNamesMatch(tmp, cur))) {
+                        occur++;
+                    }
+                    tmp = tmp.getPreviousSibling();
+                }
+
+                if(occur == 0) {
+                    tmp = cur.getNextSibling();
+
+                    while(tmp != null && occur == 0) {
+                        if((tmp.getNodeType() == Node.ELEMENT_NODE) &&
+                            (generic || fullNamesMatch(tmp,cur))) {
+                            occur++;
+                        }
+                        tmp = tmp.getNextSibling();
+                    }
+
+                    if(occur != 0) occur = 1;
+
+                } else {
+                    occur++;
+                }
+            } else if(cur.getNodeType() == Node.COMMENT_NODE) {
+                sep = "/";
+                name = "comment()";
+                next = cur.getParentNode();
+
+                /*
+                 * Thumbler index computation.
+                 */
+
+                tmp = cur.getPreviousSibling();
+
+                while(tmp != null) {
+                    if(tmp.getNodeType() == Node.COMMENT_NODE) {
+                        occur++;
+                    }
+                    tmp = tmp.getPreviousSibling();
+                }
+
+                if(occur == 0) {
+                    tmp = cur.getNextSibling();
+                    while(tmp != null && occur == 0) {
+                        if(tmp.getNodeType() == Node.COMMENT_NODE) {
+                            occur++;
+                        }
+                        tmp = tmp.getNextSibling();
+                    }
+                    if(occur != 0) occur = 1;
+                } else {
+                    occur = 1;
+                }
+
+            } else if(cur.getNodeType() == Node.TEXT_NODE ||
+                cur.getNodeType() == Node.CDATA_SECTION_NODE) {
+                    // I'm here. gist:129
+                    // http://gist.github.com/144923
+
+                sep = "/";
+                name = "text()";
+                next = cur.getParentNode();
+
+                /*
+                 * Thumbler index computation.
+                 */
+
+                tmp = cur.getPreviousSibling();
+                while(tmp != null) {
+                    if(tmp.getNodeType() == Node.TEXT_NODE ||
+                            tmp.getNodeType() == Node.CDATA_SECTION_NODE) {
+                        occur++;
+                    }
+                    tmp = tmp.getPreviousSibling();
+                }
+
+                if(occur == 0) {
+                    tmp = cur.getNextSibling();
+
+                    while(tmp != null && occur == 0) {
+                        if(tmp.getNodeType() == Node.TEXT_NODE ||
+                                tmp.getNodeType() == Node.CDATA_SECTION_NODE) {
+                            occur++;
+                        }
+                        tmp = tmp.getNextSibling();
+                    }
+                } else {
+                    occur++;
+                }
+
+            } else if(cur.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
+                sep = "/";
+                name = "processing-instruction('"+cur.getLocalName()+"')";
+                next = cur.getParentNode();
+
+                /*
+                 * Thumbler index computation.
+                 */
+
+                tmp = cur.getParentNode();
+
+                while(tmp != null) {
+                    if(tmp.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE &&
+                            tmp.getLocalName().equals(cur.getLocalName())) {
+                        occur++;
+                    }
+                    tmp = tmp.getPreviousSibling();
+                }
+
+                if(occur == 0) {
+                    tmp = cur.getNextSibling();
+
+                    while(tmp != null && occur == 0) {
+                        if(tmp.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE &&
+                                tmp.getLocalName().equals(cur.getLocalName())){
+                            occur++;
+                        }
+                        tmp = tmp.getNextSibling();
+                    }
+
+                    if(occur != 0) {
+                        occur = 1;
+                    }
+
+                } else {
+                    occur++;
+                }
+
+            } else if(cur.getNodeType() == Node.ATTRIBUTE_NODE) {
+                sep = "/@";
+                name = cur.getLocalName();
+
+                if(cur.getNamespaceURI() != null) {
+                    if(cur.getPrefix() != null) {
+                        name = cur.getPrefix() + ":" + name;
+                    }
+                }
+
+                next = ((Attr) cur).getOwnerElement();
+
+            } else {
+                next = cur.getParentNode();
+            }
+
+            if(occur == 0){
+                buffer = sep+name+buffer;
+            } else {
+                buffer = sep+name+"["+occur+"]"+buffer;
+            }
+
+            cur = next;
+
+        } while(cur != null);
+
+        return buffer;
+    }
+
+    protected static boolean compareTwoNodes(Node m, Node n) {
+        return nodesAreEqual(m.getLocalName(), n.getLocalName()) &&
+               nodesAreEqual(m.getPrefix(), n.getPrefix());
+    }
+
+    protected static boolean fullNamesMatch(Node a, Node b) {
+        return a.getNodeName().equals(b.getNodeName());
+    }
+
+    protected static String getFullName(Node n) {
+        String lname = n.getLocalName();
+        String prefix = n.getPrefix();
+        if (lname != null) {
+            if (prefix != null)
+                return prefix + ":" + lname;
+            else
+                return lname;
+        } else {
+            return n.getNodeName();
+        }
+    }
+
+    private static boolean nodesAreEqual(Object a, Object b) {
+        return (((a == null) && (b == null)) ||
+                ((a != null) && (b != null) && (b.equals(a))));
+    }
+
+    private static final Pattern encoded_pattern = Pattern.compile("&amp;|&gt;|&lt;|&#13;");
+    private static final String[] encoded = {"&amp;", "&gt;", "&lt;", "&#13;"};
+    private static final Pattern decoded_pattern = Pattern.compile("&|>|<|\r");
+    private static final String[] decoded = {"&", ">", "<", "\r"};
+
+    private static StringBuffer convert(Pattern ptn, CharSequence input, String[] oldChars, String[] newChars)  {
+        Matcher matcher = ptn.matcher(input);
+        boolean result = matcher.find();
+        StringBuffer sb = new StringBuffer(input.length() + 8);
+        while (result) {
+            String matched = matcher.group();
+            String replacement = "";
+            for (int i=0; i<oldChars.length; i++) {
+                if (matched.contains(oldChars[i])) {
+                    replacement = matched.replace(oldChars[i], newChars[i]);
+                    break;
+                }
+            }
+            matcher.appendReplacement(sb, replacement);
+            result = matcher.find();
+        }
+        matcher.appendTail(sb);
+        return sb;
+    }
+
+    public static CharSequence encodeJavaString(CharSequence str) {
+        return convert(decoded_pattern, str, decoded, encoded);
+    }
+
+    public static CharSequence decodeJavaString(CharSequence str) {
+        return convert(encoded_pattern, str, encoded, decoded);
+    }
+
+    public static String getNodeName(Node node) {
+        if(node == null) { System.out.println("node is null"); return ""; }
+        String name = node.getNodeName();
+        if(name == null) { System.out.println("name is null"); return ""; }
+        if(name.equals("#document")) {
+            return "document";
+        } else if(name.equals("#text")) {
+            return "text";
+        } else {
+            name = getLocalPart(name);
+            return (name == null) ? "" : name;
+        }
+    }
+
+    public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
+    public static boolean isNamespace(Node node) {
+        return (XMLNS_URI.equals(node.getNamespaceURI()) || isNamespace(node.getNodeName()));
+    }
+
+    public static boolean isNamespace(String nodeName) {
+        return (nodeName.startsWith("xmlns"));
+    }
+
+    public static boolean isNonDefaultNamespace(Node node) {
+        return (isNamespace(node) && ! "xmlns".equals(node.getNodeName()));
+    }
+
+    public static boolean isXmlBase(String attrName) {
+        return "xml:base".equals(attrName) || "xlink:href".equals(attrName);
+    }
+
+    public static boolean isBlank(IRubyObject obj) {
+        if ( !(obj instanceof XmlText) ) return false;
+
+        CharSequence content = ((XmlNode) obj).getContentImpl();
+        return content == null || isBlank(content);
+    }
+
+    public static boolean isBlank(CharSequence str) {
+        int len = str.length(); int beg = 0;
+        while ((beg < len) && (str.charAt(beg) <= ' ')) beg++;
+        return beg == len;
+    }
+
+    public static boolean isBlank(String str) {
+        return str.isEmpty() || isBlank((CharSequence) str);
+    }
+
+    public static CharSequence canonicalizeWhitespace(CharSequence str) {
+        final int len = str.length();
+        StringBuilder sb = new StringBuilder(len);
+        boolean newline_added = false;
+        for ( int i = 0; i < len; i++ ) {
+            char c = str.charAt(i);
+            if ( c == '\n' ) {
+                if ( ! newline_added ) {
+                    sb.append(c); newline_added = true;
+                }
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb;
+    }
+
+    public static String newQName(String newPrefix, Node node) {
+        String tagName = getLocalPart(node.getNodeName());
+        if (newPrefix == null) return tagName;
+        return newPrefix + ':' + tagName;
+    }
+
+    public static IRubyObject[] nodeListToRubyArray(Ruby ruby, NodeList nodes) {
+        IRubyObject[] array = new IRubyObject[nodes.getLength()];
+        for (int i = 0; i < nodes.getLength(); i++) {
+          array[i] = NokogiriHelpers.getCachedNodeOrCreate(ruby, nodes.item(i));
+        }
+        return array;
+    }
+
+    public static IRubyObject[] nodeArrayToArray(Ruby ruby, Node[] nodes) {
+        IRubyObject[] result = new IRubyObject[nodes.length];
+        for(int i = 0; i < nodes.length; i++) {
+            result[i] = NokogiriHelpers.getCachedNodeOrCreate(ruby, nodes[i]);
+        }
+        return result;
+    }
+
+    public static RubyArray nodeArrayToRubyArray(Ruby ruby, Node[] nodes) {
+        RubyArray n = RubyArray.newArray(ruby, nodes.length);
+        for(int i = 0; i < nodes.length; i++) {
+            n.append(NokogiriHelpers.getCachedNodeOrCreate(ruby, nodes[i]));
+        }
+        return n;
+    }
+
+    public static RubyArray namedNodeMapToRubyArray(Ruby ruby, NamedNodeMap map) {
+        RubyArray n = RubyArray.newArray(ruby, map.getLength());
+        for(int i = 0; i < map.getLength(); i++) {
+            n.append(NokogiriHelpers.getCachedNodeOrCreate(ruby, map.item(i)));
+        }
+        return n;
+    }
+
+    public static String getValidEncoding(Ruby runtime, IRubyObject encoding) {
+        if (encoding.isNil()) {
+            return guessEncoding();
+        } else {
+            return ignoreInvalidEncoding(runtime, encoding);
+        }
+    }
+
+    private static String guessEncoding() {
+        String name = System.getProperty("file.encoding");
+        if (name == null) name = "UTF-8";
+        return name;
+    }
+
+    private static Set<String> charsetNames = Charset.availableCharsets().keySet();
+
+    private static String ignoreInvalidEncoding(Ruby runtime, IRubyObject encoding) {
+        String givenEncoding = rubyStringToString(encoding);
+        if (charsetNames.contains(givenEncoding)) return givenEncoding;
+        else return guessEncoding();
+    }
+
+    public static String adjustSystemIdIfNecessary(String currentDir, String scriptFileName, String baseURI, String systemId) {
+        if (systemId == null) return systemId;
+        File file = new File(systemId);
+        if (file.isAbsolute()) return systemId;
+        String path = resolveSystemId(baseURI, systemId);
+        if (path != null) return path;
+        path = resolveSystemId(currentDir, systemId);
+        if (path != null) return path;
+        return resolveSystemId(scriptFileName, systemId);
+    }
+
+    private static String resolveSystemId(String baseName, String systemId) {
+        if (baseName == null || baseName.length() < 1) return null;
+        String parentName;
+        baseName = baseName.replace("%20", " ");
+        File base = new File(baseName);
+        if (base.isDirectory()) parentName = baseName;
+        else parentName = base.getParent();
+        if (parentName == null) return null;
+        if (parentName.toLowerCase().startsWith("file:")) parentName = parentName.substring("file:".length());
+        File dtdFile = new File(parentName + "/" + systemId);
+        if (dtdFile.exists()) return dtdFile.getPath();
+        return null;
+    }
+
+    public static boolean isUTF8(String encoding) {
+        if (encoding == null) return true; // no need to convert encoding
+        return Charset.forName(encoding).compareTo(UTF8) == 0;
+    }
+
+    public static ByteBuffer convertEncoding(Charset output_charset, CharSequence input_string) {
+        return output_charset.encode(CharBuffer.wrap(input_string)); // does replace implicitly on un-mappable characters
+    }
+
+    public static CharSequence convertEncodingByNKFIfNecessary(ThreadContext context, XmlDocument doc, CharSequence str) {
+        if (!(doc instanceof HtmlDocument)) return str;
+        String parsed_encoding = ((HtmlDocument)doc).getPraedEncoding();
+        if (parsed_encoding == null) return str;
+        String ruby_encoding = rubyStringToString(doc.getEncoding());
+        if (ruby_encoding == null) return str;
+        Charset encoding = Charset.forName(ruby_encoding);
+        if (Charset.forName(parsed_encoding).compareTo(encoding) == 0) return str;
+        if (str.length() == 0) return str; // no need to convert
+        return NokogiriHelpers.nkf(context, encoding, str);
+    }
+
+    private static final ByteList _Sw = new ByteList(new byte[] { '-','S','w' }, false);
+    private static final ByteList _Jw = new ByteList(new byte[] { '-','J','w' }, false);
+    private static final ByteList _Ew = new ByteList(new byte[] { '-','E','w' }, false);
+    private static final ByteList _Ww = new ByteList(new byte[] { '-','W','w' }, false);
+
+    // This method is used from HTML documents. HTML meta tag with encoding specification
+    // might appear after non-ascii characters are used. For example, a title tag before
+    // a meta tag. In such a case, Xerces encodes characters in UTF-8 without seeing meta tag.
+    // Nokogiri uses NKF library to convert characters correct encoding. This means the method
+    // works only for JIS/Shift_JIS/EUC-JP.
+    private static CharSequence nkf(ThreadContext context, Charset encoding, CharSequence str) {
+        final Ruby runtime = context.getRuntime();
+        final ByteList opt;
+        if (NokogiriHelpers.shift_jis.compareTo(encoding) == 0) opt = _Sw;
+        else if (NokogiriHelpers.jis.compareTo(encoding) == 0) opt = _Jw;
+        else if (NokogiriHelpers.euc_jp.compareTo(encoding) == 0) opt = _Ew;
+        else opt = _Ww; // should not come here. should be treated before this method.
+
+        Class nkfClass;
+        try {
+            nkfClass = runtime.getClassLoader().loadClass("org.jruby.RubyNKF");
+        } catch (ClassNotFoundException e2) {
+            return str;
+        }
+        Method nkf_method;
+        try {
+            nkf_method = nkfClass.getMethod("nkf", ThreadContext.class, IRubyObject.class, IRubyObject.class, IRubyObject.class);
+            RubyString r_str = 
+                (RubyString)nkf_method.invoke(null, context, null, runtime.newString(opt), runtime.newString(str.toString()));
+            return NokogiriHelpers.rubyStringToString(r_str);
+        } catch (SecurityException e) {
+            return str;
+        } catch (NoSuchMethodException e) {
+            return str;
+        } catch (IllegalArgumentException e) {
+            return str;
+        } catch (IllegalAccessException e) {
+            return str;
+        } catch (InvocationTargetException e) {
+            return str;
+        }
+    }
+
+    private static final Charset shift_jis = Charset.forName("Shift_JIS");
+    private static final Charset jis = Charset.forName("ISO-2022-JP");
+    private static final Charset euc_jp = Charset.forName("EUC-JP");
+
+    public static boolean shouldEncode(Node text) {
+        final Boolean encoded = (Boolean) text.getUserData(NokogiriHelpers.ENCODED_STRING);
+        return encoded == null || ! encoded;
+    }
+
+    public static boolean shouldDecode(Node text) {
+      return !shouldEncode(text);
+    }
+
+    public static NokogiriNamespaceCache getNamespaceCacheFormNode(Node n) {
+        XmlDocument xmlDoc = (XmlDocument)getCachedNode(n.getOwnerDocument());
+        return xmlDoc.getNamespaceCache();
+    }
+
+    public static Node renameNode(Node n, String namespaceURI, String qualifiedName) throws DOMException {
+        Document doc = n.getOwnerDocument();
+        NokogiriNamespaceCache nsCache = getNamespaceCacheFormNode(n);
+        Node result = doc.renameNode(n, namespaceURI, qualifiedName);
+        if (result != n) {
+            nsCache.replaceNode(n, result);
+        }
+        return result;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriNamespaceCache.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriNamespaceCache.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriNamespaceCache.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriNamespaceCache.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,185 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2014:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import static nokogiri.internals.NokogiriHelpers.isNamespace;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import nokogiri.XmlNamespace;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * Cache of namespages of each node. XmlDocument has one cache of this class.
+ * 
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class NokogiriNamespaceCache {
+
+    private List<String[]> keys;
+    private Map<String[], CacheEntry> cache;  // pair of the index of a given key and entry
+    private XmlNamespace defaultNamespace = null;
+
+    public NokogiriNamespaceCache() {
+        keys = new ArrayList<String[]>(); // keys are [prefix, href]
+        cache = new LinkedHashMap<String[], CacheEntry>();
+    }
+
+    public XmlNamespace getDefault() {
+        return defaultNamespace;
+    }
+
+    private String[] getKey(String prefix, String href) {
+        for (String[] key : keys) {
+            if (key[0].equals(prefix) && key[1].equals(href)) return key;
+        }
+        return null;
+    }
+
+    public XmlNamespace get(String prefix, String href) {
+        // prefix should not be null.
+        // In case of a default namespace, an empty string should be given to prefix argument.
+        if (prefix == null || href == null) return null;
+        String[] key = getKey(prefix, href);
+        if (key != null) {
+            return cache.get(key).namespace;
+        }
+        return null;
+    }
+
+    public XmlNamespace get(Node node, String prefix) {
+        if (prefix == null) return defaultNamespace;
+        for (String[] key : keys) {
+            if (key[0].equals(prefix) && cache.get(key) != null && cache.get(key).isOwner(node)) {
+                return cache.get(key).namespace;
+            }
+        }
+        return null;
+    }
+
+    public List<XmlNamespace> get(String prefix) {
+        List<XmlNamespace> namespaces = new ArrayList<XmlNamespace>();
+        if (prefix == null) {
+            namespaces.add(defaultNamespace);
+            return namespaces;
+        }
+        for (String[] key : keys) {
+            if (key[0].equals(prefix) && cache.get(key) != null) {
+                namespaces.add(cache.get(key).namespace);
+            }
+        }
+        return namespaces;
+    }
+
+    public List<XmlNamespace> get(Node node) {
+        List<XmlNamespace> namespaces = new ArrayList<XmlNamespace>();
+        for (String[] key : keys) {
+            CacheEntry entry = cache.get(key);
+            if (entry.isOwner(node)) {
+                namespaces.add(entry.namespace);
+            }
+        }
+        return namespaces;
+    }
+
+    public void put(XmlNamespace namespace, Node ownerNode) {
+        // prefix should not be null.
+        // In case of a default namespace, an empty string should be given to prefix argument.
+        String prefixString = namespace.getPrefix();
+        String hrefString = namespace.getHref();
+        if (getKey(prefixString, hrefString) != null) return;
+        String[] key = {prefixString, hrefString};
+        keys.add(key);
+        CacheEntry entry = new CacheEntry(namespace, ownerNode);
+        cache.put(key, entry);
+        if ("".equals(prefixString)) defaultNamespace = namespace;
+    }
+
+    public void remove(String prefix, String href) {
+        String[] key = getKey(prefix, href);
+        if (key == null) return;
+        keys.remove(key);
+        cache.remove(key);
+    }
+
+    public void clear() {
+        // removes namespace declarations from node
+        for (String[] key : cache.keySet()) {
+            CacheEntry entry = cache.get(key);
+            NamedNodeMap attributes = entry.ownerNode.getAttributes();
+            for (int j=0; j<attributes.getLength(); j++) {
+                String name = ((Attr)attributes.item(j)).getName();
+                if (isNamespace(name)) {
+                    attributes.removeNamedItem(name);
+                }
+            }
+        }
+        keys.clear();
+        cache.clear();
+        defaultNamespace = null;
+    }
+
+    public void replaceNode(Node oldNode, Node newNode) {
+        for (String[] key : keys) {
+            CacheEntry entry = cache.get(key);
+            if (entry.isOwner(oldNode)) {
+                entry.replaceOwner(newNode);
+            }
+        }
+    }
+
+    private class CacheEntry {
+        private XmlNamespace namespace;
+        private Node ownerNode;
+
+        CacheEntry(XmlNamespace namespace, Node ownerNode) {
+            this.namespace = namespace;
+            this.ownerNode = ownerNode;
+        }
+
+        public Boolean isOwner(Node n) {
+            return ownerNode.isSameNode(n);
+        }
+
+        public void replaceOwner(Node newNode) {
+            this.ownerNode = newNode;
+        }
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriNamespaceContext.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriNamespaceContext.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriNamespaceContext.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriNamespaceContext.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,120 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+
+/**
+ * Holder of each node's namespace.
+ * 
+ * @author Yoko Harada <yokolet@gmail.com>
+ *
+ */
+public final class NokogiriNamespaceContext implements NamespaceContext {
+
+	public static final String NOKOGIRI_PREFIX = "nokogiri";
+    public static final String NOKOGIRI_URI = "http://www.nokogiri.org/default_ns/ruby/extensions_functions";
+    public static final String NOKOGIRI_TEMPORARY_ROOT_TAG = "nokogiri-temporary-root-tag";
+    
+    private final Map<String,String> register;
+
+    public static NokogiriNamespaceContext create() {
+        return new NokogiriNamespaceContext();
+    }
+    
+    private NokogiriNamespaceContext() {
+        register = new HashMap<String, String>(6, 1);
+        register.put(NOKOGIRI_PREFIX, NOKOGIRI_URI);
+        register.put("xml", "http://www.w3.org/XML/1998/namespace");
+        register.put("xhtml", "http://www.w3.org/1999/xhtml");
+    }
+
+    public String getNamespaceURI(String prefix) {
+    	if (prefix == null) {
+            throw new IllegalArgumentException();
+        }
+        String uri = this.register.get(prefix);
+        if (uri != null) {
+            return uri;
+        }
+
+        if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) {
+            uri = this.register.get(XMLConstants.XMLNS_ATTRIBUTE);
+            return (uri == null) ? XMLConstants.XMLNS_ATTRIBUTE_NS_URI : uri;
+        }
+
+        return XMLConstants.NULL_NS_URI;
+    }
+
+    public String getPrefix(String uri) {
+    	if (uri == null) {
+            throw new IllegalArgumentException("uri is null");
+        }
+        Set<Entry<String, String>> entries = register.entrySet();
+        for (Entry<String, String> entry : entries) {
+            if (uri.equals(entry.getValue())) {
+                return entry.getKey();
+            }
+        }
+        return null;
+    }
+
+    public Iterator<String> getPrefixes(String uri) {
+        Set<Entry<String, String>> entries = register.entrySet();
+        ArrayList<String> list = new ArrayList<String>(entries.size());
+        for (Entry<String, String> entry : entries) {
+            if (uri.equals(entry.getValue())) {
+                list.add(entry.getKey());
+            }
+        }
+        return list.iterator();
+    }
+    
+    public Set<String> getAllPrefixes() {
+        return register.keySet();
+    }
+
+    public void registerNamespace(String prefix, String uri) {
+        if ("xmlns".equals(prefix)) prefix = "";
+        register.put(prefix, uri);
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler4NekoHtml.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler4NekoHtml.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler4NekoHtml.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler4NekoHtml.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,121 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import org.apache.xerces.xni.parser.XMLParseException;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Non-strict error handler for NekoHtml.
+ * 
+ * NekoHtml adds too many warnings, which makes later processing hard. For example,
+ * Nokogiri wants to know whether number of errors have been increased or not to judge 
+ * availability of creating NodeSet from a given fragment. When the fragment nodes 
+ * are to be created from HTML document, which means NekoHtml is used, always errors
+ * increases. As a result, even though the given fragment is correct HTML, NodeSet
+ * base on the given fragment won't be created. This is why all warnings are eliminated.
+ * 
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class NokogiriNonStrictErrorHandler4NekoHtml extends NokogiriErrorHandler {
+    
+    public NokogiriNonStrictErrorHandler4NekoHtml(boolean nowarning) {
+        super(false, nowarning);
+    }
+    
+    public NokogiriNonStrictErrorHandler4NekoHtml(boolean noerror, boolean nowarning) {
+        super(noerror, nowarning);
+    }
+
+    public void warning(SAXParseException ex) throws SAXException {
+        //noop. NekoHtml adds too many warnings.
+    }
+
+    public void error(SAXParseException ex) throws SAXException {
+        errors.add(ex);
+    }
+
+    public void fatalError(SAXParseException ex) throws SAXException {
+        errors.add(ex);
+    }
+
+    /**
+     * Implementation of org.apache.xerces.xni.parser.XMLErrorHandler. This method
+     * is invoked during parsing fired by HtmlDomParserContext and is a NekoHtml requirement.
+     * 
+     * @param domain The domain of the error. The domain can be any string but is 
+     *               suggested to be a valid URI. The domain can be used to conveniently
+     *               specify a web site location of the relevant specification or 
+     *               document pertaining to this warning.
+     * @param key The error key. This key can be any string and is implementation
+     *            dependent.
+     * @param e Exception.
+     */
+    public void error(String domain, String key, XMLParseException e) {
+        errors.add(e);
+    }
+
+    /**
+     * Implementation of org.apache.xerces.xni.parser.XMLErrorHandler. This method
+     * is invoked during parsing fired by HtmlDomParserContext and is a NekoHtml requirement.
+     * 
+     * @param domain The domain of the fatal error. The domain can be any string but is 
+     *               suggested to be a valid URI. The domain can be used to conveniently
+     *               specify a web site location of the relevant specification or 
+     *               document pertaining to this warning.
+     * @param key The fatal error key. This key can be any string and is implementation
+     *            dependent.
+     * @param e Exception.
+     */
+    public void fatalError(String domain, String key, XMLParseException e) {
+        errors.add(e);
+    }
+
+    /**
+     * Implementation of org.apache.xerces.xni.parser.XMLErrorHandler. This method
+     * is invoked during parsing fired by HtmlDomParserContext and is a NekoHtml requirement.
+     * 
+     * @param domain The domain of the warning. The domain can be any string but is 
+     *               suggested to be a valid URI. The domain can be used to conveniently
+     *               specify a web site location of the relevant specification or 
+     *               document pertaining to this warning.
+     * @param key The warning key. This key can be any string and is implementation
+     *            dependent.
+     * @param e Exception.
+     */
+    public void warning(String domain, String key, XMLParseException e) {
+        errors.add(e);
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,100 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import org.apache.xerces.xni.parser.XMLParseException;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Error Handler for XML document when recover is true (default).
+ * 
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class NokogiriNonStrictErrorHandler extends NokogiriErrorHandler{
+    public NokogiriNonStrictErrorHandler(boolean noerror, boolean nowarning) {
+        super(noerror, nowarning);
+    }
+
+    public void warning(SAXParseException ex) throws SAXException {
+        errors.add(ex);
+    }
+
+    public void error(SAXParseException ex) throws SAXException {
+        errors.add(ex);
+    }
+
+    public void fatalError(SAXParseException ex) throws SAXException {
+        // fix #837
+        // Xerces won't skip the reference entity (and other invalid) constructs
+        // found in the prolog, instead it will keep calling this method and we'll
+        // keep inserting the error in the document errors array until we run
+        // out of memory
+        errors.add(ex);
+        String message = ex.getMessage();
+
+        // The problem with Xerces is that some errors will cause the
+        // parser not to advance the reader and it will keep reporting
+        // the same error over and over, which will cause the parser
+        // to enter an infinite loop unless we throw the exception.
+        if (message != null && isFatal(message)) {
+          throw ex;
+        }
+    }
+
+    public void error(String domain, String key, XMLParseException e) {
+        errors.add(e);
+    }
+
+    public void fatalError(String domain, String key, XMLParseException e) {
+        errors.add(e);
+    }
+
+    public void warning(String domain, String key, XMLParseException e) {
+        errors.add(e);
+    }
+
+    /*
+     * Determine whether this is a fatal error that should cause
+     * the parsing to stop, or an error that can be ignored.
+     */
+    private static boolean isFatal(String msg) {
+        String msgLowerCase = msg.toLowerCase();
+        return
+          msgLowerCase.contains("in prolog") ||
+          msgLowerCase.contains("limit") ||
+          msgLowerCase.contains("preceding the root element must be well-formed") ||
+          msgLowerCase.contains("following the root element must be well-formed");
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriStrictErrorHandler.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriStrictErrorHandler.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriStrictErrorHandler.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriStrictErrorHandler.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,78 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import org.apache.xerces.xni.parser.XMLParseException;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Strict error handler. Even though strict is specified, Nokogiri allows to go further
+ * when NOERROR or/both NOWARNING is/are true.
+ * 
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class NokogiriStrictErrorHandler extends NokogiriErrorHandler {
+    public NokogiriStrictErrorHandler(boolean noerror, boolean nowarning) {
+        super(noerror, nowarning);
+    }
+
+    public void warning(SAXParseException spex) throws SAXException {
+        if (!nowarning) throw spex;
+        else errors.add(spex);
+    }
+
+    public void error(SAXParseException spex) throws SAXException {
+        if (!noerror) throw spex;
+        else errors.add(spex);
+    }
+
+    public void fatalError(SAXParseException spex) throws SAXException {
+        throw spex;
+    }
+
+    public void error(String domain, String key, XMLParseException e) throws XMLParseException {
+        if (!noerror) throw e;
+        else errors.add(e);
+    }
+
+    public void fatalError(String domain, String key, XMLParseException e) throws XMLParseException {
+        throw e;
+    }
+
+    public void warning(String domain, String key, XMLParseException e) throws XMLParseException {
+        if (!nowarning) throw e;
+        if (!usesNekoHtml(domain)) errors.add(e);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriXPathFunction.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriXPathFunction.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriXPathFunction.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriXPathFunction.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,123 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import java.util.List;
+
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyBoolean;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyFloat;
+import org.jruby.RubyInteger;
+import org.jruby.RubyString;
+import org.jruby.javasupport.JavaUtil;
+import org.jruby.javasupport.util.RuntimeHelpers;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.NodeList;
+
+import nokogiri.XmlNode;
+import nokogiri.XmlNodeSet;
+
+/**
+ * Xpath function handler.
+ * 
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class NokogiriXPathFunction implements XPathFunction {
+
+    private final IRubyObject handler;
+    private final String name;
+    private final int arity;
+    
+    public static NokogiriXPathFunction create(IRubyObject handler, String name, int arity) {
+        return new NokogiriXPathFunction(handler, name, arity);
+    }
+
+    private NokogiriXPathFunction(IRubyObject handler, String name, int arity) {
+        this.handler = handler;
+        this.name = name;
+        this.arity = arity;
+    }
+
+    public Object evaluate(List args) throws XPathFunctionException {
+        if (args.size() != this.arity) {
+            throw new XPathFunctionException("arity does not match");
+        }
+        
+        final Ruby runtime = this.handler.getRuntime();
+        ThreadContext context = runtime.getCurrentContext();
+
+        IRubyObject result = RuntimeHelpers.invoke(context, this.handler, this.name, fromObjectToRubyArgs(runtime, args));
+
+        return fromRubyToObject(runtime, result);
+    }
+
+    private static IRubyObject[] fromObjectToRubyArgs(final Ruby runtime, List args) {
+        IRubyObject[] newArgs = new IRubyObject[args.size()];
+        for(int i = 0; i < args.size(); i++) {
+            newArgs[i] = fromObjectToRuby(runtime, args.get(i));
+        }
+        return newArgs;
+    }
+
+    private static IRubyObject fromObjectToRuby(final Ruby runtime, Object obj) {
+        // argument object type is one of NodeList, String, Boolean, or Double.
+        if (obj instanceof NodeList) {
+            XmlNodeSet xmlNodeSet = XmlNodeSet.newEmptyNodeSet(runtime.getCurrentContext());
+            xmlNodeSet.setNodeList((NodeList) obj);
+            return xmlNodeSet;
+        }
+        return JavaUtil.convertJavaToUsableRubyObject(runtime, obj);
+    }
+
+    private static Object fromRubyToObject(final Ruby runtime, IRubyObject obj) {
+        if (obj instanceof RubyString) return obj.asJavaString();
+        if (obj instanceof RubyBoolean) return obj.toJava(Boolean.class);
+        if (obj instanceof RubyFloat) return obj.toJava(Double.class);
+        if (obj instanceof RubyInteger) {
+            if ( obj instanceof RubyFixnum ) return RubyFixnum.fix2long(obj);
+            return obj.toJava(java.math.BigInteger.class);
+        }
+        if (obj instanceof XmlNodeSet) return obj;
+        if (obj instanceof RubyArray) {
+            return XmlNodeSet.newXmlNodeSet(runtime.getCurrentContext(), ((RubyArray) obj).toJavaArray());
+        }
+        /*if (o instanceof XmlNode)*/ return ((XmlNode) obj).getNode();
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,70 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionResolver;
+
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * Xpath function resolver class, which is used in XmlXpathContext. 
+ * 
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public final class NokogiriXPathFunctionResolver implements XPathFunctionResolver {
+
+    private IRubyObject handler;
+    
+    public static NokogiriXPathFunctionResolver create(IRubyObject handler) {
+        NokogiriXPathFunctionResolver freshResolver = new NokogiriXPathFunctionResolver();
+        freshResolver.setHandler(handler);
+        return freshResolver;
+    }
+    
+    private NokogiriXPathFunctionResolver() {}
+
+    public final IRubyObject getHandler() {
+        return handler;
+    }
+
+    public void setHandler(IRubyObject handler) {
+        this.handler = handler;
+    }
+
+    public XPathFunction resolveFunction(QName name, int arity) {
+        return NokogiriXPathFunction.create(handler, name.getLocalPart(), arity);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriXPathVariableResolver.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriXPathVariableResolver.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriXPathVariableResolver.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriXPathVariableResolver.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,60 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package nokogiri.internals;
+
+import java.util.HashMap;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathVariableResolver;
+
+/**
+ * XPath variable support
+ * 
+ * @author Ken Bloom <kbloom@gmail.com>
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class NokogiriXPathVariableResolver implements XPathVariableResolver {
+
+    private final HashMap<QName,String> variables = new HashMap<QName,String>();
+
+    public static NokogiriXPathVariableResolver create() {
+        return new NokogiriXPathVariableResolver();
+    }
+    
+    private NokogiriXPathVariableResolver() {}
+    
+    public Object resolveVariable(QName variableName){
+        return variables.get(variableName);
+    }
+    public void registerVariable(String name,String value){
+        variables.put(new QName(name),value);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/NokogiriXsltErrorListener.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriXsltErrorListener.java
--- 1.8.4-1/ext/java/nokogiri/internals/NokogiriXsltErrorListener.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/NokogiriXsltErrorListener.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,87 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
+
+/**
+ * Error Listener for XSLT transformer
+ * 
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class NokogiriXsltErrorListener implements ErrorListener {
+    public enum ErrorType {
+        SUCCESS,
+        WARNING,
+        ERROR,
+        FATAL
+    }
+
+    private ErrorType type = ErrorType.SUCCESS;
+    private String errorMessage = null;
+    private Exception exception = null;
+
+    public void warning(TransformerException ex) {
+        type = ErrorType.WARNING;
+        setError(ex);
+    }
+
+    public void error(TransformerException ex) {
+       type = ErrorType.ERROR;
+       setError(ex);
+    }
+
+    public void fatalError(TransformerException ex) {
+        type = ErrorType.FATAL;
+        setError(ex);
+    }
+    
+    private void setError(TransformerException ex) {
+        errorMessage = ex.getMessage();
+        exception = ex;
+    }
+    
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+    
+    public ErrorType getErrorType() {
+        return type;
+    }
+    
+    public Exception getException() {
+        return exception;
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/ParserContext.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/ParserContext.java
--- 1.8.4-1/ext/java/nokogiri/internals/ParserContext.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/ParserContext.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,305 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
+import static org.jruby.runtime.Helpers.invoke;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.concurrent.Callable;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyIO;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+import org.jruby.util.TypeConverter;
+import org.xml.sax.InputSource;
+
+/**
+ * Base class for the various parser contexts.  Handles converting
+ * Ruby objects to InputSource objects.
+ *
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public abstract class ParserContext extends RubyObject {
+    protected InputSource source = null;
+    protected IRubyObject detected_encoding = null;
+    protected int stringDataSize = -1;
+
+    public ParserContext(Ruby runtime) {
+        // default to class 'Object' because this class isn't exposed to Ruby
+        super(runtime, runtime.getObject());
+    }
+
+    public ParserContext(Ruby runtime, RubyClass klass) {
+        super(runtime, klass);
+    }
+
+    protected InputSource getInputSource() {
+        return source;
+    }
+
+    /**
+     * Set the InputSource from <code>url</code> or <code>data</code>,
+     * which may be an IO object, a String, or a StringIO.
+     */
+    public void setInputSource(ThreadContext context, IRubyObject data, IRubyObject url) {
+        source = new InputSource();
+
+        Ruby ruby = context.getRuntime();
+
+        ParserContext.setUrl(context, source, url);
+
+        // if setEncoding returned true, then the stream is set
+        // to the EncodingReaderInputStream
+        if (setEncoding(context, data))
+          return;
+
+        RubyString stringData = null;
+        if (invoke(context, data, "respond_to?", ruby.newSymbol("to_io")).isTrue()) {
+            RubyIO io =
+                (RubyIO) TypeConverter.convertToType(data,
+                                                     ruby.getIO(),
+                                                     "to_io");
+            // use unclosedable input stream to fix #495
+            source.setByteStream(new UncloseableInputStream(io.getInStream()));
+
+        } else if (invoke(context, data, "respond_to?", ruby.newSymbol("read")).isTrue()) {
+            stringData = invoke(context, data, "read").convertToString();
+
+        } else if (invoke(context, data, "respond_to?", ruby.newSymbol("string")).isTrue()) {
+            stringData = invoke(context, data, "string").convertToString();
+
+        } else if (data instanceof RubyString) {
+            stringData = (RubyString) data;
+
+        } else {
+            throw ruby.newArgumentError("must be kind_of String or respond to :to_io, :read, or :string");
+        }
+
+        if (stringData != null) {
+            String encName = null;
+            if (stringData.encoding(context) != null) {
+                encName = stringData.encoding(context).toString();
+            }
+            Charset charset = null;
+            if (encName != null) {
+                try {
+                    charset = Charset.forName(encName);
+                } catch (UnsupportedCharsetException e) {
+                    // do nothing;
+                }
+            }
+            ByteList bytes = stringData.getByteList();
+            if (charset != null) {
+                StringReader reader = new StringReader(new String(bytes.unsafeBytes(), bytes.begin(), bytes.length(), charset));
+                source.setCharacterStream(reader);
+                source.setEncoding(charset.name());
+            } else {
+                stringDataSize = bytes.length() - bytes.begin();
+                ByteArrayInputStream stream = new ByteArrayInputStream(bytes.unsafeBytes(), bytes.begin(), bytes.length());
+                source.setByteStream(stream);
+            }
+        }
+    }
+
+    public static void setUrl(ThreadContext context, InputSource source, IRubyObject url) {
+        String path = rubyStringToString(url);
+        // Dir.chdir might be called at some point before this.
+        if (path != null) {
+          try {
+            URI uri = URI.create(path);
+            source.setSystemId(uri.toURL().toString());
+          } catch (Exception ex) {
+            // fallback to the old behavior
+            File file = new File(path);
+            if (file.isAbsolute()) {
+              source.setSystemId(path);
+            } else {
+              String pwd = context.getRuntime().getCurrentDirectory();
+              String absolutePath;
+              try {
+                absolutePath = new File(pwd, path).getCanonicalPath();
+              } catch (IOException e) {
+                absolutePath = new File(pwd, path).getAbsolutePath();
+              }
+              source.setSystemId(absolutePath);
+            }
+          }
+        }
+    }
+
+    private boolean setEncoding(ThreadContext context, IRubyObject data) {
+        if (data.getType().respondsTo("detect_encoding")) {
+            // in case of EncodingReader is used
+            // since EncodingReader won't respond to :to_io
+            NokogiriEncodingReaderWrapper reader = new NokogiriEncodingReaderWrapper(context, (RubyObject) data);
+            source.setByteStream(reader);
+            // data is EnocodingReader
+            if(reader.detectEncoding()) {
+              detected_encoding = reader.getEncoding();
+              source.setEncoding(detected_encoding.asJavaString());
+            }
+            return true;
+        }
+        return false;
+    }
+    
+    protected void setEncoding(String encoding) {
+    	source.setEncoding(encoding);
+    }
+
+    /**
+     * Set the InputSource to read from <code>file</code>, a String filename.
+     */
+    public void setInputSourceFile(ThreadContext context, IRubyObject file) {
+        source = new InputSource();
+        ParserContext.setUrl(context, source, file);
+    }
+
+    /**
+     * Set the InputSource from <code>stream</code>.
+     */
+    public void setInputSource(InputStream stream) {
+        source = new InputSource(stream);
+    }
+
+    /**
+     * Wrap Nokogiri parser options in a utility class.  This is
+     * read-only.
+     */
+    public static class Options {
+        protected static final long STRICT = 0;
+        protected static final long RECOVER = 1;
+        protected static final long NOENT = 2;
+        protected static final long DTDLOAD = 4;
+        protected static final long DTDATTR = 8;
+        protected static final long DTDVALID = 16;
+        protected static final long NOERROR = 32;
+        protected static final long NOWARNING = 64;
+        protected static final long PEDANTIC = 128;
+        protected static final long NOBLANKS = 256;
+        protected static final long SAX1 = 512;
+        protected static final long XINCLUDE = 1024;
+        protected static final long NONET = 2048;
+        protected static final long NODICT = 4096;
+        protected static final long NSCLEAN = 8192;
+        protected static final long NOCDATA = 16384;
+        protected static final long NOXINCNODE = 32768;
+
+        public final boolean strict;
+        public final boolean recover;
+        public final boolean noEnt;
+        public final boolean dtdLoad;
+        public final boolean dtdAttr;
+        public final boolean dtdValid;
+        public final boolean noError;
+        public final boolean noWarning;
+        public final boolean pedantic;
+        public final boolean noBlanks;
+        public final boolean sax1;
+        public final boolean xInclude;
+        public final boolean noNet;
+        public final boolean noDict;
+        public final boolean nsClean;
+        public final boolean noCdata;
+        public final boolean noXIncNode;
+
+        protected static boolean test(long options, long mask) {
+            return ((options & mask) == mask);
+        }
+
+        public Options(long options) {
+            strict = ((options & RECOVER) == STRICT);
+            recover = test(options, RECOVER);
+            noEnt = test(options, NOENT);
+            dtdLoad = test(options, DTDLOAD);
+            dtdAttr = test(options, DTDATTR);
+            dtdValid = test(options, DTDVALID);
+            noError = test(options, NOERROR);
+            noWarning = test(options, NOWARNING);
+            pedantic = test(options, PEDANTIC);
+            noBlanks = test(options, NOBLANKS);
+            sax1 = test(options, SAX1);
+            xInclude = test(options, XINCLUDE);
+            noNet = test(options, NONET);
+            noDict = test(options, NODICT);
+            nsClean = test(options, NSCLEAN);
+            noCdata = test(options, NOCDATA);
+            noXIncNode = test(options, NOXINCNODE);
+        }
+    }
+
+    /*
+    public static class NokogiriXInlcudeEntityResolver implements org.xml.sax.EntityResolver {
+        InputSource source;
+        public NokogiriXInlcudeEntityResolver(InputSource source) {
+            this.source = source;
+        }
+
+        @Override
+        public InputSource resolveEntity(String publicId, String systemId)
+                throws SAXException, IOException {
+            if (systemId != null) source.setSystemId(systemId);
+            if (publicId != null) source.setPublicId(publicId);
+            return source;
+        }
+    } */
+
+    public static abstract class ParserTask<T extends ParserContext> implements Callable<T> {
+
+        protected final ThreadContext context; // TODO does not seem like a good idea!?
+        protected final IRubyObject handler;
+        protected final T parser;
+
+        protected ParserTask(ThreadContext context, IRubyObject handler, T parser) {
+            this.context = context;
+            this.handler = handler;
+            this.parser = parser;
+        }
+
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/ReaderNode.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/ReaderNode.java
--- 1.8.4-1/ext/java/nokogiri/internals/ReaderNode.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/ReaderNode.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,490 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+import nokogiri.NokogiriService;
+import nokogiri.XmlAttr;
+import nokogiri.XmlDocument;
+import nokogiri.XmlSyntaxError;
+
+import org.apache.xerces.xni.XMLAttributes;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyBoolean;
+import org.jruby.RubyHash;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+
+import static nokogiri.internals.NokogiriHelpers.*;
+
+/**
+ * Abstract class of Node for XmlReader.
+ *
+ * @author Yoko Harada <yokolet@gmail.com>
+ *
+ */
+public abstract class ReaderNode {
+
+    final Ruby ruby;
+    public ReaderAttributeList attributeList;
+    public Map<String, String> namespaces;
+    public int depth, nodeType;
+    public String lang, localName, xmlBase, prefix, name, uri, value, xmlVersion = "1.0";
+    public int startOffset, endOffset;
+    public boolean hasChildren = false;
+    private Document document = null;
+
+    protected ReaderNode(final Ruby runtime) {
+        this.ruby = runtime;
+    }
+
+    public abstract String getString();
+
+    public IRubyObject getAttributeByIndex(IRubyObject index){
+        if(index.isNil()) return index;
+
+        long i = index.convertToInteger().getLongValue();
+        if(i > Integer.MAX_VALUE) {
+            throw ruby.newArgumentError("value too long to be an array index");
+        }
+
+        if (attributeList == null) return ruby.getNil();
+        if (i<0 || attributeList.length <= i) return ruby.getNil();
+        return stringOrBlank(ruby, attributeList.values.get(((Long)i).intValue()));
+    }
+
+    public IRubyObject getAttributeByName(IRubyObject name){
+        if(attributeList == null) return ruby.getNil();
+        String value = attributeList.getByName(rubyStringToString(name));
+        return stringOrNil(ruby, value);
+    }
+
+    public IRubyObject getAttributeByName(String name){
+        if(attributeList == null) return ruby.getNil();
+        String value = attributeList.getByName(name);
+        return stringOrNil(ruby, value);
+    }
+
+    public IRubyObject getAttributeCount(){
+        if(attributeList == null) return ruby.newFixnum(0);
+        return ruby.newFixnum(attributeList.length);
+    }
+
+    public IRubyObject getAttributesNodes() {
+        RubyArray array = RubyArray.newArray(ruby);
+        if (attributeList != null && attributeList.length > 0) {
+            if (document == null) {
+                XmlDocument doc = (XmlDocument) XmlDocument.rbNew(ruby.getCurrentContext(), getNokogiriClass(ruby, "Nokogiri::XML::Document"), new IRubyObject[0]);
+                document = doc.getDocument();
+            }
+            for (int i=0; i<attributeList.length; i++) {
+                if (!isNamespace(attributeList.names.get(i))) {
+                    Attr attr = document.createAttributeNS(attributeList.namespaces.get(i), attributeList.names.get(i));
+                    attr.setValue(attributeList.values.get(i));
+                    XmlAttr xmlAttr = (XmlAttr) NokogiriService.XML_ATTR_ALLOCATOR.allocate(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Attr"));
+                    xmlAttr.setNode(ruby.getCurrentContext(), attr);
+                    array.append(xmlAttr);
+                }
+            }
+        }
+        return array;
+    }
+
+    public IRubyObject getAttributes(ThreadContext context) {
+        final Ruby runtime = context.runtime;
+        if (attributeList == null) return runtime.getNil();
+        RubyHash hash = RubyHash.newHash(runtime);
+        for (int i=0; i<attributeList.length; i++) {
+            IRubyObject k = stringOrBlank(runtime, attributeList.names.get(i));
+            IRubyObject v = stringOrBlank(runtime, attributeList.values.get(i));
+            hash.fastASetCheckString(runtime, k, v); // hash.op_aset(context, k, v)
+        }
+        return hash;
+    }
+
+    public IRubyObject getDepth() {
+        return ruby.newFixnum(depth);
+    }
+
+    public IRubyObject getLang() {
+        return stringOrNil(ruby, lang);
+    }
+
+    public IRubyObject getLocalName() {
+        return stringOrNil(ruby, localName);
+    }
+
+    public IRubyObject getName() {
+        return stringOrNil(ruby, name);
+    }
+
+    public IRubyObject getNamespaces(ThreadContext context) {
+        final Ruby runtime = context.runtime;
+        if (namespaces == null) return runtime.getNil();
+        RubyHash hash = RubyHash.newHash(runtime);
+        for (Map.Entry<String, String> entry : namespaces.entrySet()) {
+            IRubyObject k = stringOrBlank(runtime, entry.getKey());
+            IRubyObject v = stringOrBlank(runtime, entry.getValue());
+            hash.fastASetCheckString(runtime, k, v); // hash.op_aset(context, k, v)
+        }
+        return hash;
+    }
+
+    public IRubyObject getXmlBase() {
+        return stringOrNil(ruby, xmlBase);
+    }
+
+    public IRubyObject getPrefix() {
+        return stringOrNil(ruby, prefix);
+    }
+
+    public IRubyObject getUri() {
+        return stringOrNil(ruby, uri);
+    }
+
+    public IRubyObject getValue() {
+        return stringOrNil(ruby, value);
+    }
+
+    public IRubyObject getXmlVersion() {
+        return ruby.newString(xmlVersion);
+    }
+
+    public RubyBoolean hasAttributes() {
+        if (attributeList == null || attributeList.length == 0) return ruby.getFalse();
+        return ruby.getTrue();
+    }
+
+    public abstract RubyBoolean hasValue();
+
+    public RubyBoolean isDefault(){
+        // TODO Implement.
+        return ruby.getFalse();
+    }
+
+    public boolean isError() { return false; }
+
+    protected void parsePrefix(String qName) {
+        int index = qName.indexOf(':');
+        if(index != -1) prefix = qName.substring(0, index);
+    }
+
+    public void setLang(String lang) {
+        this.lang = lang;
+    }
+
+    public IRubyObject toSyntaxError() { return ruby.getNil(); }
+
+    public IRubyObject getNodeType() { return ruby.newFixnum(nodeType); }
+
+    public static enum ReaderNodeType {
+        NODE(0),
+        ELEMENT(1),
+        ATTRIBUTE(2),
+        TEXT(3),
+        CDATA(4),
+        ENTITY_REFERENCE(5),
+        ENTITY(6),
+        PROCESSING_INSTRUCTION(7),
+        COMMENT(8),
+        DOCUMENT(9),
+        DOCUMENT_TYPE(10),
+        DOCUMENTFRAGMENT(11),
+        NOTATION(12),
+        WHITESPACE(13),
+        SIGNIFICANT_WHITESPACE(14),
+        END_ELEMENT(15),
+        END_ENTITY(16),
+        XML_DECLARATION(17);
+
+        private final int value;
+        ReaderNodeType(int value) {
+            this.value = value;
+        }
+
+        public int getValue() {
+            return value;
+        }
+    }
+
+    public static ClosingNode createClosingNode(Ruby ruby, String uri, String localName, String qName, int depth, Stack<String> langStack, Stack<String> xmlBaseStack) {
+        return new ClosingNode(ruby, uri, localName, qName, depth, langStack, xmlBaseStack);
+    }
+
+    public static class ClosingNode extends ReaderNode {
+
+        // public ClosingNode() {}
+
+        ClosingNode(Ruby runtime, String uri, String localName, String qName, int depth, Stack<String> langStack, Stack<String> xmlBaseStack) {
+            super(runtime);
+            nodeType = ReaderNodeType.END_ELEMENT.getValue();
+            this.uri = "".equals(uri) ? null : uri;
+            this.localName = ! isBlank(localName) ? localName : qName;
+            this.name = qName;
+            parsePrefix(qName);
+            this.depth = depth;
+            if (!langStack.isEmpty()) this.lang = langStack.peek();
+            if (!xmlBaseStack.isEmpty()) this.xmlBase = xmlBaseStack.peek();
+        }
+
+        @Override
+        public IRubyObject getAttributeCount() {
+            return ruby.newFixnum(0);
+        }
+
+        @Override
+        public RubyBoolean hasValue() {
+            return ruby.getFalse();
+        }
+
+        @Override
+        public String getString() {
+            return "</" + name + '>';
+        }
+    }
+
+    public static ElementNode createElementNode(Ruby ruby, String uri, String localName, String qName, XMLAttributes attrs, int depth, Stack<String> langStack, Stack<String> xmlBaseStack) {
+        return new ElementNode(ruby, uri, localName, qName, attrs, depth, langStack, xmlBaseStack);
+    }
+
+    public static class ElementNode extends ReaderNode {
+
+        // public ElementNode() {}
+
+        ElementNode(Ruby runtime, String uri, String localName, String qName, XMLAttributes attrs, int depth, Stack<String> langStack, Stack<String> xmlBaseStack) {
+            super(runtime);
+            this.nodeType = ReaderNodeType.ELEMENT.getValue();
+            this.uri = "".equals(uri) ? null : uri;
+            this.localName = ! isBlank(localName) ? localName : qName;
+            this.name = qName;
+            parsePrefix(qName);
+            this.depth = depth;
+            parseAttributes(attrs, langStack, xmlBaseStack);
+        }
+
+        @Override
+        public RubyBoolean hasValue() {
+            return ruby.getFalse();
+        }
+
+        private void parseAttributes(XMLAttributes attrs, Stack<String> langStack, Stack<String> xmlBaseStack) {
+            if (attrs.getLength() > 0) attributeList = new ReaderAttributeList();
+            String u, n, v;
+            for (int i = 0; i < attrs.getLength(); i++) {
+                u = attrs.getURI(i);
+                n = attrs.getQName(i);
+                v = attrs.getValue(i);
+                if (isNamespace(n)) {
+                    if (namespaces == null) namespaces = new HashMap<String, String>();
+                    namespaces.put(n, v);
+                } else {
+                    if (lang == null) lang = resolveLang(n, v, langStack);
+                    if (xmlBase == null) xmlBase = resolveXmlBase(n, v, xmlBaseStack);
+                }
+                attributeList.add(u, n, v);
+            }
+        }
+
+        private String resolveLang(String n, String v, Stack<String> langStack) {
+            if ("xml:lang".equals(n)) {
+                return v;
+            } else if (!langStack.isEmpty()) {
+                return langStack.peek();
+            } else {
+                return null;
+            }
+        }
+
+        private String resolveXmlBase(String n, String v, Stack<String> xmlBaseStack) {
+            if (isXmlBase(n)) {
+                return getXmlBaseUri(n, v, xmlBaseStack);
+            } else if (!xmlBaseStack.isEmpty()) {
+                return xmlBaseStack.peek();
+            } else {
+                return null;
+            }
+        }
+
+        private String getXmlBaseUri(String n, String v, Stack<String> xmlBaseStack) {
+            if ("xml:base".equals(n)) {
+                if (v.startsWith("http://")) {
+                    return v;
+                } else if (v.startsWith("/") && v.endsWith("/")) {
+                    String sub = v.substring(1, v.length() - 2);
+                    String base = xmlBaseStack.peek();
+                    if (base.endsWith("/")) {
+                        base = base.substring(0, base.length() - 1);
+                    }
+                    int pos = base.lastIndexOf("/");
+                    return base.substring(0, pos).concat(sub);
+                } else {
+                    String base = xmlBaseStack.peek();
+                    if (base.endsWith("/")) return base.concat(v);
+                    else return base.concat("/").concat(v);
+                }
+            } else if ("xlink:href".equals(n)) {
+							if (v.startsWith("http://")) {
+								return v;
+							} else if (!xmlBaseStack.isEmpty()) {
+                String base = xmlBaseStack.peek();
+								return base;
+							}
+            }
+            return null;
+        }
+
+        @Override
+        public String getString() {
+            StringBuffer sb = new StringBuffer(24);
+            sb.append('<').append(name);
+            if (attributeList != null) {
+                for (int i=0; i<attributeList.length; i++) {
+                    String n = attributeList.names.get(i);
+                    String v = attributeList.values.get(i);
+                    sb.append(' ').append(n).append('=')
+                      .append('"').append(v).append('"');
+                }
+            }
+            if (hasChildren) sb.append('>');
+            else sb.append("/>");
+            return sb.toString();
+        }
+    }
+
+    private static class ReaderAttributeList {
+        final List<String> namespaces  = new ArrayList<String>();
+        final List<String> names  = new ArrayList<String>();
+        final List<String> values = new ArrayList<String>();
+        int length = 0;
+
+        void add(String namespace, String name, String value) {
+            namespaces.add(namespace != null ? namespace : "");
+            names.add(name != null ? name : "");
+            values.add(value != null ? value : "");
+            length++;
+        }
+
+        String getByName(String name) {
+            for (int i=0; i<names.size(); i++) {
+                if (name.equals(names.get(i))) {
+                    return values.get(i);
+                }
+            }
+            return null;
+        }
+    }
+
+    public static class EmptyNode extends ReaderNode {
+
+        public EmptyNode(Ruby runtime) {
+            super(runtime);
+            this.nodeType = ReaderNodeType.NODE.getValue();
+        }
+
+        @Override
+        public IRubyObject getXmlVersion() {
+            return this.ruby.getNil();
+        }
+
+        @Override
+        public RubyBoolean hasValue() {
+            return ruby.getFalse();
+        }
+
+        @Override
+        public String getString() {
+            return null;
+        }
+    }
+
+    public static class ExceptionNode extends EmptyNode {
+        private final XmlSyntaxError exception;
+
+        public ExceptionNode(Ruby runtime, Exception ex) {
+            super(runtime);
+            exception = XmlSyntaxError.createXMLSyntaxError(runtime); // Nokogiri::XML::SyntaxError
+            exception.setException(ex);
+        }
+
+        @Override
+        public boolean isError() {
+            return true;
+        }
+
+        @Override
+        public IRubyObject toSyntaxError() {
+            return this.exception;
+        }
+    }
+
+    public static TextNode createTextNode(Ruby ruby, String content, int depth, Stack<String> langStack, Stack<String> xmlBaseStack) {
+        return new TextNode(ruby, content, depth, langStack, xmlBaseStack);
+    }
+
+    public static class TextNode extends ReaderNode {
+
+        // public TextNode() {}
+
+        TextNode(Ruby runtime, String content, int depth, Stack<String> langStack, Stack<String> xmlBaseStack) {
+            super(runtime);
+            this.value = content;
+            this.localName = "#text";
+            this.name = "#text";
+            this.depth = depth;
+            if (!isBlank(content)) nodeType = ReaderNodeType.TEXT.getValue();
+            else nodeType = ReaderNodeType.SIGNIFICANT_WHITESPACE.getValue();
+            if (!langStack.isEmpty()) this.lang = langStack.peek();
+            if (!xmlBaseStack.isEmpty()) this.xmlBase = xmlBaseStack.peek();
+        }
+
+        @Override
+        public RubyBoolean hasValue() {
+            return ruby.getTrue();
+        }
+
+        @Override
+        public String getString() {
+            return value;
+        }
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/SaveContextVisitor.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/SaveContextVisitor.java
--- 1.8.4-1/ext/java/nokogiri/internals/SaveContextVisitor.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/SaveContextVisitor.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,778 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import static nokogiri.internals.NokogiriHelpers.canonicalizeWhitespace;
+import static nokogiri.internals.NokogiriHelpers.encodeJavaString;
+import static nokogiri.internals.NokogiriHelpers.isNamespace;
+import static nokogiri.internals.NokogiriHelpers.isBlank;
+import static nokogiri.internals.NokogiriHelpers.shouldEncode;
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.cyberneko.html.HTMLElements;
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.Entity;
+import org.w3c.dom.EntityReference;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Notation;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+
+/**
+ * A class for serializing a document.
+ *
+ * @author sergio
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class SaveContextVisitor {
+
+    private final StringBuilder buffer;
+    private final Stack<String> indentation;
+    private String encoding;
+    private final CharSequence indentString;
+    private boolean format;
+    private final boolean noDecl;
+    private final boolean noEmpty;
+    private final boolean noXhtml;
+    private final boolean asXhtml;
+    private boolean asXml;
+    private final boolean asHtml;
+    private final boolean asBuilder;
+    private boolean htmlDoc;
+    private final boolean fragment;
+    private final boolean canonical, incl_ns, with_comments;
+    private boolean subsets;
+    private boolean exclusive;
+    private final List<Node> c14nNodeList;
+    private final Deque<Attr[]> c14nNamespaceStack;
+    private final Deque<Attr[]> c14nAttrStack;
+    //private List<String> c14nExclusiveInclusivePrefixes = null;
+
+    /*
+     * U can't touch this.
+     * http://www.youtube.com/watch?v=WJ2ZFVx6A4Q
+     *
+     * Taken from libxml save options.
+     */
+
+    public static final int FORMAT = 1;
+    public static final int NO_DECL = 2;
+    public static final int NO_EMPTY = 4;
+    public static final int NO_XHTML = 8;
+    public static final int AS_XHTML = 16;
+    public static final int AS_XML = 32;
+    public static final int AS_HTML = 64;
+    public static final int AS_BUILDER = 128;
+
+    public static final int CANONICAL = 1;
+    public static final int INCL_NS = 2;
+    public static final int WITH_COMMENTS = 4;
+    public static final int SUBSETS = 8;
+    public static final int EXCLUSIVE = 16;
+
+    public SaveContextVisitor(int options, CharSequence indent, String encoding, boolean htmlDoc, boolean fragment, int canonicalOpts) {
+        buffer = new StringBuilder();
+        this.encoding = encoding;
+        indentation = new Stack<String>(); indentation.push("");
+        this.htmlDoc = htmlDoc;
+        this.fragment = fragment;
+        c14nNodeList = new ArrayList<Node>();
+        c14nNamespaceStack = new ArrayDeque<Attr[]>();
+        c14nAttrStack = new ArrayDeque<Attr[]>();
+        format = (options & FORMAT) == FORMAT;
+
+        noDecl = (options & NO_DECL) == NO_DECL;
+        noEmpty = (options & NO_EMPTY) == NO_EMPTY;
+        noXhtml = (options & NO_XHTML) == NO_XHTML;
+        asXhtml = (options & AS_XHTML) == AS_XHTML;
+        asXml = (options & AS_XML) == AS_XML;
+        asHtml = (options & AS_HTML) == AS_HTML;
+        asBuilder = (options & AS_BUILDER) == AS_BUILDER;
+
+        canonical = (canonicalOpts & CANONICAL) == CANONICAL;
+        incl_ns = (canonicalOpts & INCL_NS) == INCL_NS;
+        with_comments = (canonicalOpts & WITH_COMMENTS) == WITH_COMMENTS;
+        subsets = (canonicalOpts & SUBSETS) == SUBSETS;
+
+        if ((format && indent == null) || (format && indent.length() == 0)) indent = "  "; // default, two spaces
+        if ((!format && indent != null) && indent.length() > 0) format = true;
+        if ((asBuilder && indent == null) || (asBuilder && indent.length() == 0)) indent = "  "; // default, two spaces
+        indentString = indent;
+        if (!asXml && !asHtml && !asXhtml && !asBuilder) asXml = true;
+    }
+
+    @Override
+    public String toString() {
+        return buffer.toString();
+    }
+
+    public StringBuilder getInternalBuffer() { return buffer; }
+
+    public void setHtmlDoc(boolean htmlDoc) {
+        this.htmlDoc = htmlDoc;
+    }
+
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    public boolean enter(Node node) {
+        if (node instanceof Document) {
+            return enter((Document)node);
+        }
+        if (node instanceof Element) {
+            return enter((Element)node);
+        }
+        if (node instanceof Attr) {
+            return enter((Attr)node);
+        }
+        if (node instanceof Text) {
+            return enter((Text)node);
+        }
+        if (node instanceof CDATASection) {
+            return enter((CDATASection)node);
+        }
+        if (node instanceof Comment) {
+            return enter((Comment)node);
+        }
+        if (node instanceof DocumentType) {
+            return enter((DocumentType)node);
+        }
+        if (node instanceof Entity) {
+            return enter((Entity)node);
+        }
+        if (node instanceof EntityReference) {
+            return enter((EntityReference) node);
+        }
+        if (node instanceof Notation) {
+            return enter((Notation)node);
+        }
+        if (node instanceof ProcessingInstruction) {
+            return enter((ProcessingInstruction)node);
+        }
+        return false;
+    }
+
+    public void leave(Node node) {
+        if (node instanceof Document) {
+            leave((Document)node);
+            return;
+        }
+        if (node instanceof Element) {
+            leave((Element)node);
+            return;
+        }
+        if (node instanceof Attr) {
+            leave((Attr)node);
+            return;
+        }
+        if (node instanceof Text) {
+            return;
+        }
+        if (node instanceof CDATASection) {
+            leave((CDATASection)node);
+            return;
+        }
+        if (node instanceof Comment) {
+            leave((Comment)node);
+            return;
+        }
+        if (node instanceof DocumentType) {
+            leave((DocumentType)node);
+            return;
+        }
+        if (node instanceof Entity) {
+            leave((Entity)node);
+            return;
+        }
+        if (node instanceof EntityReference) {
+            leave((EntityReference) node);
+            return;
+        }
+        if (node instanceof Notation) {
+            leave((Notation)node);
+            return;
+        }
+        if (node instanceof ProcessingInstruction) {
+            leave((ProcessingInstruction)node);
+            return;
+        }
+    }
+
+    public boolean enter(String string) {
+        buffer.append(string);
+        return true;
+    }
+
+    public void leave(String string) {
+        // no-op
+    }
+
+    public boolean enter(Attr attr) {
+        String name = attr.getName();
+        buffer.append(name);
+        if (!asHtml || !isHtmlBooleanAttr(name)) {
+            buffer.append('=');
+            buffer.append('"');
+            String value = replaceCharsetIfNecessary(attr);
+            buffer.append(serializeAttrTextContent(value, htmlDoc));
+            buffer.append('"');
+        }
+        return true;
+    }
+
+    private static final Pattern CHARSET =
+        Pattern.compile("charset(()|\\s+)=(()|\\s+)(\\w|\\_|\\.|\\-)+", Pattern.CASE_INSENSITIVE);
+
+    private String replaceCharsetIfNecessary(Attr attr) {
+        String value = attr.getValue();
+        if (encoding == null) return value;   // unable to replace in any case
+        if (!"content".equals(attr.getName().toLowerCase())) return value;  // must be content attr
+        if (!"meta".equals(attr.getOwnerElement().getNodeName().toLowerCase())) return value;
+        Matcher m = CHARSET.matcher(value);
+        if (!m.find()) return value;
+        if (value.contains(encoding)) return value;  // no need to replace
+        return value.replace(m.group(), "charset=" + encoding);
+    }
+
+    static final Set<String> HTML_BOOLEAN_ATTRS;
+    static {
+        final String[] _HTML_BOOLEAN_ATTRS = {
+            "checked", "compact", "declare", "defer", "disabled", "ismap",
+            "multiple", "nohref", "noresize", "noshade", "nowrap", "readonly",
+            "selected"
+        };
+        HTML_BOOLEAN_ATTRS = new HashSet<String>(Arrays.asList(_HTML_BOOLEAN_ATTRS));
+    }
+
+    private static boolean isHtmlBooleanAttr(String name) {
+        return HTML_BOOLEAN_ATTRS.contains(name);
+    }
+
+    private static CharSequence serializeAttrTextContent(String str, boolean htmlDoc) {
+        if (str == null || str.length() == 0) return "";
+
+        StringBuilder buffer = new StringBuilder(str.length() + 16);
+
+        for (int i = 0; i < str.length(); i++) {
+            char c; switch (c = str.charAt(i)) {
+                case '\n': buffer.append("&#10;"); break;
+                case '\r': buffer.append("&#13;"); break;
+                case '\t': buffer.append("&#9;"); break;
+                case '"': if (htmlDoc) buffer.append("%22");
+                else buffer.append("&quot;");
+                    break;
+                case '<': buffer.append("&lt;"); break;
+                case '>': buffer.append("&gt;"); break;
+                case '&': buffer.append("&amp;"); break;
+                default: buffer.append(c);
+            }
+        }
+
+        return buffer;
+    }
+
+    public void leave(Attr attr) {
+        // no-op
+    }
+
+    public boolean enter(CDATASection cdata) {
+        buffer.append("<![CDATA[");
+        buffer.append(cdata.getData());
+        buffer.append("]]>");
+        return true;
+    }
+
+    public void leave(CDATASection cdata) {
+        // no-op
+    }
+
+    public boolean enter(Comment comment) {
+        if (canonical) {
+            c14nNodeList.add(comment);
+            if (!with_comments) return true;
+        }
+        buffer.append("<!--");
+        buffer.append(comment.getData());
+        buffer.append("-->");
+        return true;
+    }
+
+    public void leave(Comment comment) {
+        // no-op
+    }
+
+    public boolean enter(Document document) {
+        if (!noDecl) {
+            buffer.append("<?xml version=\"");
+            buffer.append(document.getXmlVersion());
+            buffer.append("\"");
+
+            if (encoding != null) {
+                buffer.append(" encoding=\"");
+                buffer.append(encoding);
+                buffer.append("\"");
+            }
+            buffer.append("?>\n");
+        }
+        return true;
+    }
+
+    public void leave(Document document) {
+        // no-op
+    }
+
+    public boolean enter(DocumentType docType) {
+        if (canonical) {
+            c14nNodeList.add(docType);
+            return true;
+        }
+        String name = docType.getName();
+        String pubId = docType.getPublicId();
+        String sysId = docType.getSystemId();
+        String internalSubset = docType.getInternalSubset();
+        if (docType.getPreviousSibling() != null) {
+            buffer.append('\n');
+        }
+        buffer.append("<!DOCTYPE ").append(name).append(' ');
+        if (pubId != null) {
+            buffer.append("PUBLIC \"").append(pubId).append('"');
+            if (sysId != null) buffer.append(" \"").append(sysId).append('"');
+        } else if (sysId != null) {
+            buffer.append("SYSTEM \"").append(sysId).append('"');
+        }
+        if (internalSubset != null) {
+            buffer.append(' ').append('[');
+            buffer.append(internalSubset);
+            buffer.append(']');
+        }
+        buffer.append(">\n");
+        return true;
+    }
+
+    public void leave(DocumentType docType) {
+        // no-op
+    }
+
+    public boolean enter(Element element) {
+        if (canonical) {
+            c14nNodeList.add(element);
+            if (element == element.getOwnerDocument().getDocumentElement()) {
+                c14nNodeList.add(element.getOwnerDocument());
+            }
+        }
+        String current = indentation.peek();
+        buffer.append(current);
+        if (needIndent(element)) {
+            indentation.push(current + indentString);
+        }
+        String name = element.getTagName();
+        buffer.append('<').append(name);
+        Attr[] attrs = getAttrsAndNamespaces(element);
+        for (Attr attr : attrs) {
+            if (attr.getSpecified()) {
+                buffer.append(' ');
+                enter(attr);
+                leave(attr);
+            }
+        }
+        if (element.hasChildNodes()) {
+            buffer.append('>');
+            if (needBreakInOpening(element)) buffer.append('\n');
+            return true;
+        }
+        // no child
+        if (asHtml) {
+            buffer.append('>');
+        } else if (asXml && noEmpty) {
+            buffer.append('>');
+        } else if (asXhtml) {
+            if (isEmpty(name)) {
+                buffer.append(" />"); // see http://www.w3.org/TR/xhtml1/#C_2
+            } else {
+                buffer.append('>');
+            }
+        } else {
+            buffer.append("/>");
+        }
+        if (needBreakInOpening(element)) {
+            buffer.append('\n');
+        }
+        return true;
+    }
+
+    private boolean needIndent(Element element) {
+        if (containsText(element)) return false;
+        if (fragment) return false;  // a given option might be fragment and format. fragment matters
+        if (format || asBuilder) return true;
+        return false;
+    }
+
+    private boolean needBreakInOpening(Element element) {
+        if (containsText(element)) return false;
+        if (fragment) return false;
+        if (format) return true;
+        if (asBuilder && element.getFirstChild() != null && element.getFirstChild().getNodeType() == Node.ELEMENT_NODE) return true;
+        if (format && element.getNextSibling() == null && element.hasChildNodes()) return true;
+        return false;
+    }
+
+    private boolean isEmpty(String name) {
+        HTMLElements.Element element = HTMLElements.getElement(name);
+        return element.isEmpty();
+    }
+
+    private Attr[] getAttrsAndNamespaces(Element element) {
+        NamedNodeMap attrs = element.getAttributes();
+        if (!canonical) {
+            if (attrs == null || attrs.getLength() == 0) return new Attr[0];
+            Attr[] attrsAndNamespaces = new Attr[attrs.getLength()];
+            for (int i=0; i<attrs.getLength(); i++) {
+                attrsAndNamespaces[i] = (Attr) attrs.item(i);
+            }
+            return attrsAndNamespaces;
+        } else {
+            List<Attr> namespaces = new ArrayList<Attr>();
+            List<Attr> attributes = new ArrayList<Attr>();
+            if (subsets) {
+                getAttrsOfAncestors(element.getParentNode(), namespaces, attributes);
+                Attr[] namespaceOfAncestors = getSortedArray(namespaces);
+                Attr[] attributeOfAncestors = getSortedArray(attributes);
+                c14nNamespaceStack.push(namespaceOfAncestors);
+                c14nAttrStack.push(attributeOfAncestors);
+                subsets = false; // namespace propagation should be done only once on top level node.
+            }
+
+            getNamespacesAndAttrs(element, namespaces, attributes);
+
+            Attr[] namespaceArray = getSortedArray(namespaces);
+            Attr[] attributeArray = getSortedArray(attributes);
+            Attr[] allAttrs = new Attr[namespaceArray.length + attributeArray.length];
+            for (int i=0; i<allAttrs.length; i++) {
+                if (i < namespaceArray.length) {
+                    allAttrs[i] = namespaceArray[i];
+                } else {
+                    allAttrs[i] = attributeArray[i-namespaceArray.length];
+                }
+            }
+            c14nNamespaceStack.push(namespaceArray);
+            c14nAttrStack.push(attributeArray);
+            return allAttrs;
+        }
+
+    }
+
+    private void getAttrsOfAncestors(Node parent, List<Attr> namespaces, List<Attr> attributes) {
+        if (parent == null) return;
+        NamedNodeMap attrs = parent.getAttributes();
+        if (attrs == null || attrs.getLength() == 0) return;
+        for (int i=0; i < attrs.getLength(); i++) {
+            Attr attr = (Attr)attrs.item(i);
+            if (isNamespace(attr.getNodeName())) namespaces.add(attr);
+            else attributes.add(attr);
+        }
+        getAttrsOfAncestors(parent.getParentNode(), namespaces, attributes);
+    }
+
+    private void getNamespacesAndAttrs(Node current, List<Attr> namespaces, List<Attr> attributes) {
+        NamedNodeMap attrs = current.getAttributes();
+        for (int i=0; i<attrs.getLength(); i++) {
+            Attr attr = (Attr)attrs.item(i);
+            if (isNamespace(attr.getNodeName())) {
+                getNamespacesWithPropagated(namespaces, attr);
+            } else {
+                getAttributesWithPropagated(attributes, attr);
+            }
+            if (exclusive) {
+                verifyXmlSpace(attributes, attrs);
+            }
+        }
+    }
+
+    private void getNamespacesWithPropagated(List<Attr> namespaces, Attr attr) {
+        boolean newNamespace = true;
+        Iterator<Attr[]> iter = c14nNamespaceStack.iterator();
+        while (iter.hasNext()) {
+            Attr[] parentNamespaces = iter.next();
+            for (int n=0; n < parentNamespaces.length; n++) {
+                if (parentNamespaces[n].getNodeName().equals(attr.getNodeName())) {
+                   if (parentNamespaces[n].getNodeValue().equals(attr.getNodeValue())) {
+                       // exactly the same namespace should not be added
+                       newNamespace = false;
+                   } else {
+                       // in case of namespace url change, propagated namespace will be override
+                       namespaces.remove(parentNamespaces[n]);
+                   }
+                }
+            }
+            if (newNamespace && !namespaces.contains(attr)) namespaces.add(attr);
+        }
+    }
+
+    private void getAttributesWithPropagated(List<Attr> attributes, Attr attr) {
+        boolean newAttribute = true;
+        Iterator<Attr[]> iter = c14nAttrStack.iterator();
+        while (iter.hasNext()) {
+            Attr[] parentAttr = iter.next();
+            for (int n=0; n < parentAttr.length; n++) {
+                if (!parentAttr[n].getNodeName().startsWith("xml:")) continue;
+                if (parentAttr[n].getNodeName().equals(attr.getNodeName())) {
+                   if (parentAttr[n].getNodeValue().equals(attr.getNodeValue())) {
+                       // exactly the same attribute should not be added
+                       newAttribute = false;
+                   } else {
+                       // in case of attribute value change, propagated attribute will be override
+                       attributes.remove(parentAttr[n]);
+                   }
+                }
+            }
+            if (newAttribute) attributes.add(attr);
+        }
+    }
+
+    private void verifyXmlSpace(List<Attr> attributes, NamedNodeMap attrs) {
+        Attr attr = (Attr) attrs.getNamedItem("xml:space");
+        if (attr == null) {
+            for (int i=0; i < attributes.size(); i++) {
+                if (attributes.get(i).getNodeName().equals("xml:space")) {
+                    attributes.remove(i);
+                    break;
+                }
+            }
+        }
+    }
+
+    private Attr[] getSortedArray(List<Attr> attrList) {
+        Attr[] attrArray = attrList.toArray(new Attr[0]);
+        Arrays.sort(attrArray, new Comparator<Attr>() {
+            @Override
+            public int compare(Attr attr0, Attr attr1) {
+                return attr0.getNodeName().compareTo(attr1.getNodeName());
+            }
+        });
+        return attrArray;
+    }
+
+    public void leave(Element element) {
+        if (canonical) {
+            c14nNamespaceStack.poll();
+            c14nAttrStack.poll();
+        }
+        String name = element.getTagName();
+        if (element.hasChildNodes()) {
+            if (needIndentInClosing(element)) {
+                indentation.pop();
+                buffer.append(indentation.peek());
+            } else if (asBuilder) {
+                if (!containsText(element)) indentation.pop();
+            }
+            buffer.append("</").append(name).append('>');
+            if (needBreakInClosing(element)) {
+                buffer.append('\n');
+            }
+            return;
+        }
+        // no child, but HTML might need a closing tag.
+        if (asHtml || noEmpty) {
+            if (!isEmpty(name) && noEmpty) {
+                buffer.append("</").append(name).append('>');
+            }
+        }
+        if (needBreakInClosing(element)) {
+            if (!containsText(element)) indentation.pop();
+            buffer.append('\n');
+        }
+    }
+
+    private boolean needIndentInClosing(Element element) {
+        if (containsText(element)) return false;
+
+        if (fragment) return false;  // a given option might be fragment and format. fragment matters
+        if (format) return true;
+        if (asBuilder && element.getFirstChild() != null && element.getFirstChild().getNodeType() == Node.ELEMENT_NODE) return true;
+        return false;
+    }
+
+    private boolean needBreakInClosing(Element element) {
+        if (fragment) return false;
+        if (format || asBuilder) return true;
+        return false;
+    }
+
+    private boolean containsText(Element element) {
+        return (element.getFirstChild() != null && element.getFirstChild().getNodeType() == Node.TEXT_NODE);
+    }
+
+    public boolean enter(Entity entity) {
+        String name = entity.getNodeName();
+        String pubId = entity.getPublicId();
+        String sysId = entity.getSystemId();
+        String notation = entity.getNotationName();
+        buffer.append("<!ENTITY ");
+        buffer.append(name);
+        if (pubId != null) {
+            buffer.append(" PUBLIC \"");
+            buffer.append(pubId);
+            buffer.append("\"");
+        }
+        if (sysId != null) {
+            buffer.append(" SYSTEM \"");
+            buffer.append(sysId);
+            buffer.append("\"");
+        }
+        if (notation != null) {
+            buffer.append(" NDATA ");
+            buffer.append(notation);
+        }
+        buffer.append(">");
+        return true;
+    }
+
+    public void leave(Entity entity) {
+        // no-op
+    }
+
+    public boolean enter(EntityReference entityRef) {
+        buffer.append('&').append(entityRef.getNodeName()).append(';');
+        return true;
+    }
+    public void leave(EntityReference entityRef) {
+        // no-op
+    }
+
+    public boolean enter(Notation notation) {
+        String name = notation.getNodeName();
+        String pubId = notation.getPublicId();
+        String sysId = notation.getSystemId();
+        buffer.append("<!NOTATION ");
+        buffer.append(name);
+        if (pubId != null) {
+            buffer.append(" PUBLIC \"");
+            buffer.append(pubId);
+            buffer.append("\"");
+            if (sysId != null) {
+                buffer.append(" \"");
+                buffer.append(sysId);
+                buffer.append("\"");
+            }
+        } else if (sysId != null) {
+            buffer.append(" SYSTEM \"");
+            buffer.append(sysId);
+            buffer.append("\"");
+        }
+        buffer.append(">");
+        return true;
+    }
+
+    public void leave(Notation notation) {
+        // no-op
+    }
+
+    public boolean enter(ProcessingInstruction pi) {
+        buffer.append("<?");
+        buffer.append(pi.getTarget());
+        buffer.append(" ");
+        buffer.append(pi.getData());
+        if (asHtml) buffer.append(">");
+        else buffer.append("?>");
+        buffer.append("\n");
+        if (canonical) c14nNodeList.add(pi);
+        return true;
+    }
+
+    public void leave(ProcessingInstruction pi) {
+        // no-op
+    }
+
+    private boolean isHtmlScript(Text text) {
+        return htmlDoc && text.getParentNode().getNodeName().equals("script");
+    }
+
+    private boolean isHtmlStyle(Text text) {
+        return htmlDoc && text.getParentNode().getNodeName().equals("style");
+    }
+
+    public boolean enter(Text text) {
+        CharSequence textContent = text.getNodeValue();
+        if (canonical) {
+            c14nNodeList.add(text);
+            if (isBlank(textContent)) {
+                buffer.append(canonicalizeWhitespace(textContent));
+                return true;
+            }
+        }
+
+        if (shouldEncode(text) && !isHtmlScript(text) && !isHtmlStyle(text)) {
+            textContent = encodeJavaString(textContent);
+        }
+
+        textContent = encodeStringToHtmlEntity(textContent);
+        buffer.append(textContent);
+        return true;
+    }
+
+    private CharSequence encodeStringToHtmlEntity(CharSequence text) {
+        if (encoding == null) return text;
+
+        CharsetEncoder encoder = Charset.forName(encoding).newEncoder();
+        StringBuilder sb = new StringBuilder(text.length() + 16);
+        // make sure we can handle code points that are higher than 2 bytes
+        for ( int i = 0; i < text.length(); ) {
+            int code = Character.codePointAt(text, i);
+            // TODO not sure about bigger offset then 2 ?!
+            int offset = code > 65535 ? 2 : 1;
+            CharSequence substr = text.subSequence(i, i + offset);
+            boolean canEncode = encoder.canEncode(substr);
+            if (canEncode) {
+                sb.append(substr);
+            }
+            else {
+                sb.append("&#x").append(Integer.toHexString(code)).append(';');
+            }
+            i += offset;
+        }
+        return sb;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/SchemaErrorHandler.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/SchemaErrorHandler.java
--- 1.8.4-1/ext/java/nokogiri/internals/SchemaErrorHandler.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/SchemaErrorHandler.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,73 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import nokogiri.NokogiriService;
+import nokogiri.XmlSyntaxError;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Error handler for Relax and W3C XML Schema.
+ * 
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class SchemaErrorHandler implements ErrorHandler {
+
+    private final Ruby runtime;
+    final RubyArray errors;
+
+    public SchemaErrorHandler(Ruby ruby, RubyArray array) {
+        this.runtime = ruby;
+        this.errors = array;
+    }
+
+    public void warning(SAXParseException ex) throws SAXException {
+        errors.append( XmlSyntaxError.createWarning(runtime, ex) );
+    }
+
+    public void error(SAXParseException ex) throws SAXException {
+        errors.append( XmlSyntaxError.createError(runtime, ex) );
+    }
+
+    public void fatalError(SAXParseException ex) throws SAXException {
+        throw ex;
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/UncloseableInputStream.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/UncloseableInputStream.java
--- 1.8.4-1/ext/java/nokogiri/internals/UncloseableInputStream.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/UncloseableInputStream.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,102 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Delegates all the methods to another InputStream except the
+ * close() method, which is ignored. This is used to fix #495.
+ *
+ * @author John Shahid <jvshahid@gmail.com>
+ */
+public class UncloseableInputStream extends InputStream {
+  private final InputStream delegate;
+
+  /**
+   * Create a new uncloseable stream.
+   *
+   * @param delegate The InputStream to which all methods (except close)
+   * will be delegated.
+   */
+  public UncloseableInputStream(InputStream delegate) {
+    this.delegate = delegate;
+  }
+
+  @Override
+  public int read() throws IOException {
+    return delegate.read();
+  }
+
+  @Override
+  public int read(byte []b) throws IOException {
+    return delegate.read(b);
+  }
+
+  @Override
+  public int read(byte []b, int offset, int len) throws IOException {
+    return delegate.read(b, offset, len);
+  }
+
+  @Override
+  public long skip(long n) throws IOException {
+    return delegate.skip(n);
+  }
+
+  @Override
+  public int available() throws IOException {
+    return delegate.available();
+  }
+
+  @Override
+  public void close() {
+    // don't forward this to the InputStream we're delegating from
+    // we don't want the InputStream of the RubyIO to be closed
+  }
+
+  @Override
+  public void mark(int readlimit) {
+    delegate.mark(readlimit);
+  }
+
+  @Override
+  public void reset() throws IOException {
+    delegate.reset();
+  }
+
+  @Override
+  public boolean markSupported() {
+    return delegate.markSupported();
+  }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/XalanDTMManagerPatch.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/XalanDTMManagerPatch.java
--- 1.8.4-1/ext/java/nokogiri/internals/XalanDTMManagerPatch.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/XalanDTMManagerPatch.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2017 [Karol Bucek](http://kares.org/)
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the  "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package nokogiri.internals;
+
+import javax.xml.transform.dom.DOMSource;
+
+import org.apache.xml.dtm.DTM;
+import nokogiri.internals.dom2dtm.DOM2DTM;
+import nokogiri.internals.dom2dtm.DOM2DTMdefaultNamespaceDeclarationNode;
+import org.apache.xml.dtm.DTMWSFilter;
+import org.apache.xml.res.XMLErrorResources;
+import org.apache.xml.res.XMLMessages;
+import org.w3c.dom.Node;
+
+/**
+ * @author kares
+ */
+public final class XalanDTMManagerPatch extends org.apache.xml.dtm.ref.DTMManagerDefault {
+
+    /**
+     * Given a W3C DOM node, try and return a DTM handle.
+     * Note: calling this may be non-optimal, and there is no guarantee that
+     * the node will be found in any particular DTM.
+     *
+     * @param node Non-null reference to a DOM node.
+     *
+     * @return a valid DTM handle.
+     */
+    @Override
+    public /* synchronized */ int getDTMHandleFromNode(org.w3c.dom.Node node) {
+        //if (node == null) // "node must be non-null for getDTMHandleFromNode!");
+        //    throw new IllegalArgumentException(XMLMessages.createXMLMessage(XMLErrorResources.ER_NODE_NON_NULL, null));
+        assert node != null;
+
+        if (node instanceof org.apache.xml.dtm.ref.DTMNodeProxy) {
+            return ((org.apache.xml.dtm.ref.DTMNodeProxy) node).getDTMNodeNumber();
+        }
+
+        // Find the DOM2DTMs wrapped around this Document (if any)
+        // and check whether they contain the Node in question.
+        //
+        // NOTE that since a DOM2DTM may represent a subtree rather
+        // than a full document, we have to be prepared to check more
+        // than one -- and there is no guarantee that we will find
+        // one that contains ancestors or siblings of the node we're
+        // seeking.
+        //
+        // %REVIEW% We could search for the one which contains this
+        // node at the deepest level, and thus covers the widest
+        // subtree, but that's going to entail additional work
+        // checking more DTMs... and getHandleOfNode is not a
+        // cheap operation in most implementations.
+        //
+        // TODO: %REVIEW% If overflow addressing, we may recheck a DTM
+        // already examined. Ouch. But with the increased number of DTMs,
+        // scanning back to check this is painful.
+        // POSSIBLE SOLUTIONS:
+        //   Generate a list of _unique_ DTM objects?
+        //   Have each DTM cache last DOM node search?
+        for(int i = 0; i < m_dtms.length; i++) {
+            DTM thisDTM = m_dtms[i];
+            if (thisDTM instanceof DOM2DTM) {
+                int handle = ((DOM2DTM) thisDTM).getHandleOfNode(node);
+                if (handle != DTM.NULL) {
+                    return handle;
+                }
+            }
+        }
+
+        // Not found; generate a new DTM.
+        //
+        // %REVIEW% Is this really desirable, or should we return null
+        // and make folks explicitly instantiate from a DOMSource? The
+        // latter is more work but gives the caller the opportunity to
+        // explicitly add the DTM to a DTMManager... and thus to know when
+        // it can be discarded again, which is something we need to pay much
+        // more attention to. (Especially since only DTMs which are assigned
+        // to a manager can use the overflow addressing scheme.)
+        //
+        // %BUG% If the source node was a DOM2DTM$defaultNamespaceDeclarationNode
+        // and the DTM wasn't registered with this DTMManager, we will create
+        // a new DTM and _still_ not be able to find the node (since it will
+        // be resynthesized). Another reason to push hard on making all DTMs
+        // be managed DTMs.
+
+        // Since the real root of our tree may be a DocumentFragment, we need to
+        // use getParent to find the root, instead of getOwnerDocument.  Otherwise
+        // DOM2DTM#getHandleOfNode will be very unhappy.
+        Node root = node; int rootType = root.getNodeType();
+        Node p = (rootType == Node.ATTRIBUTE_NODE) ? ((org.w3c.dom.Attr) root).getOwnerElement() : root.getParentNode();
+        for (; p != null; p = p.getParentNode()) root = p;
+
+        // DOM2DTM dtm = (DOM2DTM) getDTM(new DOMSource(root), false, null);
+        DOM2DTM dtm = getDTM(new DOMSource(root), false, null/*, true, true*/);
+
+        int handle;
+
+        if (node instanceof org.apache.xml.dtm.ref.dom2dtm.DOM2DTMdefaultNamespaceDeclarationNode
+                || node instanceof DOM2DTMdefaultNamespaceDeclarationNode) {
+            // Can't return the same node since it's unique to a specific DTM,
+            // but can return the equivalent node -- find the corresponding
+            // Document Element, then ask it for the xml: namespace decl.
+            handle = dtm.getHandleOfNode(((org.w3c.dom.Attr) node).getOwnerElement());
+            handle = dtm.getAttributeNode(handle, node.getNamespaceURI(), node.getLocalName());
+        }
+        else {
+            handle = dtm.getHandleOfNode(node);
+
+            rootType = root.getNodeType();
+            // Is Node actually within the same document? If not, don't search!
+            // This would be easier if m_root was always the Document node, but
+            // we decided to allow wrapping a DTM around a subtree.
+            if((root==node) ||
+                (rootType==Node.DOCUMENT_NODE && root==node.getOwnerDocument()) ||
+                (rootType!=Node.DOCUMENT_NODE && root.getOwnerDocument()==node.getOwnerDocument())
+                )
+            {
+                // If node _is_ in m_root's tree, find its handle
+                //
+                // %OPT% This check may be improved significantly when DOM
+                // Level 3 nodeKey and relative-order tests become
+                // available!
+                for (Node cursor = node; cursor != null;
+                    cursor = (cursor.getNodeType()!=Node.ATTRIBUTE_NODE)
+                            ? cursor.getParentNode()
+                            : ((org.w3c.dom.Attr)cursor).getOwnerElement()) {
+                    if (cursor==root) {
+                        // We know this node; find its handle.
+                        return (dtm).getHandleFromNode(node);
+                    }
+                } // for ancestors of node
+            } // if node and m_root in same Document
+        }
+
+        if (DTM.NULL == handle)
+            throw new RuntimeException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COULD_NOT_RESOLVE_NODE, null)); //"Could not resolve the node to a handle!");
+
+        return handle;
+    }
+
+    private DOM2DTM getDTM(DOMSource source, boolean unique, DTMWSFilter whiteSpaceFilter/*, boolean incremental, boolean doIndexing*/) {
+        int dtmPos = getFirstFreeDTMID();
+        int documentID = dtmPos << IDENT_DTM_NODE_BITS;
+
+        DOM2DTM dtm = new DOM2DTM(this, source, documentID, whiteSpaceFilter, m_xsf, true);
+
+        addDTM(dtm, dtmPos, 0);
+        return dtm;
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/XmlDeclHandler.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/XmlDeclHandler.java
--- 1.8.4-1/ext/java/nokogiri/internals/XmlDeclHandler.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/XmlDeclHandler.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,42 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+/**
+ * Interface for receiving xmlDecl information.
+ *
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ */
+public interface XmlDeclHandler {
+    public void xmlDecl(String version, String encoding, String standalone);
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/XmlDomParserContext.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/XmlDomParserContext.java
--- 1.8.4-1/ext/java/nokogiri/internals/XmlDomParserContext.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/XmlDomParserContext.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,289 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.isBlank;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import nokogiri.NokogiriService;
+import nokogiri.XmlDocument;
+import nokogiri.XmlDtd;
+import nokogiri.XmlSyntaxError;
+
+import org.apache.xerces.parsers.DOMParser;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/**
+ * Parser class for XML DOM processing. This class actually parses XML document
+ * and creates DOM tree in Java side. However, DOM tree in Ruby side is not since
+ * we delay creating objects for performance.
+ *  
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class XmlDomParserContext extends ParserContext {
+
+    protected static final String FEATURE_LOAD_EXTERNAL_DTD =
+        "http://apache.org/xml/features/nonvalidating/load-external-dtd";
+    protected static final String FEATURE_LOAD_DTD_GRAMMAR =
+        "http://apache.org/xml/features/nonvalidating/load-dtd-grammar";
+    protected static final String FEATURE_INCLUDE_IGNORABLE_WHITESPACE =
+        "http://apache.org/xml/features/dom/include-ignorable-whitespace";
+    protected static final String CONTINUE_AFTER_FATAL_ERROR =
+        "http://apache.org/xml/features/continue-after-fatal-error";
+    protected static final String FEATURE_NOT_EXPAND_ENTITY =
+        "http://apache.org/xml/features/dom/create-entity-ref-nodes";
+    protected static final String FEATURE_VALIDATION = "http://xml.org/sax/features/validation";
+    private static final String XINCLUDE_FEATURE_ID = "http://apache.org/xml/features/xinclude";
+    private static final String SECURITY_MANAGER = "http://apache.org/xml/properties/security-manager";
+
+    protected ParserContext.Options options;
+    protected DOMParser parser;
+    protected NokogiriErrorHandler errorHandler;
+    protected String java_encoding;
+    protected IRubyObject ruby_encoding;
+
+    public XmlDomParserContext(Ruby runtime, IRubyObject options) {
+        this(runtime, runtime.getNil(), options);
+    }
+    
+    public XmlDomParserContext(Ruby runtime, IRubyObject encoding, IRubyObject options) {
+        super(runtime);
+        this.options = new ParserContext.Options(RubyFixnum.fix2long(options));
+        java_encoding = NokogiriHelpers.getValidEncoding(runtime, encoding);
+        ruby_encoding = encoding;
+        initErrorHandler();
+        initParser(runtime);
+    }
+
+    protected void initErrorHandler() {
+        if (options.recover) {
+            errorHandler = new NokogiriNonStrictErrorHandler(options.noError, options.noWarning);
+        } else {
+            errorHandler = new NokogiriStrictErrorHandler(options.noError, options.noWarning);
+        }
+    }
+
+    protected void initParser(Ruby runtime) {
+        if (options.xInclude) {
+            System.setProperty("org.apache.xerces.xni.parser.XMLParserConfiguration",
+                    "org.apache.xerces.parsers.XIncludeParserConfiguration");
+        }
+
+        parser = new NokogiriDomParser(options);
+        parser.setErrorHandler(errorHandler);
+
+        // Fix for Issue#586.  This limits entity expansion up to 100000 and nodes up to 3000.
+        setProperty(SECURITY_MANAGER, new org.apache.xerces.util.SecurityManager());
+
+        if (options.noBlanks) {
+            setFeature(FEATURE_INCLUDE_IGNORABLE_WHITESPACE, false);
+        }
+
+        if (options.recover) {
+            setFeature(CONTINUE_AFTER_FATAL_ERROR, true);
+        }
+
+        if (options.dtdValid) {
+            setFeature(FEATURE_VALIDATION, true);
+        }
+
+        if (!options.noEnt) {
+          setFeature(FEATURE_NOT_EXPAND_ENTITY, true);
+      }
+        // If we turn off loading of external DTDs complete, we don't
+        // getthe publicID.  Instead of turning off completely, we use
+        // an entity resolver that returns empty documents.
+        if (options.dtdLoad) {
+            setFeature(FEATURE_LOAD_EXTERNAL_DTD, true);
+            setFeature(FEATURE_LOAD_DTD_GRAMMAR, true);
+        }
+        parser.setEntityResolver(new NokogiriEntityResolver(runtime, errorHandler, options));
+    }
+
+    /**
+     * Convenience method that catches and ignores SAXException
+     * (unrecognized and unsupported exceptions).
+     */
+    protected void setFeature(String feature, boolean value) {
+        try {
+            parser.setFeature(feature, value);
+        } catch (SAXException e) {
+            // ignore
+        }
+    }
+
+    /**
+     * Convenience method that catches and ignores SAXException
+     * (unrecognized and unsupported exceptions).
+     */
+    protected void setProperty(String property, Object value) {
+        try {
+            parser.setProperty(property, value);
+        } catch (SAXException e) {
+            // ignore
+        }
+    }
+
+    public void addErrorsIfNecessary(ThreadContext context, XmlDocument doc) {
+        doc.setInstanceVariable("@errors", mapErrors(context, errorHandler));
+    }
+
+
+    public static RubyArray mapErrors(ThreadContext context, NokogiriErrorHandler errorHandler) {
+        final Ruby runtime = context.runtime;
+        final List<Exception> errors = errorHandler.getErrors();
+        final IRubyObject[] errorsAry = new IRubyObject[errors.size()];
+        for (int i = 0; i < errors.size(); i++) {
+            XmlSyntaxError xmlSyntaxError = XmlSyntaxError.createXMLSyntaxError(runtime);
+            xmlSyntaxError.setException(errors.get(i));
+            errorsAry[i] = xmlSyntaxError;
+        }
+        return runtime.newArrayNoCopy(errorsAry);
+    }
+
+    public XmlDocument getDocumentWithErrorsOrRaiseException(ThreadContext context, RubyClass klazz, Exception ex) {
+        if (options.recover) {
+            XmlDocument xmlDocument = getInterruptedOrNewXmlDocument(context, klazz);
+            this.addErrorsIfNecessary(context, xmlDocument);
+            XmlSyntaxError xmlSyntaxError = XmlSyntaxError.createXMLSyntaxError(context.runtime);
+            xmlSyntaxError.setException(ex);
+            ((RubyArray) xmlDocument.getInstanceVariable("@errors")).append(xmlSyntaxError);
+            return xmlDocument;
+        } else {
+            XmlSyntaxError xmlSyntaxError = XmlSyntaxError.createXMLSyntaxError(context.runtime);
+            xmlSyntaxError.setException(ex);
+            throw new RaiseException(xmlSyntaxError);
+        }
+    }
+    
+    private XmlDocument getInterruptedOrNewXmlDocument(ThreadContext context, RubyClass klazz) {
+        Document document = parser.getDocument();
+        XmlDocument xmlDocument = (XmlDocument) NokogiriService.XML_DOCUMENT_ALLOCATOR.allocate(context.getRuntime(), klazz);
+        if (document != null) {
+            xmlDocument.setDocumentNode(context, document);
+        }
+        xmlDocument.setEncoding(ruby_encoding);
+        return xmlDocument;
+    }
+
+    protected XmlDocument getNewEmptyDocument(ThreadContext context) {
+        IRubyObject[] args = new IRubyObject[0];
+        return (XmlDocument) XmlDocument.rbNew(context, getNokogiriClass(context.getRuntime(), "Nokogiri::XML::Document"), args);
+    }
+
+    /**
+     * This method is broken out so that HtmlDomParserContext can
+     * override it.
+     */
+    protected XmlDocument wrapDocument(ThreadContext context,
+                                       RubyClass klazz,
+                                       Document doc) {
+        XmlDocument xmlDocument = (XmlDocument) NokogiriService.XML_DOCUMENT_ALLOCATOR.allocate(context.getRuntime(), klazz);
+        xmlDocument.setDocumentNode(context, doc);
+        xmlDocument.setEncoding(ruby_encoding);
+
+        if (options.dtdLoad) {
+            IRubyObject xmlDtdOrNil = XmlDtd.newFromExternalSubset(context.getRuntime(), doc);
+            if (!xmlDtdOrNil.isNil()) {
+                XmlDtd xmlDtd = (XmlDtd) xmlDtdOrNil;
+                doc.setUserData(XmlDocument.DTD_EXTERNAL_SUBSET, xmlDtd, null);
+            }
+        }
+        return xmlDocument;
+    }
+
+    /**
+     * Must call setInputSource() before this method.
+     */
+    public XmlDocument parse(ThreadContext context,
+                             IRubyObject klazz,
+                             IRubyObject url) {
+        XmlDocument xmlDoc;
+        try {
+            Document doc = do_parse();
+            xmlDoc = wrapDocument(context, (RubyClass)klazz, doc);
+            xmlDoc.setUrl(url);
+            addErrorsIfNecessary(context, xmlDoc);
+            return xmlDoc;
+        } catch (SAXException e) {
+            return getDocumentWithErrorsOrRaiseException(context, (RubyClass)klazz, e);
+        } catch (IOException e) {
+            return getDocumentWithErrorsOrRaiseException(context, (RubyClass)klazz, e);
+        }
+    }
+
+    protected Document do_parse() throws SAXException, IOException {
+        try {
+          parser.parse(getInputSource());
+        } catch (NullPointerException ex) {
+          // FIXME: this is really a hack to fix #838. Xerces will throw a NullPointerException
+          // if we tried to parse '<? ?>'. We should submit a patch to Xerces.
+        }
+        if (options.noBlanks) {
+            List<Node> emptyNodes = new ArrayList<Node>();
+            findEmptyTexts(parser.getDocument(), emptyNodes);
+            if (emptyNodes.size() > 0) {
+                for (Node node : emptyNodes) {
+                    node.getParentNode().removeChild(node);
+                }
+            }
+        }
+        return parser.getDocument();
+    }
+    
+    private static void findEmptyTexts(Node node, List<Node> emptyNodes) {
+        if (node.getNodeType() == Node.TEXT_NODE && isBlank(node.getTextContent())) {
+            emptyNodes.add(node);
+        } else {
+            NodeList children = node.getChildNodes();
+            for (int i=0; i < children.getLength(); i++) {
+                findEmptyTexts(children.item(i), emptyNodes);
+            }
+        }
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/internals/XmlSaxParser.java 1.10.0+dfsg1-2/ext/java/nokogiri/internals/XmlSaxParser.java
--- 1.8.4-1/ext/java/nokogiri/internals/XmlSaxParser.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/internals/XmlSaxParser.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,65 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri.internals;
+
+import org.apache.xerces.parsers.SAXParser;
+import org.apache.xerces.xni.Augmentations;
+import org.apache.xerces.xni.XNIException;
+
+/**
+ * Extends SAXParser in order to receive xmlDecl events and pass them
+ * on to a handler.
+ *
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ */
+public class XmlSaxParser extends SAXParser {
+
+    protected XmlDeclHandler xmlDeclHandler = null;
+
+    public XmlSaxParser() {
+        super();
+    }
+
+    public void setXmlDeclHandler(XmlDeclHandler xmlDeclHandler) {
+        this.xmlDeclHandler = xmlDeclHandler;
+    }
+
+    @Override
+    public void xmlDecl(String version, String encoding, String standalone,
+                        Augmentations augs) throws XNIException {
+        super.xmlDecl(version, encoding, standalone, augs);
+        if (xmlDeclHandler != null) {
+            xmlDeclHandler.xmlDecl(version, encoding, standalone);
+        }
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/NokogiriService.java 1.10.0+dfsg1-2/ext/java/nokogiri/NokogiriService.java
--- 1.8.4-1/ext/java/nokogiri/NokogiriService.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/NokogiriService.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,597 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyModule;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.runtime.load.BasicLibraryService;
+
+/**
+ * Class to provide Nokogiri. This class is used to make "require 'nokogiri'" work
+ * in JRuby. Also, this class holds a Ruby type cache and allocators of Ruby types.
+ * 
+ * @author headius
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+public class NokogiriService implements BasicLibraryService {
+    public boolean basicLoad(Ruby ruby) {
+        init(ruby);
+        return true;
+    }
+
+    public static Map<String, RubyClass> getNokogiriClassCache(Ruby ruby) {
+        return (Map<String, RubyClass>) ruby.getModule("Nokogiri").getInternalVariable("cache");
+    }
+
+    private static Map<String, RubyClass> populateNokogiriClassCahce(Ruby ruby) {
+        Map<String, RubyClass> nokogiriClassCache = new HashMap<String, RubyClass>();
+        nokogiriClassCache.put("Nokogiri::EncodingHandler", (RubyClass)ruby.getClassFromPath("Nokogiri::EncodingHandler"));
+        nokogiriClassCache.put("Nokogiri::HTML::Document", (RubyClass)ruby.getClassFromPath("Nokogiri::HTML::Document"));
+        nokogiriClassCache.put("Nokogiri::HTML::ElementDescription", (RubyClass)ruby.getClassFromPath("Nokogiri::HTML::ElementDescription"));
+        nokogiriClassCache.put("Nokogiri::XML::Attr", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Attr"));
+        nokogiriClassCache.put("Nokogiri::XML::Document", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Document"));
+        nokogiriClassCache.put("Nokogiri::XML::DocumentFragment", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::DocumentFragment"));
+        nokogiriClassCache.put("Nokogiri::XML::DTD", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::DTD"));
+        nokogiriClassCache.put("Nokogiri::XML::Text", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Text"));
+        nokogiriClassCache.put("Nokogiri::XML::Comment", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Comment"));
+        nokogiriClassCache.put("Nokogiri::XML::Element", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Element"));
+        nokogiriClassCache.put("Nokogiri::XML::ElementContent", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::ElementContent"));
+        nokogiriClassCache.put("Nokogiri::XML::ElementDecl", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::ElementDecl"));
+        nokogiriClassCache.put("Nokogiri::XML::EntityDecl", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::EntityDecl"));
+        nokogiriClassCache.put("Nokogiri::XML::EntityReference", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::EntityReference"));
+        nokogiriClassCache.put("Nokogiri::XML::ProcessingInstruction", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::ProcessingInstruction"));
+        nokogiriClassCache.put("Nokogiri::XML::CDATA", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::CDATA"));
+        nokogiriClassCache.put("Nokogiri::XML::Node", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Node"));
+        nokogiriClassCache.put("Nokogiri::XML::NodeSet", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::NodeSet"));
+        nokogiriClassCache.put("Nokogiri::XML::Namespace", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Namespace"));
+        nokogiriClassCache.put("Nokogiri::XML::SyntaxError", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::SyntaxError"));
+        nokogiriClassCache.put("Nokogiri::XML::Reader", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Reader"));
+        nokogiriClassCache.put("Nokogiri::XML::RelaxNG", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::RelaxNG"));
+        nokogiriClassCache.put("Nokogiri::XML::Schema", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::Schema"));
+        nokogiriClassCache.put("Nokogiri::XML::XPathContext", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::XPathContext"));
+        nokogiriClassCache.put("Nokogiri::XML::AttributeDecl", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::AttributeDecl"));
+        nokogiriClassCache.put("Nokogiri::XML::SAX::ParserContext", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::SAX::ParserContext"));
+        return Collections.unmodifiableMap(nokogiriClassCache);
+    }
+
+    private void init(Ruby ruby) {
+        RubyModule nokogiri = ruby.defineModule("Nokogiri");
+        RubyModule xmlModule = nokogiri.defineModuleUnder("XML");
+        RubyModule xmlSaxModule = xmlModule.defineModuleUnder("SAX");
+        RubyModule htmlModule = nokogiri.defineModuleUnder("HTML");
+        RubyModule htmlSaxModule = htmlModule.defineModuleUnder("SAX");
+        RubyModule xsltModule = nokogiri.defineModuleUnder("XSLT");
+
+        createJavaLibraryVersionConstants(ruby, nokogiri);
+        createNokogiriModule(ruby, nokogiri);
+        createSyntaxErrors(ruby, nokogiri, xmlModule);
+        RubyClass xmlNode = createXmlModule(ruby, xmlModule);
+        createHtmlModule(ruby, htmlModule);
+        createDocuments(ruby, xmlModule, htmlModule, xmlNode);
+        createSaxModule(ruby, xmlSaxModule, htmlSaxModule);
+        createXsltModule(ruby, xsltModule);
+        nokogiri.setInternalVariable("cache", populateNokogiriClassCahce(ruby));
+    }
+
+    private void createJavaLibraryVersionConstants(Ruby ruby, RubyModule nokogiri) {
+        nokogiri.defineConstant("XERCES_VERSION", ruby.newString(org.apache.xerces.impl.Version.getVersion()));
+        nokogiri.defineConstant("NEKO_VERSION", ruby.newString(org.cyberneko.html.Version.getVersion()));
+    }
+
+    private void createNokogiriModule(Ruby ruby, RubyModule nokogiri) {
+        RubyClass encHandler = nokogiri.defineClassUnder("EncodingHandler", ruby.getObject(), ENCODING_HANDLER_ALLOCATOR);
+        encHandler.defineAnnotatedMethods(EncodingHandler.class);
+    }
+    
+    private void createSyntaxErrors(Ruby ruby, RubyModule nokogiri, RubyModule xmlModule) {
+        RubyClass syntaxError = nokogiri.defineClassUnder("SyntaxError", ruby.getStandardError(), ruby.getStandardError().getAllocator());
+        RubyClass xmlSyntaxError = xmlModule.defineClassUnder("SyntaxError", syntaxError, XML_SYNTAXERROR_ALLOCATOR);
+        xmlSyntaxError.defineAnnotatedMethods(XmlSyntaxError.class);
+    }
+    
+    private RubyClass createXmlModule(Ruby ruby, RubyModule xmlModule) {
+        RubyClass node = xmlModule.defineClassUnder("Node", ruby.getObject(), XML_NODE_ALLOCATOR);
+        node.defineAnnotatedMethods(XmlNode.class);
+        
+        RubyClass attr = xmlModule.defineClassUnder("Attr", node, XML_ATTR_ALLOCATOR);
+        attr.defineAnnotatedMethods(XmlAttr.class);
+        
+        RubyClass attrDecl = xmlModule.defineClassUnder("AttributeDecl", node, XML_ATTRIBUTE_DECL_ALLOCATOR);
+        attrDecl.defineAnnotatedMethods(XmlAttributeDecl.class);
+        
+        RubyClass characterData = xmlModule.defineClassUnder("CharacterData", node, null);
+        
+        RubyClass comment = xmlModule.defineClassUnder("Comment", characterData, XML_COMMENT_ALLOCATOR);
+        comment.defineAnnotatedMethods(XmlComment.class);
+        
+        RubyClass text = xmlModule.defineClassUnder("Text", characterData, XML_TEXT_ALLOCATOR);
+        text.defineAnnotatedMethods(XmlText.class);
+        
+        RubyModule cdata = xmlModule.defineClassUnder("CDATA", text, XML_CDATA_ALLOCATOR);
+        cdata.defineAnnotatedMethods(XmlCdata.class);
+        
+        RubyClass dtd = xmlModule.defineClassUnder("DTD", node, XML_DTD_ALLOCATOR);
+        dtd.defineAnnotatedMethods(XmlDtd.class);
+
+        RubyClass documentFragment = xmlModule.defineClassUnder("DocumentFragment", node, XML_DOCUMENT_FRAGMENT_ALLOCATOR);
+        documentFragment.defineAnnotatedMethods(XmlDocumentFragment.class);
+        
+        RubyClass element = xmlModule.defineClassUnder("Element", node, XML_ELEMENT_ALLOCATOR);
+        element.defineAnnotatedMethods(XmlElement.class);
+        
+        RubyClass elementContent = xmlModule.defineClassUnder("ElementContent", ruby.getObject(), XML_ELEMENT_CONTENT_ALLOCATOR);
+        elementContent.defineAnnotatedMethods(XmlElementContent.class);
+        
+        RubyClass elementDecl = xmlModule.defineClassUnder("ElementDecl", node, XML_ELEMENT_DECL_ALLOCATOR);
+        elementDecl.defineAnnotatedMethods(XmlElementDecl.class);
+        
+        RubyClass entityDecl = xmlModule.defineClassUnder("EntityDecl", node, XML_ENTITY_DECL_ALLOCATOR);
+        entityDecl.defineAnnotatedMethods(XmlEntityDecl.class);
+        
+        entityDecl.defineConstant("INTERNAL_GENERAL", RubyFixnum.newFixnum(ruby, XmlEntityDecl.INTERNAL_GENERAL));
+        entityDecl.defineConstant("EXTERNAL_GENERAL_PARSED", RubyFixnum.newFixnum(ruby, XmlEntityDecl.EXTERNAL_GENERAL_PARSED));
+        entityDecl.defineConstant("EXTERNAL_GENERAL_UNPARSED", RubyFixnum.newFixnum(ruby, XmlEntityDecl.EXTERNAL_GENERAL_UNPARSED));
+        entityDecl.defineConstant("INTERNAL_PARAMETER", RubyFixnum.newFixnum(ruby, XmlEntityDecl.INTERNAL_PARAMETER));
+        entityDecl.defineConstant("EXTERNAL_PARAMETER", RubyFixnum.newFixnum(ruby, XmlEntityDecl.EXTERNAL_PARAMETER));
+        entityDecl.defineConstant("INTERNAL_PREDEFINED", RubyFixnum.newFixnum(ruby, XmlEntityDecl.INTERNAL_PREDEFINED));
+        
+        RubyClass entref = xmlModule.defineClassUnder("EntityReference", node, XML_ENTITY_REFERENCE_ALLOCATOR);
+        entref.defineAnnotatedMethods(XmlEntityReference.class);
+        
+        RubyClass namespace = xmlModule.defineClassUnder("Namespace", ruby.getObject(), XML_NAMESPACE_ALLOCATOR);
+        namespace.defineAnnotatedMethods(XmlNamespace.class);
+        
+        RubyClass nodeSet = xmlModule.defineClassUnder("NodeSet", ruby.getObject(), XML_NODESET_ALLOCATOR);
+        nodeSet.defineAnnotatedMethods(XmlNodeSet.class);
+        
+        RubyClass pi = xmlModule.defineClassUnder("ProcessingInstruction", node, XML_PROCESSING_INSTRUCTION_ALLOCATOR);
+        pi.defineAnnotatedMethods(XmlProcessingInstruction.class);
+        
+        RubyClass reader = xmlModule.defineClassUnder("Reader", ruby.getObject(), XML_READER_ALLOCATOR);
+        reader.defineAnnotatedMethods(XmlReader.class);
+        
+        RubyClass schema = xmlModule.defineClassUnder("Schema", ruby.getObject(), XML_SCHEMA_ALLOCATOR);
+        schema.defineAnnotatedMethods(XmlSchema.class);
+
+        RubyClass relaxng = xmlModule.defineClassUnder("RelaxNG", schema, XML_RELAXNG_ALLOCATOR);
+        relaxng.defineAnnotatedMethods(XmlRelaxng.class);
+        
+        RubyClass xpathContext = xmlModule.defineClassUnder("XPathContext", ruby.getObject(), XML_XPATHCONTEXT_ALLOCATOR);
+        xpathContext.defineAnnotatedMethods(XmlXpathContext.class);
+        
+        return node;
+    }
+
+    private void createHtmlModule(Ruby ruby, RubyModule htmlModule) {
+        RubyClass htmlElemDesc = htmlModule.defineClassUnder("ElementDescription", ruby.getObject(), HTML_ELEMENT_DESCRIPTION_ALLOCATOR);
+        htmlElemDesc.defineAnnotatedMethods(HtmlElementDescription.class);
+        
+        RubyClass htmlEntityLookup = htmlModule.defineClassUnder("EntityLookup", ruby.getObject(), HTML_ENTITY_LOOKUP_ALLOCATOR);
+        htmlEntityLookup.defineAnnotatedMethods(HtmlEntityLookup.class);
+    }
+    
+    private void createDocuments(Ruby ruby, RubyModule xmlModule, RubyModule htmlModule, RubyClass node) {
+        RubyClass xmlDocument = xmlModule.defineClassUnder("Document", node, XML_DOCUMENT_ALLOCATOR);
+        xmlDocument.defineAnnotatedMethods(XmlDocument.class);
+        
+        //RubyModule htmlDoc = html.defineOrGetClassUnder("Document", document);
+        RubyModule htmlDocument = htmlModule.defineClassUnder("Document", xmlDocument, HTML_DOCUMENT_ALLOCATOR);
+        htmlDocument.defineAnnotatedMethods(HtmlDocument.class);
+    }
+    
+    private void createSaxModule(Ruby ruby, RubyModule xmlSaxModule, RubyModule htmlSaxModule) {
+        RubyClass xmlSaxParserContext = xmlSaxModule.defineClassUnder("ParserContext", ruby.getObject(), XML_SAXPARSER_CONTEXT_ALLOCATOR);
+        xmlSaxParserContext.defineAnnotatedMethods(XmlSaxParserContext.class);
+        
+        RubyClass xmlSaxPushParser = xmlSaxModule.defineClassUnder("PushParser", ruby.getObject(), XML_SAXPUSHPARSER_ALLOCATOR);
+        xmlSaxPushParser.defineAnnotatedMethods(XmlSaxPushParser.class);
+        
+        RubyClass htmlSaxPushParser = htmlSaxModule.defineClassUnder("PushParser", ruby.getObject(), HTML_SAXPUSHPARSER_ALLOCATOR);
+        htmlSaxPushParser.defineAnnotatedMethods(HtmlSaxPushParser.class);
+        
+        RubyClass htmlSaxParserContext = htmlSaxModule.defineClassUnder("ParserContext", xmlSaxParserContext, HTML_SAXPARSER_CONTEXT_ALLOCATOR);
+        htmlSaxParserContext.defineAnnotatedMethods(HtmlSaxParserContext.class);
+    }
+    
+    private void createXsltModule(Ruby ruby, RubyModule xsltModule) {
+        RubyClass stylesheet = xsltModule.defineClassUnder("Stylesheet", ruby.getObject(), XSLT_STYLESHEET_ALLOCATOR);
+        stylesheet.defineAnnotatedMethods(XsltStylesheet.class);
+        xsltModule.defineAnnotatedMethod(XsltStylesheet.class, "register");
+    }
+
+    private static ObjectAllocator ENCODING_HANDLER_ALLOCATOR = new ObjectAllocator() {
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            return new EncodingHandler(runtime, klazz, "");
+        }
+    };
+
+    public static final ObjectAllocator HTML_DOCUMENT_ALLOCATOR = new ObjectAllocator() {
+        private HtmlDocument htmlDocument = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (htmlDocument == null) htmlDocument = new HtmlDocument(runtime, klazz);
+            try {
+                HtmlDocument clone = (HtmlDocument) htmlDocument.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new HtmlDocument(runtime, klazz);
+            }
+        }
+    };
+
+    public static final ObjectAllocator HTML_SAXPARSER_CONTEXT_ALLOCATOR = new ObjectAllocator() {
+        private HtmlSaxParserContext htmlSaxParserContext = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (htmlSaxParserContext == null) htmlSaxParserContext = new HtmlSaxParserContext(runtime, klazz);
+            try {
+                HtmlSaxParserContext clone = (HtmlSaxParserContext) htmlSaxParserContext.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new HtmlSaxParserContext(runtime, klazz);
+            }
+        }
+    };
+
+    private static ObjectAllocator HTML_ELEMENT_DESCRIPTION_ALLOCATOR =
+        new ObjectAllocator() {
+            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+                return new HtmlElementDescription(runtime, klazz);
+            }
+        };
+
+    private static ObjectAllocator HTML_ENTITY_LOOKUP_ALLOCATOR =
+        new ObjectAllocator() {
+            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+                return new HtmlEntityLookup(runtime, klazz);
+            }
+        };
+
+    public static final ObjectAllocator XML_ATTR_ALLOCATOR = new ObjectAllocator() {
+        private XmlAttr xmlAttr = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlAttr == null) xmlAttr = new XmlAttr(runtime, klazz);
+            try {
+                XmlAttr clone = (XmlAttr) xmlAttr.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlAttr(runtime, klazz);
+            }
+        }
+    };
+
+    public static final ObjectAllocator XML_CDATA_ALLOCATOR = new ObjectAllocator() {
+        private XmlCdata xmlCdata = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlCdata == null) xmlCdata = new XmlCdata(runtime, klazz);
+            try {
+                XmlCdata clone = (XmlCdata) xmlCdata.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlCdata(runtime, klazz);
+            }
+        }
+    };
+
+    public static final ObjectAllocator XML_COMMENT_ALLOCATOR = new ObjectAllocator() {
+        private XmlComment xmlComment = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlComment == null) xmlComment = new XmlComment(runtime, klazz);
+            try {
+                XmlComment clone = (XmlComment) xmlComment.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlComment(runtime, klazz);
+            }
+        }
+    };
+
+    public static final ObjectAllocator XML_DOCUMENT_ALLOCATOR = new ObjectAllocator() {
+        private XmlDocument xmlDocument = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlDocument == null) xmlDocument = new XmlDocument(runtime, klazz);
+            try {
+                XmlDocument clone = (XmlDocument) xmlDocument.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlDocument(runtime, klazz);
+            }
+        }
+    };
+
+    public static final ObjectAllocator XML_DOCUMENT_FRAGMENT_ALLOCATOR = new ObjectAllocator() {
+        private XmlDocumentFragment xmlDocumentFragment = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlDocumentFragment == null) xmlDocumentFragment = new XmlDocumentFragment(runtime, klazz);
+            try {
+                XmlDocumentFragment clone = (XmlDocumentFragment)xmlDocumentFragment.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlDocumentFragment(runtime, klazz);
+            }
+        }
+    };
+
+    public static final ObjectAllocator XML_DTD_ALLOCATOR = new ObjectAllocator() {
+        private XmlDtd xmlDtd = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlDtd == null) xmlDtd = new XmlDtd(runtime, klazz);
+            try {
+                XmlDtd clone = (XmlDtd)xmlDtd.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlDtd(runtime, klazz);
+            }
+        }
+    };
+
+    public static final ObjectAllocator XML_ELEMENT_ALLOCATOR = new ObjectAllocator() {
+        private XmlElement xmlElement = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlElement == null) xmlElement = new XmlElement(runtime, klazz);
+            try {
+                XmlElement clone = (XmlElement)xmlElement.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlElement(runtime, klazz);
+            }
+        }
+    };
+    
+    public static ObjectAllocator XML_ELEMENT_DECL_ALLOCATOR = new ObjectAllocator() {
+        private XmlElementDecl xmlElementDecl = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlElementDecl == null) xmlElementDecl = new XmlElementDecl(runtime, klazz);
+            try {
+                XmlElementDecl clone = (XmlElementDecl)xmlElementDecl.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlElementDecl(runtime, klazz);
+            }
+        }
+    };
+
+    public static ObjectAllocator XML_ENTITY_REFERENCE_ALLOCATOR = new ObjectAllocator() {
+        private XmlEntityReference xmlEntityRef = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlEntityRef == null) xmlEntityRef = new XmlEntityReference(runtime, klazz);
+            try {
+                XmlEntityReference clone = (XmlEntityReference)xmlEntityRef.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlEntityReference(runtime, klazz);
+            }
+        }
+    };
+    
+    public static final ObjectAllocator XML_NAMESPACE_ALLOCATOR = new ObjectAllocator() {
+        private XmlNamespace xmlNamespace = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlNamespace == null) xmlNamespace = new XmlNamespace(runtime, klazz);
+            try {
+                XmlNamespace clone = (XmlNamespace) xmlNamespace.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlNamespace(runtime, klazz);
+            }
+        }
+    };
+
+    public static final ObjectAllocator XML_NODE_ALLOCATOR = new ObjectAllocator() {
+        private XmlNode xmlNode = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlNode == null) xmlNode = new XmlNode(runtime, klazz);
+            try {
+                XmlNode clone  = (XmlNode) xmlNode.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlNode(runtime, klazz);
+            }
+        }
+    };
+
+    public static final ObjectAllocator XML_NODESET_ALLOCATOR = new ObjectAllocator() {
+        private XmlNodeSet xmlNodeSet = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlNodeSet == null) xmlNodeSet = new XmlNodeSet(runtime, klazz);
+            try {
+                XmlNodeSet clone  = (XmlNodeSet) xmlNodeSet.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlNodeSet(runtime, klazz);
+            }
+        }
+    };
+    
+    public static ObjectAllocator XML_PROCESSING_INSTRUCTION_ALLOCATOR = new ObjectAllocator() {
+        private XmlProcessingInstruction xmlProcessingInstruction = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlProcessingInstruction == null) xmlProcessingInstruction = new XmlProcessingInstruction(runtime, klazz);
+            try {
+                XmlProcessingInstruction clone = (XmlProcessingInstruction)xmlProcessingInstruction.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlProcessingInstruction(runtime, klazz);
+            }
+        }
+    };
+
+    public static ObjectAllocator XML_READER_ALLOCATOR = new ObjectAllocator() {
+        private XmlReader xmlReader = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlReader == null) xmlReader = new XmlReader(runtime, klazz);
+            try {
+                XmlReader clone  = (XmlReader) xmlReader.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                xmlReader = new XmlReader(runtime, klazz);
+                return xmlReader;
+            }
+        }
+    };
+
+    private static ObjectAllocator XML_ATTRIBUTE_DECL_ALLOCATOR = new ObjectAllocator() {
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            return new XmlAttributeDecl(runtime, klazz);
+        }
+    };
+
+    private static ObjectAllocator XML_ENTITY_DECL_ALLOCATOR = new ObjectAllocator() {
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            return new XmlEntityDecl(runtime, klazz);
+        }
+    };
+
+    private static ObjectAllocator XML_ELEMENT_CONTENT_ALLOCATOR = new ObjectAllocator() {
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            throw runtime.newNotImplementedError("not implemented");
+        }
+    };
+
+    public static final ObjectAllocator XML_RELAXNG_ALLOCATOR = new ObjectAllocator() {
+        private XmlRelaxng xmlRelaxng = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlRelaxng == null) xmlRelaxng = new XmlRelaxng(runtime, klazz);
+            try {
+                XmlRelaxng clone  = (XmlRelaxng) xmlRelaxng.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlRelaxng(runtime, klazz);
+            }
+        }
+    };
+
+    public static final ObjectAllocator XML_SAXPARSER_CONTEXT_ALLOCATOR = new ObjectAllocator() {
+        private XmlSaxParserContext xmlSaxParserContext = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlSaxParserContext == null) xmlSaxParserContext = new XmlSaxParserContext(runtime, klazz);
+            try {
+                XmlSaxParserContext clone = (XmlSaxParserContext) xmlSaxParserContext.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlSaxParserContext(runtime, klazz);
+            }
+        }
+    };
+
+    private static final ObjectAllocator XML_SAXPUSHPARSER_ALLOCATOR = new ObjectAllocator() {
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            return new XmlSaxPushParser(runtime, klazz);
+        }
+    };
+
+    private static final ObjectAllocator HTML_SAXPUSHPARSER_ALLOCATOR = new ObjectAllocator() {
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            return new HtmlSaxPushParser(runtime, klazz);
+        }
+    };
+
+    public static final ObjectAllocator XML_SCHEMA_ALLOCATOR = new ObjectAllocator() {
+        private XmlSchema xmlSchema = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlSchema == null) xmlSchema = new XmlSchema(runtime, klazz);
+            try {
+                XmlSchema clone  = (XmlSchema) xmlSchema.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlSchema(runtime, klazz);
+            }
+        }
+    };
+
+    public static final ObjectAllocator XML_SYNTAXERROR_ALLOCATOR = new ObjectAllocator() {
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            return new XmlSyntaxError(runtime, klazz);
+        }
+    };
+
+    public static final ObjectAllocator XML_TEXT_ALLOCATOR = new ObjectAllocator() {
+        private XmlText xmlText = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xmlText == null) xmlText = new XmlText(runtime, klazz);
+            try {
+                XmlText clone  = (XmlText) xmlText.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlText(runtime, klazz);
+            }
+        }
+    };
+
+    public static final ObjectAllocator XML_XPATHCONTEXT_ALLOCATOR = new ObjectAllocator() {
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            return new XmlXpathContext(runtime, klazz);
+        }
+    };
+
+    public static ObjectAllocator XSLT_STYLESHEET_ALLOCATOR = new ObjectAllocator() {
+        private XsltStylesheet xsltStylesheet = null;
+        public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+            if (xsltStylesheet == null) xsltStylesheet = new XsltStylesheet(runtime, klazz);
+            try {
+                XsltStylesheet clone  = (XsltStylesheet) xsltStylesheet.clone();
+                clone.setMetaClass(klazz);
+                return clone;
+            } catch (CloneNotSupportedException e) {
+                return new XmlText(runtime, klazz);
+            }
+        }
+    };
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlAttributeDecl.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlAttributeDecl.java
--- 1.8.4-1/ext/java/nokogiri/XmlAttributeDecl.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlAttributeDecl.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,130 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * DTD attribute declaration.
+ *
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ */
+@JRubyClass(name="Nokogiri::XML::AttributeDecl", parent="Nokogiri::XML::Node")
+public class XmlAttributeDecl extends XmlNode {
+
+    public XmlAttributeDecl(Ruby ruby, RubyClass klass) {
+        super(ruby, klass);
+        throw ruby.newRuntimeError("node required");
+    }
+
+    /**
+     * Initialize based on an attributeDecl node from a NekoDTD parsed
+     * DTD.
+     *
+     * Internally, XmlAttributeDecl combines these into a single node.
+     */
+    public XmlAttributeDecl(Ruby ruby, RubyClass klass, Node attrDeclNode) {
+        super(ruby, klass, attrDeclNode);
+    }
+
+    public static IRubyObject create(ThreadContext context, Node attrDeclNode) {
+        XmlAttributeDecl self =
+            new XmlAttributeDecl(context.getRuntime(),
+                                 getNokogiriClass(context.getRuntime(), "Nokogiri::XML::AttributeDecl"),
+                                 attrDeclNode);
+        return self;
+    }
+
+    @Override
+    @JRubyMethod
+    public IRubyObject node_name(ThreadContext context) {
+        return attribute_name(context);
+    }
+
+    @Override
+    @JRubyMethod(name = "node_name=")
+    public IRubyObject node_name_set(ThreadContext context, IRubyObject name) {
+        throw context.getRuntime()
+            .newRuntimeError("cannot change name of DTD decl");
+    }
+
+    public IRubyObject element_name(ThreadContext context) {
+        return getAttribute(context, "ename");
+    }
+
+    public IRubyObject attribute_name(ThreadContext context) {
+        return getAttribute(context, "aname");
+    }
+
+    @JRubyMethod
+    public IRubyObject attribute_type(ThreadContext context) {
+        return getAttribute(context, "atype");
+    }
+
+    @JRubyMethod(name="default")
+    public IRubyObject default_value(ThreadContext context) {
+        return getAttribute(context, "default");
+    }
+
+    /**
+     * FIXME: will enumerations all be of the simple (val1|val2|val3)
+     * type string?
+     */
+    @JRubyMethod
+    public IRubyObject enumeration(ThreadContext context) {
+        RubyArray enumVals = RubyArray.newArray(context.getRuntime());
+        String atype = ((Element)node).getAttribute("atype");
+
+        if (atype != null && atype.length() != 0 && atype.charAt(0) == '(') {
+            // removed enclosing parens
+            String valueStr = atype.substring(1, atype.length() - 1);
+            String[] values = valueStr.split("\\|");
+            for (int i = 0; i < values.length; i++) {
+                enumVals.append(context.getRuntime().newString(values[i]));
+            }
+        }
+
+        return enumVals;
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlAttr.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlAttr.java
--- 1.8.4-1/ext/java/nokogiri/XmlAttr.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlAttr.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,169 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
+import nokogiri.internals.NokogiriHelpers;
+import nokogiri.internals.SaveContextVisitor;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Class for Nokogiri::XML::Attr
+ *
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+
+@JRubyClass(name="Nokogiri::XML::Attr", parent="Nokogiri::XML::Node")
+public class XmlAttr extends XmlNode {
+
+    public static final String[] HTML_BOOLEAN_ATTRS = {
+        "checked", "compact", "declare", "defer", "disabled", "ismap",
+        "multiple", "nohref", "noresize", "noshade", "nowrap", "readonly",
+        "selected"
+    };
+
+    public XmlAttr(Ruby ruby, Node attr){
+        super(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Attr"), attr);
+    }
+
+    public XmlAttr(Ruby ruby, RubyClass rubyClass) {
+        super(ruby, rubyClass);
+    }
+
+    public XmlAttr(Ruby ruby, RubyClass rubyClass, Node attr){
+        super(ruby, rubyClass, attr);
+    }
+
+    @Override
+    protected void init(ThreadContext context, IRubyObject[] args) {
+        if (args.length < 2) {
+            throw getRuntime().newArgumentError(args.length, 2);
+        }
+
+        IRubyObject doc = args[0];
+        IRubyObject content = args[1];
+
+        if(!(doc instanceof XmlDocument)) {
+            final String msg =
+                "document must be an instance of Nokogiri::XML::Document";
+            throw getRuntime().newArgumentError(msg);
+        }
+
+        XmlDocument xmlDoc = (XmlDocument)doc;
+        String str = rubyStringToString(content);
+        Node attr = xmlDoc.getDocument().createAttribute(str);
+        setNode(context, attr);
+    }
+    
+    
+    // this method is called from XmlNode.setNode()
+    // if the node is attribute, and its name has prefix "xml"
+    // the default namespace should be registered for this attribute
+    void setNamespaceIfNecessary(Ruby runtime) {
+        if ("xml".equals(node.getPrefix())) {
+           XmlNamespace.createDefaultNamespace(runtime, node); 
+        }
+    }
+
+    private boolean isHtmlBooleanAttr() {
+        String name = node.getNodeName().toLowerCase();
+
+        for(String s : HTML_BOOLEAN_ATTRS) {
+            if(s.equals(name)) return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    @JRubyMethod(name = {"content", "value", "to_s"})
+    public IRubyObject content(ThreadContext context) {
+        if (content != null && !content.isNil()) return content;
+        if (node == null) return context.getRuntime().getNil();
+        String attrValue = ((Attr)node).getValue();
+        if (attrValue == null) return context.getRuntime().getNil();
+        return RubyString.newString(context.getRuntime(), attrValue);
+    }
+    
+    @JRubyMethod(name = {"value=", "content="})
+    public IRubyObject value_set(ThreadContext context, IRubyObject content){
+        Attr attr = (Attr) node;
+        if (content != null && !content.isNil()) {
+            attr.setValue(rubyStringToString(XmlNode.encode_special_chars(context, content)));
+        }
+        setContent(content);
+        return content;
+    }
+
+    @Override
+    protected IRubyObject getNodeName(ThreadContext context) {
+        if (name != null) return name;
+        String attrName = ((Attr)node).getName();
+        if (!(doc instanceof HtmlDocument) && node.getNamespaceURI() != null) {
+            attrName = NokogiriHelpers.getLocalPart(attrName);
+        }
+        return attrName == null ? context.getRuntime().getNil() : RubyString.newString(context.getRuntime(), attrName);
+    }
+
+    @Override
+    public void accept(ThreadContext context, SaveContextVisitor visitor) {
+        visitor.enter((Attr)node);
+        visitor.leave((Attr)node);
+    }
+    
+    private boolean isHtml(ThreadContext context) {
+        return document(context).getMetaClass().isKindOfModule(getNokogiriClass(context.getRuntime(), "Nokogiri::HTML::Document"));
+    }
+
+    @Override
+    public IRubyObject unlink(ThreadContext context) {
+        Attr attr = (Attr) node;
+        Element parent = attr.getOwnerElement();
+        parent.removeAttributeNode(attr);
+
+        return this;
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlCdata.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlCdata.java
--- 1.8.4-1/ext/java/nokogiri/XmlCdata.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlCdata.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,83 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
+
+import nokogiri.internals.SaveContextVisitor;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.anno.JRubyClass;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+/**
+ * Class for Nokogiri::XML::CDATA
+ *
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+
+@JRubyClass(name="Nokogiri::XML::CDATA", parent="Nokogiri::XML::Text")
+public class XmlCdata extends XmlText {
+    public XmlCdata(Ruby ruby, RubyClass rubyClass) {
+        super(ruby, rubyClass);
+    }
+    
+    public XmlCdata(Ruby ruby, RubyClass rubyClass, Node node) {
+        super(ruby, rubyClass, node);
+    }
+
+    @Override
+    protected void init(ThreadContext context, IRubyObject[] args) {
+        if (args.length < 2) {
+            throw getRuntime().newArgumentError(args.length, 2);
+        }
+        IRubyObject doc = args[0];
+        content = args[1];
+        XmlDocument xmlDoc =(XmlDocument) ((XmlNode) doc).document(context);
+        Document document = xmlDoc.getDocument();
+        Node node = document.createCDATASection((content.isNil()) ? null : rubyStringToString(content));
+        setNode(context, node);
+    }
+
+    @Override
+    public void accept(ThreadContext context, SaveContextVisitor visitor) {
+        visitor.enter((CDATASection)node);
+        visitor.leave((CDATASection)node);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlComment.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlComment.java
--- 1.8.4-1/ext/java/nokogiri/XmlComment.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlComment.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,97 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
+import nokogiri.internals.SaveContextVisitor;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.anno.JRubyClass;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+/**
+ * Class for Nokogiri::XML::Comment
+ *
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+
+@JRubyClass(name="Nokogiri::XML::Comment", parent="Nokogiri::XML::CharacterData")
+public class XmlComment extends XmlNode {
+    public XmlComment(Ruby ruby, RubyClass rubyClass, Node node) {
+        super(ruby, rubyClass, node);
+    }
+
+    public XmlComment(Ruby runtime, RubyClass klass) {
+        super(runtime, klass);
+    }
+
+    @Override
+    protected void init(ThreadContext context, IRubyObject[] args) {
+        if (args.length < 2)
+            throw getRuntime().newArgumentError(args.length, 2);
+
+        IRubyObject doc = args[0];
+        IRubyObject text = args[1];
+
+        XmlDocument xmlDoc;
+        if (doc instanceof XmlDocument) {
+            xmlDoc = (XmlDocument) doc;
+            
+        } else if (doc instanceof XmlNode) {
+            XmlNode xmlNode = (XmlNode) doc;
+            xmlDoc = (XmlDocument)xmlNode.document(context);
+        } else {
+            throw getRuntime().newArgumentError("first argument must be a XML::Document or XML::Node");
+        }
+        if (xmlDoc != null) {
+            Document document = xmlDoc.getDocument();
+            Node node = document.createComment(rubyStringToString(text));
+            setNode(context, node);
+        }
+    }
+
+    @Override
+    public boolean isComment() { return true; }
+
+    @Override
+    public void accept(ThreadContext context, SaveContextVisitor visitor) {
+        visitor.enter((Comment)node);
+        visitor.leave((Comment)node);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlDocumentFragment.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlDocumentFragment.java
--- 1.8.4-1/ext/java/nokogiri/XmlDocumentFragment.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlDocumentFragment.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,204 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getLocalNameForNamespace;
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.getPrefix;
+import static nokogiri.internals.NokogiriHelpers.isNamespace;
+import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.javasupport.util.RuntimeHelpers;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+
+/**
+ * Class for Nokogiri::XML::DocumentFragment
+ * 
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::DocumentFragment", parent="Nokogiri::XML::Node")
+public class XmlDocumentFragment extends XmlNode {
+    private XmlElement fragmentContext = null;
+
+    public XmlDocumentFragment(Ruby ruby) {
+        this(ruby, getNokogiriClass(ruby, "Nokogiri::XML::DocumentFragment"));
+    }
+
+    public XmlDocumentFragment(Ruby ruby, RubyClass klazz) {
+        super(ruby, klazz);
+    }
+
+    @JRubyMethod(name="new", meta = true, required=1, optional=2)
+    public static IRubyObject rbNew(ThreadContext context, IRubyObject cls, IRubyObject[] args) {
+        
+        if(args.length < 1) {
+            throw context.getRuntime().newArgumentError(args.length, 1);
+        }
+
+        if(!(args[0] instanceof XmlDocument)){
+            throw context.getRuntime().newArgumentError("first parameter must be a Nokogiri::XML::Document instance");
+        }
+
+        XmlDocument doc = (XmlDocument) args[0];
+        
+        // make wellformed fragment, ignore invalid namespace, or add appropriate namespace to parse
+        if (args.length > 1 && args[1] instanceof RubyString) {
+            if (XmlDocumentFragment.isTag((RubyString)args[1])) {
+                args[1] = RubyString.newString(context.getRuntime(), addNamespaceDeclIfNeeded(doc, rubyStringToString(args[1])));
+            }
+        }
+
+        XmlDocumentFragment fragment = (XmlDocumentFragment) NokogiriService.XML_DOCUMENT_FRAGMENT_ALLOCATOR.allocate(context.getRuntime(), (RubyClass)cls);
+        fragment.setDocument(context, doc);
+        fragment.setNode(context, doc.getDocument().createDocumentFragment());
+
+        //TODO: Get namespace definitions from doc.
+        if (args.length == 3 && args[2] != null && args[2] instanceof XmlElement) {
+            fragment.fragmentContext = (XmlElement)args[2];
+        }
+        RuntimeHelpers.invoke(context, fragment, "initialize", args);
+        return fragment;
+    }
+
+    private static final ByteList TAG_BEG = ByteList.create("<");
+    private static final ByteList TAG_END = ByteList.create(">");
+
+    private static boolean isTag(final RubyString str) {
+        return str.getByteList().startsWith(TAG_BEG) && str.getByteList().endsWith(TAG_END);
+    }
+
+    private static boolean isNamespaceDefined(String qName, NamedNodeMap nodeMap) {
+        if (isNamespace(qName.intern())) return true;
+        for (int i=0; i < nodeMap.getLength(); i++) {
+            Attr attr = (Attr)nodeMap.item(i);
+            if (isNamespace(attr.getNodeName())) {
+                String localPart = getLocalNameForNamespace(attr.getNodeName());
+                if (getPrefix(qName).equals(localPart)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private static final Pattern QNAME_RE = Pattern.compile("[^</:>\\s]+:[^</:>=\\s]+");
+    private static final Pattern START_TAG_RE = Pattern.compile("<[^</>]+>");
+
+    private static String addNamespaceDeclIfNeeded(XmlDocument doc, String tags) {
+        if (doc.getDocument() == null) return tags;
+        if (doc.getDocument().getDocumentElement() == null) return tags;
+        Matcher matcher = START_TAG_RE.matcher(tags);
+        Map<CharSequence, CharSequence> rewriteTable = null;
+        while (matcher.find()) {
+            String start_tag = matcher.group();
+            Matcher matcher2 = QNAME_RE.matcher(start_tag);
+            while (matcher2.find()) {
+                String qName = matcher2.group();
+                NamedNodeMap nodeMap = doc.getDocument().getDocumentElement().getAttributes();
+                if (isNamespaceDefined(qName, nodeMap)) {
+                    CharSequence namespaceDecl = getNamespaceDecl(getPrefix(qName), nodeMap);
+                    if (namespaceDecl != null) {
+                        if (rewriteTable == null) rewriteTable = new HashMap(8, 1);
+                        StringBuilder str = new StringBuilder(qName.length() + namespaceDecl.length() + 3);
+                        String key = str.append('<').append(qName).append('>').toString();
+                        str.setCharAt(key.length() - 1, ' '); // (last) '>' -> ' '
+                        rewriteTable.put(key, str.append(namespaceDecl).append('>'));
+                    }
+                }
+            }
+        }
+        if (rewriteTable != null) {
+            for (Map.Entry<CharSequence, CharSequence> e : rewriteTable.entrySet()) {
+                tags = tags.replace(e.getKey(), e.getValue());
+            }
+        }
+        
+        return tags;
+    }
+    
+    private static CharSequence getNamespaceDecl(final String prefix, NamedNodeMap nodeMap) {
+        for (int i=0; i < nodeMap.getLength(); i++) {
+            Attr attr = (Attr) nodeMap.item(i);
+            if (prefix.equals(attr.getLocalName())) {
+                return new StringBuilder().
+                    append(attr.getName()).append('=').append('"').append(attr.getValue()).append('"');
+            }
+        }
+        return null;
+    }
+
+    public XmlElement getFragmentContext() {
+        return fragmentContext;
+    }
+
+    //@Override
+    public void add_child(ThreadContext context, XmlNode child) {
+        // Some magic for DocumentFragment
+
+        Ruby ruby = context.getRuntime();
+        XmlNodeSet children = (XmlNodeSet) child.children(context);
+
+        long length = children.length();
+
+        RubyArray childrenArray = children.convertToArray();
+
+        if(length != 0) {
+            for(int i = 0; i < length; i++) {
+                XmlNode item = (XmlNode) ((XmlNode) childrenArray.aref(ruby.newFixnum(i))).dup_implementation(context, true);
+                add_child(context, item);
+            }
+        }
+    }
+
+    @Override
+    public void relink_namespace(ThreadContext context) {
+        ((XmlNodeSet) children(context)).relink_namespace(context);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlDocument.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlDocument.java
--- 1.8.4-1/ext/java/nokogiri/XmlDocument.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlDocument.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,646 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2014:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.clearXpathContext;
+import static nokogiri.internals.NokogiriHelpers.getCachedNodeOrCreate;
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.isNamespace;
+import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
+import static nokogiri.internals.NokogiriHelpers.stringOrNil;
+
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import nokogiri.internals.NokogiriHelpers;
+import nokogiri.internals.NokogiriNamespaceCache;
+import nokogiri.internals.SaveContextVisitor;
+import nokogiri.internals.XmlDomParserContext;
+import nokogiri.internals.c14n.CanonicalFilter;
+import nokogiri.internals.c14n.CanonicalizationException;
+import nokogiri.internals.c14n.Canonicalizer;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyNil;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.javasupport.JavaUtil;
+import org.jruby.javasupport.util.RuntimeHelpers;
+import org.jruby.runtime.Arity;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Class for Nokogiri::XML::Document
+ *
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ * @author John Shahid <jvshahid@gmail.com>
+ */
+
+@JRubyClass(name="Nokogiri::XML::Document", parent="Nokogiri::XML::Node")
+public class XmlDocument extends XmlNode {
+    private NokogiriNamespaceCache nsCache;
+
+    /* UserData keys for storing extra info in the document node. */
+    public final static String DTD_RAW_DOCUMENT = "DTD_RAW_DOCUMENT";
+    public final static String DTD_INTERNAL_SUBSET = "DTD_INTERNAL_SUBSET";
+    public final static String DTD_EXTERNAL_SUBSET = "DTD_EXTERNAL_SUBSET";
+
+    /* DocumentBuilderFactory implementation class name. This needs to set a classloader into it.
+     * Setting an appropriate classloader resolves issue 380.
+     */
+    private static final String DOCUMENTBUILDERFACTORY_IMPLE_NAME = "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl";
+
+    private static boolean substituteEntities = false;
+    private static boolean loadExternalSubset = false; // TODO: Verify this.
+
+    /** cache variables */
+    protected IRubyObject encoding = null;
+    protected IRubyObject url = null;
+
+    public XmlDocument(Ruby ruby, RubyClass klazz) {
+        super(ruby, klazz, createNewDocument());
+    }
+
+    public XmlDocument(Ruby ruby, Document document) {
+        this(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Document"), document);
+    }
+
+    public XmlDocument(Ruby ruby, RubyClass klass, Document document) {
+        super(ruby, klass, document);
+        initializeNamespaceCacheIfNecessary();
+        createAndCacheNamespaces(ruby, document.getDocumentElement());
+        stabilizeTextContent(document);
+        setInstanceVariable("@decorators", ruby.getNil());
+    }
+
+    public void setDocumentNode(ThreadContext context, Node node) {
+        super.setNode(context, node);
+        initializeNamespaceCacheIfNecessary();
+        Ruby runtime = context.getRuntime();
+        if (node != null) {
+            Document document = (Document)node;
+            stabilizeTextContent(document);
+            createAndCacheNamespaces(runtime, document.getDocumentElement());
+        }
+        setInstanceVariable("@decorators", runtime.getNil());
+    }
+
+    public void setEncoding(IRubyObject encoding) {
+        this.encoding = encoding;
+    }
+
+    public IRubyObject getEncoding() {
+        return encoding;
+    }
+
+    // not sure, but like attribute values, text value will be lost
+    // unless it is referred once before this document is used.
+    // this seems to happen only when the fragment is parsed from Node#in_context.
+    protected void stabilizeTextContent(Document document) {
+        if (document.getDocumentElement() != null) document.getDocumentElement().getTextContent();
+    }
+
+    private void createAndCacheNamespaces(Ruby ruby, Node node) {
+        if (node == null) return;
+        if (node.hasAttributes()) {
+            NamedNodeMap nodeMap = node.getAttributes();
+            for (int i=0; i<nodeMap.getLength(); i++) {
+                Node n = nodeMap.item(i);
+                if (n instanceof Attr) {
+                    Attr attr = (Attr)n;
+                    String attrName = attr.getName();
+                    // not sure, but need to get value always before document is referred.
+                    // or lose attribute value
+                    String attrValue = attr.getValue(); // don't delete this line
+                    if (isNamespace(attrName)) {
+                        // create and cache
+                        XmlNamespace.createFromAttr(ruby, attr);
+                    }
+                }
+            }
+        }
+        NodeList children = node.getChildNodes();
+        for (int i=0; i<children.getLength(); i++) {
+            createAndCacheNamespaces(ruby, children.item(i));
+        }
+    }
+
+    // When a document is created from fragment with a context (reference) document,
+    // namespace should be resolved based on the context document.
+    public XmlDocument(Ruby ruby, RubyClass klass, Document document, XmlDocument contextDoc) {
+        super(ruby, klass, document);
+        nsCache = contextDoc.getNamespaceCache();
+        XmlNamespace default_ns = nsCache.getDefault();
+        String default_href = rubyStringToString(default_ns.href(ruby.getCurrentContext()));
+        resolveNamespaceIfNecessary(ruby.getCurrentContext(), document.getDocumentElement(), default_href);
+    }
+
+    private void resolveNamespaceIfNecessary(ThreadContext context, Node node, String default_href) {
+        if (node == null) return;
+        String nodePrefix = node.getPrefix();
+        if (nodePrefix == null) { // default namespace
+            NokogiriHelpers.renameNode(node, default_href, node.getNodeName());
+        } else {
+            XmlNamespace xmlNamespace = nsCache.get(node, nodePrefix);
+            String href = rubyStringToString(xmlNamespace.href(context));
+            NokogiriHelpers.renameNode(node, href, node.getNodeName());
+        }
+        resolveNamespaceIfNecessary(context, node.getNextSibling(), default_href);
+        NodeList children = node.getChildNodes();
+        for (int i=0; i<children.getLength(); i++) {
+            resolveNamespaceIfNecessary(context, children.item(i), default_href);
+        }
+    }
+
+    public NokogiriNamespaceCache getNamespaceCache() {
+        return nsCache;
+    }
+
+    public void initializeNamespaceCacheIfNecessary() {
+        if (nsCache == null) nsCache = new NokogiriNamespaceCache();
+    }
+
+    public void setNamespaceCache(NokogiriNamespaceCache nsCache) {
+        this.nsCache = nsCache;
+    }
+
+    public Document getDocument() {
+        return (Document) node;
+    }
+
+    @Override
+    protected IRubyObject getNodeName(ThreadContext context) {
+        if (name == null) name = context.getRuntime().newString("document");
+        return name;
+    }
+
+    public void setUrl(IRubyObject url) {
+        this.url = url;
+    }
+
+    protected IRubyObject getUrl() {
+        return this.url;
+    }
+
+    @JRubyMethod
+    public IRubyObject url(ThreadContext context) {
+        return getUrl();
+    }
+
+    public static Document createNewDocument() {
+        try {
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(DOCUMENTBUILDERFACTORY_IMPLE_NAME, NokogiriService.class.getClassLoader());
+            return factory.newDocumentBuilder().newDocument();
+        } catch (ParserConfigurationException e) {
+            return null;        // this will end is disaster...
+        }
+    }
+
+    /*
+     * call-seq:
+     *  new(version = default)
+     *
+     * Create a new document with +version+ (defaults to "1.0")
+     */
+    @JRubyMethod(name="new", meta = true, rest = true, required=0)
+    public static IRubyObject rbNew(ThreadContext context, IRubyObject klazz, IRubyObject[] args) {
+        XmlDocument xmlDocument;
+        try {
+            Document docNode = createNewDocument();
+            if ("Nokogiri::HTML::Document".equals(((RubyClass)klazz).getName())) {
+                xmlDocument = (XmlDocument) NokogiriService.HTML_DOCUMENT_ALLOCATOR.allocate(context.getRuntime(), (RubyClass) klazz);
+                xmlDocument.setDocumentNode(context, docNode);
+            } else {
+                // XML::Document and sublass
+                xmlDocument = (XmlDocument) NokogiriService.XML_DOCUMENT_ALLOCATOR.allocate(context.getRuntime(), (RubyClass) klazz);
+                xmlDocument.setDocumentNode(context, docNode);
+            }
+        } catch (Exception ex) {
+            throw context.getRuntime().newRuntimeError("couldn't create document: "+ex.toString());
+        }
+
+        RuntimeHelpers.invoke(context, xmlDocument, "initialize", args);
+
+        return xmlDocument;
+    }
+
+    @JRubyMethod(required=1, optional=4)
+    public IRubyObject create_entity(ThreadContext context, IRubyObject[] argv) {
+        // FIXME: Entity node should be create by some right way.
+        // this impl passes tests, but entity doesn't exists in DTD, which
+        // would cause validation failure.
+        if (argv.length == 0) throw context.getRuntime().newRuntimeError("Could not create entity");
+        String tagName = rubyStringToString(argv[0]);
+        Node n = this.getOwnerDocument().createElement(tagName);
+        return XmlEntityDecl.create(context, n, argv);
+    }
+
+    @Override
+    IRubyObject document(Ruby runtime) {
+        return this;
+    }
+
+    @JRubyMethod(name="encoding=")
+    public IRubyObject encoding_set(ThreadContext context, IRubyObject encoding) {
+        this.encoding = encoding;
+        return this;
+    }
+
+    @JRubyMethod
+    public IRubyObject encoding(ThreadContext context) {
+        if (this.encoding == null || this.encoding.isNil()) {
+            if (getDocument().getXmlEncoding() == null) {
+                this.encoding = context.getRuntime().getNil();
+            } else {
+                this.encoding = context.getRuntime().newString(getDocument().getXmlEncoding());
+            }
+        }
+
+        return this.encoding.isNil() ? this.encoding : this.encoding.asString().encode(context, context.getRuntime().newString("UTF-8"));
+    }
+
+    @JRubyMethod(meta = true)
+    public static IRubyObject load_external_subsets_set(ThreadContext context, IRubyObject cls, IRubyObject value) {
+        XmlDocument.loadExternalSubset = value.isTrue();
+        return context.getRuntime().getNil();
+    }
+
+    /**
+     * TODO: handle encoding?
+     *
+     * @param args[0] a Ruby IO or StringIO
+     * @param args[1] url or nil
+     * @param args[2] encoding
+     * @param args[3] bitset of parser options
+     */
+    public static IRubyObject newFromData(ThreadContext context,
+                                          IRubyObject klass,
+                                          IRubyObject[] args) {
+        Ruby ruby = context.getRuntime();
+        Arity.checkArgumentCount(ruby, args, 4, 4);
+        XmlDomParserContext ctx =
+            new XmlDomParserContext(ruby, args[2], args[3]);
+        ctx.setInputSource(context, args[0], args[1]);
+        return ctx.parse(context, klass, args[1]);
+    }
+
+    @JRubyMethod(meta = true, rest = true)
+    public static IRubyObject read_io(ThreadContext context,
+                                      IRubyObject klass,
+                                      IRubyObject[] args) {
+        return newFromData(context, klass, args);
+    }
+
+    @JRubyMethod(meta = true, rest = true)
+    public static IRubyObject read_memory(ThreadContext context,
+                                          IRubyObject klass,
+                                          IRubyObject[] args) {
+        return newFromData(context, klass, args);
+    }
+
+    /** not a JRubyMethod */
+    public static IRubyObject read_memory(ThreadContext context,
+                                          IRubyObject[] args) {
+        return read_memory(context,
+                           getNokogiriClass(context.getRuntime(), "Nokogiri::XML::Document"),
+                           args);
+    }
+
+    @JRubyMethod(name="remove_namespaces!")
+    public IRubyObject remove_namespaces(ThreadContext context) {
+        removeNamespceRecursively(context, this);
+        nsCache.clear();
+        clearXpathContext(getNode());
+        return this;
+    }
+
+    private void removeNamespceRecursively(ThreadContext context, XmlNode xmlNode) {
+        Node node = xmlNode.node;
+        if (node.getNodeType() == Node.ELEMENT_NODE) {
+            node.setPrefix(null);
+            NokogiriHelpers.renameNode(node, null, node.getLocalName());
+            NamedNodeMap attrs = node.getAttributes();
+            for (int i=0; i<attrs.getLength(); i++) {
+                Attr attr = (Attr) attrs.item(i);
+                if (isNamespace(attr.getNodeName())) {
+                    ((org.w3c.dom.Element)node).removeAttributeNode(attr);
+                } else {
+                    attr.setPrefix(null);
+                    NokogiriHelpers.renameNode(attr, null, attr.getLocalName());
+                }
+            }
+        }
+        XmlNodeSet nodeSet = (XmlNodeSet) xmlNode.children(context);
+        for (long i=0; i < nodeSet.length(); i++) {
+            XmlNode childNode = (XmlNode)nodeSet.slice(context, RubyFixnum.newFixnum(context.getRuntime(), i));
+            removeNamespceRecursively(context, childNode);
+        }
+    }
+
+    @JRubyMethod
+    public IRubyObject root(ThreadContext context) {
+        Node rootNode = getDocument().getDocumentElement();
+        try {
+            Boolean isValid = (Boolean)rootNode.getUserData(NokogiriHelpers.VALID_ROOT_NODE);
+            if (!isValid) return context.getRuntime().getNil();
+        } catch (NullPointerException e) {
+            // does nothing since nil wasn't set to the root node before.
+        }
+        if (rootNode == null)
+            return context.getRuntime().getNil();
+        else
+            return getCachedNodeOrCreate(context.getRuntime(), rootNode);
+    }
+
+    @JRubyMethod(name="root=")
+    public IRubyObject root_set(ThreadContext context, IRubyObject newRoot_) {
+        // in case of document fragment, temporary root node should be deleted.
+
+        // Java can't have a root whose value is null. Instead of setting null,
+        // the method sets user data so that other methods are able to know the root
+        // should be nil.
+        if (newRoot_ instanceof RubyNil) {
+            getDocument().getDocumentElement().setUserData(NokogiriHelpers.VALID_ROOT_NODE, false, null);
+            return newRoot_;
+        }
+        XmlNode newRoot = asXmlNode(context, newRoot_);
+
+        IRubyObject root = root(context);
+        if (root.isNil()) {
+            Node newRootNode;
+            if (getDocument() == newRoot.getOwnerDocument()) {
+                newRootNode = newRoot.node;
+            } else {
+                // must copy otherwise newRoot may exist in two places
+                // with different owner document.
+                newRootNode = getDocument().importNode(newRoot.node, true);
+            }
+            add_child_node(context, getCachedNodeOrCreate(context.getRuntime(), newRootNode));
+        } else {
+            Node rootNode = asXmlNode(context, root).node;
+            ((XmlNode)getCachedNodeOrCreate(context.getRuntime(), rootNode)).replace_node(context, newRoot);
+        }
+
+        return newRoot;
+    }
+
+    @JRubyMethod
+    public IRubyObject version(ThreadContext context) {
+        return stringOrNil(context.getRuntime(), getDocument().getXmlVersion());
+    }
+
+    @JRubyMethod(meta = true)
+    public static IRubyObject substitute_entities_set(ThreadContext context, IRubyObject cls, IRubyObject value) {
+        XmlDocument.substituteEntities = value.isTrue();
+        return context.getRuntime().getNil();
+    }
+
+    public IRubyObject getInternalSubset(ThreadContext context) {
+        IRubyObject dtd = (IRubyObject) node.getUserData(DTD_INTERNAL_SUBSET);
+
+        if (dtd == null) {
+            Document document = getDocument();
+            if (document.getUserData(XmlDocument.DTD_RAW_DOCUMENT) != null) {
+                dtd = XmlDtd.newFromInternalSubset(context.getRuntime(), document);
+            } else if (document.getDoctype() != null) {
+                DocumentType docType = document.getDoctype();
+                IRubyObject name, publicId, systemId;
+                name = publicId = systemId = context.getRuntime().getNil();
+                if (docType.getName() != null) {
+                    name = context.getRuntime().newString(docType.getName());
+                }
+                if (docType.getPublicId() != null) {
+                    publicId = context.getRuntime().newString(docType.getPublicId());
+                }
+                if (docType.getSystemId() != null) {
+                    systemId = context.getRuntime().newString(docType.getSystemId());
+                }
+                dtd = XmlDtd.newEmpty(context.getRuntime(),
+                                      document,
+                                      name,
+                                      publicId,
+                                      systemId);
+            } else {
+                dtd = context.getRuntime().getNil();
+            }
+
+            setInternalSubset(dtd);
+        }
+
+        return dtd;
+    }
+
+    /**
+     * Assumes XmlNode#internal_subset() has returned nil. (i.e. there
+     * is not already an internal subset).
+     */
+    public IRubyObject createInternalSubset(ThreadContext context,
+                                            IRubyObject name,
+                                            IRubyObject external_id,
+                                            IRubyObject system_id) {
+        XmlDtd dtd = XmlDtd.newEmpty(context.getRuntime(),
+                                     this.getDocument(),
+                                     name, external_id, system_id);
+        setInternalSubset(dtd);
+        return dtd;
+    }
+
+    protected void setInternalSubset(IRubyObject data) {
+        node.setUserData(DTD_INTERNAL_SUBSET, data, null);
+    }
+
+    public IRubyObject getExternalSubset(ThreadContext context) {
+        IRubyObject dtd = (IRubyObject) node.getUserData(DTD_EXTERNAL_SUBSET);
+
+        if (dtd == null) return context.getRuntime().getNil();
+        return dtd;
+    }
+
+    /**
+     * Assumes XmlNode#external_subset() has returned nil. (i.e. there
+     * is not already an external subset).
+     */
+    public IRubyObject createExternalSubset(ThreadContext context,
+                                            IRubyObject name,
+                                            IRubyObject external_id,
+                                            IRubyObject system_id) {
+        XmlDtd dtd = XmlDtd.newEmpty(context.getRuntime(),
+                                     this.getDocument(),
+                                     name, external_id, system_id);
+        setExternalSubset(dtd);
+        return dtd;
+    }
+
+    protected void setExternalSubset(IRubyObject data) {
+        node.setUserData(DTD_EXTERNAL_SUBSET, data, null);
+    }
+
+    @Override
+    public void accept(ThreadContext context, SaveContextVisitor visitor) {
+        Document document = getDocument();
+        visitor.enter(document);
+        NodeList children = document.getChildNodes();
+        for (int i=0; i<children.getLength(); i++) {
+            Node child = children.item(i);
+            short type = child.getNodeType();
+            if (type == Node.COMMENT_NODE) {
+                XmlComment xmlComment = (XmlComment) getCachedNodeOrCreate(context.getRuntime(), child);
+                xmlComment.accept(context, visitor);
+            } else if (type == Node.DOCUMENT_TYPE_NODE) {
+                XmlDtd xmlDtd = (XmlDtd) getCachedNodeOrCreate(context.getRuntime(), child);
+                xmlDtd.accept(context, visitor);
+            } else if (type == Node.PROCESSING_INSTRUCTION_NODE) {
+                XmlProcessingInstruction xmlProcessingInstruction = (XmlProcessingInstruction) getCachedNodeOrCreate(context.getRuntime(), child);
+                xmlProcessingInstruction.accept(context, visitor);
+            } else if (type == Node.TEXT_NODE) {
+                XmlText xmlText = (XmlText) getCachedNodeOrCreate(context.getRuntime(), child);
+                xmlText.accept(context, visitor);
+            } else if (type == Node.ELEMENT_NODE) {
+                XmlElement xmlElement = (XmlElement) getCachedNodeOrCreate(context.getRuntime(), child);
+                xmlElement.accept(context, visitor);
+            }
+        }
+        visitor.leave(document);
+    }
+
+    @JRubyMethod(meta=true)
+    public static IRubyObject wrapJavaDocument(ThreadContext context, IRubyObject klazz, IRubyObject arg) {
+        XmlDocument xmlDocument = (XmlDocument) NokogiriService.XML_DOCUMENT_ALLOCATOR.allocate(context.getRuntime(), getNokogiriClass(context.getRuntime(), "Nokogiri::XML::Document"));
+        RuntimeHelpers.invoke(context, xmlDocument, "initialize");
+        Document document = (Document)arg.toJava(Document.class);
+        xmlDocument.setDocumentNode(context, document);
+        return xmlDocument;
+    }
+
+    @JRubyMethod
+    public IRubyObject toJavaDocument(ThreadContext context) {
+        return JavaUtil.convertJavaToUsableRubyObject(context.getRuntime(), node);
+    }
+
+    /* call-seq:
+     *  doc.canonicalize(mode=XML_C14N_1_0,inclusive_namespaces=nil,with_comments=false)
+     *  doc.canonicalize { |obj, parent| ... }
+     *
+     * Canonicalize a document and return the results.  Takes an optional block
+     * that takes two parameters: the +obj+ and that node's +parent+.
+     * The  +obj+ will be either a Nokogiri::XML::Node, or a Nokogiri::XML::Namespace
+     * The block must return a non-nil, non-false value if the +obj+ passed in
+     * should be included in the canonicalized document.
+     */
+    @JRubyMethod(optional=3)
+    public IRubyObject canonicalize(ThreadContext context, IRubyObject[] args, Block block) {
+        int mode = 0;
+        String inclusive_namespace = null;
+        Boolean with_comments = false;
+        if (args.length > 0 && !(args[0].isNil())) {
+            mode = RubyFixnum.fix2int(args[0]);
+        }
+        if (args.length > 1 ) {
+            if (!args[1].isNil() && !(args[1] instanceof List)) {
+                throw context.getRuntime().newTypeError("Expected array");
+            }
+            if (!args[1].isNil()) {
+              inclusive_namespace = ((RubyArray)args[1])
+                .join(context, context.getRuntime().newString(" "))
+                .asString()
+                .asJavaString(); // OMG I wish I knew JRuby better, this is ugly
+            }
+        }
+        if (args.length > 2) {
+            with_comments = args[2].isTrue();
+        }
+        String algorithmURI = null;
+        switch(mode) {
+        case 0:  // XML_C14N_1_0
+            if (with_comments) algorithmURI = Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS;
+            else algorithmURI = Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS;
+            break;
+        case 1:  // XML_C14N_EXCLUSIVE_1_0
+            if (with_comments) algorithmURI = Canonicalizer.ALGO_ID_C14N_EXCL_WITH_COMMENTS;
+            else algorithmURI = Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
+            break;
+        case 2: // XML_C14N_1_1 = 2
+            if (with_comments) algorithmURI = Canonicalizer.ALGO_ID_C14N11_WITH_COMMENTS;
+            else algorithmURI = Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS;
+        }
+        try {
+            Canonicalizer canonicalizer = Canonicalizer.getInstance(algorithmURI);
+            XmlNode startingNode = getStartingNode(block);
+            byte[] result;
+            CanonicalFilter filter = new CanonicalFilter(context, block);
+            if (inclusive_namespace == null) {
+                result = canonicalizer.canonicalizeSubtree(startingNode.getNode(), filter);
+            } else {
+                result = canonicalizer.canonicalizeSubtree(startingNode.getNode(), inclusive_namespace, filter);
+            }
+            String resultString = new String(result, "UTF-8");
+            return stringOrNil(context.getRuntime(), resultString);
+        } catch (CanonicalizationException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (UnsupportedEncodingException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        return context.getRuntime().getNil();
+    }
+
+    private XmlNode getStartingNode(Block block) {
+        if (block.isGiven()) {
+            if (block.getBinding().getSelf() instanceof XmlNode) {
+                return (XmlNode)block.getBinding().getSelf();
+            }
+        }
+        return this;
+    }
+
+    public void resetNamespaceCache(ThreadContext context) {
+        nsCache = new NokogiriNamespaceCache();
+        createAndCacheNamespaces(context.getRuntime(), node);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlDtd.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlDtd.java
--- 1.8.4-1/ext/java/nokogiri/XmlDtd.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlDtd.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,484 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.nonEmptyStringOrNil;
+import static nokogiri.internals.NokogiriHelpers.stringOrNil;
+import static org.jruby.runtime.Helpers.invoke;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.xerces.xni.QName;
+import org.cyberneko.dtd.DTDConfiguration;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyHash;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import nokogiri.internals.NokogiriHelpers;
+import nokogiri.internals.SaveContextVisitor;
+
+/**
+ * Class for Nokogiri::XML::DTD
+ * 
+ * @author sergio
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+
+@JRubyClass(name="Nokogiri::XML::DTD", parent="Nokogiri::XML::Node")
+public class XmlDtd extends XmlNode {
+    /** cache of children, Nokogiri::XML::NodeSet */
+    protected IRubyObject children = null;
+
+    /** cache of name => XmlAttributeDecl */
+    protected RubyHash attributes = null;
+
+    /** cache of name => XmlElementDecl */
+    protected RubyHash elements = null;
+
+    /** cache of name => XmlEntityDecl */
+    protected RubyHash entities = null;
+
+    /** cache of name => Nokogiri::XML::Notation */
+    protected RubyHash notations = null;
+    protected RubyClass notationClass;
+
+    /** temporary store of content models before they are added to
+     * their XmlElementDecl. */
+    protected RubyHash contentModels;
+
+    /** node name */
+    protected IRubyObject name;
+
+    /** public ID (or external ID) */
+    protected IRubyObject pubId;
+
+    /** system ID */
+    protected IRubyObject sysId;
+
+    public XmlDtd(Ruby ruby, RubyClass rubyClass) {
+        super(ruby, rubyClass);
+    }
+    
+    public void setNode(Ruby runtime, Node dtd) {
+        this.node = dtd;
+        notationClass = (RubyClass) runtime.getClassFromPath("Nokogiri::XML::Notation");
+
+        name = pubId = sysId = runtime.getNil();
+        if (dtd == null) return;
+
+        // This is the dtd declaration stored in the document; it
+        // contains the DTD name (root element) and public and system
+        // ids. The actual declarations are in the NekoDTD 'dtd'
+        // variable. I don't know of a way to consolidate the two.
+
+        DocumentType otherDtd = dtd.getOwnerDocument().getDoctype();
+        if (otherDtd != null) {
+            name = stringOrNil(runtime, otherDtd.getNodeName());
+            pubId = nonEmptyStringOrNil(runtime, otherDtd.getPublicId());
+            sysId = nonEmptyStringOrNil(runtime, otherDtd.getSystemId());
+        }
+    }
+
+    public XmlDtd(Ruby ruby, RubyClass rubyClass, Node dtd) {
+        super(ruby, rubyClass, dtd);
+        setNode(ruby, dtd);
+    }
+
+    public static XmlDtd newEmpty(Ruby runtime,
+                                  Document doc,
+                                  IRubyObject name,
+                                  IRubyObject external_id,
+                                  IRubyObject system_id) {
+
+        DocumentType placeholder;
+        if (doc.getDoctype() == null) {
+          String javaName = NokogiriHelpers.rubyStringToString(name);
+          String javaExternalId = NokogiriHelpers.rubyStringToString(external_id);
+          String javaSystemId = NokogiriHelpers.rubyStringToString(system_id);
+          placeholder = doc.getImplementation().createDocumentType(javaName, javaExternalId, javaSystemId);
+          doc.appendChild(placeholder);
+        } else {
+          placeholder = doc.getDoctype();
+        }
+        // FIXME: what if the document had a doc type, why are we here ?
+        XmlDtd dtd = (XmlDtd) NokogiriService.XML_DTD_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::DTD"));
+        dtd.setNode(runtime, placeholder);
+        dtd.name = name;
+        dtd.pubId = external_id;
+        dtd.sysId = system_id;
+        return dtd;
+    }
+
+
+    /**
+     * Create an unparented element that contains DTD declarations
+     * parsed from the internal subset attached as user data to
+     * <code>doc</code>.  The attached dtd must be the tree from
+     * NekoDTD. The owner document of the returned tree will be
+     * <code>doc</doc>.
+     *
+     * NekoDTD parser returns a new document node containing elements
+     * representing the dtd declarations. The plan is to get the root
+     * element and adopt it into the correct document, stipping the
+     * Document provided by NekoDTD.
+     *
+     */
+    public static XmlDtd newFromInternalSubset(Ruby runtime, Document doc) {
+        Object dtdTree_ = doc.getUserData(XmlDocument.DTD_RAW_DOCUMENT);
+        if (dtdTree_ == null) {
+            XmlDtd xmlDtd = (XmlDtd) NokogiriService.XML_DTD_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::DTD"));
+            xmlDtd.setNode(runtime, null);
+            return xmlDtd;
+        }
+
+        Node dtdTree = (Node) dtdTree_;
+        Node dtd = getInternalSubset(dtdTree);
+        if (dtd == null) {
+            XmlDtd xmlDtd = (XmlDtd) NokogiriService.XML_DTD_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::DTD"));
+            xmlDtd.setNode(runtime, null);
+            return xmlDtd;
+        } else {
+            // Import the node into doc so it has the correct owner document.
+            dtd = doc.importNode(dtd, true);
+            XmlDtd xmlDtd = (XmlDtd) NokogiriService.XML_DTD_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::DTD"));
+            xmlDtd.setNode(runtime, dtd);
+            return xmlDtd;
+        }
+    }
+
+    public static IRubyObject newFromExternalSubset(Ruby runtime, Document doc) {
+        Object dtdTree_ = doc.getUserData(XmlDocument.DTD_RAW_DOCUMENT);
+        if (dtdTree_ == null) {
+            return runtime.getNil();
+        }
+
+        Node dtdTree = (Node) dtdTree_;
+        Node dtd = getExternalSubset(dtdTree);
+        if (dtd == null) {
+            return runtime.getNil();
+        } else if (!dtd.hasChildNodes()) {
+            return runtime.getNil();
+        } else {
+            // Import the node into doc so it has the correct owner document.
+            dtd = doc.importNode(dtd, true);
+            XmlDtd xmlDtd = (XmlDtd) NokogiriService.XML_DTD_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::DTD"));
+            xmlDtd.setNode(runtime, dtd);
+            return xmlDtd;
+        }
+    }
+
+    /*
+     * <code>dtd</code> is the document node of a NekoDTD tree.
+     * NekoDTD tree looks like this:
+     *
+     * <code><pre>
+     * [#document: null]
+     *   [#comment: ...]
+     *   [#comment: ...]
+     *   [dtd: null]   // a DocumentType; isDTD(node) => false
+     *   [dtd: null]   // root of dtd, an Element node; isDTD(node) => true
+     *     ... decls, content models, etc. ...
+     *     [externalSubset: null] pubid="the pubid" sysid="the sysid"
+     *       ... external subset decls, etc. ...
+     * </pre></code>
+     */
+    protected static Node getInternalSubset(Node dtdTree) {
+        Node root;
+        for (root = dtdTree.getFirstChild(); ; root = root.getNextSibling()) {
+            if (root == null)
+                return null;
+            else if (isDTD(root))
+                return root;      // we have second dtd which is root
+        }
+    }
+
+    protected static Node getExternalSubset(Node dtdTree) {
+        Node dtd = getInternalSubset(dtdTree);
+        if (dtd == null) return null;
+        for (Node ext = dtd.getFirstChild(); ; ext = ext.getNextSibling()) {
+            if (ext == null)
+                return null;
+            else if (isExternalSubset(ext))
+                return ext;
+        }
+    }
+
+    /**
+     * This overrides the #attributes method defined in
+     * lib/nokogiri/xml/node.rb.
+     */
+    @JRubyMethod
+    public IRubyObject attributes(ThreadContext context) {
+        if (attributes == null) extractDecls(context);
+
+        return attributes;
+    }
+
+    @JRubyMethod
+    public IRubyObject elements(ThreadContext context) {
+        if (elements == null) extractDecls(context);
+
+        return elements;
+    }
+
+    @JRubyMethod
+    public IRubyObject entities(ThreadContext context) {
+        if (entities == null) extractDecls(context);
+
+        return entities;
+    }
+
+    @JRubyMethod
+    public IRubyObject notations(ThreadContext context) {
+        if (notations == null) extractDecls(context);
+
+        return notations;
+    }
+
+    /**
+     * Our "node" object is as-returned by NekoDTD.  The actual
+     * "children" that we're interested in (Attribute declarations,
+     * etc.) are a few layers deep.
+     */
+    @Override
+    @JRubyMethod
+    public IRubyObject children(ThreadContext context) {
+        if (children == null) extractDecls(context);
+
+        return children;
+    }
+
+    /**
+     * Returns the name of the dtd.
+     */
+    @Override
+    @JRubyMethod
+    public IRubyObject node_name(ThreadContext context) {
+        return name;
+    }
+
+    @Override
+    @JRubyMethod(name = "node_name=")
+    public IRubyObject node_name_set(ThreadContext context, IRubyObject name) {
+        throw context.getRuntime()
+            .newRuntimeError("cannot change name of DTD");
+    }
+
+    @JRubyMethod
+    public IRubyObject system_id(ThreadContext context) {
+        return sysId;
+    }
+
+    @JRubyMethod
+    public IRubyObject external_id(ThreadContext context) {
+        return pubId;
+    }
+    
+    @JRubyMethod
+    public IRubyObject validate(ThreadContext context, IRubyObject doc) {
+        RubyArray errors = RubyArray.newArray(context.getRuntime());
+        if (doc instanceof XmlDocument) {
+          errors = (RubyArray) ((XmlDocument)doc).getInstanceVariable("@errors");
+        }
+        return errors;
+    }
+
+    public static boolean nameEquals(Node node, QName name) {
+        return name.localpart.equals(node.getNodeName());
+    }
+
+    public static boolean isExternalSubset(Node node) {
+        return nameEquals(node, DTDConfiguration.E_EXTERNAL_SUBSET);
+    }
+
+    /**
+     * Checks instanceof Element so we return false for a DocumentType
+     * node (NekoDTD uses Element for all its nodes).
+     */
+    public static boolean isDTD(Node node) {
+        return (node instanceof Element &&
+                nameEquals(node, DTDConfiguration.E_DTD));
+    }
+
+    public static boolean isAttributeDecl(Node node) {
+        return nameEquals(node, DTDConfiguration.E_ATTRIBUTE_DECL);
+    }
+
+    public static boolean isElementDecl(Node node) {
+        return nameEquals(node, DTDConfiguration.E_ELEMENT_DECL);
+    }
+
+    public static boolean isEntityDecl(Node node) {
+        return (nameEquals(node, DTDConfiguration.E_INTERNAL_ENTITY_DECL) ||
+                nameEquals(node, DTDConfiguration.E_UNPARSED_ENTITY_DECL));
+    }
+
+    public static boolean isNotationDecl(Node node) {
+        return nameEquals(node, DTDConfiguration.E_NOTATION_DECL);
+    }
+
+    public static boolean isContentModel(Node node) {
+        return nameEquals(node, DTDConfiguration.E_CONTENT_MODEL);
+    }
+
+    /**
+     * Recursively extract various DTD declarations and store them in
+     * the various collections.
+     */
+    protected void extractDecls(ThreadContext context) {
+        Ruby runtime = context.getRuntime();
+
+        // initialize data structures
+        attributes = RubyHash.newHash(runtime);
+        elements = RubyHash.newHash(runtime);
+        entities = RubyHash.newHash(runtime);
+        notations = RubyHash.newHash(runtime);
+        contentModels = RubyHash.newHash(runtime);
+        children = runtime.getNil();
+
+        // recursively extract decls
+        if (node == null) return; // leave all the decl hash's empty
+
+        // convert allDecls to a NodeSet
+        children = XmlNodeSet.newXmlNodeSet(context, extractDecls(context, node.getFirstChild()));
+
+        // add attribute decls as attributes to the matching element decl
+        RubyArray keys = attributes.keys();
+        for (int i = 0; i < keys.getLength(); ++i) {
+            IRubyObject akey = keys.entry(i);
+            IRubyObject val;
+
+            val = attributes.op_aref(context, akey);
+            if (val.isNil()) continue;
+            XmlAttributeDecl attrDecl = (XmlAttributeDecl) val;
+            IRubyObject ekey = attrDecl.element_name(context);
+            val = elements.op_aref(context, ekey);
+            if (val.isNil()) continue;
+            XmlElementDecl elemDecl = (XmlElementDecl) val;
+
+            elemDecl.appendAttrDecl(attrDecl);
+        }
+
+        // add content models to the matching element decl
+        keys = contentModels.keys();
+        for (int i = 0; i < keys.getLength(); ++i) {
+            IRubyObject key = keys.entry(i);
+            IRubyObject cm = contentModels.op_aref(context, key);
+
+            IRubyObject elem = elements.op_aref(context, key);
+            if (elem.isNil()) continue;
+            if (((XmlElementDecl)elem).isEmpty()) continue;
+            ((XmlElementDecl) elem).setContentModel(cm);
+        }
+    }
+
+    /**
+     * The <code>node</code> is either the first child of the root dtd
+     * node (as returned by getInternalSubset()) or the first child of
+     * the external subset node (as returned by getExternalSubset()).
+     *
+     * This recursive function will not descend into an
+     * 'externalSubset' node, thus for an internal subset it only
+     * extracts nodes in the internal subset, and for an external
+     * subset it extracts everything and assumess <code>node</code>
+     * and all children are part of the external subset.
+     */
+    protected IRubyObject[] extractDecls(ThreadContext context, Node node) {
+        List<IRubyObject> decls = new ArrayList<IRubyObject>();
+        while (node != null) {
+            if (isExternalSubset(node)) {
+                break;
+            } else if (isAttributeDecl(node)) {
+                XmlAttributeDecl decl = (XmlAttributeDecl)
+                    XmlAttributeDecl.create(context, node);
+                attributes.op_aset(context, decl.attribute_name(context), decl);
+                decls.add(decl);
+            } else if (isElementDecl(node)) {
+                XmlElementDecl decl = (XmlElementDecl)
+                    XmlElementDecl.create(context, node);
+                elements.op_aset(context, decl.element_name(context), decl);
+                decls.add(decl);
+            } else if (isEntityDecl(node)) {
+                XmlEntityDecl decl = (XmlEntityDecl)
+                    XmlEntityDecl.create(context, node);
+                entities.op_aset(context, decl.node_name(context), decl);
+                decls.add(decl);
+            } else if (isNotationDecl(node)) {
+                XmlNode tmp = (XmlNode)
+                    NokogiriHelpers.constructNode(context.getRuntime(), node);
+                IRubyObject decl = invoke(context, notationClass, "new",
+                                          tmp.getAttribute(context, "name"),
+                                          tmp.getAttribute(context, "pubid"),
+                                          tmp.getAttribute(context, "sysid"));
+                notations.op_aset(context,
+                                  tmp.getAttribute(context, "name"), decl);
+                decls.add(decl);
+            } else if (isContentModel(node)) {
+                XmlElementContent cm =
+                    new XmlElementContent(context.getRuntime(),
+                                          (XmlDocument) document(context),
+                                          node);
+                contentModels.op_aset(context, cm.element_name(context), cm);
+            } else {
+                // recurse
+                decls.addAll(Arrays.asList(extractDecls(context, node.getFirstChild())));
+            }
+
+            node = node.getNextSibling();
+        }
+
+        return decls.toArray(new IRubyObject[decls.size()]);
+    }
+
+    @Override
+    public void accept(ThreadContext context, SaveContextVisitor visitor) {
+        // since we use nekoDTD to parse dtd, node might be ElementImpl type
+        // An external subset doesn't need to show up, so this method just see docType.
+        DocumentType docType = node.getOwnerDocument().getDoctype();
+        visitor.enter(docType);
+        visitor.leave(docType);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlElementContent.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlElementContent.java
--- 1.8.4-1/ext/java/nokogiri/XmlElementContent.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlElementContent.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,382 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getLocalPart;
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.getPrefix;
+import static nokogiri.internals.NokogiriHelpers.nonEmptyStringOrNil;
+
+import org.cyberneko.dtd.DTDConfiguration;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * DTD element content model. This converts the nice tree of content
+ * model declarations returned by NekoDTD into the convoluted binary
+ * tree used by libxml.
+ *
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ */
+@JRubyClass(name="Nokogiri::XML::ElementContent")
+public class XmlElementContent extends RubyObject {
+    protected String element_name = null;
+
+    protected String name;
+    protected Type type;
+    protected Occur occur;
+    protected IRubyObject left;
+    protected IRubyObject right;
+
+    /** values hardcoded from nokogiri/xml/element_content.rb; this
+     * makes me uneasy, but it works */
+    public enum Type {
+        PCDATA (1),
+        ELEMENT (2),
+        SEQ (3),
+        OR (4);
+
+        private final int value;
+        Type(int value) {
+            this.value = value;
+        }
+        public IRubyObject value(Ruby runtime) {
+            return runtime.newFixnum(value);
+        }
+    }
+
+    public enum Occur {
+        ONCE (1),
+        OPT (2),
+        MULT (3),
+        PLUS (4);
+
+        private final int value;
+        Occur(int value) {
+            this.value = value;
+        }
+        public IRubyObject value(Ruby runtime) {
+            return runtime.newFixnum(value);
+        }
+    }
+
+    public XmlElementContent(Ruby runtime, RubyClass klass,
+                             XmlDocument document, Node node) {
+        this(runtime, klass, document, new NodeIter(node));
+        element_name = ((Element)node).getAttribute("ename");
+
+        /*
+         * This is a bit of a hack to match libxml behavior.
+         *
+         * If the tree contains but a single group with a single
+         * element, we can simply return the bare element without the
+         * surrounding group.
+         *
+         * TODO: is SEQ/ONCE with a single child the only case for
+         * reduction?
+         *
+         * - pmahoney
+         */
+        if (!this.left.isNil()) {
+            XmlElementContent left = (XmlElementContent) this.left;
+            if (type == Type.SEQ &&
+                occur == Occur.ONCE &&
+                left.type == Type.ELEMENT &&
+                right.isNil()) {
+                this.name = left.name;
+                this.type = left.type;
+                this.occur = left.occur;
+                this.left = this.right; // both nil
+            }
+        }
+    }
+
+    public XmlElementContent(Ruby runtime, XmlDocument document, Node node) {
+        this(runtime, getNokogiriClass(runtime, "Nokogiri::XML::ElementContent"), document, node);
+    }
+
+    public XmlElementContent(Ruby runtime, RubyClass klass,
+                             XmlDocument doc, NodeIter iter) {
+        super(runtime, klass);
+
+        setInstanceVariable("@document", doc);
+
+        name = null;
+        type = Type.SEQ;
+        occur = Occur.ONCE;
+        left = runtime.getNil();
+        right = runtime.getNil();
+
+        apply(runtime, klass, doc, iter);
+    }
+
+    protected XmlElementContent(Ruby runtime, RubyClass klass,
+                                Type type, XmlDocument doc, NodeIter iter,
+                                XmlElementContent left) {
+        super(runtime, klass);
+
+        setInstanceVariable("@document", doc);
+
+        name = null;
+        this.type = type;
+        occur = Occur.ONCE;
+        this.left = left;
+        right = runtime.getNil();
+
+        switch (type) {
+        case SEQ:
+        case OR:
+            applyGroup(runtime, klass, doc, iter);
+        default:
+            // noop
+        }
+    }
+
+    /**
+     * Applies the current node in <code>iter</code> to this content
+     * model.  When finished, <code>iter</code> will point to the last
+     * processed node.
+     */
+    protected void apply(Ruby runtime, RubyClass klass,
+                         XmlDocument doc,
+                         NodeIter iter) {
+        if (iter.isNull()) return;
+
+        Element elem = (Element) iter.current();
+
+        if (isGroup(elem) && iter.hasChildren()) {
+            iter.firstChild();
+            applyGroup(runtime, klass, doc, iter);
+            iter.parent();
+        } else if (isElement(elem)) {
+            name = elem.getAttribute("name");
+            type = Type.ELEMENT;
+        }
+
+        iter.nextSibling();
+        if (iter.isNull()) return;
+        if (isOccurrence(iter.current())) {
+            setOccur(((Element)iter.current()).getAttribute("type"));
+            iter.nextSibling();
+        }
+    }
+
+    protected void applyGroup(Ruby runtime, RubyClass klass,
+                              XmlDocument doc, NodeIter iter) {
+        // LEFT branch
+
+        if (iter.isNull()) return;
+
+        if (left.isNil()) {
+            left = new XmlElementContent(runtime, klass, doc, iter);
+
+            if (iter.isNull()) return;
+
+            if (isSeparator(iter.current())) {
+                setType(((Element)iter.current()).getAttribute("type"));
+                iter.nextSibling(); // skip separator
+            }
+        }
+
+        // RIGHT branch
+
+        if (iter.isNull()) return;
+
+        right = new XmlElementContent(runtime, klass, doc, iter);
+
+        if (iter.isNull()) return;
+        if (isSeparator(iter.current()))
+                iter.nextSibling(); // skip separator
+        if (iter.isNull()) return;
+
+        // binary tree can only hold two children.  If we have more,
+        // the right child is another tree with the same sequence
+        // "type".  The "left" of the new tree is what we've
+        // currently consumed as our "right" branch of this tree.
+        right = new XmlElementContent(runtime, klass, type, doc, iter,
+                                      (XmlElementContent) right);
+    }
+
+    /**
+     * Set the type based on the separator node type string.
+     */
+    protected void setType(String type) {
+        if ("|".equals(type)) this.type = Type.OR;
+        else if (",".equals(type)) this.type = Type.SEQ;
+    }
+
+    protected void setOccur(String type) {
+        if ("*".equals(type)) this.occur = Occur.MULT;
+        else if ("+".equals(type)) this.occur = Occur.PLUS;
+    }
+
+    public static boolean isGroup(Node node) {
+        return XmlDtd.nameEquals(node, DTDConfiguration.E_GROUP);
+    }
+
+    // content model element, not Element node type
+    public static boolean isElement(Node node) {
+        return XmlDtd.nameEquals(node, DTDConfiguration.E_ELEMENT);
+    }
+
+    public static boolean isSeparator(Node node) {
+        return XmlDtd.nameEquals(node, DTDConfiguration.E_SEPARATOR);
+    }
+
+    public static boolean isOccurrence(Node node) {
+        return XmlDtd.nameEquals(node, DTDConfiguration.E_OCCURRENCE);
+    }
+
+    /**
+     * Return the name of the element to which this content model
+     * applies.  Only works for the root of the tree.
+     */
+    public IRubyObject element_name(ThreadContext context) {
+        return nonEmptyStringOrNil(context.getRuntime(), element_name);
+    }
+
+    @JRubyMethod
+    public IRubyObject prefix(ThreadContext context) {
+        return nonEmptyStringOrNil(context.getRuntime(), getPrefix(name));
+    }
+
+    @JRubyMethod
+    public IRubyObject name(ThreadContext context) {
+        return nonEmptyStringOrNil(context.getRuntime(), getLocalPart(name));
+    }
+
+    @JRubyMethod
+    public IRubyObject type(ThreadContext context) {
+        return type.value(context.getRuntime());
+    }
+
+    @JRubyMethod
+    public IRubyObject occur(ThreadContext context) {
+        return occur.value(context.getRuntime());
+    }
+
+    @JRubyMethod
+    public IRubyObject c1(ThreadContext context) {
+        return left;
+    }
+
+    @JRubyMethod
+    public IRubyObject c2(ThreadContext context) {
+        return right;
+    }
+
+    /**
+     * Iterator for a tree of Nodes.  Has a current position that
+     * points to a given node. Calling nextSibling() on the last
+     * sibling results in a current position of null.  This position
+     * is not fatal and can be escaped by calling parent() (which
+     * moves to the parent of previous sibling).  The null position is
+     * used to indicate the end of a list.
+     */
+    protected static class NodeIter {
+        protected Node pre;
+        protected Node cur;
+
+        /**
+         * The first time, we fake a previous sibling element.  Thus,
+         * initially, current() is null, and the first call should be
+         * nextSibling().
+         */
+        public NodeIter(Node node) {
+            pre = null;
+            cur = node.getFirstChild(); // skip root contentModel node
+        }
+
+        public Node current() {
+            return cur;
+        }
+
+        public boolean isNull() {
+            return (cur == null);
+        }
+
+        public boolean hasChildren() {
+            return (cur != null && cur.hasChildNodes());
+        }
+
+        /**
+         * Descend to the first child.
+         */
+        public Node firstChild() {
+            if (cur == null) throw new RuntimeException("no children");
+            Node ch = cur.getFirstChild();
+            if (ch == null) throw new RuntimeException("no children");
+
+            cur = ch;
+            return cur;
+        }
+
+        /**
+         * Move to the next sibling
+         */
+        public Node nextSibling() {
+            if (cur == null) {
+                throw new RuntimeException("no next sibling");
+            } else {
+                Node ns = cur.getNextSibling();
+                if (ns == null) {
+                    pre = cur;
+                    cur = null;
+                } else {
+                    cur = ns;
+                }
+                return cur;
+            }
+        }
+
+        /**
+         * Move to the parent.
+         */
+        public Node parent() {
+            if (cur == null) cur = pre;
+
+            Node p = cur.getParentNode();
+            if (p == null) throw new RuntimeException("no parent");
+
+            cur = p;
+            return cur;
+        }
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlElementDecl.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlElementDecl.java
--- 1.8.4-1/ext/java/nokogiri/XmlElementDecl.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlElementDecl.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,152 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getLocalPart;
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.getPrefix;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Node;
+
+/**
+ * DTD element declaration.
+ *
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::ElementDecl", parent="Nokogiri::XML::Node")
+public class XmlElementDecl extends XmlNode {
+    RubyArray attrDecls;
+    IRubyObject contentModel;
+    
+    public XmlElementDecl(Ruby ruby, RubyClass klazz) {
+        super(ruby, klazz);
+    }
+    
+    public void setNode(ThreadContext context, Node node) {
+        super.setNode(context, node);
+        attrDecls = RubyArray.newArray(context.getRuntime());
+        contentModel = context.getRuntime().getNil();
+    }
+
+    /**
+     * Initialize based on an elementDecl node from a NekoDTD parsed
+     * DTD.
+     */
+    public XmlElementDecl(Ruby ruby, RubyClass klass, Node elemDeclNode) {
+        super(ruby, klass, elemDeclNode);
+        attrDecls = RubyArray.newArray(ruby);
+        contentModel = ruby.getNil();
+    }
+
+    public static IRubyObject create(ThreadContext context, Node elemDeclNode) {
+        XmlElementDecl self =
+            new XmlElementDecl(context.getRuntime(),
+                               getNokogiriClass(context.getRuntime(), "Nokogiri::XML::ElementDecl"),
+                               elemDeclNode);
+        return self;
+    }
+
+    public IRubyObject element_name(ThreadContext context) {
+        return getAttribute(context, "ename");
+    }
+
+    public void setContentModel(IRubyObject cm) {
+        contentModel = cm;
+    }
+
+    @Override
+    @JRubyMethod
+    public IRubyObject content(ThreadContext context) {
+        return contentModel;
+    }
+
+    public boolean isEmpty() {
+        return "EMPTY".equals(getAttribute("model"));
+    }
+
+    @JRubyMethod
+    public IRubyObject prefix(ThreadContext context) {
+        String enamePrefix = getPrefix(getAttribute("ename"));
+        if (enamePrefix == null)
+            return context.getRuntime().getNil();
+        else
+            return context.getRuntime().newString(enamePrefix);
+    }
+
+    /**
+     * Returns the local part of the element name.
+     */
+    @Override
+    @JRubyMethod
+    public IRubyObject node_name(ThreadContext context) {
+        String ename = getLocalPart(getAttribute("ename"));
+        return context.getRuntime().newString(ename);
+    }
+
+    @Override
+    @JRubyMethod(name = "node_name=")
+    public IRubyObject node_name_set(ThreadContext context, IRubyObject name) {
+        throw context.getRuntime()
+            .newRuntimeError("cannot change name of DTD decl");
+    }
+
+    @Override
+    @JRubyMethod
+    public IRubyObject attribute_nodes(ThreadContext context) {
+        return attrDecls;
+    }
+
+    @Override
+    @JRubyMethod
+    public IRubyObject attribute(ThreadContext context, IRubyObject name) {
+        throw context.getRuntime()
+            .newRuntimeError("attribute by name not implemented");
+    }
+
+    public void appendAttrDecl(XmlAttributeDecl decl) {
+        attrDecls.append(decl);
+    }
+
+    @JRubyMethod
+    public IRubyObject element_type(ThreadContext context) {
+        return context.getRuntime().newFixnum(node.getNodeType());
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlElement.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlElement.java
--- 1.8.4-1/ext/java/nokogiri/XmlElement.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlElement.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,87 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.anno.JRubyClass;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import nokogiri.internals.SaveContextVisitor;
+
+/**
+ * Class for Nokogiri::XML::Element
+ * 
+ * @author sergio
+ * @author Yoko Harada <yokolet@gamil.com>
+ */
+@JRubyClass(name="Nokogiri::XML::Element", parent="Nokogiri::XML::Node")
+public class XmlElement extends XmlNode {
+
+    public XmlElement(Ruby runtime, RubyClass klazz) {
+        super(runtime, klazz);
+    }
+
+    public XmlElement(Ruby runtime, RubyClass klazz, Node element) {
+        super(runtime, klazz, element);
+    }
+    
+    @Override
+    public void setNode(ThreadContext context, Node node) {
+      super.setNode(context, node);
+      if (doc != null)
+        setInstanceVariable("@document", doc);
+    }
+    
+    @Override
+    public void accept(ThreadContext context, SaveContextVisitor visitor) {
+        visitor.enter((Element) node);
+        XmlNodeSet xmlNodeSet = (XmlNodeSet) children(context);
+        if (xmlNodeSet.length() > 0) {
+            IRubyObject[] nodes = XmlNodeSet.getNodes(context, xmlNodeSet);
+            for( int i = 0; i < nodes.length; i++ ) {
+                Object item = nodes[i];
+                if (item instanceof XmlNode) {
+                    ((XmlNode) item).accept(context, visitor);
+                }
+                else if (item instanceof XmlNamespace) {
+                    ((XmlNamespace) item).accept(context, visitor);
+                }
+            }
+        }
+        visitor.leave((Element) node);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlEntityDecl.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlEntityDecl.java
--- 1.8.4-1/ext/java/nokogiri/XmlEntityDecl.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlEntityDecl.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,161 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Node;
+
+/**
+ * DTD entity declaration.
+ *
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::EntityDecl", parent="Nokogiri::XML::Node")
+public class XmlEntityDecl extends XmlNode {
+    public static final int INTERNAL_GENERAL = 1;
+    public static final int EXTERNAL_GENERAL_PARSED = 2;
+    public static final int EXTERNAL_GENERAL_UNPARSED  = 3;
+    public static final int INTERNAL_PARAMETER = 4;
+    public static final int EXTERNAL_PARAMETER = 5;
+    public static final int INTERNAL_PREDEFINED = 6;
+    
+    private IRubyObject entityType;
+    private IRubyObject name;
+    private IRubyObject external_id;
+    private IRubyObject system_id;
+    private IRubyObject content;
+
+    public XmlEntityDecl(Ruby ruby, RubyClass klass) {
+        super(ruby, klass);
+        throw ruby.newRuntimeError("node required");
+    }
+
+    /**
+     * Initialize based on an entityDecl node from a NekoDTD parsed
+     * DTD.
+     */
+    public XmlEntityDecl(Ruby ruby, RubyClass klass, Node entDeclNode) {
+        super(ruby, klass, entDeclNode);
+        entityType = RubyFixnum.newFixnum(ruby, XmlEntityDecl.INTERNAL_GENERAL);
+        name = external_id = system_id = content = ruby.getNil();       
+    }
+    
+    public XmlEntityDecl(Ruby ruby, RubyClass klass, Node entDeclNode, IRubyObject[] argv) {
+        super(ruby, klass, entDeclNode);
+        name = argv[0];
+        entityType = RubyFixnum.newFixnum(ruby, XmlEntityDecl.INTERNAL_GENERAL);
+        external_id = system_id = content = ruby.getNil(); 
+        if (argv.length > 1) entityType = argv[1];
+        if (argv.length > 4) {
+            external_id = argv[2];
+            system_id = argv[3];
+            content = argv[4];
+        }
+    }
+
+    public static IRubyObject create(ThreadContext context, Node entDeclNode) {
+        XmlEntityDecl self =
+            new XmlEntityDecl(context.getRuntime(),
+                              getNokogiriClass(context.getRuntime(), "Nokogiri::XML::EntityDecl"),
+                              entDeclNode);
+        return self;
+    }
+    
+    // when entity is created by create_entity method
+    public static IRubyObject create(ThreadContext context, Node entDeclNode, IRubyObject[] argv) {
+        XmlEntityDecl self =
+            new XmlEntityDecl(context.getRuntime(),
+                              getNokogiriClass(context.getRuntime(), "Nokogiri::XML::EntityDecl"),
+                              entDeclNode, argv);
+        return self;
+    }
+
+    /**
+     * Returns the local part of the element name.
+     */
+    @Override
+    @JRubyMethod
+    public IRubyObject node_name(ThreadContext context) {
+        IRubyObject value = getAttribute(context, "name");
+        if (value.isNil()) value = name;
+        return value;
+    }
+
+    @Override
+    @JRubyMethod(name = "node_name=")
+    public IRubyObject node_name_set(ThreadContext context, IRubyObject name) {
+        throw context.getRuntime()
+            .newRuntimeError("cannot change name of DTD decl");
+    }
+
+    @JRubyMethod
+    public IRubyObject content(ThreadContext context) {
+        IRubyObject value = getAttribute(context, "value");
+        if (value.isNil()) value = content;
+        return value;
+    }
+
+    // TODO: what is content vs. original_content?
+    @JRubyMethod
+    public IRubyObject original_content(ThreadContext context) {
+        return getAttribute(context, "value");
+    }
+
+    @JRubyMethod
+    public IRubyObject system_id(ThreadContext context) {
+        IRubyObject value = getAttribute(context, "sysid");
+        if (value.isNil()) value = system_id;
+        return value;
+    }
+
+    @JRubyMethod
+    public IRubyObject external_id(ThreadContext context) {
+        IRubyObject value = getAttribute(context, "pubid");
+        if (value.isNil()) value = external_id;
+        return value;
+    }
+
+    @JRubyMethod
+    public IRubyObject entity_type(ThreadContext context) {
+        return entityType;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlEntityReference.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlEntityReference.java
--- 1.8.4-1/ext/java/nokogiri/XmlEntityReference.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlEntityReference.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,101 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getCachedNodeOrCreate;
+import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
+import nokogiri.internals.SaveContextVisitor;
+
+import org.apache.xerces.dom.CoreDocumentImpl;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.anno.JRubyClass;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+/**
+ * Class for Nokogiri::XML::EntityReference
+ * 
+ * @author sergio
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::EntityReference", parent="Nokogiri::XML::Node")
+public class XmlEntityReference extends XmlNode {
+
+    public XmlEntityReference(Ruby ruby, RubyClass klazz) {
+        super(ruby, klazz);
+    }
+
+    public XmlEntityReference(Ruby ruby, RubyClass klass, Node node) {
+        super(ruby, klass, node);
+    }
+
+    protected void init(ThreadContext context, IRubyObject[] args) {
+        if (args.length < 2) {
+            throw getRuntime().newArgumentError(args.length, 2);
+        }
+
+        IRubyObject doc = args[0];
+        IRubyObject name = args[1];
+
+        Document document = ((XmlNode) doc).getOwnerDocument();
+        // FIXME: disable error checking as a workaround for #719. this depends on the internals of Xerces.
+        CoreDocumentImpl internalDocument = (CoreDocumentImpl) document;
+        boolean oldErrorChecking = internalDocument.getErrorChecking();
+        internalDocument.setErrorChecking(false);
+        Node node = document.createEntityReference(rubyStringToString(name));
+        internalDocument.setErrorChecking(oldErrorChecking);
+        setNode(context, node);
+    }
+    
+    @Override
+    public void accept(ThreadContext context, SaveContextVisitor visitor) {
+        visitor.enter(node);
+        Node child = node.getFirstChild();
+        while (child != null) {
+            IRubyObject nokoNode = getCachedNodeOrCreate(context.getRuntime(), child);
+            if (nokoNode instanceof XmlNode) {
+                XmlNode cur = (XmlNode) nokoNode;
+                cur.accept(context, visitor);
+            } else if (nokoNode instanceof XmlNamespace) {
+                XmlNamespace cur = (XmlNamespace) nokoNode;
+                cur.accept(context, visitor);
+            }
+            child = child.getNextSibling();
+        }
+        visitor.leave(node);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlNamespace.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlNamespace.java
--- 1.8.4-1/ext/java/nokogiri/XmlNamespace.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlNamespace.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,216 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.CACHED_NODE;
+import static nokogiri.internals.NokogiriHelpers.getCachedNodeOrCreate;
+import static nokogiri.internals.NokogiriHelpers.getLocalNameForNamespace;
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.stringOrNil;
+import nokogiri.internals.NokogiriHelpers;
+import nokogiri.internals.SaveContextVisitor;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+/**
+ * Class for Nokogiri::XML::Namespace
+ * 
+ * @author serabe
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::Namespace")
+public class XmlNamespace extends RubyObject {
+    private Attr attr;
+    private IRubyObject prefix;
+    private IRubyObject href;
+    private String prefixString;
+    private String hrefString;
+
+    public XmlNamespace(Ruby ruby, RubyClass klazz) {
+        super(ruby, klazz);
+    }
+    
+    public Node getNode() {
+        return attr;
+    }
+    
+    public String getPrefix() {
+        return prefixString;
+    }
+    
+    public String getHref() {
+        return hrefString;
+    }
+    
+    void deleteHref() {
+        hrefString = "http://www.w3.org/XML/1998/namespace";
+        href = NokogiriHelpers.stringOrNil(getRuntime(), hrefString);
+        attr.getOwnerElement().removeAttributeNode(attr);
+    }
+
+    public void init(Attr attr, IRubyObject prefix, IRubyObject href, IRubyObject xmlDocument) {
+        init(attr, prefix, href, (String) prefix.toJava(String.class), (String) href.toJava(String.class), xmlDocument);
+    }
+    
+    public void init(Attr attr, IRubyObject prefix, IRubyObject href, String prefixString, String hrefString, IRubyObject xmlDocument) {
+        this.attr = attr;
+        this.prefix = prefix;
+        this.href = href;
+        this.prefixString = prefixString;
+        this.hrefString = hrefString;
+        setInstanceVariable("@document", xmlDocument);
+    }
+    
+    public static XmlNamespace createFromAttr(Ruby runtime, Attr attr) {
+        String prefixValue = getLocalNameForNamespace(attr.getName());
+        IRubyObject prefix_value;
+        if (prefixValue == null) {
+            prefix_value = runtime.getNil();
+            prefixValue = "";
+        } else {
+            prefix_value = RubyString.newString(runtime, prefixValue);
+        }
+        String hrefValue = attr.getValue();
+        IRubyObject href_value = RubyString.newString(runtime, hrefValue);
+        // check namespace cache
+        XmlDocument xmlDocument = (XmlDocument)getCachedNodeOrCreate(runtime, attr.getOwnerDocument());
+        xmlDocument.initializeNamespaceCacheIfNecessary();
+        XmlNamespace xmlNamespace = xmlDocument.getNamespaceCache().get(prefixValue, hrefValue);
+        if (xmlNamespace != null) return xmlNamespace;
+        
+        // creating XmlNamespace instance
+        XmlNamespace namespace =
+            (XmlNamespace) NokogiriService.XML_NAMESPACE_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Namespace")); 
+        namespace.init(attr, prefix_value, href_value, prefixValue, hrefValue, xmlDocument);
+        
+        // updateing namespace cache
+        xmlDocument.getNamespaceCache().put(namespace, attr.getOwnerElement());
+        return namespace;
+    }
+    
+    public static XmlNamespace createFromPrefixAndHref(Node owner, IRubyObject prefix, IRubyObject href) {
+        String prefixValue = prefix.isNil() ? "" : (String) prefix.toJava(String.class);
+        String hrefValue = (String) href.toJava(String.class);
+        Ruby runtime = prefix.getRuntime();
+        Document document = owner.getOwnerDocument();
+        // check namespace cache
+        XmlDocument xmlDocument = (XmlDocument)getCachedNodeOrCreate(runtime, document);
+        xmlDocument.initializeNamespaceCacheIfNecessary();
+        XmlNamespace xmlNamespace = xmlDocument.getNamespaceCache().get(prefixValue, hrefValue);
+        if (xmlNamespace != null) return xmlNamespace;
+
+        // creating XmlNamespace instance
+        XmlNamespace namespace =
+            (XmlNamespace) NokogiriService.XML_NAMESPACE_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Namespace"));
+        String attrName = "xmlns";
+        if (!"".equals(prefixValue)) {
+            attrName = attrName + ":" + prefixValue;
+        }
+        Attr attrNode = document.createAttribute(attrName);
+        attrNode.setNodeValue(hrefValue);
+
+        // initialize XmlNamespace object
+        namespace.init(attrNode, prefix, href, prefixValue, hrefValue, xmlDocument);
+        
+        // updating namespace cache
+        xmlDocument.getNamespaceCache().put(namespace, owner);
+        return namespace;
+    }
+    
+    // owner should be an Attr node
+    public static XmlNamespace createDefaultNamespace(Ruby runtime, Node owner) {
+        String prefixValue = owner.getPrefix();
+        String hrefValue = owner.getNamespaceURI();
+        Document document = owner.getOwnerDocument();
+        // check namespace cache
+        XmlDocument xmlDocument = (XmlDocument)getCachedNodeOrCreate(runtime, document);
+        XmlNamespace xmlNamespace = xmlDocument.getNamespaceCache().get(prefixValue, hrefValue);
+        if (xmlNamespace != null) return xmlNamespace;
+
+        // creating XmlNamespace instance
+        XmlNamespace namespace =
+            (XmlNamespace) NokogiriService.XML_NAMESPACE_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Namespace"));
+
+        IRubyObject prefix = stringOrNil(runtime, prefixValue);
+        IRubyObject href = stringOrNil(runtime, hrefValue);
+        // initialize XmlNamespace object
+        namespace.init((Attr)owner, prefix, href, prefixValue, hrefValue, xmlDocument);
+        
+        // updating namespace cache
+        xmlDocument.getNamespaceCache().put(namespace, owner);
+        return namespace;
+    }
+    
+    /**
+     * Create and return a copy of this object.
+     *
+     * @return a clone of this object
+     */
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+    public boolean isEmpty() {
+        return prefix.isNil() && href.isNil();
+    }
+
+    @JRubyMethod
+    public IRubyObject href(ThreadContext context) {
+        return href;
+    }
+
+    @JRubyMethod
+    public IRubyObject prefix(ThreadContext context) {
+        return prefix;
+    }
+    
+    public void accept(ThreadContext context, SaveContextVisitor visitor) {
+        String string = " " + prefix + "=\"" + href + "\"";
+        visitor.enter(string);
+        visitor.leave(string);
+        // is below better?
+        //visitor.enter(attr);
+        //visitor.leave(attr);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlNode.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlNode.java
--- 1.8.4-1/ext/java/nokogiri/XmlNode.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlNode.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,1787 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2014:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static java.lang.Math.max;
+import static nokogiri.internals.NokogiriHelpers.clearXpathContext;
+import static nokogiri.internals.NokogiriHelpers.convertEncoding;
+import static nokogiri.internals.NokogiriHelpers.convertString;
+import static nokogiri.internals.NokogiriHelpers.getCachedNodeOrCreate;
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.isBlank;
+import static nokogiri.internals.NokogiriHelpers.nodeArrayToRubyArray;
+import static nokogiri.internals.NokogiriHelpers.nonEmptyStringOrNil;
+import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
+import static nokogiri.internals.NokogiriHelpers.stringOrNil;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.xerces.dom.CoreDocumentImpl;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyInteger;
+import org.jruby.RubyModule;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.javasupport.util.RuntimeHelpers;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.Visibility;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
+import nokogiri.internals.HtmlDomParserContext;
+import nokogiri.internals.NokogiriHelpers;
+import nokogiri.internals.NokogiriNamespaceCache;
+import nokogiri.internals.SaveContextVisitor;
+import nokogiri.internals.XmlDomParserContext;
+
+/**
+ * Class for Nokogiri::XML::Node
+ *
+ * @author sergio
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ * @author Yoko Harada <yokolet@gmail.com>
+ * @author John Shahid <jvshahid@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::Node")
+public class XmlNode extends RubyObject {
+    protected static final String TEXT_WRAPPER_NAME = "nokogiri_text_wrapper";
+
+    /** The underlying Node object. */
+    protected Node node;
+
+    /* Cached objects */
+    protected IRubyObject content = null;
+    protected IRubyObject doc = null;
+    protected IRubyObject name = null;
+
+    /*
+     * Taken from http://ejohn.org/blog/comparing-document-position/
+     * Used for compareDocumentPosition.
+     * <ironic>Thanks to both java api and w3 doc for its helpful documentation</ironic>
+     */
+
+    protected static final int IDENTICAL_ELEMENTS = 0;
+    protected static final int IN_DIFFERENT_DOCUMENTS = 1;
+    protected static final int SECOND_PRECEDES_FIRST = 2;
+    protected static final int FIRST_PRECEDES_SECOND = 4;
+    protected static final int SECOND_CONTAINS_FIRST = 8;
+    protected static final int FIRST_CONTAINS_SECOND = 16;
+
+    /**
+     * Cast <code>node</code> to an XmlNode or raise a type error
+     * in <code>context</code>.
+     */
+    protected static XmlNode asXmlNode(ThreadContext context, IRubyObject node) {
+        if ( !(node instanceof XmlNode) ) {
+            final Ruby runtime = context.getRuntime();
+            throw runtime.newTypeError(node == null ? runtime.getNil() : node, getNokogiriClass(runtime, "Nokogiri::XML::Node"));
+        }
+        return (XmlNode) node;
+    }
+
+    /**
+     * Cast <code>node</code> to an XmlNode, or null if RubyNil, or
+     * raise a type error in <code>context</code>.
+     */
+    protected static XmlNode asXmlNodeOrNull(ThreadContext context, IRubyObject node) {
+        if (node == null || node.isNil()) return null;
+        return asXmlNode(context, node);
+    }
+
+    /**
+     * Coalesce to adjacent TextNodes.
+     * @param context
+     * @param prev Previous node to cur.
+     * @param cur Next node to prev.
+     */
+    public static void coalesceTextNodes(ThreadContext context, IRubyObject prev, IRubyObject cur) {
+        XmlNode p = asXmlNode(context, prev);
+        XmlNode c = asXmlNode(context, cur);
+
+        Node pNode = p.node;
+        Node cNode = c.node;
+
+        pNode.setNodeValue(pNode.getNodeValue()+cNode.getNodeValue());
+        p.content = null;       // clear cached content
+
+        c.assimilateXmlNode(context, p);
+    }
+
+    /**
+     * Coalesce text nodes around <code>anchorNode</code>.  If
+     * <code>anchorNode</code> has siblings (previous or next) that
+     * are text nodes, the content will be merged into
+     * <code>anchorNode</code> and the redundant nodes will be removed
+     * from the DOM.
+     *
+     * To match libxml behavior (?) the final content of
+     * <code>anchorNode</code> and any removed nodes will be
+     * identical.
+     *
+     * @param context
+     * @param anchorNode
+     */
+    protected static void coalesceTextNodes(ThreadContext context,
+                                            IRubyObject anchorNode,
+                                            AdoptScheme scheme) {
+        XmlNode xa = asXmlNode(context, anchorNode);
+
+        XmlNode xp = asXmlNodeOrNull(context, xa.previous_sibling(context));
+        XmlNode xn = asXmlNodeOrNull(context, xa.next_sibling(context));
+
+        Node p = xp == null ? null : xp.node;
+        Node a = xa.node;
+        Node n = xn == null ? null : xn.node;
+
+        Node parent = a.getParentNode();
+
+        boolean shouldMergeP = scheme == AdoptScheme.NEXT_SIBLING || scheme == AdoptScheme.CHILD || scheme == AdoptScheme.REPLACEMENT;
+        boolean shouldMergeN = scheme == AdoptScheme.PREV_SIBLING || scheme == AdoptScheme.REPLACEMENT;
+
+        // apply the merge right to left
+        if (shouldMergeN && n != null && n.getNodeType() == Node.TEXT_NODE) {
+            xa.setContent(a.getNodeValue() + n.getNodeValue());
+            parent.removeChild(n);
+            xn.assimilateXmlNode(context, xa);
+        }
+        if (shouldMergeP && p != null && p.getNodeType() == Node.TEXT_NODE) {
+            xp.setContent(p.getNodeValue() + a.getNodeValue());
+            parent.removeChild(a);
+            xa.assimilateXmlNode(context, xp);
+        }
+    }
+
+    /**
+     * This is the allocator for XmlNode class.  It should only be
+     * called from Ruby code.
+     */
+    public XmlNode(Ruby ruby, RubyClass cls) {
+        super(ruby, cls);
+    }
+
+    /**
+     * This is a constructor to create an XmlNode from an already
+     * existing node.  It may be called by Java code.
+     */
+    public XmlNode(Ruby ruby, RubyClass cls, Node node) {
+        super(ruby, cls);
+        setNode(ruby.getCurrentContext(), node);
+    }
+
+    protected void decorate(final ThreadContext context) {
+        if (node != null) {
+            resetCache();
+
+            if (node.getNodeType() != Node.DOCUMENT_NODE) {
+                doc = document(context.runtime);
+
+                if (doc != null && ! doc.isNil()) {
+                    RuntimeHelpers.invoke(context, doc, "decorate", this);
+                }
+            }
+        }
+    }
+
+    /**
+     * Create and return a copy of this object.
+     *
+     * @return a clone of this object
+     */
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+    protected void resetCache() {
+        node.setUserData(NokogiriHelpers.CACHED_NODE, this, null);
+    }
+
+    /**
+     * Allocate a new object, perform initialization, call that
+     * object's initialize method, and call any block passing the
+     * object as the only argument.  If <code>cls</code> is
+     * Nokogiri::XML::Node, creates a new Nokogiri::XML::Element
+     * instead.
+     *
+     * This static method seems to be inherited, strangely enough.
+     * E.g. creating a new XmlAttr from Ruby code calls this method if
+     * XmlAttr does not define its own 'new' method.
+     *
+     * Since there is some Java bookkeeping that always needs to
+     * happen, we don't define the 'initialize' method in Java because
+     * we'd have to count on subclasses calling 'super'.
+     *
+     * The main consequence of this is that every subclass needs to
+     * define its own 'new' method.
+     *
+     * As a convenience, this method does the following:
+     *
+     * <ul>
+     *
+     * <li>allocates a new object using the allocator assigned to
+     * <code>cls</code></li>
+     *
+     * <li>calls the Java method init(); subclasses can override this,
+     * otherwise they should implement a specific 'new' method</li>
+     *
+     * <li>invokes the Ruby initializer</li>
+     *
+     * <li>if a block is given, calls the block with the new node as
+     * the argument</li>
+     *
+     * </ul>
+     *
+     * -pmahoney
+     */
+    @JRubyMethod(name = "new", meta = true, rest = true)
+    public static IRubyObject rbNew(ThreadContext context, IRubyObject cls,
+                                    IRubyObject[] args, Block block) {
+        Ruby ruby = context.getRuntime();
+        RubyClass klazz = (RubyClass) cls;
+
+        if ("Nokogiri::XML::Node".equals(klazz.getName())) {
+            klazz = getNokogiriClass(ruby, "Nokogiri::XML::Element");
+        }
+
+        XmlNode xmlNode = (XmlNode) klazz.allocate();
+        xmlNode.init(context, args);
+        xmlNode.callInit(args, block);
+        assert xmlNode.node != null;
+        if (block.isGiven()) block.call(context, xmlNode);
+        return xmlNode;
+    }
+
+    /**
+     * Initialize the object from Ruby arguments.  Should be
+     * overridden by subclasses.  Should check for a minimum number of
+     * args but not for an exact number.  Any extra args will then be
+     * passed to 'initialize'.  The way 'new' and this 'init' function
+     * interact means that subclasses cannot arbitrarily change the
+     * require aruments by defining an 'initialize' method.  This is
+     * how the C libxml wrapper works also.
+     *
+     * As written it performs initialization for a new Element with
+     * the given <code>name</code> within the document
+     * <code>doc</code>.  So XmlElement need not override this.  This
+     * implementation cannot be moved to XmlElement however, because
+     * subclassing XmlNode must result in something that behaves much
+     * like XmlElement.
+     */
+    protected void init(ThreadContext context, IRubyObject[] args) {
+        if (args.length < 2)
+            throw context.getRuntime().newArgumentError(args.length, 2);
+
+        IRubyObject name = args[0];
+        IRubyObject doc = args[1];
+
+        Document document = asXmlNode(context, doc).getOwnerDocument();
+        if (document == null) {
+            throw getRuntime().newArgumentError("node must have owner document");
+        }
+
+        Element element;
+        String node_name = rubyStringToString(name);
+        String prefix = NokogiriHelpers.getPrefix(node_name);
+        String namespace_uri = null;
+        if (document.getDocumentElement() != null) {
+            namespace_uri = document.getDocumentElement().lookupNamespaceURI(prefix);
+        }
+        element = document.createElementNS(namespace_uri, node_name);
+        setNode(context, element);
+    }
+
+    /**
+     * Set the underlying node of this node to the underlying node of
+     * <code>otherNode</code>.
+     *
+     * FIXME: also update the cached node?
+     */
+    protected void assimilateXmlNode(ThreadContext context, IRubyObject otherNode) {
+        XmlNode toAssimilate = asXmlNode(context, otherNode);
+
+        this.node = toAssimilate.node;
+        content = null;         // clear cache
+    }
+
+    /**
+     * See org.w3.dom.Node#normalize.
+     */
+    public void normalize() {
+        node.normalize();
+    }
+
+    public Node getNode() {
+        return node;
+    }
+
+    public static Node getNodeFromXmlNode(ThreadContext context, IRubyObject xmlNode) {
+        return asXmlNode(context, xmlNode).node;
+    }
+
+    protected String indentString(IRubyObject indentStringObject, String xml) {
+        String[] lines = xml.split("\n");
+
+        if(lines.length <= 1) return xml;
+
+        String[] resultLines  = new String[lines.length];
+
+        String curLine;
+        boolean closingTag = false;
+        String indentString = rubyStringToString(indentStringObject);
+        int lengthInd = indentString.length();
+        StringBuilder curInd = new StringBuilder();
+
+        resultLines[0] = lines[0];
+
+        for(int i = 1; i < lines.length; i++) {
+
+            curLine = lines[i].trim();
+
+            if(curLine.length() == 0) continue;
+
+            if(curLine.startsWith("</")) {
+                closingTag = true;
+                curInd.setLength(max(0,curInd.length() - lengthInd));
+            }
+
+            resultLines[i] = curInd.toString() + curLine;
+
+            if(!curLine.endsWith("/>") && !closingTag) {
+                curInd.append(indentString);
+            }
+
+            closingTag = false;
+        }
+
+        StringBuilder result = new StringBuilder();
+        for(int i = 0; i < resultLines.length; i++) {
+            result.append(resultLines[i]).append('\n');
+        }
+
+        return result.toString();
+    }
+
+    public boolean isComment() { return false; }
+
+    public boolean isElement() {
+        if (node instanceof Element) return true; // in case of subclassing
+        else return false;
+    }
+
+    public boolean isProcessingInstruction() { return false; }
+
+    /**
+     * Return the string value of the attribute <code>key</code> or
+     * nil.
+     *
+     * Only applies where the underlying Node is an Element node, but
+     * implemented here in XmlNode because not all nodes with
+     * underlying Element nodes subclass XmlElement, such as the DTD
+     * declarations like XmlElementDecl.
+     */
+    protected IRubyObject getAttribute(ThreadContext context, String key) {
+        return getAttribute(context.getRuntime(), key);
+    }
+
+    protected IRubyObject getAttribute(Ruby runtime, String key) {
+        String value = getAttribute(key);
+        return nonEmptyStringOrNil(runtime, value);
+    }
+
+    protected String getAttribute(String key) {
+        if (node.getNodeType() != Node.ELEMENT_NODE) return null;
+
+        String value = ((Element)node).getAttribute(key);
+        return value.length() == 0 ? null : value;
+    }
+
+    /**
+     * This method should be called after a node has been adopted in a new
+     * document. This method will ensure that the node is renamed with the
+     * appriopriate NS uri. First the prefix of the node is extracted, then is
+     * used to lookup the namespace uri in the new document starting at the
+     * current node and traversing the ancestors. If the namespace uri wasn't
+     * empty (or null) all children and the node has attributes and/or children
+     * then the algorithm is recursively applied to the children.
+     */
+    public void relink_namespace(ThreadContext context) {
+        if (!(node instanceof Element)) {
+            return;
+        }
+
+        Element e = (Element) node;
+
+        // disable error checking to prevent lines like the following
+        // from throwing a `NAMESPACE_ERR' exception:
+        // Nokogiri::XML::DocumentFragment.parse("<o:div>a</o:div>")
+        // since the `o' prefix isn't defined anywhere.
+        e.getOwnerDocument().setStrictErrorChecking(false);
+
+        String prefix = e.getPrefix();
+        String nsURI = e.lookupNamespaceURI(prefix);
+        this.node = NokogiriHelpers.renameNode(e, nsURI, e.getNodeName());
+
+        if (nsURI == null || nsURI == "") {
+            return;
+        }
+
+        String currentPrefix = e.getParentNode().lookupPrefix(nsURI);
+        String currentURI = e.getParentNode().lookupNamespaceURI(prefix);
+        boolean isDefault = e.getParentNode().isDefaultNamespace(nsURI);
+
+        // add xmlns attribute if this is a new root node or if the node's
+        // namespace isn't a default namespace in the new document
+        if (e.getParentNode().getNodeType() == Node.DOCUMENT_NODE) {
+          // this is the root node, so we must set the namespaces attributes
+          // anyway
+          e.setAttribute(prefix == null ? "xmlns":"xmlns:"+prefix, nsURI);
+        } else if (prefix == null) {
+           if (!isDefault)
+             // this is a default namespace but isn't the default where this
+             // node is being added
+             e.setAttribute("xmlns", nsURI);
+        } else if (currentPrefix != prefix || currentURI != nsURI) {
+          // this is a prefixed namespace but doens't have the same prefix or
+          // the prefix is set to a diffent URI
+          e.setAttribute("xmlns:"+prefix, nsURI);
+        }
+
+        if (e.hasAttributes()) {
+            NamedNodeMap attrs = e.getAttributes();
+
+            for (int i = 0; i < attrs.getLength(); i++) {
+                Attr attr = (Attr) attrs.item(i);
+                String attrPrefix = attr.getPrefix();
+                if (attrPrefix == null) {
+                    attrPrefix = NokogiriHelpers.getPrefix(attr.getNodeName());
+                }
+                String nodeName = attr.getNodeName();
+                String nsUri;
+                if ("xml".equals(attrPrefix)) {
+                    nsUri = "http://www.w3.org/XML/1998/namespace";
+                } else if ("xmlns".equals(attrPrefix) || nodeName.equals("xmlns")) {
+                    nsUri = "http://www.w3.org/2000/xmlns/";
+                } else {
+                    nsUri = attr.lookupNamespaceURI(attrPrefix);
+                }
+
+                if (nsUri == e.getNamespaceURI()) {
+                    nsUri = null;
+                }
+
+                if (!(nsUri == null || "".equals(nsUri) || "http://www.w3.org/XML/1998/namespace".equals(nsUri))) {
+                    // Create a new namespace object and add it to the document
+                    // namespace cache.
+                    // TODO: why do we need the namespace cache ?
+                    XmlNamespace.createFromAttr(context.getRuntime(), attr);
+                }
+                NokogiriHelpers.renameNode(attr, nsUri, nodeName);
+            }
+        }
+
+        if (this.node.hasChildNodes()) {
+            XmlNodeSet nodeSet = (XmlNodeSet)(children(context));
+            nodeSet.relink_namespace(context);
+        }
+    }
+
+    // Users might extend XmlNode. This method works for such a case.
+    public void accept(ThreadContext context, SaveContextVisitor visitor) {
+        visitor.enter(node);
+        XmlNodeSet xmlNodeSet = (XmlNodeSet) children(context);
+        if (xmlNodeSet.length() > 0) {
+            RubyArray array = (RubyArray) xmlNodeSet.to_a(context);
+            for(int i = 0; i < array.getLength(); i++) {
+                Object item = array.get(i);
+                if (item instanceof XmlNode) {
+                  XmlNode cur = (XmlNode) item;
+                  cur.accept(context, visitor);
+                } else if (item instanceof XmlNamespace) {
+                    XmlNamespace cur = (XmlNamespace)item;
+                    cur.accept(context, visitor);
+                }
+            }
+        }
+        visitor.leave(node);
+    }
+
+    public void setName(IRubyObject name) {
+        this.name = name;
+    }
+
+    public void setDocument(ThreadContext context, IRubyObject doc) {
+        this.doc = doc;
+
+        setDocumentAndDecorate(context, this, doc);
+    }
+
+    // shared logic with XmlNodeSet
+    static void setDocumentAndDecorate(ThreadContext context, RubyObject self, IRubyObject doc) {
+        self.setInstanceVariable("@document", doc);
+        if (doc != null) {
+            RuntimeHelpers.invoke(context, doc, "decorate", self);
+        }
+    }
+
+    public void setNode(ThreadContext context, Node node) {
+        this.node = node;
+
+        decorate(context);
+
+        if (this instanceof XmlAttr) {
+            ((XmlAttr)this).setNamespaceIfNecessary(context.getRuntime());
+        }
+    }
+
+    public void updateNodeNamespaceIfNecessary(ThreadContext context, XmlNamespace ns) {
+        String oldPrefix = this.node.getPrefix();
+        String uri = rubyStringToString(ns.href(context));
+
+        /*
+         * Update if both prefixes are null or equal
+         */
+        boolean update = (oldPrefix == null && ns.prefix(context).isNil()) ||
+                            (oldPrefix != null && !ns.prefix(context).isNil()
+                && oldPrefix.equals(rubyStringToString(ns.prefix(context))));
+
+        if(update) {
+            this.node = NokogiriHelpers.renameNode(this.node, uri, this.node.getNodeName());
+        }
+    }
+
+    protected IRubyObject getNodeName(ThreadContext context) {
+        if (name != null) return name;
+
+        String str = null;
+        if (node != null) {
+            str = node.getNodeName();
+            str = NokogiriHelpers.getLocalPart(str);
+        }
+        if (str == null) str = "";
+        if (str.startsWith("#")) str = str.substring(1);  // eliminates '#'
+        return name = NokogiriHelpers.stringOrBlank(context.getRuntime(), str);
+    }
+
+    /**
+     * Add a namespace definition to this node.  To the underlying
+     * node, add an attribute of the form
+     * <code>xmlns:prefix="uri"</code>.
+     */
+    @JRubyMethod(name = {"add_namespace_definition", "add_namespace"})
+    public IRubyObject add_namespace_definition(ThreadContext context,
+                                                IRubyObject prefix,
+                                                IRubyObject href) {
+        String prefixString = rubyStringToString(prefix);
+        String hrefString ;
+
+        // try to search the namespace first
+        if (href.isNil()) {
+            hrefString = this.findNamespaceHref(context, rubyStringToString(prefix));
+            if (hrefString == null) {
+                return context.nil;
+            }
+            href = context.getRuntime().newString(hrefString);
+        } else {
+            hrefString = rubyStringToString(href);
+        }
+
+        NokogiriNamespaceCache nsCache = NokogiriHelpers.getNamespaceCacheFormNode(node);
+        XmlNamespace cachedNamespace = nsCache.get(prefixString, hrefString);
+
+        if (cachedNamespace != null) return cachedNamespace;
+
+        Node namespaceOwner;
+        if (node.getNodeType() == Node.ELEMENT_NODE) {
+            namespaceOwner = node;
+            Element element = (Element) node;
+            // adds namespace as node's attribute
+            final String uri = "http://www.w3.org/2000/xmlns/";
+            String qName =
+                prefix.isNil() ? "xmlns" : "xmlns:" + prefixString;
+
+            element.setAttributeNS(uri, qName, hrefString);
+        }
+        else if (node.getNodeType() == Node.ATTRIBUTE_NODE) namespaceOwner = ((Attr)node).getOwnerElement();
+        else namespaceOwner = node.getParentNode();
+        XmlNamespace ns = XmlNamespace.createFromPrefixAndHref(namespaceOwner, prefix, href);
+        if (node != namespaceOwner) {
+
+            this.node = NokogiriHelpers.renameNode(node, ns.getHref(), ns.getPrefix() + ":" + node.getLocalName());
+        }
+        updateNodeNamespaceIfNecessary(context, ns);
+
+        return ns;
+    }
+
+    @JRubyMethod(name = {"attribute", "attr"})
+    public IRubyObject attribute(ThreadContext context, IRubyObject name){
+        NamedNodeMap attrs = this.node.getAttributes();
+        Node attr = attrs.getNamedItem(rubyStringToString(name));
+        if(attr == null) {
+            return  context.getRuntime().getNil();
+        }
+        return getCachedNodeOrCreate(context.getRuntime(), attr);
+    }
+
+    @JRubyMethod
+    public IRubyObject attribute_nodes(ThreadContext context) {
+        NamedNodeMap nodeMap = this.node.getAttributes();
+
+        Ruby ruby = context.getRuntime();
+        if(nodeMap == null){
+            return ruby.newEmptyArray();
+        }
+
+        RubyArray attr = ruby.newArray();
+
+        for(int i = 0; i < nodeMap.getLength(); i++) {
+            if ((doc instanceof HtmlDocument) || !NokogiriHelpers.isNamespace(nodeMap.item(i))) {
+                attr.append(getCachedNodeOrCreate(context.getRuntime(), nodeMap.item(i)));
+            }
+        }
+
+        return attr;
+    }
+
+    @JRubyMethod
+    public IRubyObject attribute_with_ns(ThreadContext context, IRubyObject name, IRubyObject namespace) {
+        String namej = rubyStringToString(name);
+        String nsj = (namespace.isNil()) ? null : rubyStringToString(namespace);
+
+        Node el = this.node.getAttributes().getNamedItemNS(nsj, namej);
+
+        if(el == null) {
+            return context.getRuntime().getNil();
+        }
+        return NokogiriHelpers.getCachedNodeOrCreate(context.getRuntime(), el);
+    }
+
+    @JRubyMethod(name = "blank?")
+    public IRubyObject blank_p(ThreadContext context) {
+        // according to libxml doc, 
+        // a node is blank if if it is a Text or CDATA node consisting of whitespace only
+        if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) {
+            String data = node.getTextContent();
+            return context.runtime.newBoolean(data == null || isBlank(data));
+        }
+        return context.runtime.getFalse();
+    }
+
+    @JRubyMethod
+    public IRubyObject child(ThreadContext context) {
+        return getCachedNodeOrCreate(context.getRuntime(), node.getFirstChild());
+    }
+
+    @JRubyMethod
+    public IRubyObject children(ThreadContext context) {
+        XmlNodeSet xmlNodeSet = XmlNodeSet.newEmptyNodeSet(context);
+
+        NodeList nodeList = node.getChildNodes();
+        if (nodeList.getLength() > 0) {
+            xmlNodeSet.setNodeList(nodeList); // initializes @document from first node
+        }
+        else { // TODO this is very ripe for refactoring
+            setDocumentAndDecorate(context, xmlNodeSet, doc);
+        }
+
+        return xmlNodeSet;
+    }
+
+    @JRubyMethod
+    public IRubyObject first_element_child(ThreadContext context) {
+        List<Node> elementNodes = new ArrayList<Node>();
+        addElements(node, elementNodes, true);
+        if (elementNodes.size() == 0) return context.getRuntime().getNil();
+        return getCachedNodeOrCreate(context.getRuntime(), elementNodes.get(0));
+    }
+
+    @JRubyMethod
+    public IRubyObject last_element_child(ThreadContext context) {
+        List<Node> elementNodes = new ArrayList<Node>();
+        addElements(node, elementNodes, false);
+        if (elementNodes.size() == 0) return context.getRuntime().getNil();
+        return getCachedNodeOrCreate(context.getRuntime(), elementNodes.get(elementNodes.size()-1));
+    }
+
+    @JRubyMethod(name = {"element_children", "elements"})
+    public IRubyObject element_children(ThreadContext context) {
+        List<Node> elementNodes = new ArrayList<Node>();
+        addElements(node, elementNodes, false);
+        IRubyObject[] array = NokogiriHelpers.nodeArrayToArray(context.runtime,
+                                                               elementNodes.toArray(new Node[0]));
+        XmlNodeSet xmlNodeSet = XmlNodeSet.newXmlNodeSet(context, array);
+        return xmlNodeSet;
+    }
+
+    private void addElements(Node n, List<Node> nodes, boolean isFirstOnly) {
+        NodeList children = n.getChildNodes();
+        if (children.getLength() == 0) return;
+        for (int i=0; i< children.getLength(); i++) {
+            Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE) {
+                nodes.add(child);
+                if (isFirstOnly) return;
+            }
+        }
+    }
+
+    /**
+     * call-seq:
+     *  compare(other)
+     *
+     * Compare this Node to +other+ with respect to their Document
+     */
+    @JRubyMethod(visibility=Visibility.PRIVATE)
+    public IRubyObject compare(ThreadContext context, IRubyObject other) {
+        if (!(other instanceof XmlNode)) {
+            return context.getRuntime().newFixnum(-2);
+        }
+
+        Node otherNode = asXmlNode(context, other).node;
+
+        // Do not touch this if, if it's not for a good reason.
+        if (node.getNodeType() == Node.DOCUMENT_NODE ||
+           otherNode.getNodeType() == Node.DOCUMENT_NODE) {
+            return context.getRuntime().newFixnum(-1);
+        }
+
+        try{
+            int res = node.compareDocumentPosition(otherNode);
+            if ((res & FIRST_PRECEDES_SECOND) == FIRST_PRECEDES_SECOND) {
+                return context.getRuntime().newFixnum(-1);
+            } else if ((res & SECOND_PRECEDES_FIRST) == SECOND_PRECEDES_FIRST) {
+                return context.getRuntime().newFixnum(1);
+            } else if (res == IDENTICAL_ELEMENTS) {
+                return context.getRuntime().newFixnum(0);
+            }
+
+            return context.getRuntime().newFixnum(-2);
+        } catch (Exception ex) {
+            return context.getRuntime().newFixnum(-2);
+        }
+    }
+
+    /**
+     * TODO: this is a stub implementation.  It's not clear what
+     * 'in_context' is supposed to do.  Also should take
+     * <code>options</code> into account.
+     */
+    @JRubyMethod(required = 2, visibility = Visibility.PRIVATE)
+    public IRubyObject in_context(ThreadContext context,
+                                  IRubyObject str,
+                                  IRubyObject options) {
+        RubyModule klass;
+        XmlDomParserContext ctx;
+        InputStream istream;
+        XmlDocument document;
+
+        IRubyObject d = document(context);
+        Ruby runtime = context.getRuntime();
+        if (d != null && d instanceof XmlDocument) {
+            document = (XmlDocument)d;
+        } else {
+            return runtime.getNil();
+        }
+
+        if (document instanceof HtmlDocument) {
+            klass = getNokogiriClass(runtime, "Nokogiri::HTML::Document");
+            ctx = new HtmlDomParserContext(runtime, options);
+            ((HtmlDomParserContext)ctx).enableDocumentFragment();
+            istream = new ByteArrayInputStream((rubyStringToString(str)).getBytes());
+        } else {
+            klass = getNokogiriClass(runtime, "Nokogiri::XML::Document");
+            ctx = new XmlDomParserContext(runtime, options);
+            String input = rubyStringToString(str);
+            istream = new ByteArrayInputStream(input.getBytes());
+        }
+
+        ctx.setInputSource(istream);
+        // TODO: for some reason, document.getEncoding() can be null or nil (don't know why)
+        // run `test_parse_with_unparented_html_text_context_node' few times to see this happen
+        if (document instanceof HtmlDocument && !(document.getEncoding() == null || document.getEncoding().isNil())) {
+        	HtmlDomParserContext htmlCtx= (HtmlDomParserContext) ctx;
+        	htmlCtx.setEncoding(document.getEncoding().asJavaString());
+        }
+        
+        XmlDocument doc = ctx.parse(context, klass, runtime.getNil());
+
+        RubyArray documentErrors = getErrorArray(document);
+        RubyArray docErrors = getErrorArray(doc);
+        if (isErrorIncreased(documentErrors, docErrors)) {
+            for (int i = 0; i < docErrors.getLength(); i++) {
+                documentErrors.add(docErrors.entry(i));
+            }
+            document.setInstanceVariable("@errors", documentErrors);
+            XmlNodeSet xmlNodeSet = XmlNodeSet.newXmlNodeSet(context, new IRubyObject[0]);
+            return xmlNodeSet;
+        }
+
+        // The first child might be document type node (dtd declaration).
+        // XmlNodeSet to be return should not have dtd decl in its list.
+        Node first;
+        if (doc.node.getFirstChild().getNodeType() == Node.DOCUMENT_TYPE_NODE) {
+            first = doc.node.getFirstChild().getNextSibling();
+        } else {
+            first = doc.node.getFirstChild();
+        }
+
+        IRubyObject[] nodes = new IRubyObject[]{NokogiriHelpers.getCachedNodeOrCreate(runtime, first)};
+        XmlNodeSet xmlNodeSet = XmlNodeSet.newXmlNodeSet(context, nodes);
+        return xmlNodeSet;
+    }
+
+    private RubyArray getErrorArray(XmlDocument document) {
+        IRubyObject obj = document.getInstanceVariable("@errors");
+        if (obj != null && obj instanceof RubyArray) {
+            return (RubyArray)obj;
+        }
+        return RubyArray.newArray(document.getRuntime());
+    }
+
+    private boolean isErrorIncreased(RubyArray baseErrors, RubyArray createdErrors) {
+        int length = ((RubyArray) createdErrors.op_diff(baseErrors)).size();
+        return length > 0;
+    }
+
+    @JRubyMethod(name = {"content", "text", "inner_text"})
+    public IRubyObject content(ThreadContext context) {
+        return stringOrNil(context.getRuntime(), getContentImpl());
+    }
+
+    public CharSequence getContentImpl() {
+        if (!node.hasChildNodes() && node.getNodeValue() == null &&
+            (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE)) {
+            return null;
+        }
+        CharSequence textContent;
+        if (this instanceof XmlDocument) {
+            Node node = ((Document)this.node).getDocumentElement();
+            if (node == null) {
+                textContent = "";
+            } else {
+                Node documentElement = ((Document) this.node).getDocumentElement();
+                textContent = getTextContentRecursively(new StringBuilder(), documentElement);
+            }
+        } else {
+            textContent = getTextContentRecursively(new StringBuilder(), node);
+        }
+        // textContent = NokogiriHelpers.convertEncodingByNKFIfNecessary(context, (XmlDocument) document(context), textContent);
+        return textContent;
+    }
+
+    private StringBuilder getTextContentRecursively(StringBuilder buffer, Node currentNode) {
+        CharSequence textContent = currentNode.getNodeValue();
+        if (textContent != null && NokogiriHelpers.shouldDecode(currentNode)) {
+            textContent = NokogiriHelpers.decodeJavaString(textContent);
+        }
+        if (textContent != null) buffer.append(textContent);
+        NodeList children = currentNode.getChildNodes();
+        for (int i = 0; i < children.getLength(); i++) {
+            Node child = children.item(i);
+            if (hasTextContent(child)) getTextContentRecursively(buffer, child);
+        }
+        return buffer;
+    }
+
+    private boolean hasTextContent(Node child) {
+        return child.getNodeType() != Node.COMMENT_NODE && child.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE;
+    }
+
+    @JRubyMethod
+    public final IRubyObject document(ThreadContext context) {
+        return document(context.runtime);
+    }
+
+    IRubyObject document(final Ruby runtime) {
+        if (doc == null) {
+            doc = (XmlDocument) node.getOwnerDocument().getUserData(NokogiriHelpers.CACHED_NODE);
+        }
+        if (doc == null) {
+            doc = getCachedNodeOrCreate(runtime, node.getOwnerDocument());
+            node.getOwnerDocument().setUserData(NokogiriHelpers.CACHED_NODE, doc, null);
+        }
+        return doc;
+    }
+
+    public IRubyObject dup() {
+        return dup_implementation(getMetaClass().getClassRuntime(), true);
+    }
+
+    @JRubyMethod
+    public IRubyObject dup(ThreadContext context) {
+        return dup_implementation(context, true);
+    }
+
+    @JRubyMethod
+    public IRubyObject dup(ThreadContext context, IRubyObject depth) {
+        boolean deep = depth instanceof RubyInteger && RubyFixnum.fix2int(depth) != 0;
+        return dup_implementation(context, deep);
+    }
+
+    protected final IRubyObject dup_implementation(ThreadContext context, boolean deep) {
+       return dup_implementation(context.getRuntime(), deep);
+    }
+
+    protected IRubyObject dup_implementation(Ruby runtime, boolean deep) {
+        XmlNode clone;
+        try {
+            clone = (XmlNode) clone();
+        } catch (CloneNotSupportedException e) {
+            throw runtime.newRuntimeError(e.toString());
+        }
+        Node newNode = node.cloneNode(deep);
+        clone.node = newNode;
+        return clone;
+    }
+
+    public static RubyString encode_special_chars(ThreadContext context, IRubyObject string) {
+        CharSequence str = NokogiriHelpers.encodeJavaString( rubyStringToString(string) );
+        return RubyString.newString(context.getRuntime(), str);
+    }
+
+    /**
+     * Instance method version of the above static method.
+     */
+    @JRubyMethod(name="encode_special_chars")
+    public IRubyObject i_encode_special_chars(ThreadContext context,
+                                              IRubyObject string) {
+        return encode_special_chars(context, string);
+    }
+
+    /**
+     * Get the attribute at the given key, <code>key</code>.
+     * Assumes that this node has attributes (i.e. that key? returned
+     * true).
+     */
+    @JRubyMethod(visibility = Visibility.PRIVATE)
+    public IRubyObject get(ThreadContext context, IRubyObject rbkey) {
+        if (node instanceof Element) {
+            if (rbkey == null || rbkey.isNil()) context.getRuntime().getNil();
+            String key = rubyStringToString(rbkey);
+            Element element = (Element) node;
+            if (!element.hasAttribute(key)) return context.getRuntime().getNil();
+            String value = element.getAttribute(key);
+            return stringOrNil(context.getRuntime(), value);
+        }
+        return context.getRuntime().getNil();
+    }
+
+    /**
+     * Returns the owner document, checking if this node is the
+     * document, or returns null if there is no owner.
+     */
+    protected Document getOwnerDocument() {
+        if (node.getNodeType() == Node.DOCUMENT_NODE) {
+            return (Document) node;
+        } else {
+            return node.getOwnerDocument();
+        }
+    }
+
+    @JRubyMethod
+    public IRubyObject internal_subset(ThreadContext context) {
+        Document document = getOwnerDocument();
+
+        if(document == null) {
+            return context.getRuntime().getNil();
+        }
+
+        XmlDocument xdoc =
+            (XmlDocument) getCachedNodeOrCreate(context.getRuntime(), document);
+        IRubyObject xdtd = xdoc.getInternalSubset(context);
+        return xdtd;
+    }
+
+    @JRubyMethod
+    public IRubyObject create_internal_subset(ThreadContext context,
+                                              IRubyObject name,
+                                              IRubyObject external_id,
+                                              IRubyObject system_id) {
+        IRubyObject subset = internal_subset(context);
+        if (!subset.isNil()) {
+            throw context.getRuntime()
+                .newRuntimeError("Document already has internal subset");
+        }
+
+        Document document = getOwnerDocument();
+        if(document == null) {
+            return context.getRuntime().getNil();
+        }
+
+        XmlDocument xdoc =
+            (XmlDocument) getCachedNodeOrCreate(context.getRuntime(), document);
+        IRubyObject xdtd = xdoc.createInternalSubset(context, name,
+                                                     external_id, system_id);
+        return xdtd;
+    }
+
+    @JRubyMethod
+    public IRubyObject external_subset(ThreadContext context) {
+        Document document = getOwnerDocument();
+
+        if (document == null) {
+            return context.getRuntime().getNil();
+        }
+
+        XmlDocument xdoc =
+            (XmlDocument) getCachedNodeOrCreate(context.getRuntime(), document);
+        IRubyObject xdtd = xdoc.getExternalSubset(context);
+        return xdtd;
+    }
+
+    @JRubyMethod
+    public IRubyObject create_external_subset(ThreadContext context,
+                                              IRubyObject name,
+                                              IRubyObject external_id,
+                                              IRubyObject system_id) {
+        IRubyObject subset = external_subset(context);
+        if (!subset.isNil()) {
+            throw context.getRuntime()
+                .newRuntimeError("Document already has external subset");
+        }
+
+        Document document = getOwnerDocument();
+        if(document == null) {
+            return context.getRuntime().getNil();
+        }
+        XmlDocument xdoc = (XmlDocument) getCachedNodeOrCreate(context.getRuntime(), document);
+        IRubyObject xdtd = xdoc.createExternalSubset(context, name, external_id, system_id);
+        return xdtd;
+    }
+
+    /**
+     * Test if this node has an attribute named <code>rbkey</code>.
+     * Overridden in XmlElement.
+     */
+    @JRubyMethod(name = {"key?", "has_attribute?"})
+    public IRubyObject key_p(ThreadContext context, IRubyObject rbkey) {
+        if (node instanceof Element) {
+            String key = rubyStringToString(rbkey);
+            Element element = (Element) node;
+            if (element.hasAttribute(key)) {
+                return context.getRuntime().getTrue();
+            } else {
+                NamedNodeMap namedNodeMap = element.getAttributes();
+                for (int i=0; i<namedNodeMap.getLength(); i++) {
+                    Node n = namedNodeMap.item(i);
+                    if (key.equals(n.getLocalName())) {
+                        return context.getRuntime().getTrue();
+                    }
+                }
+            }
+            return context.getRuntime().getFalse();
+        } else {
+            return context.getRuntime().getNil();
+        }
+    }
+
+    @JRubyMethod
+    public IRubyObject namespace(ThreadContext context) {
+        Ruby runtime = context.getRuntime();
+        if (doc instanceof HtmlDocument) return runtime.getNil();
+        NokogiriNamespaceCache nsCache = NokogiriHelpers.getNamespaceCacheFormNode(node);
+        String namespaceURI = node.getNamespaceURI();
+        if (namespaceURI == null || namespaceURI == "") {
+            return runtime.getNil();
+        }
+
+        String prefix = node.getPrefix();
+        XmlNamespace namespace = nsCache.get(prefix == null ? "" : prefix, namespaceURI);
+        if (namespace == null || namespace.isEmpty()) {
+          // if it's not in the cache, create an unowned, uncached namespace and
+          // return that. XmlReader can't insert namespaces into the cache, so
+          // this is necessary for XmlReader to work correctly.
+          namespace = new XmlNamespace(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Namespace"));
+          IRubyObject rubyPrefix = NokogiriHelpers.stringOrNil(runtime, prefix);
+          IRubyObject rubyUri = NokogiriHelpers.stringOrNil(runtime, namespaceURI);
+          namespace.init(null, rubyPrefix, rubyUri, doc);
+        }
+
+        return namespace;
+    }
+
+    /**
+     * Return an array of XmlNamespace nodes based on the attributes
+     * of this node.
+     */
+    @JRubyMethod
+    public IRubyObject namespace_definitions(ThreadContext context) {
+        // don't use namespace_definitions cache anymore since
+        // namespaces might be deleted. Reflecting the result of 
+        // namesapce removals is complicated, so the cache might not be
+        // updated.
+        Ruby ruby = context.getRuntime();
+        RubyArray namespace_definitions = ruby.newArray();
+        if (doc == null) return namespace_definitions;
+        if (doc instanceof HtmlDocument) return namespace_definitions;
+        List<XmlNamespace> namespaces = ((XmlDocument)doc).getNamespaceCache().get(node);
+        for (XmlNamespace namespace : namespaces) {
+            namespace_definitions.append(namespace);
+        }
+
+        return namespace_definitions;
+    }
+
+    /**
+     * Return an array of XmlNamespace nodes defined on this node and
+     * on any ancestor node.
+     */
+    @JRubyMethod
+    public IRubyObject namespace_scopes(ThreadContext context) {
+        RubyArray scoped_namespaces = context.getRuntime().newArray();
+        if (doc == null) return scoped_namespaces;
+        if (doc instanceof HtmlDocument) return scoped_namespaces;
+
+        Node previousNode;
+        if (node.getNodeType() == Node.ELEMENT_NODE) {
+            previousNode = node;
+        } else if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
+            previousNode = ((Attr)node).getOwnerElement();
+        } else {
+            previousNode = findPreviousElement(node);
+        }
+        if (previousNode == null) return scoped_namespaces;
+
+        List<String> prefixes_in_scope = new ArrayList<String>();
+        NokogiriNamespaceCache nsCache = NokogiriHelpers.getNamespaceCacheFormNode(previousNode);
+        for (Node previous=previousNode; previous != null; ) {
+            List<XmlNamespace> namespaces = nsCache.get(previous);
+            for (XmlNamespace namespace : namespaces) {
+                if (prefixes_in_scope.contains(namespace.getPrefix())) continue;
+                scoped_namespaces.append(namespace);
+                prefixes_in_scope.add(namespace.getPrefix());
+            }
+            previous = findPreviousElement(previous);
+        }
+        return scoped_namespaces;
+    }
+
+    private Node findPreviousElement(Node n) {
+        Node previous = n.getPreviousSibling() == null ? n.getParentNode() : n.getPreviousSibling();
+        if (previous == null || previous.getNodeType() == Node.DOCUMENT_NODE) return null;
+        if (previous.getNodeType() == Node.ELEMENT_NODE) {
+            return previous;
+        } else {
+            return findPreviousElement(previous);
+        }
+    }
+
+    @JRubyMethod(name="namespaced_key?")
+    public IRubyObject namespaced_key_p(ThreadContext context, IRubyObject elementLName, IRubyObject namespaceUri) {
+        return this.attribute_with_ns(context, elementLName, namespaceUri).isNil() ?
+            context.getRuntime().getFalse() : context.getRuntime().getTrue();
+    }
+
+    protected void setContent(IRubyObject content) {
+        String javaContent = rubyStringToString(content);
+        node.setTextContent(javaContent);
+        if (javaContent == null || javaContent.length() == 0) return;
+        if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) return;
+        if (node.getFirstChild() != null) {
+            node.getFirstChild().setUserData(NokogiriHelpers.ENCODED_STRING, true, null);
+        }
+    }
+
+    private void setContent(String content) {
+        node.setTextContent(content);
+        this.content = null;    // clear cache
+    }
+
+    @JRubyMethod(name = "native_content=")
+    public IRubyObject native_content_set(ThreadContext context, IRubyObject content) {
+        setContent(content);
+        return content;
+    }
+
+    @JRubyMethod
+    public IRubyObject lang(ThreadContext context) {
+        IRubyObject currentObj = this ;
+        while (!currentObj.isNil()) {
+            XmlNode currentNode = asXmlNode(context, currentObj);
+            IRubyObject lang = currentNode.getAttribute(context.getRuntime(), "xml:lang");
+            if (!lang.isNil()) { return lang ; }
+
+            currentObj = currentNode.parent(context);
+        }
+        return context.nil ;
+    }
+
+    @JRubyMethod(name = "lang=")
+    public IRubyObject set_lang(ThreadContext context, IRubyObject lang) {
+        setAttribute(context, "xml:lang", rubyStringToString(lang));
+        return context.nil ;
+    }
+
+    /**
+     * @param args {IRubyObject io,
+     *              IRubyObject encoding,
+     *              IRubyObject indentString,
+     *              IRubyObject options}
+     */
+    @JRubyMethod(required=4, visibility=Visibility.PRIVATE)
+    public IRubyObject native_write_to(ThreadContext context, IRubyObject[] args) {
+
+        IRubyObject io = args[0];
+        IRubyObject encoding = args[1];
+        IRubyObject indentString = args[2];
+        IRubyObject options = args[3];
+
+        String encString = encoding.isNil() ? null : rubyStringToString(encoding);
+
+        SaveContextVisitor visitor = 
+            new SaveContextVisitor(RubyFixnum.fix2int(options), rubyStringToString(indentString), encString, isHtmlDoc(context), isFragment(), 0);
+        accept(context, visitor);
+
+        final IRubyObject rubyString;
+        if (NokogiriHelpers.isUTF8(encString)) {
+            rubyString = convertString(context.getRuntime(), visitor.getInternalBuffer());
+        } else {
+            ByteBuffer bytes = convertEncoding(Charset.forName(encString), visitor.getInternalBuffer());
+            ByteList str = new ByteList(bytes.array(), bytes.arrayOffset(), bytes.remaining());
+            rubyString = RubyString.newString(context.getRuntime(), str);
+        }
+        RuntimeHelpers.invoke(context, io, "write", rubyString);
+
+        return io;
+    }
+
+    private boolean isHtmlDoc(ThreadContext context) {
+        return document(context).getMetaClass().isKindOfModule(getNokogiriClass(context.getRuntime(), "Nokogiri::HTML::Document"));
+    }
+
+    private boolean isFragment() {
+        if (node instanceof DocumentFragment) return true;
+        if (node.getParentNode() != null && node.getParentNode() instanceof DocumentFragment) return true;
+        return false;
+    }
+
+    @JRubyMethod(name = {"next_sibling", "next"})
+    public IRubyObject next_sibling(ThreadContext context) {
+        return getCachedNodeOrCreate(context.getRuntime(), node.getNextSibling());
+    }
+
+    @JRubyMethod(name = {"previous_sibling", "previous"})
+    public IRubyObject previous_sibling(ThreadContext context) {
+        return getCachedNodeOrCreate(context.getRuntime(), node.getPreviousSibling());
+    }
+
+    @JRubyMethod(meta = true, rest = true)
+    public static IRubyObject new_from_str(ThreadContext context,
+                                           IRubyObject cls,
+                                           IRubyObject[] args) {
+        XmlDocument doc = (XmlDocument) XmlDocument.read_memory(context, args);
+        return doc.root(context);
+    }
+
+    @JRubyMethod(name = {"node_name", "name"})
+    public IRubyObject node_name(ThreadContext context) {
+        return getNodeName(context);
+    }
+
+    @JRubyMethod(name = {"node_name=", "name="})
+    public IRubyObject node_name_set(ThreadContext context, IRubyObject nodeName) {
+        String newName = rubyStringToString(nodeName);
+        this.node = NokogiriHelpers.renameNode(node, null, newName);
+        setName(nodeName);
+        return this;
+    }
+
+    @JRubyMethod(visibility = Visibility.PRIVATE)
+    public IRubyObject set(ThreadContext context, IRubyObject rbkey, IRubyObject rbval) {
+        if (node instanceof Element) {
+            setAttribute(context, rubyStringToString(rbkey), rubyStringToString(rbval));
+            return this;
+        } else {
+            return rbval;
+        }
+    }
+
+    private void setAttribute(ThreadContext context, String key, String val) {
+        Element element = (Element) node;
+
+        String uri = null;
+        int colonIndex = key.indexOf(":");
+        if (colonIndex > 0) {
+            String prefix = key.substring(0, colonIndex);
+            if (prefix.equals("xml")) {
+                uri = "http://www.w3.org/XML/1998/namespace";
+            } else if (prefix.equals("xmlns")) {
+                uri = "http://www.w3.org/2000/xmlns/";
+            } else {
+                uri = node.lookupNamespaceURI(prefix);
+            }
+        }
+
+        if (uri != null) {
+            element.setAttributeNS(uri, key, val);
+        } else {
+            element.setAttribute(key, val);
+        }
+        clearXpathContext(node);
+    }
+
+    private String findNamespaceHref(ThreadContext context, String prefix) {
+      XmlNode currentNode = this;
+      while(currentNode != document(context)) {
+        RubyArray namespaces = (RubyArray) currentNode.namespace_scopes(context);
+        Iterator iterator = namespaces.iterator();
+        while(iterator.hasNext()) {
+          XmlNamespace namespace = (XmlNamespace) iterator.next();
+          if (namespace.getPrefix().equals(prefix)) {
+            return namespace.getHref();
+          }
+        }
+        if (currentNode.parent(context).isNil()) {
+            break;
+        } else {
+            currentNode = (XmlNode) currentNode.parent(context);
+        }
+      }
+      return null;
+    }
+
+    @JRubyMethod
+    public IRubyObject parent(ThreadContext context) {
+        /*
+         * Check if this node is the root node of the document.
+         * If so, parent is the document.
+         */
+        if (node.getOwnerDocument() != null &&
+            node.getOwnerDocument().getDocumentElement() == node) {
+            return document(context);
+        } else {
+            return getCachedNodeOrCreate(context.getRuntime(), node.getParentNode());
+        }
+    }
+
+    @JRubyMethod
+    public IRubyObject path(ThreadContext context) {
+        return RubyString.newString(context.getRuntime(), NokogiriHelpers.getNodeCompletePath(this.node));
+    }
+
+    @JRubyMethod
+    public IRubyObject pointer_id(ThreadContext context) {
+        return RubyFixnum.newFixnum(context.getRuntime(), this.node.hashCode());
+    }
+
+    @JRubyMethod(visibility=Visibility.PRIVATE)
+    public IRubyObject set_namespace(ThreadContext context, IRubyObject namespace) {
+        if (namespace.isNil()) {
+            if (doc != null) {
+                Node n = node;
+                String prefix = n.getPrefix();
+                String href = n.getNamespaceURI();
+                ((XmlDocument)doc).getNamespaceCache().remove(prefix == null ? "" : prefix, href);
+                this.node = NokogiriHelpers.renameNode(n, null, NokogiriHelpers.getLocalPart(n.getNodeName()));
+            }
+        } else {
+            XmlNamespace ns = (XmlNamespace) namespace;
+            String prefix = rubyStringToString(ns.prefix(context));
+            String href = rubyStringToString(ns.href(context));
+
+            // Assigning node = ...renameNode() or not seems to make no
+            // difference.  Why not? -pmahoney
+
+            // It actually makes a great deal of difference. renameNode()
+            // will operate in place if it can, but sometimes it can't.
+            // The node you passed in *might* come back as you expect, but
+            // it might not. It's much safer to throw away the original
+            // and keep the return value. -mbklein
+            String new_name = NokogiriHelpers.newQName(prefix, node);
+            this.node = NokogiriHelpers.renameNode(node, href, new_name);
+        }
+
+        clearXpathContext(getNode());
+
+        return this;
+    }
+
+    @JRubyMethod(name = {"unlink", "remove"})
+    public IRubyObject unlink(ThreadContext context) {
+        final Node parent = node.getParentNode();
+        if (parent != null) {
+            parent.removeChild(node);
+            clearXpathContext(parent);
+        }
+        return this;
+    }
+
+    /**
+     * The C-library simply returns libxml2 magic numbers.  Here we
+     * convert Java Xml nodes to the appropriate constant defined in
+     * xml/node.rb.
+     */
+    @JRubyMethod(name = {"node_type", "type"})
+    public IRubyObject node_type(ThreadContext context) {
+        String type;
+        switch (node.getNodeType()) {
+        case Node.ELEMENT_NODE:
+            if (this instanceof XmlElementDecl)
+                type = "ELEMENT_DECL";
+            else if (this instanceof XmlAttributeDecl)
+                type = "ATTRIBUTE_DECL";
+            else if (this instanceof XmlEntityDecl)
+                type = "ENTITY_DECL";
+            else
+                type = "ELEMENT_NODE";
+            break;
+        case Node.ATTRIBUTE_NODE: type = "ATTRIBUTE_NODE"; break;
+        case Node.TEXT_NODE: type = "TEXT_NODE"; break;
+        case Node.CDATA_SECTION_NODE: type = "CDATA_SECTION_NODE"; break;
+        case Node.ENTITY_REFERENCE_NODE: type = "ENTITY_REF_NODE"; break;
+        case Node.ENTITY_NODE: type = "ENTITY_NODE"; break;
+        case Node.PROCESSING_INSTRUCTION_NODE: type = "PI_NODE"; break;
+        case Node.COMMENT_NODE: type = "COMMENT_NODE"; break;
+        case Node.DOCUMENT_NODE:
+            if (this instanceof HtmlDocument)
+                type = "HTML_DOCUMENT_NODE";
+            else
+                type = "DOCUMENT_NODE";
+            break;
+        case Node.DOCUMENT_TYPE_NODE: type = "DOCUMENT_TYPE_NODE"; break;
+        case Node.DOCUMENT_FRAGMENT_NODE: type = "DOCUMENT_FRAG_NODE"; break;
+        case Node.NOTATION_NODE: type = "NOTATION_NODE"; break;
+        default:
+            return context.getRuntime().newFixnum(0);
+        }
+
+        return getNokogiriClass(context.getRuntime(), "Nokogiri::XML::Node").getConstant(type);
+    }
+
+    @JRubyMethod
+    public IRubyObject line(ThreadContext context) {
+        Node root = getOwnerDocument();
+        int[] counter = new int[1];
+        count(root, counter);
+        return RubyFixnum.newFixnum(context.getRuntime(), counter[0]+1);
+    }
+
+    private boolean count(Node node, int[] counter) {
+        if (node == this.node) {
+            return true;
+        }
+        NodeList list = node.getChildNodes();
+        for (int i=0; i<list.getLength(); i++) {
+            Node n = list.item(i);
+            if (n instanceof Text
+                    && ((Text)n).getData().contains("\n")) {
+                counter[0] += 1;
+            }
+            if (count(n, counter)) return true;
+        }
+        return false;
+    }
+
+    @JRubyMethod
+    public IRubyObject next_element(ThreadContext context) {
+        Node nextNode = node.getNextSibling();
+        Ruby ruby = context.getRuntime();
+        if (nextNode == null) return ruby.getNil();
+        if (nextNode instanceof Element) {
+            return getCachedNodeOrCreate(context.getRuntime(), nextNode);
+        }
+        Node deeper = nextNode.getNextSibling();
+        if (deeper == null) return ruby.getNil();
+        return getCachedNodeOrCreate(context.getRuntime(), deeper);
+    }
+
+    @JRubyMethod
+    public IRubyObject previous_element(ThreadContext context) {
+        Node prevNode = node.getPreviousSibling();
+        Ruby ruby = context.getRuntime();
+        if (prevNode == null) return ruby.getNil();
+        if (prevNode instanceof Element) {
+            return getCachedNodeOrCreate(context.getRuntime(), prevNode);
+        }
+        Node shallower = prevNode.getPreviousSibling();
+        if (shallower == null) return ruby.getNil();
+        return getCachedNodeOrCreate(context.getRuntime(), shallower);
+    }
+
+    protected enum AdoptScheme {
+        CHILD, PREV_SIBLING, NEXT_SIBLING, REPLACEMENT
+    }
+
+    /**
+     * Adopt XmlNode <code>other</code> into the document of
+     * <code>this</code> using the specified scheme.
+     */
+    protected IRubyObject adoptAs(ThreadContext context, AdoptScheme scheme,
+                                  IRubyObject other_) {
+        XmlNode other = asXmlNode(context, other_);
+        // this.doc might be null since this node can be empty node.
+        if (this.doc != null) {
+            other.setDocument(context, this.doc);
+        }
+        IRubyObject nodeOrTags = other;
+        Node thisNode = node;
+        Node otherNode = other.node;
+
+         try {
+            Document prev = otherNode.getOwnerDocument();
+            Document doc = thisNode.getOwnerDocument();
+            if (doc == null && thisNode instanceof Document) {
+              // we are adding the new node to a new empty document
+              doc = (Document) thisNode;
+            }
+            clearXpathContext(prev);
+            clearXpathContext(doc);
+            if (doc != null && doc != otherNode.getOwnerDocument()) {
+                Node ret = doc.adoptNode(otherNode);
+                // FIXME: this is really a hack, see documentation of fixUserData() for more details.
+                fixUserData(prev, ret);
+                if (ret == null) {
+                    throw context.getRuntime().newRuntimeError("Failed to take ownership of node");
+                }
+                otherNode = ret;
+            }
+
+            Node parent = thisNode.getParentNode();
+
+            switch (scheme) {
+            case CHILD:
+                Node[] children = adoptAsChild(context, thisNode, otherNode);
+                if (children.length == 1 && otherNode == children[0]) {
+                    break;
+                } else {
+                    nodeOrTags = nodeArrayToRubyArray(context.getRuntime(), children);
+                }
+                break;
+            case PREV_SIBLING:
+                adoptAsPrevSibling(context, parent, thisNode, otherNode);
+                break;
+            case NEXT_SIBLING:
+                adoptAsNextSibling(context, parent, thisNode, otherNode);
+                break;
+            case REPLACEMENT:
+                adoptAsReplacement(context, parent, thisNode, otherNode);
+                break;
+            }
+         } catch (Exception e) {
+             throw context.getRuntime().newRuntimeError(e.toString());
+         }
+
+        if (otherNode.getNodeType() == Node.TEXT_NODE) {
+            coalesceTextNodes(context, other, scheme);
+        }
+
+        if (this instanceof XmlDocument) {
+            ((XmlDocument) this).resetNamespaceCache(context);
+        }
+
+        other.relink_namespace(context);
+
+        return nodeOrTags;
+    }
+
+    /**
+     * This is a hack to fix #839. We should submit a patch to Xerces.
+     * It looks like CoreDocumentImpl.adoptNode() doesn't copy
+     * the user data associated with child nodes (recursively).
+     */
+    private void fixUserData(Document previous, Node ret) {
+      String key = NokogiriHelpers.ENCODED_STRING;
+      for (Node child = ret.getFirstChild(); child != null; child = child.getNextSibling()) {
+        CoreDocumentImpl previousDocument = (CoreDocumentImpl) previous;
+        child.setUserData(key, previousDocument.getUserData(child, key), null);
+        fixUserData(previous, child);
+      }
+    }
+
+    protected Node[] adoptAsChild(ThreadContext context, Node parent,
+                                Node otherNode) {
+        /*
+         * This is a bit of a hack.  C-Nokogiri allows adding a bare
+         * text node as the root element.  Java (and XML spec?) does
+         * not.  So we wrap the text node in an element.
+         */
+        if (parent.getNodeType() == Node.DOCUMENT_NODE && otherNode.getNodeType() == Node.TEXT_NODE) {
+          Element e = (Element) parent.getFirstChild();
+          if (e == null || !e.getNodeName().equals(TEXT_WRAPPER_NAME)) {
+            e = ((Document)parent).createElement(TEXT_WRAPPER_NAME);
+            adoptAsChild(context, parent, e);
+          }
+          e.appendChild(otherNode);
+          otherNode = e;
+        } else {
+          addNamespaceURIIfNeeded(otherNode);
+          parent.appendChild(otherNode);
+        }
+        Node[] nodes = new Node[1];
+        nodes[0] = otherNode;
+        return nodes;
+    }
+
+    private void addNamespaceURIIfNeeded(Node child) {
+        if (this instanceof XmlDocumentFragment && ((XmlDocumentFragment)this).getFragmentContext() != null) {
+            XmlElement fragmentContext = ((XmlDocumentFragment)this).getFragmentContext();
+            String namespace_uri = fragmentContext.node.getNamespaceURI();
+            if (namespace_uri != null && namespace_uri.length() > 0) {
+                NokogiriHelpers.renameNode(child, namespace_uri, child.getNodeName());
+            }
+        }
+    }
+
+    protected void adoptAsPrevSibling(ThreadContext context,
+                                      Node parent,
+                                      Node thisNode, Node otherNode) {
+        if (parent == null) {
+            /* I'm not sure what do do here...  A node with no
+             * parent can't exactly have a 'sibling', so we make
+             * otherNode parentless also. */
+            if (otherNode.getParentNode() != null)
+                otherNode.getParentNode().removeChild(otherNode);
+            return;
+        }
+
+        parent.insertBefore(otherNode, thisNode);
+    }
+
+    protected void adoptAsNextSibling(ThreadContext context,
+                                      Node parent,
+                                      Node thisNode, Node otherNode) {
+        if (parent == null) {
+            /* I'm not sure what do do here...  A node with no
+             * parent can't exactly have a 'sibling', so we make
+             * otherNode parentless also. */
+            if (otherNode.getParentNode() != null)
+                otherNode.getParentNode().removeChild(otherNode);
+
+            return;
+        }
+
+        Node nextSib = thisNode.getNextSibling();
+
+        if (nextSib != null) {
+            parent.insertBefore(otherNode, nextSib);
+        } else {
+            parent.appendChild(otherNode);
+        }
+    }
+
+    protected void adoptAsReplacement(ThreadContext context,
+                                      Node parentNode,
+                                      Node thisNode, Node otherNode) {
+        if (parentNode == null) {
+            /* nothing to replace? */
+            return;
+        }
+
+        try {
+            parentNode.replaceChild(otherNode, thisNode);
+        } catch (Exception e) {
+            String prefix = "could not replace child: ";
+            throw context.getRuntime().newRuntimeError(prefix + e.toString());
+        }
+    }
+
+    /**
+     * Add <code>other</code> as a child of <code>this</code>.
+     */
+    @JRubyMethod(visibility=Visibility.PRIVATE)
+    public IRubyObject add_child_node(ThreadContext context, IRubyObject other) {
+        return adoptAs(context, AdoptScheme.CHILD, other);
+    }
+
+    /**
+     * Replace <code>this</code> with <code>other</code>.
+     */
+    @JRubyMethod(visibility=Visibility.PRIVATE)
+    public IRubyObject replace_node(ThreadContext context, IRubyObject other) {
+        return adoptAs(context, AdoptScheme.REPLACEMENT, other);
+    }
+
+    /**
+     * Add <code>other</code> as a sibling before <code>this</code>.
+     */
+    @JRubyMethod(visibility=Visibility.PRIVATE)
+    public IRubyObject add_previous_sibling_node(ThreadContext context, IRubyObject other) {
+        return adoptAs(context, AdoptScheme.PREV_SIBLING, other);
+    }
+
+    /**
+     * Add <code>other</code> as a sibling after <code>this</code>.
+     */
+    @JRubyMethod(visibility=Visibility.PRIVATE)
+    public IRubyObject add_next_sibling_node(ThreadContext context, IRubyObject other) {
+        return adoptAs(context, AdoptScheme.NEXT_SIBLING, other);
+    }
+
+    /**
+     * call-seq:
+     *   process_xincludes(options)
+     *
+     * Loads and substitutes all xinclude elements below the node. The
+     * parser context will be initialized with +options+.
+     * 
+     */
+    @JRubyMethod(visibility=Visibility.PRIVATE)
+    public IRubyObject process_xincludes(ThreadContext context, IRubyObject options) {
+        XmlDocument xmlDocument = (XmlDocument)document(context);
+        RubyArray errors = (RubyArray)xmlDocument.getInstanceVariable("@errors");
+        while(errors.getLength() > 0) {
+            XmlSyntaxError error = (XmlSyntaxError)errors.shift(context);
+            if (error.toString().contains("Include operation failed")) {
+                throw new RaiseException(error);
+            }
+        }
+        return this;
+    }
+
+    @JRubyMethod(visibility=Visibility.PRIVATE)
+    public IRubyObject clear_xpath_context(ThreadContext context) {
+        clearXpathContext(getNode());
+        return context.nil ;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlNodeSet.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlNodeSet.java
--- 1.8.4-1/ext/java/nokogiri/XmlNodeSet.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlNodeSet.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,439 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.XmlNode.setDocumentAndDecorate;
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.nodeListToRubyArray;
+
+import java.util.Arrays;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyObject;
+import org.jruby.RubyRange;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Class for Nokogiri::XML::NodeSet
+ *
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::NodeSet")
+public class XmlNodeSet extends RubyObject implements NodeList {
+
+    private IRubyObject[] nodes;
+
+    @JRubyMethod(name = "new", meta = true, rest = true)
+    public static IRubyObject rbNew(ThreadContext context, IRubyObject cls,
+                                    IRubyObject[] args, Block block) {
+      RubyClass klass = (RubyClass) cls;
+      XmlNodeSet set = (XmlNodeSet) klass.allocate();
+      set.setNodes(new IRubyObject[0]);
+      set.callInit(args, block);
+      return set;
+    }
+
+    public XmlNodeSet(Ruby ruby, RubyClass klazz) {
+        super(ruby, klazz);
+    }
+
+    private static XmlNodeSet create(final Ruby runtime) {
+        return (XmlNodeSet) NokogiriService.XML_NODESET_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::NodeSet"));
+    }
+
+    public static XmlNodeSet newEmptyNodeSet(ThreadContext context) {
+        XmlNodeSet set = create(context.getRuntime());
+        set.nodes = new IRubyObject[0];
+        return set;
+    }
+
+    public static XmlNodeSet newXmlNodeSet(ThreadContext context, IRubyObject[] nodes) {
+        XmlNodeSet xmlNodeSet = create(context.runtime);
+        xmlNodeSet.setNodes(nodes);
+        return xmlNodeSet;
+    }
+
+    /**
+     * Create and return a copy of this object.
+     *
+     * @return a clone of this object
+     */
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+    void setNodes(IRubyObject[] array) {
+        this.nodes = array;
+
+        IRubyObject first = array.length > 0 ? array[0] : null;
+        initialize(getRuntime(), first);
+    }
+
+    private void setReference(XmlNodeSet reference) {
+        IRubyObject first = reference.nodes.length > 0 ? reference.nodes[0] : null;
+        initialize(getRuntime(), first);
+    }
+
+    public void setNodeList(NodeList nodeList) {
+        setNodes(nodeListToRubyArray(getRuntime(), nodeList));
+    }
+
+    final void initialize(Ruby runtime, IRubyObject refNode) {
+        if (refNode instanceof XmlNode) {
+            IRubyObject doc = ((XmlNode) refNode).document(runtime);
+            setDocumentAndDecorate(runtime.getCurrentContext(), this, doc);
+        }
+    }
+
+    public int length() {
+      return nodes == null ? 0 : nodes.length;
+    }
+
+    public void relink_namespace(ThreadContext context) {
+        for (int i = 0; i < nodes.length; i++) {
+            if (nodes[i] instanceof XmlNode) {
+                ((XmlNode) nodes[i]).relink_namespace(context);
+            }
+        }
+    }
+
+    @JRubyMethod(name="&")
+    public IRubyObject op_and(ThreadContext context, IRubyObject nodeSet) {
+        IRubyObject[] otherNodes = getNodes(context, nodeSet);
+
+        if (otherNodes == null || otherNodes.length == 0) {
+          return newEmptyNodeSet(context);
+        }
+
+        if (nodes == null || nodes.length == 0) {
+          return newEmptyNodeSet(context);
+        }
+
+        IRubyObject[] curr = nodes;
+        IRubyObject[] other = getNodes(context, nodeSet);
+        IRubyObject[] result = new IRubyObject[nodes.length];
+
+        int last = 0;
+outer:
+        for (int i = 0; i < curr.length; i++) {
+          IRubyObject n = curr[i];
+
+          for (int j = 0; j < other.length; j++) {
+            if (other[j] == n) {
+              result[last++] = n;
+              continue outer;
+            }
+          }
+        }
+
+        XmlNodeSet newSet = newXmlNodeSet(context, Arrays.copyOf(result, last));
+        newSet.setReference(this);
+        return newSet;
+    }
+
+    @JRubyMethod
+    public IRubyObject delete(ThreadContext context, IRubyObject node_or_namespace) {
+        IRubyObject nodeOrNamespace = asXmlNodeOrNamespace(context, node_or_namespace);
+
+        if (nodes.length == 0) {
+          return context.nil;
+        }
+
+        IRubyObject[] orig = nodes;
+        IRubyObject[] result = new IRubyObject[nodes.length];
+
+        int last = 0;
+
+        for (int i = 0; i < orig.length; i++) {
+          IRubyObject n = orig[i];
+
+          if (n == nodeOrNamespace) {
+            continue;
+          }
+
+          result[last++] = n;
+        }
+
+        if (nodeOrNamespace instanceof XmlNamespace) {
+          ((XmlNamespace) nodeOrNamespace).deleteHref();
+        }
+
+        nodes = Arrays.copyOf(result, last);
+
+        if (nodes.length < orig.length) {
+          // if we found the node return it
+          return nodeOrNamespace;
+        }
+
+        return context.nil;
+    }
+
+    @JRubyMethod
+    public IRubyObject dup(ThreadContext context) {
+        return newXmlNodeSet(context, nodes);
+    }
+
+    @JRubyMethod(name = "include?")
+    public IRubyObject include_p(ThreadContext context, IRubyObject node_or_namespace) {
+        for (int i = 0; i < nodes.length; i++) {
+          if (nodes[i] == node_or_namespace) {
+            return context.tru;
+          }
+        }
+
+        return context.runtime.getFalse();
+    }
+
+    @JRubyMethod(name = {"length", "size"})
+    public IRubyObject length(ThreadContext context) {
+        return context.getRuntime().newFixnum(nodes.length);
+    }
+
+    @JRubyMethod(name="-")
+    public IRubyObject op_diff(ThreadContext context, IRubyObject nodeSet) {
+        IRubyObject[] otherNodes = getNodes(context, nodeSet);
+
+        if (otherNodes.length == 0) {
+          return dup(context);
+        }
+
+        if (nodes.length == 0) {
+          return newEmptyNodeSet(context);
+        }
+
+        IRubyObject[] curr = nodes;
+        IRubyObject[] other = getNodes(context, nodeSet);
+        IRubyObject[] result = new IRubyObject[nodes.length];
+
+        int last = 0;
+outer:
+        for (int i = 0; i < curr.length; i++) {
+          IRubyObject n = curr[i];
+
+          for (int j = 0; j < other.length; j++) {
+            if (other[j] == n) {
+              continue outer;
+            }
+          }
+
+          result[last++] = n;
+        }
+
+        XmlNodeSet newSet = newXmlNodeSet(context, Arrays.copyOf(result, last));
+        newSet.setReference(this);
+        return newSet;
+    }
+
+    @JRubyMethod(name={"|", "+"})
+    public IRubyObject op_or(ThreadContext context, IRubyObject nodeSet) {
+        IRubyObject[] otherNodes = getNodes(context, nodeSet);
+
+        if (nodes.length == 0) {
+          return ((XmlNodeSet) nodeSet).dup(context);
+        }
+
+        if (otherNodes.length == 0) {
+          return dup(context);
+        }
+
+        IRubyObject[] curr = nodes;
+        IRubyObject[] other = getNodes(context, nodeSet);
+        IRubyObject[] result = Arrays.copyOf(curr, curr.length + other.length);
+
+        int last = curr.length;
+outer:
+        for (int i = 0; i < other.length; i++) {
+          IRubyObject n = other[i];
+
+          for (int j = 0; j < curr.length; j++) {
+            if (curr[j] == n) {
+              continue outer;
+            }
+          }
+
+          result[last++] = n;
+        }
+
+        XmlNodeSet newSet = newXmlNodeSet(context, Arrays.copyOf(result, last));
+        newSet.setReference(this);
+        return newSet;
+    }
+
+    @JRubyMethod(name = {"push", "<<"})
+    public IRubyObject push(ThreadContext context, IRubyObject node_or_namespace) {
+        nodes = Arrays.copyOf(nodes, nodes.length+1);
+        nodes[nodes.length-1] = node_or_namespace;
+        return this;
+    }
+
+    //  replace with
+    //  https://github.com/jruby/jruby/blame/13a3ec76d883a162b9d46c374c6e9eeea27b3261/core/src/main/java/org/jruby/RubyRange.java#L974
+    //  once we upgraded the min JRuby version to >= 9.2
+    private static IRubyObject rangeBeginLength(ThreadContext context, IRubyObject rangeMaybe, int len, int[] begLen) {
+        RubyRange range = (RubyRange) rangeMaybe;
+        int min = range.begin(context).convertToInteger().getIntValue();
+        int max = range.end(context).convertToInteger().getIntValue();
+
+        if (min < 0) {
+          min += len;
+          if (min < 0) {
+            throw context.runtime.newRangeError(min + ".." + (range.isExcludeEnd() ? "." : "") + max + " out of range");
+          }
+        }
+
+        if (max < 0) {
+          max += len;
+        }
+
+        if (!range.isExcludeEnd()) {
+          max++;
+        }
+
+        begLen[0] = min;
+        begLen[1] = max;
+        return context.tru;
+    }
+
+
+    @JRubyMethod(name={"[]", "slice"})
+    public IRubyObject slice(ThreadContext context, IRubyObject indexOrRange) {
+        if (indexOrRange instanceof RubyFixnum) {
+          int idx = ((RubyFixnum)indexOrRange).getIntValue();
+
+          if (idx < 0) {
+            idx += nodes.length;
+          }
+
+          if (idx >= nodes.length || idx < 0) {
+            return context.nil;
+          }
+
+          return nodes[idx];
+        }
+
+        int[] begLen = new int[2];
+        rangeBeginLength(context, indexOrRange, nodes.length, begLen);
+        int min = begLen[0];
+        int max = begLen[1];
+        return subseq(context, min, max - min);
+    }
+
+    @JRubyMethod(name={"[]", "slice"})
+    public IRubyObject slice(ThreadContext context, IRubyObject start, IRubyObject length) {
+        int s = ((RubyFixnum) start).getIntValue();
+        int l = ((RubyFixnum) length).getIntValue();
+
+        if (s < 0) {
+          s += nodes.length;
+        }
+
+        return subseq(context, s, l);
+    }
+
+    public IRubyObject subseq(ThreadContext context, int start, int length) {
+        if (start > nodes.length) {
+          return context.nil;
+        }
+
+        if (start < 0 || length < 0) {
+          return context.nil;
+        }
+
+        if (start + length > nodes.length) {
+          length = nodes.length - start;
+        }
+
+        int to = start + length;
+
+        IRubyObject[] newNodes = Arrays.copyOfRange(nodes, start, to);
+
+        return newXmlNodeSet(context, newNodes);
+    }
+
+    @JRubyMethod(name = {"to_a", "to_ary"})
+    public IRubyObject to_a(ThreadContext context) {
+        return context.runtime.newArrayNoCopy(nodes);
+    }
+
+    @JRubyMethod(name = {"unlink", "remove"})
+    public IRubyObject unlink(ThreadContext context) {
+        for (int i = 0; i < nodes.length; i++) {
+            if (nodes[i] instanceof XmlNode) {
+                ((XmlNode) nodes[i] ).unlink(context);
+            }
+        }
+        return this;
+    }
+
+    private static XmlNodeSet newXmlNodeSet(ThreadContext context, XmlNodeSet reference) {
+        XmlNodeSet xmlNodeSet = create(context.getRuntime());
+        xmlNodeSet.setReference(reference);
+        xmlNodeSet.nodes = new IRubyObject[0];
+        return xmlNodeSet;
+    }
+
+    private static IRubyObject asXmlNodeOrNamespace(ThreadContext context, IRubyObject possibleNode) {
+        if (possibleNode instanceof XmlNode || possibleNode instanceof XmlNamespace) {
+            return possibleNode;
+        }
+        throw context.getRuntime().newArgumentError("node must be a Nokogiri::XML::Node or Nokogiri::XML::Namespace");
+    }
+
+    static IRubyObject[] getNodes(ThreadContext context, IRubyObject possibleNodeSet) {
+        if (possibleNodeSet instanceof XmlNodeSet) {
+            return ((XmlNodeSet) possibleNodeSet).nodes;
+        }
+        throw context.getRuntime().newArgumentError("node must be a Nokogiri::XML::NodeSet");
+    }
+
+    public int getLength() {
+        return nodes.length;
+    }
+
+    public Node item(int index) {
+        Object n = nodes[index];
+        if (n instanceof XmlNode) return ((XmlNode)n).node;
+        if (n instanceof XmlNamespace) return ((XmlNamespace)n).getNode();
+        return null;
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlProcessingInstruction.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlProcessingInstruction.java
--- 1.8.4-1/ext/java/nokogiri/XmlProcessingInstruction.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlProcessingInstruction.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,99 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
+import nokogiri.internals.SaveContextVisitor;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.javasupport.util.RuntimeHelpers;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.ProcessingInstruction;
+
+/**
+ * Class for Nokogiri::XML::ProcessingInstruction
+ * 
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::ProcessingInstruction", parent="Nokogiri::XML::Node")
+public class XmlProcessingInstruction extends XmlNode {
+
+    public XmlProcessingInstruction(Ruby ruby, RubyClass klazz) {
+        super(ruby, klazz);
+    }
+    
+    public XmlProcessingInstruction(Ruby ruby, RubyClass klazz, Node node) {
+        super(ruby, klazz, node);
+    }
+
+    @JRubyMethod(name="new", meta=true, rest=true, required=3)
+    public static IRubyObject rbNew(ThreadContext context,
+                                    IRubyObject klazz,
+                                    IRubyObject[] args) {
+
+        IRubyObject doc = args[0];
+        IRubyObject target = args[1];
+        IRubyObject data = args[2];
+
+        Document document = ((XmlNode) doc).getOwnerDocument();
+        Node node =
+            document.createProcessingInstruction(rubyStringToString(target),
+                                                 rubyStringToString(data));
+        XmlProcessingInstruction self =
+            new XmlProcessingInstruction(context.getRuntime(),
+                                         (RubyClass) klazz,
+                                         node);
+
+        RuntimeHelpers.invoke(context, self, "initialize", args);
+
+        // TODO: if_block_given.
+
+        return self;
+    }
+
+    @Override
+    public boolean isProcessingInstruction() { return true; }
+    
+    @Override
+    public void accept(ThreadContext context, SaveContextVisitor visitor) {
+        visitor.enter((ProcessingInstruction)node);
+        visitor.leave((ProcessingInstruction)node);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlReader.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlReader.java
--- 1.8.4-1/ext/java/nokogiri/XmlReader.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlReader.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,532 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.stringOrBlank;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Stack;
+
+import nokogiri.internals.NokogiriEntityResolver;
+import nokogiri.internals.ParserContext;
+import nokogiri.internals.ParserContext.Options;
+import nokogiri.internals.ReaderNode;
+import nokogiri.internals.ReaderNode.ClosingNode;
+import nokogiri.internals.ReaderNode.ElementNode;
+import nokogiri.internals.ReaderNode.TextNode;
+import nokogiri.internals.UncloseableInputStream;
+
+import org.apache.xerces.impl.Constants;
+import org.apache.xerces.impl.xs.opti.DefaultXMLDocumentHandler;
+import org.apache.xerces.parsers.StandardParserConfiguration;
+import org.apache.xerces.util.EntityResolver2Wrapper;
+import org.apache.xerces.xni.Augmentations;
+import org.apache.xerces.xni.NamespaceContext;
+import org.apache.xerces.xni.QName;
+import org.apache.xerces.xni.XMLAttributes;
+import org.apache.xerces.xni.XMLLocator;
+import org.apache.xerces.xni.XMLResourceIdentifier;
+import org.apache.xerces.xni.XMLString;
+import org.apache.xerces.xni.XNIException;
+import org.apache.xerces.xni.parser.XMLErrorHandler;
+import org.apache.xerces.xni.parser.XMLInputSource;
+import org.apache.xerces.xni.parser.XMLParseException;
+import org.apache.xerces.xni.parser.XMLPullParserConfiguration;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyBoolean;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.IOInputStream;
+import org.xml.sax.InputSource;
+
+/**
+ * Class for Nokogiri:XML::Reader
+ *
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::Reader")
+public class XmlReader extends RubyObject {
+
+    private static final int XML_TEXTREADER_MODE_INITIAL = 0;
+    private static final int XML_TEXTREADER_MODE_INTERACTIVE = 1;
+    private static final int XML_TEXTREADER_MODE_ERROR = 2;
+    private static final int XML_TEXTREADER_MODE_EOF = 3;
+    private static final int XML_TEXTREADER_MODE_CLOSED = 4;
+    private static final int XML_TEXTREADER_MODE_READING = 5;
+
+    List<ReaderNode> nodeQueue;
+    private int state;
+    private int position = 0;
+    private XMLPullParserConfiguration config;
+    private boolean continueParsing = true;
+
+    public XmlReader(Ruby runtime, RubyClass klazz) {
+        super(runtime, klazz);
+    }
+
+    /**
+     * Create and return a copy of this object.
+     *
+     * @return a clone of this object
+     */
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+    public void init(Ruby runtime) {
+        nodeQueue = new LinkedList<ReaderNode>();
+        nodeQueue.add(new ReaderNode.EmptyNode(runtime));
+    }
+
+    private void setInput(ThreadContext context, InputStream in, IRubyObject url, Options options){
+        this.setState(XML_TEXTREADER_MODE_READING);
+        config = this.createReader(context.getRuntime(), options);
+        InputSource inputSource = new InputSource();
+        ParserContext.setUrl(context, inputSource, url);
+        XMLInputSource xmlInputSource = new XMLInputSource(inputSource.getPublicId(),
+            inputSource.getSystemId(), null, in, null);
+        try {
+          config.setInputSource(xmlInputSource);
+        } catch (IOException e) {
+          throw context.getRuntime().newRuntimeError(e.getMessage());
+        }
+        this.setState(XML_TEXTREADER_MODE_CLOSED);
+    }
+
+    private void setState(int state) { this.state = state; }
+
+    @JRubyMethod
+    public IRubyObject attribute(ThreadContext context, IRubyObject name) {
+        return currentNode().getAttributeByName(name);
+    }
+
+    @JRubyMethod
+    public IRubyObject attribute_at(ThreadContext context, IRubyObject index) {
+        return currentNode().getAttributeByIndex(index);
+    }
+
+    @JRubyMethod
+    public IRubyObject attribute_count(ThreadContext context) {
+        return currentNode().getAttributeCount();
+    }
+
+    @JRubyMethod
+    public IRubyObject attribute_nodes(ThreadContext context) {
+        return currentNode().getAttributesNodes();
+    }
+
+    @JRubyMethod
+    public IRubyObject attr_nodes(ThreadContext context) {
+        return currentNode().getAttributesNodes();
+    }
+
+    @JRubyMethod(name = "attributes?")
+    public IRubyObject attributes_p(ThreadContext context) {
+        return currentNode().hasAttributes();
+    }
+
+    @JRubyMethod
+    public IRubyObject base_uri(ThreadContext context) {
+        return currentNode().getXmlBase();
+    }
+
+    @JRubyMethod(name="default?")
+    public IRubyObject default_p(ThreadContext context){
+        return currentNode().isDefault();
+    }
+
+    @JRubyMethod
+    public IRubyObject depth(ThreadContext context) {
+        return currentNode().getDepth();
+    }
+
+    @JRubyMethod(name = {"empty_element?", "self_closing?"})
+    public IRubyObject empty_element_p(ThreadContext context) {
+        ReaderNode readerNode = currentNode();
+        ensureNodeClosed(context);
+
+        if (readerNode == null) return context.getRuntime().getNil();
+        if (!(readerNode instanceof ElementNode)) context.getRuntime().getFalse();
+        return RubyBoolean.newBoolean(context.getRuntime(), !readerNode.hasChildren);
+    }
+
+    @JRubyMethod(meta = true, rest = true)
+    public static IRubyObject from_io(ThreadContext context, IRubyObject cls, IRubyObject args[]) {
+        // Only to pass the  source test.
+        Ruby runtime = context.getRuntime();
+        // Not nil allowed!
+        if(args[0].isNil()) throw runtime.newArgumentError("io cannot be nil");
+
+        XmlReader reader = (XmlReader) NokogiriService.XML_READER_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Reader"));
+        reader.init(runtime);
+        reader.setInstanceVariable("@source", args[0]);
+        reader.setInstanceVariable("@errors", runtime.newArray());
+        IRubyObject url = context.nil;
+        if (args.length > 1) url = args[1];
+        if (args.length > 2) reader.setInstanceVariable("@encoding", args[2]);
+
+        Options options;
+        if (args.length > 3) {
+          options = new ParserContext.Options((Long)args[3].toJava(Long.class));
+        } else {
+          // use the default options RECOVER | NONET
+          options = new ParserContext.Options(2048 | 1);
+        }
+
+        InputStream in = new UncloseableInputStream(new IOInputStream(args[0]));
+        reader.setInput(context, in, url, options);
+        return reader;
+    }
+
+    @JRubyMethod(meta = true, rest = true)
+    public static IRubyObject from_memory(ThreadContext context, IRubyObject cls, IRubyObject args[]) {
+        // args[0]: string, args[1]: url, args[2]: encoding, args[3]: options
+        Ruby runtime = context.getRuntime();
+        // Not nil allowed!
+        if(args[0].isNil()) throw runtime.newArgumentError("string cannot be nil");
+
+        XmlReader reader = (XmlReader) NokogiriService.XML_READER_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Reader"));
+        reader.init(runtime);
+        reader.setInstanceVariable("@source", args[0]);
+        reader.setInstanceVariable("@errors", runtime.newArray());
+        IRubyObject url = context.nil;
+        if (args.length > 1) url = args[1];
+        if (args.length > 2) reader.setInstanceVariable("@encoding", args[2]);
+
+        Options options;
+        if (args.length > 3) {
+          options = new ParserContext.Options((Long)args[3].toJava(Long.class));
+        } else {
+          // use the default options RECOVER | NONET
+          options = new ParserContext.Options(2048 | 1);
+        }
+        IRubyObject stringIO = runtime.getClass("StringIO").newInstance(context, args[0], Block.NULL_BLOCK);
+        InputStream in = new UncloseableInputStream(new IOInputStream(stringIO));
+        reader.setInput(context, in, url, options);
+        return reader;
+    }
+
+    @JRubyMethod
+    public IRubyObject node_type(ThreadContext context) {
+        IRubyObject node_type = currentNode().getNodeType();
+        return node_type == null ? RubyFixnum.zero(context.getRuntime()) : node_type;
+    }
+
+    @JRubyMethod
+    public IRubyObject inner_xml(ThreadContext context) {
+        ensureNodeClosed(context);
+        return stringOrBlank(context.getRuntime(), getInnerXml(currentNode()));
+    }
+
+    private String getInnerXml(ReaderNode current) {
+        if (current.depth < 0) return null;
+        if (!current.hasChildren) return null;
+        StringBuffer sb = new StringBuffer();
+        for (int i = current.startOffset + 1; i <= current.endOffset - 1; i++) {
+          sb.append(nodeQueue.get(i).getString());
+        }
+        return new String(sb);
+    }
+
+    @JRubyMethod
+    public IRubyObject outer_xml(ThreadContext context) {
+        ensureNodeClosed(context);
+        return stringOrBlank(context.getRuntime(), getOuterXml());
+    }
+
+    private String getOuterXml() {
+        ReaderNode current = currentNode();
+        if (current == null || current.depth < 0) return null;
+
+        if (current instanceof ClosingNode) {
+            return "<" + current.name + "/>";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        for (int i = position; i <= current.endOffset; i++) {
+            sb.append(nodeQueue.get(i).getString());
+        }
+        return new String(sb);
+    }
+
+    @JRubyMethod
+    public IRubyObject lang(ThreadContext context) {
+        return currentNode().getLang();
+    }
+
+    @JRubyMethod
+    public IRubyObject local_name(ThreadContext context) {
+        return currentNode().getLocalName();
+    }
+
+    @JRubyMethod
+    public IRubyObject name(ThreadContext context) {
+        return currentNode().getName();
+    }
+
+    @JRubyMethod
+    public IRubyObject namespace_uri(ThreadContext context) {
+        return currentNode().getUri();
+    }
+
+    @JRubyMethod
+    public IRubyObject namespaces(ThreadContext context) {
+        return currentNode().getNamespaces(context);
+    }
+
+    @JRubyMethod
+    public IRubyObject prefix(ThreadContext context) {
+        return currentNode().getPrefix();
+    }
+
+    private void readMoreData(ThreadContext context) {
+        if (!continueParsing) throw context.runtime.newRuntimeError("Cannot parse more data");
+        try {
+            continueParsing = config.parse(false);
+        }
+        catch (XNIException e) {
+            throw new RaiseException(XmlSyntaxError.createXMLSyntaxError(context.runtime, e)); // Nokogiri::XML::SyntaxError
+        }
+        catch (IOException e) {
+            throw context.runtime.newRuntimeError(e.toString());
+        }
+    }
+
+    private void ensureNodeClosed(ThreadContext context) {
+        ReaderNode node = currentNode();
+        if (node instanceof TextNode) return;
+        while (node.endOffset < 1) readMoreData(context);
+    }
+
+    @JRubyMethod
+    public IRubyObject read(ThreadContext context) {
+        position++;
+        try {
+            while (nodeQueue.size() <= position && continueParsing) {
+                readMoreData(context);
+            }
+            return setAndRaiseErrorsIfAny(context.runtime, null);
+        }
+        catch (RaiseException ex) {
+            return setAndRaiseErrorsIfAny(context.runtime, ex);
+        }
+    }
+
+    private IRubyObject setAndRaiseErrorsIfAny(final Ruby runtime, final RaiseException ex) throws RaiseException {
+        final ReaderNode currentNode = currentNode();
+        if (currentNode == null) return runtime.getNil();
+        if (currentNode.isError()) {
+            RubyArray errors = (RubyArray) getInstanceVariable("@errors");
+            IRubyObject error = currentNode.toSyntaxError();
+            errors.append(error);
+            setInstanceVariable("@errors", errors);
+
+            throw ex != null ? ex : new RaiseException((XmlSyntaxError) error);
+        }
+        if ( ex != null ) throw ex;
+        return this;
+    }
+
+    private ReaderNode currentNode() {
+        if (position >= nodeQueue.size()) return null;
+        return nodeQueue.get(position);
+    }
+
+    @JRubyMethod
+    public IRubyObject state(ThreadContext context) {
+        return context.getRuntime().newFixnum(this.state);
+    }
+
+    @JRubyMethod
+    public IRubyObject value(ThreadContext context) {
+        return currentNode().getValue();
+    }
+
+    @JRubyMethod(name = "value?")
+    public IRubyObject value_p(ThreadContext context) {
+        return currentNode().hasValue();
+    }
+
+    @JRubyMethod
+    public IRubyObject xml_version(ThreadContext context) {
+        return currentNode().getXmlVersion();
+    }
+
+    protected XMLPullParserConfiguration createReader(Ruby ruby, Options options) {
+        StandardParserConfiguration config = new StandardParserConfiguration();
+        DocumentHandler handler = new DocumentHandler(ruby);
+        // XMLReader reader = XMLReaderFactory.createXMLReader();
+        config.setDocumentHandler(handler);
+        config.setDTDHandler(handler);
+        config.setErrorHandler(handler);
+        config.setEntityResolver(new EntityResolver2Wrapper(new NokogiriEntityResolver(ruby, null, options)));
+        // config.setFeature("http://xml.org/sax/features/xmlns-uris", true);
+        // config.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
+        config.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", options.dtdLoad || options.dtdValid);
+        return config;
+    }
+
+    private class DocumentHandler extends DefaultXMLDocumentHandler implements XMLErrorHandler {
+
+        Stack<String> langStack;
+        int depth;
+        Stack<String> xmlBaseStack;
+        Stack<ReaderNode.ElementNode> elementStack;
+        private final Ruby ruby;
+
+        public DocumentHandler(Ruby ruby) {
+          this.ruby = ruby;
+        }
+
+        @Override
+        public void startGeneralEntity(String name, XMLResourceIdentifier identifier,
+                                       String encoding, Augmentations augs) throws XNIException {
+            Object entitySkipped;
+            if (augs != null && (entitySkipped = augs.getItem(Constants.ENTITY_SKIPPED)) != null && ((Boolean) entitySkipped)) {
+                nodeQueue.add(new ReaderNode.ExceptionNode(ruby, null));
+            }
+        }
+
+
+
+        @Override
+        public void startDocument(XMLLocator locator, String encoding, NamespaceContext context, Augmentations augs) {
+            depth = 0;
+            langStack = new Stack<String>();
+            xmlBaseStack = new Stack<String>();
+            elementStack = new Stack<ReaderNode.ElementNode>();
+        }
+
+        @Override
+        public void endDocument(Augmentations augs) {
+            langStack = null;
+            xmlBaseStack = null;
+            elementStack = null;
+        }
+
+        @Override
+        public void startElement(QName element, XMLAttributes attrs, Augmentations augs) {
+          commonElement(element, attrs, false);
+        }
+
+        @Override
+        public void endElement(QName element, Augmentations augs) {
+            String uri = element.uri;
+            String localName = element.localpart;
+            String qName = element.rawname;
+            depth--;
+            ElementNode startElementNode = elementStack.pop();
+            ReaderNode node = ReaderNode.createClosingNode(ruby, uri, localName, qName, depth, langStack, xmlBaseStack);
+
+            startElementNode.endOffset = nodeQueue.size() - 1;
+
+            if (startElementNode.endOffset != startElementNode.startOffset) {
+              // this node isn't empty
+              node.attributeList = startElementNode.attributeList;
+              node.namespaces = startElementNode.namespaces;
+              node.startOffset = startElementNode.startOffset;
+              node.endOffset = ++startElementNode.endOffset;
+              node.hasChildren = startElementNode.hasChildren = true;
+              nodeQueue.add(node);
+            }
+            if (!langStack.isEmpty()) langStack.pop();
+            if (!xmlBaseStack.isEmpty()) xmlBaseStack.pop();
+        }
+
+        @Override
+        public void emptyElement(QName element, XMLAttributes attrs, Augmentations augs) {
+            commonElement(element, attrs, true);
+        }
+
+        private void commonElement(QName element, XMLAttributes attrs, boolean isEmpty) {
+            String qName = element.rawname;
+            String uri = element.uri;
+            String localName = element.localpart;
+            ReaderNode readerNode = ReaderNode.createElementNode(ruby, uri, localName, qName, attrs, depth, langStack, xmlBaseStack);
+            if (!elementStack.isEmpty()) {
+              ElementNode parent = elementStack.peek();
+              parent.hasChildren = true;
+            }
+            nodeQueue.add(readerNode);
+            readerNode.startOffset = nodeQueue.size() - 1;
+            if (!isEmpty) {
+              depth++;
+              if (readerNode.lang != null) langStack.push(readerNode.lang);
+              if (readerNode.xmlBase != null) xmlBaseStack.push(readerNode.xmlBase);
+              elementStack.push((ReaderNode.ElementNode)readerNode);
+            } else {
+              readerNode.endOffset = readerNode.startOffset;
+              readerNode.hasChildren = false;
+            }
+        }
+
+        @Override
+        public void characters(XMLString string, Augmentations augs) {
+            ReaderNode.TextNode node = ReaderNode.createTextNode(ruby, string.toString(), depth, langStack, xmlBaseStack);
+            nodeQueue.add(node);
+            node.startOffset = node.endOffset = nodeQueue.size() - 1;
+        }
+
+        @Override
+        public void error(String domain, String key, XMLParseException ex) {
+            nodeQueue.add(new ReaderNode.ExceptionNode(ruby, ex));
+            throw ex;
+        }
+
+        @Override
+        public void fatalError(String domain, String key, XMLParseException ex) {
+            nodeQueue.add(new ReaderNode.ExceptionNode(ruby, ex));
+            throw ex;
+        }
+
+        @Override
+        public void warning(String domain, String key, XMLParseException ex) {
+            nodeQueue.add(new ReaderNode.ExceptionNode(ruby, ex));
+            throw ex;
+        }
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlRelaxng.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlRelaxng.java
--- 1.8.4-1/ext/java/nokogiri/XmlRelaxng.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlRelaxng.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,144 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.iso_relax.verifier.Schema;
+import org.iso_relax.verifier.Verifier;
+import org.iso_relax.verifier.VerifierConfigurationException;
+import org.iso_relax.verifier.VerifierFactory;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.anno.JRubyClass;
+import org.jruby.runtime.ThreadContext;
+import org.w3c.dom.Document;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Class for Nokogiri::XML::RelaxNG
+ * 
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::RelaxNG", parent="Nokogiri::XML::Schema")
+public class XmlRelaxng extends XmlSchema {
+    private Verifier verifier;
+
+    public XmlRelaxng(Ruby ruby, RubyClass klazz) {
+        super(ruby, klazz);
+    }
+    
+    private void setVerifier(Verifier verifier) {
+        this.verifier = verifier;
+    }
+    
+    static XmlSchema createSchemaInstance(ThreadContext context, RubyClass klazz, Source source) {
+        Ruby runtime = context.getRuntime();
+        XmlRelaxng xmlRelaxng = (XmlRelaxng) NokogiriService.XML_RELAXNG_ALLOCATOR.allocate(runtime, klazz);
+        xmlRelaxng.setInstanceVariable("@errors", runtime.newEmptyArray());
+        
+        try {
+            Schema schema = xmlRelaxng.getSchema(source, context);
+            xmlRelaxng.setVerifier(schema.newVerifier());
+            return xmlRelaxng;
+        } catch (VerifierConfigurationException ex) {
+            throw context.getRuntime().newRuntimeError("Could not parse document: " + ex.getMessage());
+        }
+    }
+
+    private Schema getSchema(Source source, ThreadContext context) {
+        InputStream is;
+        VerifierFactory factory = new com.thaiopensource.relaxng.jarv.VerifierFactoryImpl();
+        if (source instanceof StreamSource) {
+            StreamSource ss = (StreamSource)source;
+            is = ss.getInputStream();
+        } else { //if (this.source instanceof DOMSource)
+            DOMSource ds = (DOMSource)source;
+            StringWriter xmlAsWriter = new StringWriter();
+            StreamResult result = new StreamResult(xmlAsWriter);
+            try {
+                TransformerFactory.newInstance().newTransformer().transform(ds, result);
+            } catch (TransformerConfigurationException ex) {
+                throw context.getRuntime()
+                    .newRuntimeError("Could not parse document: "+ex.getMessage());
+            } catch (TransformerException ex) {
+                throw context.getRuntime()
+                    .newRuntimeError("Could not parse document: "+ex.getMessage());
+            }
+            try {
+                is = new ByteArrayInputStream(xmlAsWriter.toString().getBytes("UTF-8"));
+            } catch (UnsupportedEncodingException ex) {
+                throw context.getRuntime()
+                    .newRuntimeError("Could not parse document: "+ex.getMessage());
+            }
+        }
+
+        try {
+            return factory.compileSchema(is);
+        } catch (VerifierConfigurationException ex) {
+            throw context.getRuntime()
+                .newRuntimeError("Could not parse document: "+ex.getMessage());
+        } catch (SAXException ex) {
+            throw context.getRuntime()
+                .newRuntimeError("Could not parse document: "+ex.getMessage());
+        } catch (IOException ex) {
+            throw context.getRuntime().newIOError(ex.getMessage());
+        }
+    }
+    
+    @Override
+    protected void setErrorHandler(ErrorHandler errorHandler) {
+        verifier.setErrorHandler(errorHandler);
+    }
+    
+    @Override
+    protected void validate(Document document) throws SAXException, IOException {
+        verifier.verify(document);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlSaxParserContext.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlSaxParserContext.java
--- 1.8.4-1/ext/java/nokogiri/XmlSaxParserContext.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlSaxParserContext.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,369 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static org.jruby.javasupport.util.RuntimeHelpers.invoke;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import nokogiri.internals.NokogiriHandler;
+import nokogiri.internals.NokogiriHelpers;
+import nokogiri.internals.ParserContext;
+import nokogiri.internals.XmlSaxParser;
+
+import org.apache.xerces.parsers.AbstractSAXParser;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyModule;
+import org.jruby.RubyObjectAdapter;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.javasupport.JavaEmbedUtils;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Base class for the SAX parsers.
+ *
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::SAX::ParserContext")
+public class XmlSaxParserContext extends ParserContext {
+
+    protected static final String FEATURE_NAMESPACES =
+        "http://xml.org/sax/features/namespaces";
+    protected static final String FEATURE_NAMESPACE_PREFIXES =
+        "http://xml.org/sax/features/namespace-prefixes";
+    protected static final String FEATURE_LOAD_EXTERNAL_DTD =
+        "http://apache.org/xml/features/nonvalidating/load-external-dtd";
+    protected static final String FEATURE_CONTINUE_AFTER_FATAL_ERROR =
+        "http://apache.org/xml/features/continue-after-fatal-error";
+
+    protected AbstractSAXParser parser;
+
+    protected NokogiriHandler handler;
+    private boolean replaceEntities = true;
+    private boolean recovery = false;
+
+    public XmlSaxParserContext(final Ruby ruby, RubyClass rubyClass) {
+        super(ruby, rubyClass);
+    }
+        
+    protected void initialize(Ruby runtime) {
+        try {
+            parser = createParser();
+        }
+        catch (SAXException se) {
+            throw RaiseException.createNativeRaiseException(runtime, se);
+        }
+    }
+
+    /**
+     * Create and return a copy of this object.
+     *
+     * @return a clone of this object
+     */
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+    protected AbstractSAXParser createParser() throws SAXException {
+        XmlSaxParser parser = new XmlSaxParser();
+        parser.setFeature(FEATURE_NAMESPACE_PREFIXES, true);
+        parser.setFeature(FEATURE_LOAD_EXTERNAL_DTD, false);
+        return parser;
+    }
+
+    /**
+     * Create a new parser context that will parse the string
+     * <code>data</code>.
+     */
+    @JRubyMethod(name="memory", meta=true)
+    public static IRubyObject parse_memory(ThreadContext context,
+                                           IRubyObject klazz,
+                                           IRubyObject data) {
+        final Ruby runtime = context.runtime;
+        XmlSaxParserContext ctx = newInstance(runtime, (RubyClass) klazz);
+        ctx.initialize(runtime);
+        ctx.setInputSource(context, data, runtime.getNil());
+        return ctx;
+    }
+
+    /**
+     * Create a new parser context that will read from the file
+     * <code>data</code> and parse.
+     */
+    @JRubyMethod(name="file", meta=true)
+    public static IRubyObject parse_file(ThreadContext context,
+                                         IRubyObject klazz,
+                                         IRubyObject data) {
+        final Ruby runtime = context.runtime;
+        XmlSaxParserContext ctx = newInstance(runtime, (RubyClass) klazz);
+        ctx.initialize(context.getRuntime());
+        ctx.setInputSourceFile(context, data);
+        return ctx;
+    }
+
+    /**
+     * Create a new parser context that will read from the IO or
+     * StringIO <code>data</code> and parse.
+     *
+     * TODO: Currently ignores encoding <code>enc</code>.
+     */
+    @JRubyMethod(name="io", meta=true)
+    public static IRubyObject parse_io(ThreadContext context,
+                                       IRubyObject klazz,
+                                       IRubyObject data,
+                                       IRubyObject enc) {
+        //int encoding = (int)enc.convertToInteger().getLongValue();
+        final Ruby runtime = context.runtime;
+        XmlSaxParserContext ctx = newInstance(runtime, (RubyClass) klazz);
+        ctx.initialize(runtime);
+        ctx.setInputSource(context, data, runtime.getNil());
+        return ctx;
+    }
+
+    /**
+     * Create a new parser context that will read from a raw input stream.
+     * Meant to be run in a separate thread by XmlSaxPushParser.
+     */
+    static XmlSaxParserContext parse_stream(final Ruby runtime, RubyClass klazz, InputStream stream) {
+        XmlSaxParserContext ctx = newInstance(runtime, klazz);
+        ctx.initialize(runtime);
+        ctx.setInputSource(stream);
+        return ctx;
+    }
+
+    private static XmlSaxParserContext newInstance(final Ruby runtime, final RubyClass klazz) {
+        return (XmlSaxParserContext) NokogiriService.XML_SAXPARSER_CONTEXT_ALLOCATOR.allocate(runtime, klazz);
+    }
+
+    /**
+     * Set a property of the underlying parser.
+     */
+    protected void setProperty(String key, Object val)
+        throws SAXNotRecognizedException, SAXNotSupportedException {
+        parser.setProperty(key, val);
+    }
+
+    protected void setContentHandler(ContentHandler handler) {
+        parser.setContentHandler(handler);
+    }
+
+    protected void setErrorHandler(ErrorHandler handler) {
+        parser.setErrorHandler(handler);
+    }
+
+    public final NokogiriHandler getNokogiriHandler() { return handler; }
+
+    /**
+     * Perform any initialization prior to parsing with the handler
+     * <code>handlerRuby</code>. Convenience hook for subclasses.
+     */
+    protected void preParse(Ruby runtime, IRubyObject handlerRuby, NokogiriHandler handler) {
+        ((XmlSaxParser) parser).setXmlDeclHandler(handler);
+        if (recovery) {
+            try {
+                parser.setFeature(FEATURE_CONTINUE_AFTER_FATAL_ERROR, true);
+            }
+            catch (Exception e) {
+                throw RaiseException.createNativeRaiseException(runtime, e);
+            }
+        }
+    }
+
+    protected void postParse(Ruby runtime, IRubyObject handlerRuby, NokogiriHandler handler) {
+        // noop
+    }
+
+    protected void do_parse() throws SAXException, IOException {
+        parser.parse(getInputSource());
+    }
+
+    @JRubyMethod
+    public IRubyObject parse_with(ThreadContext context, IRubyObject handlerRuby) {
+        final Ruby runtime = context.getRuntime();
+
+        if(!invoke(context, handlerRuby, "respond_to?", runtime.newSymbol("document")).isTrue()) {
+            throw runtime.newArgumentError("argument must respond_to document");
+        }
+
+        NokogiriHandler handler = this.handler = new NokogiriHandler(runtime, handlerRuby);
+        preParse(runtime, handlerRuby, handler);
+
+        setContentHandler(handler);
+        setErrorHandler(handler);
+
+        try{
+            setProperty("http://xml.org/sax/properties/lexical-handler", handler);
+        }
+        catch (Exception ex) {
+            throw runtime.newRuntimeError("Problem while creating XML SAX Parser: " + ex.toString());
+        }
+
+        try{
+            try {
+                do_parse();
+            }
+            catch (SAXParseException ex) {
+                // A bad document (<foo><bar></foo>) should call the
+                // error handler instead of raising a SAX exception.
+
+                // However, an EMPTY document should raise a RuntimeError.
+                // This is a bit kludgy, but AFAIK SAX doesn't distinguish
+                // between empty and bad whereas Nokogiri does.
+                String message = ex.getMessage();
+                if (message != null && message.contains("Premature end of file.") && stringDataSize < 1) {
+                    throw runtime.newRuntimeError("couldn't parse document: " + message);
+                }
+                handler.error(ex);
+            }
+        }
+        catch (SAXException ex) {
+            throw RaiseException.createNativeRaiseException(runtime, ex);
+        }
+        catch (IOException ex) {
+            throw runtime.newIOErrorFromException(ex);
+        }
+
+        postParse(runtime, handlerRuby, handler);
+
+        //maybeTrimLeadingAndTrailingWhitespace(context, handlerRuby);
+
+        return runtime.getNil();
+    }
+
+    /**
+     * Can take a boolean assignment.
+     *
+     * @param context
+     * @param value
+     * @return
+     */
+    @JRubyMethod(name = "replace_entities=")
+    public IRubyObject set_replace_entities(ThreadContext context, IRubyObject value) {
+        replaceEntities = value.isTrue();
+        return this;
+    }
+
+    @JRubyMethod(name="replace_entities")
+    public IRubyObject get_replace_entities(ThreadContext context) {
+        return context.runtime.newBoolean(replaceEntities);
+    }
+
+    /**
+     * Can take a boolean assignment.
+     *
+     * @param context
+     * @param value
+     * @return
+     */
+    @JRubyMethod(name = "recovery=")
+    public IRubyObject set_recovery(ThreadContext context, IRubyObject value) {
+        recovery = value.isTrue();
+        return this;
+    }
+
+    @JRubyMethod(name="recovery")
+    public IRubyObject get_recovery(ThreadContext context) {
+        return context.runtime.newBoolean(recovery);
+    }
+
+    /**
+     * If the handler's document is a FragmentHandler, attempt to trim
+     * leading and trailing whitespace.
+     *
+     * This is a bit hackish and depends heavily on the internals of
+     * FragmentHandler.
+     */
+    protected void maybeTrimLeadingAndTrailingWhitespace(ThreadContext context, IRubyObject parser) {
+        RubyObjectAdapter adapter = JavaEmbedUtils.newObjectAdapter();
+        RubyModule mod = context.getRuntime().getClassFromPath("Nokogiri::XML::FragmentHandler");
+
+        IRubyObject handler = adapter.getInstanceVariable(parser, "@document");
+        if (handler == null || handler.isNil() || !adapter.isKindOf(handler, mod))
+            return;
+        IRubyObject stack = adapter.getInstanceVariable(handler, "@stack");
+        if (stack == null || stack.isNil())
+            return;
+        // doc is finally a DocumentFragment whose nodes we can check
+        IRubyObject doc = adapter.callMethod(stack, "first");
+        if (doc == null || doc.isNil())
+            return;
+
+        IRubyObject children;
+
+        for (;;) {
+            children = adapter.callMethod(doc, "children");
+            IRubyObject first = adapter.callMethod(children, "first");
+            if (NokogiriHelpers.isBlank(first)) adapter.callMethod(first, "unlink");
+            else break;
+        }
+
+        for (;;) {
+            children = adapter.callMethod(doc, "children");
+            IRubyObject last = adapter.callMethod(children, "last");
+            if (NokogiriHelpers.isBlank(last)) adapter.callMethod(last, "unlink");
+            else break;
+        }
+
+        // While we have a document, normalize it.
+        ((XmlNode) doc).normalize();
+    }
+
+    @JRubyMethod(name="column")
+    public IRubyObject column(ThreadContext context) {
+        final Integer number = handler.getColumn();
+        if (number == null) return context.getRuntime().getNil();
+        return RubyFixnum.newFixnum(context.getRuntime(), number.longValue());
+    }
+
+    @JRubyMethod(name="line")
+    public IRubyObject line(ThreadContext context) {
+        final Integer number = handler.getLine();
+        if (number == null) return context.getRuntime().getNil();
+        return RubyFixnum.newFixnum(context.getRuntime(), number.longValue());
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlSaxPushParser.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlSaxPushParser.java
--- 1.8.4-1/ext/java/nokogiri/XmlSaxPushParser.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlSaxPushParser.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,284 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static org.jruby.javasupport.util.RuntimeHelpers.invoke;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.ThreadFactory;
+
+import nokogiri.internals.*;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyException;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.xml.sax.SAXException;
+
+/**
+ * Class for Nokogiri::XML::SAX::PushParser
+ *
+ * @author Patrick Mahoney <pat@polycrystal.org>
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::SAX::PushParser")
+public class XmlSaxPushParser extends RubyObject {
+    ParserContext.Options options;
+    IRubyObject saxParser;
+
+    NokogiriBlockingQueueInputStream stream;
+
+    private ParserTask parserTask = null;
+    private FutureTask<XmlSaxParserContext> futureTask = null;
+    private ExecutorService executor = null;
+    RaiseException ex = null;
+
+    public XmlSaxPushParser(Ruby ruby, RubyClass rubyClass) {
+        super(ruby, rubyClass);
+    }
+
+    @Override
+    public void finalize() {
+        try {
+            terminateImpl();
+        }
+        catch (Exception e) { /* ignored */ }
+    }
+
+    @JRubyMethod
+    public IRubyObject initialize_native(final ThreadContext context, IRubyObject saxParser, IRubyObject fileName) {
+        options = new ParserContext.Options(0);
+        this.saxParser = saxParser;
+        return this;
+    }
+
+    private transient IRubyObject parse_options;
+
+    private IRubyObject parse_options(final ThreadContext context) {
+        if (parse_options == null) {
+            parse_options = invoke(context, context.runtime.getClassFromPath("Nokogiri::XML::ParseOptions"), "new");
+        }
+        return parse_options;
+    }
+
+    @JRubyMethod(name="options")
+    public IRubyObject getOptions(ThreadContext context) {
+        return invoke(context, parse_options(context), "options");
+    }
+
+    @JRubyMethod(name="options=")
+    public IRubyObject setOptions(ThreadContext context, IRubyObject opts) {
+        invoke(context, parse_options(context), "options=", opts);
+        options = new ParserContext.Options(opts.convertToInteger().getLongValue());
+        return getOptions(context);
+    }
+
+    /**
+     * Can take a boolean assignment.
+     *
+     * @param context
+     * @param value
+     * @return
+     */
+    @JRubyMethod(name = "replace_entities=")
+    public IRubyObject setReplaceEntities(ThreadContext context, IRubyObject value) {
+        // Ignore the value.
+        return this;
+    }
+
+    @JRubyMethod(name="replace_entities")
+    public IRubyObject getReplaceEntities(ThreadContext context) {
+        // The java parser always replaces entities.
+        return context.getRuntime().getTrue();
+    }
+
+    @JRubyMethod
+    public IRubyObject native_write(ThreadContext context, IRubyObject chunk,
+                                    IRubyObject isLast) {
+        if (ex != null) {
+            // parser has already errored, rethrow the exception
+            throw ex;
+        }
+
+        try {
+            initialize_task(context);
+        } catch (IOException e) {
+            throw context.runtime.newRuntimeError(e.getMessage());
+        }
+        final ByteArrayInputStream data = NokogiriHelpers.stringBytesToStream(chunk);
+        if (data == null) {
+            return this;
+        }
+
+        int errorCount0 = parserTask.getErrorCount();
+
+        try {
+            Future<Void> task = stream.addChunk(data);
+            task.get();
+        } catch (ClosedStreamException ex) {
+            // this means the stream is closed, ignore this exception
+        } catch (Exception e) {
+            throw context.runtime.newRuntimeError(e.toString());
+        }
+
+        if (isLast.isTrue()) {
+            parserTask.getNokogiriHandler().endDocument();
+            terminateTask(context.runtime);
+        }
+
+        if (!options.recover && parserTask.getErrorCount() > errorCount0) {
+            terminateTask(context.runtime);
+            throw ex = parserTask.getLastError();
+        }
+
+        return this;
+    }
+
+    private void initialize_task(ThreadContext context) throws IOException {
+        if (futureTask == null || stream == null) {
+            stream = new NokogiriBlockingQueueInputStream();
+
+            assert saxParser != null : "saxParser null";
+            parserTask = new ParserTask(context, saxParser, stream);
+            futureTask = new FutureTask<XmlSaxParserContext>(parserTask);
+            executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
+                @Override
+                public Thread newThread(Runnable r) {
+                    Thread t = new Thread(r);
+                    t.setName("XmlSaxPushParser");
+                    t.setDaemon(true);
+                    return t;
+                }
+            });
+            executor.submit(futureTask);
+        }
+    }
+
+    private void terminateTask(final Ruby runtime) {
+        if (executor == null) return;
+
+        try {
+            terminateImpl();
+        }
+        catch (InterruptedException e) {
+            throw runtime.newRuntimeError(e.toString());
+        }
+        catch (Exception e) {
+            throw runtime.newRuntimeError(e.toString());
+        }
+    }
+
+    private synchronized void terminateImpl() throws InterruptedException, ExecutionException {
+        terminateExecution(executor, stream, futureTask);
+
+        executor = null; stream = null; futureTask = null;
+    }
+
+    // SHARED for HtmlSaxPushParser
+    static void terminateExecution(final ExecutorService executor, final NokogiriBlockingQueueInputStream stream,
+                                   final FutureTask<?> futureTask)
+        throws InterruptedException, ExecutionException {
+
+        if (executor == null) return;
+
+        try {
+            Future<Void> task = stream.addChunk(NokogiriBlockingQueueInputStream.END);
+            task.get();
+        }
+        catch (ClosedStreamException ex) {
+            // ignore this exception, it means the stream was closed
+        }
+        futureTask.cancel(true);
+        executor.shutdown();
+    }
+
+    private static XmlSaxParserContext parse(final Ruby runtime, final InputStream stream) {
+        RubyClass klazz = getNokogiriClass(runtime, "Nokogiri::XML::SAX::ParserContext");
+        return XmlSaxParserContext.parse_stream(runtime, klazz, stream);
+    }
+
+    static class ParserTask extends ParserContext.ParserTask<XmlSaxParserContext> {
+
+        final InputStream stream;
+
+        private ParserTask(ThreadContext context, IRubyObject handler, InputStream stream) {
+            this(context, handler, parse(context.runtime, stream), stream);
+        }
+
+        // IMPL with HtmlSaxPushParser
+        protected ParserTask(ThreadContext context, IRubyObject handler, XmlSaxParserContext parser, InputStream stream) {
+            super(context, handler, parser);
+            this.stream = stream;
+        }
+
+        @Override
+        public XmlSaxParserContext call() throws Exception {
+            try {
+                parser.parse_with(context, handler);
+            }
+            finally { stream.close(); }
+            // we have to close the stream before exiting, otherwise someone
+            // can add a chunk and block on task.get() forever.
+            return parser;
+        }
+
+        final NokogiriHandler getNokogiriHandler() {
+            return parser.getNokogiriHandler();
+        }
+
+        synchronized final int getErrorCount() {
+            // check for null because thread may not have started yet
+            if (parser.getNokogiriHandler() == null) return 0;
+            return parser.getNokogiriHandler().getErrorCount();
+        }
+
+        synchronized final RaiseException getLastError() {
+            return parser.getNokogiriHandler().getLastError();
+        }
+
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlSchema.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlSchema.java
--- 1.8.4-1/ext/java/nokogiri/XmlSchema.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlSchema.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,328 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.adjustSystemIdIfNecessary;
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+
+import javax.xml.XMLConstants;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+
+import nokogiri.internals.IgnoreSchemaErrorsErrorHandler;
+import nokogiri.internals.SchemaErrorHandler;
+import nokogiri.internals.XmlDomParserContext;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.Visibility;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Document;
+import org.w3c.dom.ls.LSInput;
+import org.w3c.dom.ls.LSResourceResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Class for Nokogiri::XML::Schema
+ *
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::Schema")
+public class XmlSchema extends RubyObject {
+    private Validator validator;
+
+    public XmlSchema(Ruby ruby, RubyClass klazz) {
+        super(ruby, klazz);
+    }
+
+    /**
+     * Create and return a copy of this object.
+     *
+     * @return a clone of this object
+     */
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+    private Schema getSchema(Source source, String currentDir, String scriptFileName) throws SAXException {
+        SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        SchemaResourceResolver resourceResolver = new SchemaResourceResolver(currentDir, scriptFileName, null);
+        schemaFactory.setResourceResolver(resourceResolver);
+        schemaFactory.setErrorHandler(new IgnoreSchemaErrorsErrorHandler());
+        return schemaFactory.newSchema(source);
+    }
+
+    private void setValidator(Validator validator) {
+        this.validator = validator;
+    }
+
+    static XmlSchema createSchemaInstance(ThreadContext context, RubyClass klazz, Source source) {
+        Ruby runtime = context.getRuntime();
+        XmlSchema xmlSchema = (XmlSchema) NokogiriService.XML_SCHEMA_ALLOCATOR.allocate(runtime, klazz);
+        xmlSchema.setInstanceVariable("@errors", runtime.newEmptyArray());
+
+        try {
+            Schema schema = xmlSchema.getSchema(source, context.getRuntime().getCurrentDirectory(), context.getRuntime().getInstanceConfig().getScriptFileName());
+            xmlSchema.setValidator(schema.newValidator());
+            return xmlSchema;
+        } catch (SAXException ex) {
+            throw context.getRuntime().newRuntimeError("Could not parse document: " + ex.getMessage());
+        }
+    }
+
+    /*
+     * call-seq:
+     *  from_document(doc)
+     *
+     * Create a new Schema from the Nokogiri::XML::Document +doc+
+     */
+    @JRubyMethod(meta=true)
+    public static IRubyObject from_document(ThreadContext context, IRubyObject klazz, IRubyObject document) {
+        XmlDocument doc = ((XmlDocument) ((XmlNode) document).document(context));
+
+        RubyArray errors = (RubyArray) doc.getInstanceVariable("@errors");
+        if (!errors.isEmpty()) {
+            throw new RaiseException((XmlSyntaxError) errors.first());
+        }
+
+        DOMSource source = new DOMSource(doc.getDocument());
+
+        IRubyObject uri = doc.url(context);
+
+        if (!uri.isNil()) {
+            source.setSystemId(uri.convertToString().asJavaString());
+        }
+
+        return getSchema(context, (RubyClass)klazz, source);
+    }
+
+    private static IRubyObject getSchema(ThreadContext context, RubyClass klazz, Source source) {
+        String moduleName = klazz.getName();
+        if ("Nokogiri::XML::Schema".equals(moduleName)) {
+            return XmlSchema.createSchemaInstance(context, klazz, source);
+        } else if ("Nokogiri::XML::RelaxNG".equals(moduleName)) {
+            return XmlRelaxng.createSchemaInstance(context, klazz, source);
+        }
+        return context.getRuntime().getNil();
+    }
+
+    @JRubyMethod(meta=true)
+    public static IRubyObject read_memory(ThreadContext context, IRubyObject klazz, IRubyObject content) {
+        String data = content.convertToString().asJavaString();
+        return getSchema(context, (RubyClass) klazz, new StreamSource(new StringReader(data)));
+    }
+
+    @JRubyMethod(visibility=Visibility.PRIVATE)
+    public IRubyObject validate_document(ThreadContext context, IRubyObject document) {
+        return validate_document_or_file(context, (XmlDocument)document);
+    }
+
+    @JRubyMethod(visibility=Visibility.PRIVATE)
+    public IRubyObject validate_file(ThreadContext context, IRubyObject file) {
+        Ruby ruby = context.getRuntime();
+
+        XmlDomParserContext ctx = new XmlDomParserContext(ruby, RubyFixnum.newFixnum(ruby, 1L));
+        ctx.setInputSourceFile(context, file);
+        XmlDocument xmlDocument = ctx.parse(context, getNokogiriClass(ruby, "Nokogiri::XML::Document"), ruby.getNil());
+        return validate_document_or_file(context, xmlDocument);
+    }
+
+    IRubyObject validate_document_or_file(ThreadContext context, XmlDocument xmlDocument) {
+        RubyArray errors = (RubyArray) this.getInstanceVariable("@errors");
+        ErrorHandler errorHandler = new SchemaErrorHandler(context.runtime, errors);
+        setErrorHandler(errorHandler);
+
+        try {
+            validate(xmlDocument.getDocument());
+        }
+        catch (SAXException ex) {
+            XmlSyntaxError xmlSyntaxError = XmlSyntaxError.createXMLSyntaxError(context.runtime);
+            xmlSyntaxError.setException(ex);
+            errors.append(xmlSyntaxError);
+        }
+        catch (IOException ex) {
+            throw context.runtime.newIOError(ex.getMessage());
+        }
+
+        return errors;
+    }
+
+    protected void setErrorHandler(ErrorHandler errorHandler) {
+        validator.setErrorHandler(errorHandler);
+    }
+
+    protected void validate(Document document) throws SAXException, IOException {
+        DOMSource docSource = new DOMSource(document);
+        validator.validate(docSource);
+    }
+
+    private class SchemaResourceResolver implements LSResourceResolver {
+        SchemaLSInput lsInput = new SchemaLSInput();
+        String currentDir;
+        String scriptFileName;
+        //String defaultURI;
+
+        SchemaResourceResolver(String currentDir, String scriptFileName, Object input) {
+            this.currentDir = currentDir;
+            this.scriptFileName = scriptFileName;
+            if (input == null) return;
+            if (input instanceof String) {
+                lsInput.setStringData((String)input);
+            } else if (input instanceof Reader) {
+                lsInput.setCharacterStream((Reader)input);
+            } else if (input instanceof InputStream) {
+                lsInput.setByteStream((InputStream)input);
+            }
+        }
+
+        @Override
+        public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
+            String adjusted = adjustSystemIdIfNecessary(currentDir, scriptFileName, baseURI, systemId);
+            lsInput.setPublicId(publicId);
+            lsInput.setSystemId(adjusted != null? adjusted : systemId);
+            lsInput.setBaseURI(baseURI);
+            return lsInput;
+        }
+    }
+
+    private class SchemaLSInput implements LSInput {
+        protected String fPublicId;
+        protected String fSystemId;
+        protected String fBaseSystemId;
+        protected InputStream fByteStream;
+        protected Reader fCharStream;
+        protected String fData;
+        protected String fEncoding;
+        protected boolean fCertifiedText = false;
+
+        @Override
+        public String getBaseURI() {
+            return fBaseSystemId;
+        }
+
+        @Override
+        public InputStream getByteStream() {
+            return fByteStream;
+        }
+
+        @Override
+        public boolean getCertifiedText() {
+            return fCertifiedText;
+        }
+
+        @Override
+        public Reader getCharacterStream() {
+            return fCharStream;
+        }
+
+        @Override
+        public String getEncoding() {
+            return fEncoding;
+        }
+
+        @Override
+        public String getPublicId() {
+            return fPublicId;
+        }
+
+        @Override
+        public String getStringData() {
+            return fData;
+        }
+
+        @Override
+        public String getSystemId() {
+            return fSystemId;
+        }
+
+        @Override
+        public void setBaseURI(String baseURI) {
+            fBaseSystemId = baseURI;
+        }
+
+        @Override
+        public void setByteStream(InputStream byteStream) {
+            fByteStream = byteStream;
+        }
+
+        @Override
+        public void setCertifiedText(boolean certified) {
+            fCertifiedText = certified;
+        }
+
+        @Override
+        public void setCharacterStream(Reader charStream) {
+            fCharStream = charStream;
+        }
+
+        @Override
+        public void setEncoding(String encoding) {
+            fEncoding = encoding;
+        }
+
+        @Override
+        public void setPublicId(String pubId) {
+            fPublicId = pubId;
+        }
+
+        @Override
+        public void setStringData(String stringData) {
+            fData = stringData;
+        }
+
+        @Override
+        public void setSystemId(String sysId) {
+            fSystemId = sysId;
+        }
+
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlSyntaxError.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlSyntaxError.java
--- 1.8.4-1/ext/java/nokogiri/XmlSyntaxError.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlSyntaxError.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,142 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2012:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.stringOrNil;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyException;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Class for Nokogiri::XML::SyntaxError
+ *
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::SyntaxError", parent="Nokogiri::SyntaxError")
+public class XmlSyntaxError extends RubyException {
+
+    private Exception exception;
+    private boolean messageSet; // whether a custom error message was set
+
+    public XmlSyntaxError(Ruby runtime, RubyClass klazz) {
+        super(runtime, klazz);
+    }
+
+    public XmlSyntaxError(Ruby runtime, RubyClass rubyClass, Exception ex) {
+        super(runtime, rubyClass, ex.getMessage());
+        this.exception = ex;
+    }
+
+    public XmlSyntaxError(Ruby runtime, RubyClass rubyClass, String message, Exception ex) {
+        super(runtime, rubyClass, message);
+        this.exception = ex; this.messageSet = true;
+    }
+
+    public static XmlSyntaxError createXMLSyntaxError(final Ruby runtime) {
+        RubyClass klazz = (RubyClass) runtime.getClassFromPath("Nokogiri::XML::SyntaxError");
+        return new XmlSyntaxError(runtime, klazz);
+    }
+
+    public static XmlSyntaxError createXMLSyntaxError(final Ruby runtime, final Exception ex) {
+        RubyClass klazz = (RubyClass) runtime.getClassFromPath("Nokogiri::XML::SyntaxError");
+        return new XmlSyntaxError(runtime, klazz, ex);
+    }
+
+    public static XmlSyntaxError createHTMLSyntaxError(final Ruby runtime) {
+        RubyClass klazz = (RubyClass) runtime.getClassFromPath("Nokogiri::HTML::SyntaxError");
+        return new XmlSyntaxError(runtime, klazz);
+    }
+
+    public static RubyException createXMLXPathSyntaxError(final Ruby runtime, final String msg, final Exception ex) {
+        RubyClass klazz = (RubyClass) runtime.getClassFromPath("Nokogiri::XML::XPath::SyntaxError");
+        return new XmlSyntaxError(runtime, klazz, msg, ex);
+    }
+
+    public static XmlSyntaxError createWarning(Ruby runtime, SAXParseException e) {
+        XmlSyntaxError xmlSyntaxError = createXMLSyntaxError(runtime);
+        xmlSyntaxError.setException(runtime, e, 1);
+        return xmlSyntaxError;
+    }
+
+    public static XmlSyntaxError createError(Ruby runtime, SAXParseException e) {
+        XmlSyntaxError xmlSyntaxError = createXMLSyntaxError(runtime);
+        xmlSyntaxError.setException(runtime, e, 2);
+        return xmlSyntaxError;
+    }
+
+    public static XmlSyntaxError createFatalError(Ruby runtime, SAXParseException e) {
+        XmlSyntaxError xmlSyntaxError = createXMLSyntaxError(runtime);
+        xmlSyntaxError.setException(runtime, e, 3);
+        return xmlSyntaxError;
+    }
+
+    public void setException(Exception exception) {
+        this.exception = exception;
+    }
+
+    public void setException(Ruby runtime, SAXParseException exception, int level) {
+        this.exception = exception;
+        setInstanceVariable("@level", runtime.newFixnum(level));
+        setInstanceVariable("@line", runtime.newFixnum(exception.getLineNumber()));
+        setInstanceVariable("@column", runtime.newFixnum(exception.getColumnNumber()));
+        setInstanceVariable("@file", stringOrNil(runtime, exception.getSystemId()));
+    }
+
+    // NOTE: special care - due JRuby 1.7.x
+    
+    @Override
+    public IRubyObject to_s(ThreadContext context) { return to_s19(context); }
+
+    @JRubyMethod(name = "to_s")
+    public RubyString to_s19(ThreadContext context) {
+        RubyString msg = msg(context.runtime);
+        return msg != null ? msg : super.to_s(context).asString();
+    }
+
+    private RubyString msg(final Ruby runtime) {
+        if (exception != null && exception.getMessage() != null) {
+            if (messageSet) return null;
+            return runtime.newString( exception.getMessage() );
+        }
+        return null;
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlText.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlText.java
--- 1.8.4-1/ext/java/nokogiri/XmlText.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlText.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,107 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getCachedNodeOrCreate;
+import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
+import nokogiri.internals.SaveContextVisitor;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.anno.JRubyClass;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+
+/**
+ * Class for Nokogiri::XML::Text
+ *
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::Text", parent="Nokogiri::XML::CharacterData")
+public class XmlText extends XmlNode {
+
+    public XmlText(Ruby runtime, RubyClass rubyClass, Node node) {
+        super(runtime, rubyClass, node);
+    }
+
+    public XmlText(Ruby runtime, RubyClass klass) {
+        super(runtime, klass);
+    }
+
+    @Override
+    protected void init(ThreadContext context, IRubyObject[] args) {
+        if (args.length < 2) {
+            throw getRuntime().newArgumentError(args.length, 2);
+        }
+
+        content = args[0];
+        IRubyObject xNode = args[1];
+
+        XmlNode xmlNode = asXmlNode(context, xNode);
+        XmlDocument xmlDoc = (XmlDocument)xmlNode.document(context);
+        doc = xmlDoc;
+        Document document = xmlDoc.getDocument();
+        // text node content should not be encoded when it is created by Text node.
+        // while content should be encoded when it is created by Element node.
+        Node node = document.createTextNode(rubyStringToString(content));
+        setNode(context, node);
+    }
+    
+    @Override
+    protected IRubyObject getNodeName(ThreadContext context) {
+        if (name == null) name = context.getRuntime().newString("text");
+        return name;
+    }
+
+    @Override
+    public void accept(ThreadContext context, SaveContextVisitor visitor) {
+        visitor.enter((Text)node);
+        Node child = node.getFirstChild();
+        while (child != null) {
+            IRubyObject nokoNode = getCachedNodeOrCreate(context.getRuntime(), child);
+            if (nokoNode instanceof XmlNode) {
+                XmlNode cur = (XmlNode) nokoNode;
+                cur.accept(context, visitor);
+            } else if (nokoNode instanceof XmlNamespace) {
+                XmlNamespace cur = (XmlNamespace) nokoNode;
+                cur.accept(context, visitor);
+            }
+            child = child.getNextSibling();
+        }
+        visitor.leave(node);
+    }
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XmlXpathContext.java 1.10.0+dfsg1-2/ext/java/nokogiri/XmlXpathContext.java
--- 1.8.4-1/ext/java/nokogiri/XmlXpathContext.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XmlXpathContext.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,271 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2014:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import java.util.Set;
+
+import javax.xml.transform.TransformerException;
+
+import org.apache.xml.dtm.DTM;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.jaxp.JAXPPrefixResolver;
+import org.apache.xpath.jaxp.JAXPVariableStack;
+import org.apache.xpath.objects.XObject;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.SafePropertyAccessor;
+import org.w3c.dom.Node;
+
+import nokogiri.internals.NokogiriNamespaceContext;
+import nokogiri.internals.NokogiriXPathFunctionResolver;
+import nokogiri.internals.NokogiriXPathVariableResolver;
+
+/**
+ * Class for Nokogiri::XML::XpathContext
+ *
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ * @author John Shahid <jvshahid@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XML::XPathContext")
+public class XmlXpathContext extends RubyObject {
+
+    static {
+        final String DTMManager = "org.apache.xml.dtm.DTMManager";
+        if (SafePropertyAccessor.getProperty(DTMManager) == null) {
+            try { // use patched "org.apache.xml.dtm.ref.DTMManagerDefault"
+                System.setProperty(DTMManager, nokogiri.internals.XalanDTMManagerPatch.class.getName());
+            }
+            catch (SecurityException ex) { /* no-op - will work although might be slower */ }
+        }
+    }
+
+    /**
+     * user-data key for (cached) {@link XPathContext}
+     */
+    public static final String XPATH_CONTEXT = "CACHED_XPATH_CONTEXT";
+
+    private XmlNode context;
+
+    public XmlXpathContext(Ruby runtime, RubyClass klass) {
+        super(runtime, klass);
+    }
+
+    public XmlXpathContext(Ruby runtime, RubyClass klass, XmlNode node) {
+        this(runtime, klass);
+        initNode(node);
+    }
+
+    private void initNode(XmlNode node) {
+        context = node;
+    }
+
+    @JRubyMethod(name = "new", meta = true)
+    public static IRubyObject rbNew(ThreadContext context, IRubyObject klazz, IRubyObject node) {
+        try {
+            return new XmlXpathContext(context.runtime, (RubyClass) klazz, (XmlNode) node);
+        }
+        catch (IllegalArgumentException e) {
+            throw context.getRuntime().newRuntimeError(e.getMessage());
+        }
+    }
+
+    @JRubyMethod
+    public IRubyObject evaluate(ThreadContext context, IRubyObject expr, IRubyObject handler) {
+
+        String src = expr.convertToString().asJavaString();
+        if (!handler.isNil()) {
+            if (!isContainsPrefix(src)) {
+                StringBuilder replacement = new StringBuilder();
+                Set<String> methodNames = handler.getMetaClass().getMethods().keySet();
+                final String PREFIX = NokogiriNamespaceContext.NOKOGIRI_PREFIX;
+                for (String name : methodNames) {
+                    replacement.setLength(0);
+                    replacement.ensureCapacity(PREFIX.length() + 1 + name.length());
+                    replacement.append(PREFIX).append(':').append(name);
+                    src = src.replace(name, replacement); // replace(name, NOKOGIRI_PREFIX + ':' + name)
+                }
+            }
+        }
+
+        return node_set(context, src, handler);
+    }
+
+    @JRubyMethod
+    public IRubyObject evaluate(ThreadContext context, IRubyObject expr) {
+        return this.evaluate(context, expr, context.getRuntime().getNil());
+    }
+
+    private final NokogiriNamespaceContext nsContext = NokogiriNamespaceContext.create();
+
+    @JRubyMethod
+    public IRubyObject register_ns(IRubyObject prefix, IRubyObject uri) {
+        nsContext.registerNamespace(prefix.asJavaString(), uri.asJavaString());
+        return this;
+    }
+
+    private NokogiriXPathVariableResolver variableResolver; // binds (if any)
+
+    @JRubyMethod
+    public IRubyObject register_variable(IRubyObject name, IRubyObject value) {
+        NokogiriXPathVariableResolver variableResolver = this.variableResolver;
+        if (variableResolver == null) {
+            variableResolver = NokogiriXPathVariableResolver.create();
+            this.variableResolver = variableResolver;
+        }
+        variableResolver.registerVariable(name.asJavaString(), value.asJavaString());
+        return this;
+    }
+
+    private IRubyObject node_set(ThreadContext context, String expr, IRubyObject handler) {
+        final NokogiriXPathFunctionResolver fnResolver =
+            handler.isNil() ? null : NokogiriXPathFunctionResolver.create(handler);
+        try {
+            return tryGetNodeSet(context, expr, fnResolver);
+        }
+        catch (TransformerException ex) {
+            throw new RaiseException(XmlSyntaxError.createXMLXPathSyntaxError(context.runtime, expr, ex)); // Nokogiri::XML::XPath::SyntaxError
+        }
+    }
+
+    private IRubyObject tryGetNodeSet(ThreadContext context, String expr, NokogiriXPathFunctionResolver fnResolver) throws TransformerException {
+        final Node contextNode = this.context.node;
+
+        final JAXPPrefixResolver prefixResolver = new JAXPPrefixResolver(nsContext);
+        XPath xpathInternal = new XPath(expr, null, prefixResolver, XPath.SELECT);
+
+        // We always need to have a ContextNode with Xalan XPath implementation
+        // To allow simple expression evaluation like 1+1 we are setting
+        // dummy Document as Context Node
+        final XObject xobj;
+        if ( contextNode == null )
+            xobj = xpathInternal.execute(getXPathContext(fnResolver), DTM.NULL, prefixResolver);
+        else
+            xobj = xpathInternal.execute(getXPathContext(fnResolver), contextNode, prefixResolver);
+
+        switch (xobj.getType()) {
+            case XObject.CLASS_BOOLEAN : return context.getRuntime().newBoolean(xobj.bool());
+            case XObject.CLASS_NUMBER :  return context.getRuntime().newFloat(xobj.num());
+            case XObject.CLASS_NODESET :
+                XmlNodeSet xmlNodeSet = XmlNodeSet.newEmptyNodeSet(context);
+                xmlNodeSet.setNodeList(xobj.nodelist());
+                xmlNodeSet.initialize(context.getRuntime(), this.context);
+                return xmlNodeSet;
+            default : return context.getRuntime().newString(xobj.str());
+        }
+    }
+
+    private XPathContext getXPathContext(final NokogiriXPathFunctionResolver fnResolver) {
+        Node doc = context.getNode().getOwnerDocument();
+        if (doc == null) doc = context.getNode();
+
+        XPathContext xpathContext = (XPathContext) doc.getUserData(XPATH_CONTEXT);
+
+        if ( xpathContext == null ) {
+            xpathContext = newXPathContext(fnResolver);
+            if ( variableResolver == null ) {
+                // NOTE: only caching without variables - could be improved by more sophisticated caching
+                doc.setUserData(XPATH_CONTEXT, xpathContext, null);
+            }
+        }
+        else {
+            Object owner = xpathContext.getOwnerObject();
+            if ( ( owner == null && fnResolver == null ) ||
+                ( owner instanceof JAXPExtensionsProvider && ((JAXPExtensionsProvider) owner).hasSameResolver(fnResolver) ) ) {
+                // can be re-used assuming it has the same variable-stack (for now only cached if no variables)
+                if ( variableResolver == null ) return xpathContext;
+            }
+            xpathContext = newXPathContext(fnResolver); // otherwise we can not use the cached xpath-context
+        }
+
+        if ( variableResolver != null ) {
+            xpathContext.setVarStack(new JAXPVariableStack(variableResolver));
+        }
+
+        return xpathContext;
+    }
+
+    private static XPathContext newXPathContext(final NokogiriXPathFunctionResolver functionResolver) {
+        if ( functionResolver == null ) return new XPathContext(false);
+        return new XPathContext(new JAXPExtensionsProvider(functionResolver), false);
+    }
+
+    private boolean isContainsPrefix(final String str) {
+        final StringBuilder prefix_ = new StringBuilder();
+        for ( String prefix : nsContext.getAllPrefixes() ) {
+            prefix_.setLength(0);
+            prefix_.ensureCapacity(prefix.length() + 1);
+            prefix_.append(prefix).append(':');
+            if ( str.contains(prefix_) ) { // prefix + ':'
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static final class JAXPExtensionsProvider extends org.apache.xpath.jaxp.JAXPExtensionsProvider {
+
+        final NokogiriXPathFunctionResolver resolver;
+
+        JAXPExtensionsProvider(NokogiriXPathFunctionResolver resolver) {
+            super(resolver, false);
+            this.resolver = resolver;
+        }
+
+        //@Override
+        //public boolean equals(Object obj) {
+        //    if (obj instanceof JAXPExtensionsProvider) {
+        //        return hasSameResolver(((JAXPExtensionsProvider) obj).resolver);
+        //    }
+        //    return false;
+        //}
+
+        final boolean hasSameResolver(final NokogiriXPathFunctionResolver resolver) {
+            return resolver == this.resolver || resolver != null && (
+                resolver.getHandler() == null ? this.resolver.getHandler() == null : (
+                    resolver.getHandler() == this.resolver.getHandler()
+                    // resolver.getHandler().eql( this.resolver.getHandler() )
+                )
+            );
+        }
+
+    }
+
+}
diff -pruN 1.8.4-1/ext/java/nokogiri/XsltStylesheet.java 1.10.0+dfsg1-2/ext/java/nokogiri/XsltStylesheet.java
--- 1.8.4-1/ext/java/nokogiri/XsltStylesheet.java	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/java/nokogiri/XsltStylesheet.java	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,358 @@
+/**
+ * (The MIT License)
+ *
+ * Copyright (c) 2008 - 2011:
+ *
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
+ * * {Mike Dalessio}[http://mike.daless.io]
+ * * {Charles Nutter}[http://blog.headius.com]
+ * * {Sergio Arbeo}[http://www.serabe.com]
+ * * {Patrick Mahoney}[http://polycrystal.org]
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package nokogiri;
+
+import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
+import static nokogiri.internals.NokogiriHelpers.stringOrBlank;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PipedReader;
+import java.io.PipedWriter;
+import java.io.StringReader;
+import java.nio.CharBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import nokogiri.internals.NokogiriXsltErrorListener;
+
+import org.apache.xalan.transformer.TransformerImpl;
+import org.apache.xml.serializer.SerializationHandler;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyHash;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.javasupport.util.RuntimeHelpers;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.w3c.dom.Document;
+
+/**
+ * Class for Nokogiri::XSLT::Stylesheet
+ *
+ * @author sergio
+ * @author Yoko Harada <yokolet@gmail.com>
+ */
+@JRubyClass(name="Nokogiri::XSLT::Stylesheet")
+public class XsltStylesheet extends RubyObject {
+
+    private TransformerFactory factory = null;
+    private Templates sheet = null;
+    private IRubyObject stylesheet = null;
+    private boolean htmlish = false;
+
+    public XsltStylesheet(Ruby ruby, RubyClass rubyClass) {
+        super(ruby, rubyClass);
+    }
+    
+    /**
+     * Create and return a copy of this object.
+     *
+     * @return a clone of this object
+     */
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+    private void addParametersToTransformer(ThreadContext context, Transformer transf, IRubyObject parameters) {
+        Ruby runtime = context.getRuntime();
+
+        if (parameters instanceof RubyHash) {
+            setHashParameters(transf, (RubyHash)parameters);
+        } else if (parameters instanceof RubyArray) {
+            setArrayParameters(transf, runtime, (RubyArray)parameters);
+        } else {
+            throw runtime.newTypeError("parameters should be given either Array or Hash");
+        }
+    }
+    
+    private void setHashParameters(Transformer transformer, RubyHash hash) {
+        Set<String> keys = hash.keySet();
+        for (String key : keys) {
+            String value = (String)hash.get(key);
+            transformer.setParameter(key, unparseValue(value));
+        }
+    }
+    
+    private void setArrayParameters(Transformer transformer, Ruby runtime, RubyArray params) {
+        int limit = params.getLength();
+        if(limit % 2 == 1) limit--;
+
+        for(int i = 0; i < limit; i+=2) {
+            String name = params.aref(runtime.newFixnum(i)).asJavaString();
+            String value = params.aref(runtime.newFixnum(i+1)).asJavaString();
+            transformer.setParameter(name, unparseValue(value));
+        }
+    }
+    
+    private Pattern p = Pattern.compile("'.{1,}'");
+
+    private String unparseValue(String orig) {
+        Matcher m = p.matcher(orig);
+        if ((orig.startsWith("\"") && orig.endsWith("\"")) || m.matches()) {
+            orig = orig.substring(1, orig.length()-1);
+        }
+
+        return orig;
+    }
+
+    @JRubyMethod(meta = true, rest = true)
+    public static IRubyObject parse_stylesheet_doc(ThreadContext context, IRubyObject klazz, IRubyObject[] args) {
+        
+        Ruby runtime = context.getRuntime();
+
+        ensureFirstArgIsDocument(runtime, args[0]);
+
+        XmlDocument xmlDoc = (XmlDocument) args[0];
+        ensureDocumentHasNoError(context, xmlDoc);
+        
+        Document doc = ((XmlDocument) xmlDoc.dup_implementation(context, true)).getDocument();
+
+        XsltStylesheet xslt =
+            (XsltStylesheet) NokogiriService.XSLT_STYLESHEET_ALLOCATOR.allocate(runtime, (RubyClass)klazz);
+
+        try {
+            xslt.init(args[1], doc);
+        } catch (TransformerConfigurationException ex) {
+            throw runtime.newRuntimeError("could not parse xslt stylesheet");
+        }
+
+        return xslt;
+    }
+    
+    private void init(IRubyObject stylesheet, Document document) throws TransformerConfigurationException {
+        this.stylesheet = stylesheet;  // either RubyString or RubyFile
+        if (factory == null) factory = TransformerFactory.newInstance();
+        NokogiriXsltErrorListener elistener = new NokogiriXsltErrorListener();
+        factory.setErrorListener(elistener);
+        sheet = factory.newTemplates(new DOMSource(document));
+    }
+    
+    private static void ensureFirstArgIsDocument(Ruby runtime, IRubyObject arg) {
+        if (arg instanceof XmlDocument) {
+            return;
+        } else {
+            throw runtime.newArgumentError("doc must be a Nokogiri::XML::Document instance");
+        }
+    }
+    
+    private static void ensureDocumentHasNoError(ThreadContext context, XmlDocument xmlDoc) {
+        Ruby runtime = context.getRuntime();
+        RubyArray errors_of_xmlDoc = (RubyArray) xmlDoc.getInstanceVariable("@errors");
+        if (!errors_of_xmlDoc.isEmpty()) {
+            throw runtime.newRuntimeError(errors_of_xmlDoc.first().asString().asJavaString());
+        }
+    }
+
+    @JRubyMethod
+    public IRubyObject serialize(ThreadContext context, IRubyObject doc) throws IOException, TransformerException {
+    	XmlDocument xmlDoc = (XmlDocument) doc;
+    	TransformerImpl transformer = (TransformerImpl) this.sheet.newTransformer();
+    	ByteArrayOutputStream writer = new ByteArrayOutputStream();
+    	StreamResult streamResult = new StreamResult(writer);
+    	SerializationHandler serializationHandler = transformer.createSerializationHandler(streamResult);
+    	serializationHandler.serialize(xmlDoc.getNode());
+    	return context.getRuntime().newString(writer.toString());
+    }
+
+    @JRubyMethod(rest = true, required=1, optional=2)
+    public IRubyObject transform(ThreadContext context, IRubyObject[] args) {
+        Ruby runtime = context.getRuntime();
+
+        argumentTypeCheck(runtime, args[0]);
+
+        NokogiriXsltErrorListener elistener = new NokogiriXsltErrorListener();
+        DOMSource domSource = new DOMSource(((XmlDocument) args[0]).getDocument());
+        final DOMResult result; String stringResult = null;
+        try{
+            result = tryXsltTransformation(context, args, domSource, elistener); // DOMResult
+            if (result.getNode().getFirstChild() == null) {
+                stringResult = retryXsltTransformation(context, args, domSource, elistener); // StreamResult
+            }
+        } catch(TransformerConfigurationException ex) {
+            throw runtime.newRuntimeError(ex.getMessage());
+        } catch(TransformerException ex) {
+            throw runtime.newRuntimeError(ex.getMessage());
+        } catch (IOException ex) {
+            throw runtime.newRuntimeError(ex.getMessage());
+        }
+
+        switch (elistener.getErrorType()) {
+            case ERROR:
+            case FATAL:
+                throw runtime.newRuntimeError(elistener.getErrorMessage());
+            case WARNING:
+            default:
+                // no-op
+        }
+
+        if (stringResult == null) {
+            return createDocumentFromDomResult(context, runtime, result);
+        } else {
+            return createDocumentFromString(context, runtime, stringResult);
+        }
+    }
+    
+    private DOMResult tryXsltTransformation(ThreadContext context, IRubyObject[] args, DOMSource domSource, NokogiriXsltErrorListener elistener) throws TransformerException {
+        Transformer transf = sheet.newTransformer();
+        transf.reset();
+        transf.setErrorListener(elistener);
+        if (args.length > 1) {
+            addParametersToTransformer(context, transf, args[1]);
+        }
+
+        DOMResult result = new DOMResult();
+        transf.transform(domSource, result);
+        return result;
+    }
+    
+    private String retryXsltTransformation(ThreadContext context,
+                                           IRubyObject[] args,
+                                           DOMSource domSource,
+                                           NokogiriXsltErrorListener elistener)
+            throws TransformerException, IOException {
+        Templates templates = getTemplatesFromStreamSource();
+        Transformer transf = templates.newTransformer();
+        transf.setErrorListener(elistener);
+        if (args.length > 1) {
+            addParametersToTransformer(context, transf, args[1]);
+        }
+        PipedWriter pwriter = new PipedWriter();
+        PipedReader preader = new PipedReader();
+        pwriter.connect(preader);
+        StreamResult result = new StreamResult(pwriter);
+        transf.transform(domSource, result);
+
+        char[] cbuf = new char[1024];
+        int len = preader.read(cbuf, 0, 1024);
+        StringBuilder builder = new StringBuilder(len);
+        builder.append(cbuf, 0, len);
+        htmlish = isHtml(builder); // judge from the first chunk
+        
+        while (len == 1024) {
+            len = preader.read(cbuf, 0, 1024);
+            if (len > 0) {
+                builder.append(cbuf, 0, len);
+            }
+        }
+        
+        preader.close();
+        pwriter.close();
+        
+        return builder.toString();
+    }
+    
+    private IRubyObject createDocumentFromDomResult(ThreadContext context, Ruby runtime, DOMResult domResult) {
+        if ("html".equals(domResult.getNode().getFirstChild().getNodeName())) {
+            HtmlDocument htmlDocument = (HtmlDocument) getNokogiriClass(runtime, "Nokogiri::HTML::Document").allocate();
+            htmlDocument.setDocumentNode(context, (Document) domResult.getNode());
+            return htmlDocument;
+        } else {
+            XmlDocument xmlDocument = (XmlDocument) NokogiriService.XML_DOCUMENT_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Document"));
+            xmlDocument.setDocumentNode(context, (Document) domResult.getNode());
+            return xmlDocument;
+        }
+    }
+    
+    private Templates getTemplatesFromStreamSource() throws TransformerConfigurationException {
+        if (stylesheet instanceof RubyString) {
+            StringReader reader = new StringReader(stylesheet.asJavaString());
+            StreamSource xsltStreamSource = new StreamSource(reader);
+            return factory.newTemplates(xsltStreamSource);
+        }
+        return null;
+    }
+    
+    private static final Pattern HTML_TAG = Pattern.compile("<(%s)*html", Pattern.CASE_INSENSITIVE);
+    
+    private static boolean isHtml(CharSequence chunk) {
+        Matcher match = HTML_TAG.matcher(chunk);
+        return match.find();
+    }
+    
+    private IRubyObject createDocumentFromString(ThreadContext context, Ruby runtime, String stringResult) {
+        IRubyObject[] args = new IRubyObject[4];
+        args[0] = stringOrBlank(runtime, stringResult);
+        args[1] = runtime.getNil();  // url
+        args[2] = runtime.getNil();  // encoding
+        RubyClass parse_options = (RubyClass)runtime.getClassFromPath("Nokogiri::XML::ParseOptions");
+        if (htmlish) {
+            args[3] = parse_options.getConstant("DEFAULT_HTML");
+            RubyClass htmlDocumentClass = getNokogiriClass(runtime, "Nokogiri::HTML::Document");
+            return RuntimeHelpers.invoke(context, htmlDocumentClass, "parse", args);
+        } else {
+            args[3] = parse_options.getConstant("DEFAULT_XML");
+            RubyClass xmlDocumentClass = getNokogiriClass(runtime, "Nokogiri::XML::Document");            
+            XmlDocument xmlDocument = (XmlDocument) RuntimeHelpers.invoke(context, xmlDocumentClass, "parse", args);
+            if (((Document)xmlDocument.getNode()).getDocumentElement() == null) {
+                RubyArray errors = (RubyArray) xmlDocument.getInstanceVariable("@errors");
+                RuntimeHelpers.invoke(context, errors, "<<", args[0]);
+            }
+            return xmlDocument;
+        }
+    }
+    
+    private static void argumentTypeCheck(Ruby runtime, IRubyObject arg) {
+        if (arg instanceof XmlDocument) return;
+        throw runtime.newArgumentError("argument must be a Nokogiri::XML::Document");
+    }
+    
+    @JRubyMethod(name = {"registr", "register"}, meta = true)
+    public static IRubyObject register(ThreadContext context, IRubyObject cls, IRubyObject uri, IRubyObject receiver) {
+        throw context.getRuntime().newNotImplementedError("Nokogiri::XSLT.register method is not implemented");
+        /* When API conflict is solved, this method should be below:
+        // ThreadContext is used while executing xslt extension function
+        registry.put("context", context);
+        registry.put("receiver", receiver);
+        return context.getRuntime().getNil();
+        */
+    }
+}
diff -pruN 1.8.4-1/ext/nokogiri/extconf.rb 1.10.0+dfsg1-2/ext/nokogiri/extconf.rb
--- 1.8.4-1/ext/nokogiri/extconf.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/nokogiri/extconf.rb	2019-01-04 15:20:12.000000000 +0000
@@ -400,9 +400,11 @@ if openbsd? && !using_system_libraries?
   ENV['CFLAGS'] = "#{ENV['CFLAGS']} -I /usr/local/include"
 end
 
-RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
+if ENV['CC']
+  RbConfig::CONFIG['CC'] = RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC']
+end
 # use same c compiler for libxml and libxslt
-ENV['CC'] = RbConfig::MAKEFILE_CONFIG['CC']
+ENV['CC'] = RbConfig::CONFIG['CC']
 
 $LIBS << " #{ENV["LIBS"]}"
 
@@ -432,9 +434,9 @@ if RUBY_PLATFORM =~ /mingw/i
   $CPPFLAGS << ' "-Idummypath"'
 end
 
-if RbConfig::MAKEFILE_CONFIG['CC'] =~ /gcc/
+if RbConfig::CONFIG['CC'] =~ /gcc/
   $CFLAGS << " -O3" unless $CFLAGS[/-O\d/]
-  $CFLAGS << " -Wall -Wcast-qual -Wwrite-strings -Wextra -Wmissing-noreturn -Winline"
+  $CFLAGS << " -Wall -Wcast-qual -Wwrite-strings -Wmissing-noreturn -Winline"
 end
 
 case
@@ -460,7 +462,7 @@ else
   # The gem version constraint in the Rakefile is not respected at install time.
   # Keep this version in sync with the one in the Rakefile !
   require 'rubygems'
-  gem 'mini_portile2', '~> 2.3.0'
+  gem 'mini_portile2', '~> 2.4.0'
   require 'mini_portile2'
   message "Using mini_portile version #{MiniPortile::VERSION}\n"
 
@@ -560,7 +562,8 @@ EOM
       *(libiconv_recipe ? "--with-iconv=#{libiconv_recipe.path}" : iconv_configure_flags),
       "--with-c14n",
       "--with-debug",
-      "--with-threads"
+      "--with-threads",
+      *(darwin? ? ["RANLIB=/usr/bin/ranlib", "AR=/usr/bin/ar"] : "")
     ]
   end
 
@@ -573,7 +576,8 @@ EOM
       "--without-python",
       "--without-crypto",
       "--with-debug",
-      "--with-libxml-prefix=#{sh_export_path(libxml2_recipe.path)}"
+      "--with-libxml-prefix=#{sh_export_path(libxml2_recipe.path)}",
+      *(darwin? ? ["RANLIB=/usr/bin/ranlib", "AR=/usr/bin/ar"] : "")
     ]
   end
 
diff -pruN 1.8.4-1/ext/nokogiri/xml_attr.c 1.10.0+dfsg1-2/ext/nokogiri/xml_attr.c
--- 1.8.4-1/ext/nokogiri/xml_attr.c	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/nokogiri/xml_attr.c	2019-01-04 15:20:12.000000000 +0000
@@ -4,37 +4,40 @@
  * call-seq:
  *  value=(content)
  *
- * Set the value for this Attr to +content+
+ * Set the value for this Attr to +content+. Use `nil` to remove the value
+ * (e.g., a HTML boolean attribute).
  */
 static VALUE set_value(VALUE self, VALUE content)
 {
   xmlAttrPtr attr;
-  Data_Get_Struct(self, xmlAttr, attr);
+  xmlChar *value;
+  xmlNode *cur;
 
-  if (attr->children) { xmlFreeNodeList(attr->children); }
+  Data_Get_Struct(self, xmlAttr, attr);
 
+  if (attr->children) {
+    xmlFreeNodeList(attr->children);
+  }
   attr->children = attr->last = NULL;
 
-  if (content) {
-    xmlChar *buffer;
-    xmlNode *tmp;
-
-    /* Encode our content */
-    buffer = xmlEncodeEntitiesReentrant(attr->doc, (unsigned char *)StringValueCStr(content));
-
-    attr->children = xmlStringGetNodeList(attr->doc, buffer);
-    attr->last = NULL;
-    tmp = attr->children;
-
-    /* Loop through the children */
-    for(tmp = attr->children; tmp; tmp = tmp->next) {
-      tmp->parent = (xmlNode *)attr;
-      tmp->doc = attr->doc;
-      if (tmp->next == NULL) { attr->last = tmp; }
-    }
+  if (content == Qnil) {
+    return content;
+  }
 
-    /* Free up memory */
-    xmlFree(buffer);
+  value = xmlEncodeEntitiesReentrant(attr->doc, (unsigned char *)StringValueCStr(content));
+  if (xmlStrlen(value) == 0) {
+    attr->children = xmlNewDocText(attr->doc, value);
+  } else {
+    attr->children = xmlStringGetNodeList(attr->doc, value);
+  }
+  xmlFree(value);
+
+  for (cur = attr->children; cur; cur = cur->next) {
+    cur->parent = (xmlNode *)attr;
+    cur->doc = attr->doc;
+    if (cur->next == NULL) {
+      attr->last = cur;
+    }
   }
 
   return content;
@@ -74,7 +77,9 @@ static VALUE new(int argc, VALUE *argv,
   rb_node = Nokogiri_wrap_xml_node(klass, (xmlNodePtr)node);
   rb_obj_call_init(rb_node, argc, argv);
 
-  if (rb_block_given_p()) { rb_yield(rb_node); }
+  if (rb_block_given_p()) {
+    rb_yield(rb_node);
+  }
 
   return rb_node;
 }
diff -pruN 1.8.4-1/ext/nokogiri/xml_document.c 1.10.0+dfsg1-2/ext/nokogiri/xml_document.c
--- 1.8.4-1/ext/nokogiri/xml_document.c	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/nokogiri/xml_document.c	2019-01-04 15:20:12.000000000 +0000
@@ -7,7 +7,10 @@ static int dealloc_node_i(xmlNodePtr key
     xmlFreePropList((xmlAttrPtr)node);
     break;
   case XML_NAMESPACE_DECL:
-    xmlFree(node);
+    xmlFreeNs((xmlNsPtr)node);
+    break;
+  case XML_DTD_NODE:
+    xmlFreeDtd((xmlDtdPtr)node);
     break;
   default:
     if(node->parent == NULL) {
diff -pruN 1.8.4-1/ext/nokogiri/xml_namespace.c 1.10.0+dfsg1-2/ext/nokogiri/xml_namespace.c
--- 1.8.4-1/ext/nokogiri/xml_namespace.c	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/nokogiri/xml_namespace.c	2019-01-04 15:20:12.000000000 +0000
@@ -63,7 +63,7 @@ static int part_of_an_xpath_node_set_eh(
 
 VALUE Nokogiri_wrap_xml_namespace(xmlDocPtr doc, xmlNsPtr node)
 {
-  VALUE ns, document, node_cache;
+  VALUE ns = 0, document, node_cache;
 
   assert(doc->type == XML_DOCUMENT_NODE || doc->type == XML_HTML_DOCUMENT_NODE);
 
@@ -71,8 +71,6 @@ VALUE Nokogiri_wrap_xml_namespace(xmlDoc
 
   if (doc->type == XML_DOCUMENT_FRAG_NODE) doc = doc->doc;
 
-  ns = Data_Wrap_Struct(cNokogiriXmlNamespace, 0, 0, node);
-
   if (DOC_RUBY_OBJECT_TEST(doc)) {
     document = DOC_RUBY_OBJECT(doc);
 
@@ -91,6 +89,8 @@ VALUE Nokogiri_wrap_xml_namespace(xmlDoc
     }
 
     rb_iv_set(ns, "@document", document);
+  } else {
+    ns = Data_Wrap_Struct(cNokogiriXmlNamespace, 0, 0, node);
   }
 
   node->_private = (void *)ns;
diff -pruN 1.8.4-1/ext/nokogiri/xml_namespace.h 1.10.0+dfsg1-2/ext/nokogiri/xml_namespace.h
--- 1.8.4-1/ext/nokogiri/xml_namespace.h	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/nokogiri/xml_namespace.h	2019-01-04 15:20:12.000000000 +0000
@@ -7,8 +7,7 @@ void init_xml_namespace();
 
 extern VALUE cNokogiriXmlNamespace ;
 
-VALUE Nokogiri_wrap_xml_namespace(xmlDocPtr doc, xmlNsPtr node) ;
-VALUE Nokogiri_wrap_xml_namespace2(VALUE document, xmlNsPtr node) ;
+VALUE Nokogiri_wrap_xml_namespace(xmlDocPtr doc, xmlNsPtr node);
 
 #define NOKOGIRI_NAMESPACE_EH(node) ((node)->type == XML_NAMESPACE_DECL)
 
diff -pruN 1.8.4-1/ext/nokogiri/xml_node.c 1.10.0+dfsg1-2/ext/nokogiri/xml_node.c
--- 1.8.4-1/ext/nokogiri/xml_node.c	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/ext/nokogiri/xml_node.c	2019-01-04 15:20:12.000000000 +0000
@@ -31,13 +31,13 @@ typedef xmlNodePtr (*pivot_reparentee_fu
 static void relink_namespace(xmlNodePtr reparented)
 {
   xmlNodePtr child;
-  xmlNsPtr ns;
 
   if (reparented->type != XML_ATTRIBUTE_NODE &&
       reparented->type != XML_ELEMENT_NODE) { return; }
 
   if (reparented->ns == NULL || reparented->ns->prefix == NULL) {
-    xmlChar *name = 0, *prefix = 0;
+    xmlNsPtr ns = NULL;
+    xmlChar *name = NULL, *prefix = NULL;
 
     name = xmlSplitQName2(reparented->name, &prefix);
 
@@ -99,6 +99,25 @@ static void relink_namespace(xmlNodePtr
     }
   }
 
+  /*
+   *  Search our parents for an existing definition of current namespace,
+   *  because the definition it's pointing to may have just been removed nsDef.
+   *
+   *  And although that would technically probably be OK, I'd feel better if we
+   *  referred to a namespace that's still present in a node's nsDef somewhere
+   *  in the doc.
+   */
+  if (reparented->ns) {
+    xmlNsPtr ns = xmlSearchNs(reparented->doc, reparented, reparented->ns->prefix);
+    if (ns
+        && ns != reparented->ns
+        && xmlStrEqual(ns->prefix, reparented->ns->prefix)
+        && xmlStrEqual(ns->href, reparented->ns->href)
+       ) {
+      xmlSetNs(reparented, ns);
+    }
+  }
+
   /* Only walk all children if there actually is a namespace we need to */
   /* reparent. */
   if (NULL == reparented->ns) { return; }
@@ -282,7 +301,11 @@ ok:
     }
 
     if (original_ns_prefix_is_default && reparentee->ns != NULL && reparentee->ns->prefix != NULL) {
-      /* issue #391, where new node's prefix may become the string "default" */
+      /*
+       *  issue #391, where new node's prefix may become the string "default"
+       *  see libxml2 tree.c xmlNewReconciliedNs which implements this behavior.
+       */
+      xmlFree(reparentee->ns->prefix);
       reparentee->ns->prefix = NULL;
     }
   }
@@ -509,22 +532,39 @@ static VALUE internal_subset(VALUE self)
 /*
  * call-seq:
  *  dup
+ *  dup(depth)
+ *  dup(depth, new_parent_doc)
  *
- * Copy this node.  An optional depth may be passed in, but it defaults
- * to a deep copy.  0 is a shallow copy, 1 is a deep copy.
+ * Copy this node.
+ * An optional depth may be passed in. 0 is a shallow copy, 1 (the default) is a deep copy.
+ * An optional new_parent_doc may also be passed in, which will be the new
+ * node's parent document. Defaults to the current node's document.
+ * current document.
  */
 static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
 {
-  VALUE level;
+  VALUE r_level, r_new_parent_doc;
+  int level;
+  int n_args;
+  xmlDocPtr new_parent_doc;
   xmlNodePtr node, dup;
 
-  if(rb_scan_args(argc, argv, "01", &level) == 0) {
-    level = INT2NUM((long)1);
+  Data_Get_Struct(self, xmlNode, node);
+
+  n_args = rb_scan_args(argc, argv, "02", &r_level, &r_new_parent_doc);
+
+  if (n_args < 1) {
+    r_level = INT2NUM((long)1);
   }
+  level = (int)NUM2INT(r_level);
 
-  Data_Get_Struct(self, xmlNode, node);
+  if (n_args < 2) {
+    new_parent_doc = node->doc;
+  } else {
+    Data_Get_Struct(r_new_parent_doc, xmlDoc, new_parent_doc);
+  }
 
-  dup = xmlDocCopyNode(node, node->doc, (int)NUM2INT(level));
+  dup = xmlDocCopyNode(node, new_parent_doc, level);
   if(dup == NULL) { return Qnil; }
 
   nokogiri_root_node(dup);
@@ -1308,11 +1348,11 @@ static VALUE line(VALUE self)
  */
 static VALUE add_namespace_definition(VALUE self, VALUE prefix, VALUE href)
 {
-  xmlNodePtr node, namespacee;
+  xmlNodePtr node, namespace;
   xmlNsPtr ns;
 
   Data_Get_Struct(self, xmlNode, node);
-  namespacee = node ;
+  namespace = node ;
 
   ns = xmlSearchNs(
          node->doc,
@@ -1322,10 +1362,10 @@ static VALUE add_namespace_definition(VA
 
   if(!ns) {
     if (node->type != XML_ELEMENT_NODE) {
-      namespacee = node->parent;
+      namespace = node->parent;
     }
     ns = xmlNewNs(
-           namespacee,
+           namespace,
            (const xmlChar *)StringValueCStr(href),
            (const xmlChar *)(NIL_P(prefix) ? NULL : StringValueCStr(prefix))
          );
@@ -1333,7 +1373,7 @@ static VALUE add_namespace_definition(VA
 
   if (!ns) { return Qnil ; }
 
-  if(NIL_P(prefix) || node != namespacee) { xmlSetNs(node, ns); }
+  if(NIL_P(prefix) || node != namespace) { xmlSetNs(node, ns); }
 
   return Nokogiri_wrap_xml_namespace(node->doc, ns);
 }
diff -pruN 1.8.4-1/Gemfile 1.10.0+dfsg1-2/Gemfile
--- 1.8.4-1/Gemfile	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/Gemfile	2019-01-04 15:20:12.000000000 +0000
@@ -4,20 +4,20 @@
 
 source "https://rubygems.org/"
 
-gem "mini_portile2", "~>2.3.0"
+gem "mini_portile2", "~>2.4.0"
 
 gem "hoe-bundler", "~>1.2", :group => [:development, :test]
-gem "hoe-debugging", "~>1.4", :group => [:development, :test]
+gem "hoe-debugging", "~>2.0", :group => [:development, :test]
 gem "hoe-gemspec", "~>1.0", :group => [:development, :test]
 gem "hoe-git", "~>1.6", :group => [:development, :test]
 gem "minitest", "~>5.8.4", :group => [:development, :test]
 gem "rake", "~>12.0", :group => [:development, :test]
 gem "rake-compiler", "~>1.0.3", :group => [:development, :test]
-gem "rake-compiler-dock", "~>0.6.2", :group => [:development, :test]
-gem "racc", "~>1.4.14", :group => [:development, :test], :platform => [:ruby, :mingw, :x64_mingw]
-gem "rexical", "~>1.0.5", :group => [:development, :test], :platform => [:ruby, :mingw, :x64_mingw]
+gem "rake-compiler-dock", "~>0.7.0", :group => [:development, :test]
+gem "racc", "~>1.4.14", :group => [:development, :test]
+gem "rexical", "~>1.0.5", :group => [:development, :test]
 gem "concourse", "~>0.15", :group => [:development, :test]
-gem "rdoc", "~>4.0", :group => [:development, :test]
-gem "hoe", "~>3.16", :group => [:development, :test]
+gem "rdoc", ">=4.0", "<7", :group => [:development, :test]
+gem "hoe", "~>3.17", :group => [:development, :test]
 
 # vim: syntax=ruby
diff -pruN 1.8.4-1/.github/ISSUE_TEMPLATE.md 1.10.0+dfsg1-2/.github/ISSUE_TEMPLATE.md
--- 1.8.4-1/.github/ISSUE_TEMPLATE.md	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/.github/ISSUE_TEMPLATE.md	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,21 @@
+## If you're having trouble installing Nokogiri ...
+
+**Have you tried following [the installation tutorial][tutorial]?**
+
+**What is the output of `gem install`?**
+
+**What are the contents of the `mkmf.log` file?**
+
+**What operating system are you using?**
+
+
+## If this isn't an installation issue ...
+
+**What problems are you experiencing?**
+
+**What's the output from `nokogiri -v`?**
+
+**Can you provide a self-contained script that reproduces what you're seeing?**
+
+
+[tutorial]: http://www.nokogiri.org/tutorials/installing_nokogiri.html
diff -pruN 1.8.4-1/.gitignore 1.10.0+dfsg1-2/.gitignore
--- 1.8.4-1/.gitignore	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/.gitignore	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,36 @@
+*.gemspec
+*.rbc
+*.tmproj
+*~
+.*.swp
+.bundle
+.classpath
+.project
+.rake_tasks
+.ruby-gemset
+.ruby-version
+.rvmrc
+.settings
+.yardoc
+Gemfile*lock
+TAGS
+build
+concourse/private.yml
+ext/java/Canna
+ext/java/nokogiri/**/*.class
+ext/nokogiri/*.dll
+gems
+lib/nokogiri/**/nokogiri.so
+lib/nokogiri/nokogiri.bundle
+lib/nokogiri/nokogiri.jar
+lib/nokogiri/nokogiri.rb
+pkg
+ports
+stash
+tags
+test/*/*_mini.rb
+test/*_mini.rb
+test/test_jruby_footer.rb
+test/test_jruby_header.rb
+tmp
+vendor
diff -pruN 1.8.4-1/.hoerc 1.10.0+dfsg1-2/.hoerc
--- 1.8.4-1/.hoerc	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/.hoerc	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,41 @@
+# -*- yaml -*-
+---
+# this regex is what `rake check_manifest` should consider excluded from the gem
+exclude: !ruby/regexp '/
+(^\.\/
+  ((\.git
+    |.yardoc
+    |concourse
+    |gems
+    |ports
+    |suppressions
+    |tasks
+    |test
+    |tmp
+  )\/)
+  |\.(autotest
+     |codeclimate\.yml
+     |cross_rubies
+     |editorconfig
+     |gemtest
+     |github
+     |gitignore
+     |hoerc
+     |travis\.yml
+     )
+  |Gemfile.*
+  |Manifest.txt
+  |Rakefile
+  |appveyor\.yml
+  |build_all
+  |CHANGELOG.md
+  |CONTRIBUTING.md
+  |ROADMAP.md
+  |SECURITY.md
+  |STANDARD_RESPONSES.md
+  |Y_U_NO_GEMSPEC.md
+  |C_CODING_STYLE.*
+  |patches/sort-patches-by-date
+)
+|\.gitkeep
+/x'
diff -pruN 1.8.4-1/lib/nokogiri/css/parser.rb 1.10.0+dfsg1-2/lib/nokogiri/css/parser.rb
--- 1.8.4-1/lib/nokogiri/css/parser.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/lib/nokogiri/css/parser.rb	2019-01-04 15:20:12.000000000 +0000
@@ -31,64 +31,64 @@ end
 
 racc_action_table = [
     24,    93,    56,    57,    33,    55,    94,    23,    24,    22,
-    12,    93,    33,    27,    89,    52,    92,    22,   -23,    25,
-   109,    98,    23,    33,    26,    18,    20,    25,    27,   -23,
+    12,    93,    33,    27,    35,    52,    44,    22,   -23,    25,
+    45,    98,    23,    33,    26,    18,    20,    25,    27,   -23,
     23,    24,    26,    18,    20,    33,    27,    11,    39,    24,
-    22,    23,    95,    33,    18,    91,    90,    27,    22,    12,
-    25,    90,    96,    23,    33,    26,    18,    20,    25,    27,
-   -23,    23,    24,    26,    18,    20,    33,    27,    74,    39,
-    99,    22,    23,    45,    24,    18,    33,   103,    27,    56,
-    87,    25,    60,    46,    23,    49,    26,    18,    20,   104,
-    27,    39,    24,    51,    23,    93,    44,    18,    26,    33,
-    27,    66,   106,    56,    58,   110,    60,    33,    85,    33,
-    86,    51,    56,    87,    39,    60,    26,    23,    88,    33,
-    18,    20,    39,    27,    39,    23,    35,    23,    18,    33,
-    18,    27,   nil,    27,    39,   nil,    33,    23,   nil,   nil,
-    18,    22,   nil,    27,    39,   101,   100,    23,   102,   nil,
-    18,    39,   nil,    27,    23,    82,    83,    18,    20,   nil,
-    27,   nil,   nil,   nil,    82,    83,    78,    79,    80,   nil,
-    81,   nil,   nil,   nil,    77,    78,    79,    80,   nil,    81,
-     4,     5,    43,    77,     4,     5,    10,   nil,    56,    87,
-     6,    60,     8,     7,     6,   nil,     8,     7,     4,     5,
-    10,    56,    87,   nil,    60,   nil,   nil,   nil,     6,   nil,
-     8,     7 ]
+    22,    23,    74,    33,    18,    91,    90,    27,    22,    12,
+    25,    24,   -23,    23,    85,    26,    18,    20,    25,    27,
+    66,    23,    24,    26,    18,    20,    33,    27,    86,    88,
+    51,    22,    89,    92,    24,    26,    56,    87,    95,    60,
+    90,    25,    96,    46,    23,    49,    26,    18,    20,    99,
+    27,    33,    33,    51,   103,   104,    56,    58,    26,    60,
+    93,   106,    33,    33,    33,   109,    39,    39,   110,    23,
+    23,   nil,    18,    18,    20,    27,    27,    39,    39,    39,
+    23,    23,    23,    18,    18,    18,    27,    27,    27,    33,
+    33,   101,   100,   nil,   102,    22,    56,    87,   nil,    60,
+    33,   nil,   nil,   nil,    39,    39,   nil,    23,    23,   nil,
+    18,    18,    20,    27,    27,    39,    82,    83,    23,    56,
+    87,    18,    60,   nil,    27,    82,    83,    78,    79,    80,
+   nil,    81,   nil,   nil,   nil,    77,    78,    79,    80,   nil,
+    81,     4,     5,    10,    77,     4,     5,    43,   nil,    56,
+    87,     6,    60,     8,     7,     6,   nil,     8,     7,     4,
+     5,    10,   nil,   nil,   nil,   nil,   nil,   nil,   nil,     6,
+   nil,     8,     7 ]
 
 racc_action_check = [
-     3,    58,    24,    24,     3,    24,    57,    15,    42,     3,
-    64,    57,    42,    15,    54,    24,    56,    42,    58,     3,
-    94,    64,     3,    31,     3,     3,     3,    42,     3,    22,
-    42,     9,    42,    42,    42,     9,    42,     1,    31,    43,
-     9,    31,    59,    43,    31,    55,    55,    31,    43,     1,
-     9,    60,    61,     9,    30,     9,     9,     9,    43,     9,
-    46,    43,    12,    43,    43,    43,    12,    43,    45,    30,
-    75,    12,    30,    21,    23,    30,    29,    84,    30,    93,
-    93,    12,    93,    23,    12,    23,    12,    12,    12,    86,
-    12,    29,    27,    23,    29,    87,    18,    29,    23,    25,
-    29,    27,    91,    25,    25,   106,    25,    28,    49,    62,
-    50,    27,    51,    51,    25,    51,    27,    25,    53,    14,
-    25,    25,    28,    25,    62,    28,    11,    62,    28,    32,
-    62,    28,   nil,    62,    14,   nil,    39,    14,   nil,   nil,
-    14,    39,   nil,    14,    32,    76,    76,    32,    76,   nil,
-    32,    39,   nil,    32,    39,    47,    47,    39,    39,   nil,
-    39,   nil,   nil,   nil,    48,    48,    47,    47,    47,   nil,
-    47,   nil,   nil,   nil,    47,    48,    48,    48,   nil,    48,
-    17,    17,    17,    48,     0,     0,     0,   nil,    90,    90,
-    17,    90,    17,    17,     0,   nil,     0,     0,    26,    26,
-    26,    92,    92,   nil,    92,   nil,   nil,   nil,    26,   nil,
-    26,    26 ]
+     3,    58,    24,    24,     3,    24,    57,    15,     9,     3,
+    64,    57,     9,    15,    11,    24,    18,     9,    58,     3,
+    21,    64,     3,    14,     3,     3,     3,     9,     3,    22,
+     9,    12,     9,     9,     9,    12,     9,     1,    14,    42,
+    12,    14,    45,    42,    14,    55,    55,    14,    42,     1,
+    12,    27,    46,    12,    49,    12,    12,    12,    42,    12,
+    27,    42,    43,    42,    42,    42,    43,    42,    50,    53,
+    27,    43,    54,    56,    23,    27,    51,    51,    59,    51,
+    60,    43,    61,    23,    43,    23,    43,    43,    43,    75,
+    43,    28,    25,    23,    84,    86,    25,    25,    23,    25,
+    87,    91,    29,    30,    31,    94,    28,    25,   106,    28,
+    25,   nil,    28,    25,    25,    28,    25,    29,    30,    31,
+    29,    30,    31,    29,    30,    31,    29,    30,    31,    32,
+    39,    76,    76,   nil,    76,    39,    90,    90,   nil,    90,
+    62,   nil,   nil,   nil,    32,    39,   nil,    32,    39,   nil,
+    32,    39,    39,    32,    39,    62,    47,    47,    62,    92,
+    92,    62,    92,   nil,    62,    48,    48,    47,    47,    47,
+   nil,    47,   nil,   nil,   nil,    47,    48,    48,    48,   nil,
+    48,     0,     0,     0,    48,    17,    17,    17,   nil,    93,
+    93,     0,    93,     0,     0,    17,   nil,    17,    17,    26,
+    26,    26,   nil,   nil,   nil,   nil,   nil,   nil,   nil,    26,
+   nil,    26,    26 ]
 
 racc_action_pointer = [
-   177,    37,   nil,    -2,   nil,   nil,   nil,   nil,   nil,    29,
-   nil,   126,    60,   nil,   113,   -17,   nil,   173,    85,   nil,
-   nil,    44,     0,    72,    -8,    93,   191,    90,   101,    70,
-    48,    17,   123,   nil,   nil,   nil,   nil,   nil,   nil,   130,
-   nil,   nil,     6,    37,   nil,    57,    31,   152,   161,    83,
-    81,   102,   nil,    95,    -9,    34,     4,    -1,   -11,    19,
-    39,    29,   103,   nil,    -2,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,    45,   135,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,    52,   nil,    78,    83,   nil,   nil,
-   178,    95,   191,    69,     7,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,    92,   nil,   nil,   nil,
+   174,    37,   nil,    -2,   nil,   nil,   nil,   nil,   nil,     6,
+   nil,    14,    29,   nil,    17,   -17,   nil,   178,     5,   nil,
+   nil,    -9,     0,    72,    -8,    86,   192,    49,    85,    96,
+    97,    98,   123,   nil,   nil,   nil,   nil,   nil,   nil,   124,
+   nil,   nil,    37,    60,   nil,    31,    23,   153,   162,    29,
+    39,    66,   nil,    46,    49,    34,    61,    -1,   -11,    55,
+    68,    59,   134,   nil,    -2,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,    64,   121,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,    69,   nil,    84,    88,   nil,   nil,
+   126,    94,   149,   179,    92,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,    95,   nil,   nil,   nil,
    nil ]
 
 racc_action_default = [
@@ -106,26 +106,26 @@ racc_action_default = [
    -41 ]
 
 racc_goto_table = [
-    53,    38,    13,     1,    54,    48,    62,    42,    34,    65,
-    37,    36,    63,    75,    84,    67,    68,    69,    70,    71,
-    62,    40,    41,    50,    47,   nil,    63,   nil,   nil,    64,
+    53,    38,    13,     1,    42,    48,    62,    37,    34,    65,
+    40,    36,    63,    75,    84,    67,    68,    69,    70,    71,
+    62,    41,    50,    47,    54,   nil,    63,   nil,   nil,    64,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
    nil,    72,    73,   nil,   nil,   nil,   nil,   nil,   nil,    97,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   105,   nil,   107,   108 ]
 
 racc_goto_check = [
-    18,    12,     2,     1,    19,     9,     7,     5,     2,     9,
-     8,     2,    12,    17,    17,    12,    12,    12,    12,    12,
-     7,    10,    11,    15,    16,   nil,    12,   nil,   nil,     1,
+    18,    12,     2,     1,     5,     9,     7,     8,     2,     9,
+    10,     2,    12,    17,    17,    12,    12,    12,    12,    12,
+     7,    11,    15,    16,    19,   nil,    12,   nil,   nil,     1,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
    nil,     2,     2,   nil,   nil,   nil,   nil,   nil,   nil,    12,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,    18,   nil,    18,    18 ]
 
 racc_goto_pointer = [
-   nil,     3,    -1,   nil,   nil,   -10,   nil,   -19,    -4,   -18,
-     6,     7,   -13,   nil,   nil,     0,     1,   -34,   -24,   -20,
+   nil,     3,    -1,   nil,   nil,   -13,   nil,   -19,    -7,   -18,
+    -5,     6,   -13,   nil,   nil,    -1,     0,   -34,   -24,     0,
    nil,   nil,   nil,   nil ]
 
 racc_goto_default = [
diff -pruN 1.8.4-1/lib/nokogiri/version.rb 1.10.0+dfsg1-2/lib/nokogiri/version.rb
--- 1.8.4-1/lib/nokogiri/version.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/lib/nokogiri/version.rb	2019-01-04 15:20:12.000000000 +0000
@@ -1,6 +1,6 @@
 module Nokogiri
   # The version of Nokogiri you are using
-  VERSION = '1.8.4'
+  VERSION = '1.10.0'
 
   class VersionInfo # :nodoc:
     def jruby?
diff -pruN 1.8.4-1/lib/nokogiri/xml/document_fragment.rb 1.10.0+dfsg1-2/lib/nokogiri/xml/document_fragment.rb
--- 1.8.4-1/lib/nokogiri/xml/document_fragment.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/lib/nokogiri/xml/document_fragment.rb	2019-01-04 15:20:12.000000000 +0000
@@ -25,6 +25,17 @@ module Nokogiri
         children.each { |child| child.parent = self }
       end
 
+      if Nokogiri.uses_libxml?
+        def dup
+          new_document = document.dup
+          new_fragment = self.class.new(new_document)
+          children.each do |child|
+            child.dup(1, new_document).parent = new_fragment
+          end
+          new_fragment
+        end
+      end
+
       ###
       # return the name for DocumentFragment
       def name
diff -pruN 1.8.4-1/lib/nokogiri/xml/node.rb 1.10.0+dfsg1-2/lib/nokogiri/xml/node.rb
--- 1.8.4-1/lib/nokogiri/xml/node.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/lib/nokogiri/xml/node.rb	2019-01-04 15:20:12.000000000 +0000
@@ -161,6 +161,18 @@ module Nokogiri
         end
       end
 
+
+      ###
+      # Add html around this node
+      #
+      # Returns self
+      def wrap(html)
+        new_parent = document.parse(html).first
+        add_next_sibling(new_parent)
+        new_parent.add_child(self)
+        self
+      end
+
       ###
       # Add +node_or_tags+ as a child of this Node.
       # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
diff -pruN 1.8.4-1/lib/nokogiri/xml/node_set.rb 1.10.0+dfsg1-2/lib/nokogiri/xml/node_set.rb
--- 1.8.4-1/lib/nokogiri/xml/node_set.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/lib/nokogiri/xml/node_set.rb	2019-01-04 15:20:12.000000000 +0000
@@ -203,6 +203,7 @@ module Nokogiri
         0.upto(length - 1) do |x|
           yield self[x]
         end
+        self
       end
 
       ###
@@ -230,14 +231,9 @@ module Nokogiri
       end
 
       ###
-      # Wrap this NodeSet with +html+ or the results of the builder in +blk+
-      def wrap(html, &blk)
-        each do |j|
-          new_parent = document.parse(html).first
-          j.add_next_sibling(new_parent)
-          new_parent.add_child(j)
-        end
-        self
+      # Wrap this NodeSet with +html+
+      def wrap html
+        map { |node| node.wrap html }
       end
 
       ###
diff -pruN 1.8.4-1/LICENSE-DEPENDENCIES.md 1.10.0+dfsg1-2/LICENSE-DEPENDENCIES.md
--- 1.8.4-1/LICENSE-DEPENDENCIES.md	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/LICENSE-DEPENDENCIES.md	2019-01-04 15:20:12.000000000 +0000
@@ -1,3 +1,5 @@
+## Vendored Dependency Licenses
+
 Nokogiri ships with some third party dependencies, which are listed
 here along with their licenses.
 
@@ -20,9 +22,9 @@ present in any non-Windows releases.
 
 -----
 
-# default platform release
+## default platform release
 
-## libxml2
+### libxml2
 
 MIT
 
@@ -53,7 +55,7 @@ http://xmlsoft.org/
     THE SOFTWARE.
     
 
-## libxslt
+### libxslt
 
 MIT
 
@@ -113,9 +115,9 @@ http://xmlsoft.org/libxslt/
     ings in this Software without prior written authorization from him.
     ----------------------------------------------------------------------
     
-# `java` platform release
+## `java` platform release
 
-## isorelax
+### isorelax
 
 MIT
 
@@ -144,7 +146,7 @@ http://iso-relax.sourceforge.net/
     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 
-## jing
+### jing
 
 BSD-3-Clause
 
@@ -185,7 +187,7 @@ http://www.thaiopensource.com/relaxng/ji
     SUCH DAMAGE.
 
     
-## nekodtd
+### nekodtd
 
 Apache 1.0-derived
 
@@ -238,7 +240,7 @@ https://people.apache.org/~andyc/neko/do
     
     This license is based on the Apache Software License, version 1.1.
 
-## nekohtml
+### nekohtml
 
 Apache 2.0
 
@@ -447,7 +449,7 @@ http://nekohtml.sourceforge.net/
        See the License for the specific language governing permissions and
        limitations under the License.
 
-## xalan
+### xalan
 
 Apache 2.0
 
@@ -658,7 +660,7 @@ covers xalan.jar and serializer.jar
        limitations under the License.
     
 
-## xerces
+### xerces
 
 Apache 2.0
 
@@ -868,7 +870,7 @@ https://xerces.apache.org/xerces2-j/
        limitations under the License.
     
 
-## xml-apis
+### xml-apis
 
 Apache 2.0
 
@@ -1088,13 +1090,13 @@ https://xerces.apache.org/xml-commons/
        limitations under the License.
 
 
-# binary windows release
+## binary windows release
 
 NOTE: these libraries are redistributed ONLY with the binary
 cross-compiled Windows platform version of Nokogiri, both x86-mingw32
 and x64-mingw32.
 
-## zlib
+### zlib
 
 zlib license
 
@@ -1122,7 +1124,7 @@ http://www.zlib.net/zlib_license.html
       jloup@gzip.org          madler@alumni.caltech.edu
     
 
-## libiconv
+### libiconv
 
 LGPL
 
diff -pruN 1.8.4-1/LICENSE.md 1.10.0+dfsg1-2/LICENSE.md
--- 1.8.4-1/LICENSE.md	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/LICENSE.md	2019-01-04 15:20:12.000000000 +0000
@@ -1,32 +1,9 @@
-(The MIT License)
+The MIT License
 
-Copyright (c) 2008 - 2018:
+Copyright 2008 -- 2018 by Aaron Patterson, Mike Dalessio, Charles Nutter, Sergio Arbeo, Patrick Mahoney, Yoko Harada, Akinori MUSHA, John Shahid, Lars Kanis
 
-* [Aaron Patterson](http://tenderlovemaking.com)
-* [Mike Dalessio](http://mike.daless.io)
-* [Charles Nutter](http://blog.headius.com)
-* [Sergio Arbeo](http://www.serabe.com)
-* [Patrick Mahoney](http://polycrystal.org)
-* [Yoko Harada](http://yokolet.blogspot.com)
-* [Akinori MUSHA](https://akinori.org)
-* [John Shahid](https://github.com/jvshahid)
-* [Lars Kanis](https://github.com/larskanis)
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-'Software'), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff -pruN 1.8.4-1/Manifest.txt 1.10.0+dfsg1-2/Manifest.txt
--- 1.8.4-1/Manifest.txt	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/Manifest.txt	2019-01-04 15:20:12.000000000 +0000
@@ -1,25 +1,7 @@
-.autotest
-.cross_rubies
-.editorconfig
-.gemtest
-.travis.yml
-CHANGELOG.md
-CONTRIBUTING.md
-C_CODING_STYLE.rdoc
-Gemfile
-Gemfile-libxml-ruby
 LICENSE-DEPENDENCIES.md
 LICENSE.md
-Manifest.txt
 README.md
-ROADMAP.md
-Rakefile
-SECURITY.md
-STANDARD_RESPONSES.md
-Y_U_NO_GEMSPEC.md
-appveyor.yml
 bin/nokogiri
-build_all
 dependencies.yml
 ext/java/nokogiri/EncodingHandler.java
 ext/java/nokogiri/HtmlDocument.java
@@ -108,7 +90,8 @@ ext/java/nokogiri/internals/c14n/NameSpa
 ext/java/nokogiri/internals/c14n/NodeFilter.java
 ext/java/nokogiri/internals/c14n/UtfHelpper.java
 ext/java/nokogiri/internals/c14n/XMLUtils.java
-ext/java/org/apache/xml/dtm/ref/dom2dtm/DOM2DTMExt.java
+ext/java/nokogiri/internals/dom2dtm/DOM2DTM.java
+ext/java/nokogiri/internals/dom2dtm/DOM2DTMdefaultNamespaceDeclarationNode.java
 ext/nokogiri/depend
 ext/nokogiri/extconf.rb
 ext/nokogiri/html_document.c
@@ -251,118 +234,3 @@ lib/xercesImpl.jar
 lib/xml-apis.jar
 lib/xsd/xmlparser/nokogiri.rb
 patches/libxml2/0001-Revert-Do-not-URI-escape-in-server-side-includes.patch
-patches/sort-patches-by-date
-suppressions/README.txt
-suppressions/nokogiri_ruby-2.supp
-tasks/test.rb
-test/css/test_nthiness.rb
-test/css/test_parser.rb
-test/css/test_tokenizer.rb
-test/css/test_xpath_visitor.rb
-test/decorators/test_slop.rb
-test/files/2ch.html
-test/files/GH_1042.html
-test/files/address_book.rlx
-test/files/address_book.xml
-test/files/atom.xml
-test/files/bar/bar.xsd
-test/files/bogus.xml
-test/files/dont_hurt_em_why.xml
-test/files/encoding.html
-test/files/encoding.xhtml
-test/files/exslt.xml
-test/files/exslt.xslt
-test/files/foo/foo.xsd
-test/files/metacharset.html
-test/files/namespace_pressure_test.xml
-test/files/noencoding.html
-test/files/po.xml
-test/files/po.xsd
-test/files/saml/saml20assertion_schema.xsd
-test/files/saml/saml20protocol_schema.xsd
-test/files/saml/xenc_schema.xsd
-test/files/saml/xmldsig_schema.xsd
-test/files/shift_jis.html
-test/files/shift_jis.xml
-test/files/shift_jis_no_charset.html
-test/files/slow-xpath.xml
-test/files/snuggles.xml
-test/files/staff.dtd
-test/files/staff.xml
-test/files/staff.xslt
-test/files/test_document_url/bar.xml
-test/files/test_document_url/document.dtd
-test/files/test_document_url/document.xml
-test/files/tlm.html
-test/files/to_be_xincluded.xml
-test/files/valid_bar.xml
-test/files/xinclude.xml
-test/helper.rb
-test/html/sax/test_parser.rb
-test/html/sax/test_parser_context.rb
-test/html/sax/test_parser_text.rb
-test/html/sax/test_push_parser.rb
-test/html/test_attributes.rb
-test/html/test_builder.rb
-test/html/test_document.rb
-test/html/test_document_encoding.rb
-test/html/test_document_fragment.rb
-test/html/test_element_description.rb
-test/html/test_named_characters.rb
-test/html/test_node.rb
-test/html/test_node_encoding.rb
-test/namespaces/test_additional_namespaces_in_builder_doc.rb
-test/namespaces/test_namespaces_aliased_default.rb
-test/namespaces/test_namespaces_in_builder_doc.rb
-test/namespaces/test_namespaces_in_cloned_doc.rb
-test/namespaces/test_namespaces_in_created_doc.rb
-test/namespaces/test_namespaces_in_parsed_doc.rb
-test/namespaces/test_namespaces_preservation.rb
-test/test_convert_xpath.rb
-test/test_css_cache.rb
-test/test_encoding_handler.rb
-test/test_memory_leak.rb
-test/test_nokogiri.rb
-test/test_soap4r_sax.rb
-test/test_xslt_transforms.rb
-test/xml/node/test_save_options.rb
-test/xml/node/test_subclass.rb
-test/xml/sax/test_parser.rb
-test/xml/sax/test_parser_context.rb
-test/xml/sax/test_parser_text.rb
-test/xml/sax/test_push_parser.rb
-test/xml/test_attr.rb
-test/xml/test_attribute_decl.rb
-test/xml/test_builder.rb
-test/xml/test_c14n.rb
-test/xml/test_cdata.rb
-test/xml/test_comment.rb
-test/xml/test_document.rb
-test/xml/test_document_encoding.rb
-test/xml/test_document_fragment.rb
-test/xml/test_dtd.rb
-test/xml/test_dtd_encoding.rb
-test/xml/test_element_content.rb
-test/xml/test_element_decl.rb
-test/xml/test_entity_decl.rb
-test/xml/test_entity_reference.rb
-test/xml/test_namespace.rb
-test/xml/test_node.rb
-test/xml/test_node_attributes.rb
-test/xml/test_node_encoding.rb
-test/xml/test_node_inheritance.rb
-test/xml/test_node_reparenting.rb
-test/xml/test_node_set.rb
-test/xml/test_parse_options.rb
-test/xml/test_processing_instruction.rb
-test/xml/test_reader.rb
-test/xml/test_reader_encoding.rb
-test/xml/test_relax_ng.rb
-test/xml/test_schema.rb
-test/xml/test_syntax_error.rb
-test/xml/test_text.rb
-test/xml/test_unparented_node.rb
-test/xml/test_xinclude.rb
-test/xml/test_xpath.rb
-test/xslt/test_custom_functions.rb
-test/xslt/test_exception_handling.rb
diff -pruN 1.8.4-1/nokogiri.gemspec 1.10.0+dfsg1-2/nokogiri.gemspec
--- 1.8.4-1/nokogiri.gemspec	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/nokogiri.gemspec	1970-01-01 00:00:00.000000000 +0000
@@ -1,78 +0,0 @@
-#########################################################
-# This file has been automatically generated by gem2tgz #
-#########################################################
-# -*- encoding: utf-8 -*-
-# stub: nokogiri 1.8.4 ruby lib
-# stub: ext/nokogiri/extconf.rb
-
-Gem::Specification.new do |s|
-  s.name = "nokogiri".freeze
-  s.version = "1.8.4"
-
-  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
-  s.require_paths = ["lib".freeze]
-  s.authors = ["Aaron Patterson".freeze, "Mike Dalessio".freeze, "Yoko Harada".freeze, "Tim Elliott".freeze, "Akinori MUSHA".freeze, "John Shahid".freeze, "Lars Kanis".freeze]
-  s.date = "2018-07-03"
-  s.description = "Nokogiri (\u{92f8}) is an HTML, XML, SAX, and Reader parser.  Among\nNokogiri's many features is the ability to search documents via XPath\nor CSS3 selectors.".freeze
-  s.email = ["aaronp@rubyforge.org".freeze, "mike.dalessio@gmail.com".freeze, "yokolet@gmail.com".freeze, "tle@holymonkey.com".freeze, "knu@idaemons.org".freeze, "jvshahid@gmail.com".freeze, "lars@greiz-reinsdorf.de".freeze]
-  s.executables = ["nokogiri".freeze]
-  s.extensions = ["ext/nokogiri/extconf.rb".freeze]
-  s.extra_rdoc_files = ["CHANGELOG.md".freeze, "CONTRIBUTING.md".freeze, "C_CODING_STYLE.rdoc".freeze, "LICENSE-DEPENDENCIES.md".freeze, "LICENSE.md".freeze, "Manifest.txt".freeze, "README.md".freeze, "ROADMAP.md".freeze, "SECURITY.md".freeze, "STANDARD_RESPONSES.md".freeze, "Y_U_NO_GEMSPEC.md".freeze, "ext/nokogiri/html_document.c".freeze, "ext/nokogiri/html_element_description.c".freeze, "ext/nokogiri/html_entity_lookup.c".freeze, "ext/nokogiri/html_sax_parser_context.c".freeze, "ext/nokogiri/html_sax_push_parser.c".freeze, "ext/nokogiri/nokogiri.c".freeze, "ext/nokogiri/xml_attr.c".freeze, "ext/nokogiri/xml_attribute_decl.c".freeze, "ext/nokogiri/xml_cdata.c".freeze, "ext/nokogiri/xml_comment.c".freeze, "ext/nokogiri/xml_document.c".freeze, "ext/nokogiri/xml_document_fragment.c".freeze, "ext/nokogiri/xml_dtd.c".freeze, "ext/nokogiri/xml_element_content.c".freeze, "ext/nokogiri/xml_element_decl.c".freeze, "ext/nokogiri/xml_encoding_handler.c".freeze, "ext/nokogiri/xml_entity_decl.c".freeze, "ext/nokogiri/xml_entity_reference.c".freeze, "ext/nokogiri/xml_io.c".freeze, "ext/nokogiri/xml_libxml2_hacks.c".freeze, "ext/nokogiri/xml_namespace.c".freeze, "ext/nokogiri/xml_node.c".freeze, "ext/nokogiri/xml_node_set.c".freeze, "ext/nokogiri/xml_processing_instruction.c".freeze, "ext/nokogiri/xml_reader.c".freeze, "ext/nokogiri/xml_relax_ng.c".freeze, "ext/nokogiri/xml_sax_parser.c".freeze, "ext/nokogiri/xml_sax_parser_context.c".freeze, "ext/nokogiri/xml_sax_push_parser.c".freeze, "ext/nokogiri/xml_schema.c".freeze, "ext/nokogiri/xml_syntax_error.c".freeze, "ext/nokogiri/xml_text.c".freeze, "ext/nokogiri/xml_xpath_context.c".freeze, "ext/nokogiri/xslt_stylesheet.c".freeze, "suppressions/README.txt".freeze]
-  s.files = [".autotest".freeze, ".cross_rubies".freeze, ".editorconfig".freeze, ".gemtest".freeze, ".travis.yml".freeze, "CHANGELOG.md".freeze, "CONTRIBUTING.md".freeze, "C_CODING_STYLE.rdoc".freeze, "Gemfile".freeze, "Gemfile-libxml-ruby".freeze, "LICENSE-DEPENDENCIES.md".freeze, "LICENSE.md".freeze, "Manifest.txt".freeze, "README.md".freeze, "ROADMAP.md".freeze, "Rakefile".freeze, "SECURITY.md".freeze, "STANDARD_RESPONSES.md".freeze, "Y_U_NO_GEMSPEC.md".freeze, "appveyor.yml".freeze, "bin/nokogiri".freeze, "build_all".freeze, "dependencies.yml".freeze, "ext/nokogiri/depend".freeze, "ext/nokogiri/extconf.rb".freeze, "ext/nokogiri/html_document.c".freeze, "ext/nokogiri/html_document.h".freeze, "ext/nokogiri/html_element_description.c".freeze, "ext/nokogiri/html_element_description.h".freeze, "ext/nokogiri/html_entity_lookup.c".freeze, "ext/nokogiri/html_entity_lookup.h".freeze, "ext/nokogiri/html_sax_parser_context.c".freeze, "ext/nokogiri/html_sax_parser_context.h".freeze, "ext/nokogiri/html_sax_push_parser.c".freeze, "ext/nokogiri/html_sax_push_parser.h".freeze, "ext/nokogiri/nokogiri.c".freeze, "ext/nokogiri/nokogiri.h".freeze, "ext/nokogiri/xml_attr.c".freeze, "ext/nokogiri/xml_attr.h".freeze, "ext/nokogiri/xml_attribute_decl.c".freeze, "ext/nokogiri/xml_attribute_decl.h".freeze, "ext/nokogiri/xml_cdata.c".freeze, "ext/nokogiri/xml_cdata.h".freeze, "ext/nokogiri/xml_comment.c".freeze, "ext/nokogiri/xml_comment.h".freeze, "ext/nokogiri/xml_document.c".freeze, "ext/nokogiri/xml_document.h".freeze, "ext/nokogiri/xml_document_fragment.c".freeze, "ext/nokogiri/xml_document_fragment.h".freeze, "ext/nokogiri/xml_dtd.c".freeze, "ext/nokogiri/xml_dtd.h".freeze, "ext/nokogiri/xml_element_content.c".freeze, "ext/nokogiri/xml_element_content.h".freeze, "ext/nokogiri/xml_element_decl.c".freeze, "ext/nokogiri/xml_element_decl.h".freeze, "ext/nokogiri/xml_encoding_handler.c".freeze, "ext/nokogiri/xml_encoding_handler.h".freeze, "ext/nokogiri/xml_entity_decl.c".freeze, "ext/nokogiri/xml_entity_decl.h".freeze, "ext/nokogiri/xml_entity_reference.c".freeze, "ext/nokogiri/xml_entity_reference.h".freeze, "ext/nokogiri/xml_io.c".freeze, "ext/nokogiri/xml_io.h".freeze, "ext/nokogiri/xml_libxml2_hacks.c".freeze, "ext/nokogiri/xml_libxml2_hacks.h".freeze, "ext/nokogiri/xml_namespace.c".freeze, "ext/nokogiri/xml_namespace.h".freeze, "ext/nokogiri/xml_node.c".freeze, "ext/nokogiri/xml_node.h".freeze, "ext/nokogiri/xml_node_set.c".freeze, "ext/nokogiri/xml_node_set.h".freeze, "ext/nokogiri/xml_processing_instruction.c".freeze, "ext/nokogiri/xml_processing_instruction.h".freeze, "ext/nokogiri/xml_reader.c".freeze, "ext/nokogiri/xml_reader.h".freeze, "ext/nokogiri/xml_relax_ng.c".freeze, "ext/nokogiri/xml_relax_ng.h".freeze, "ext/nokogiri/xml_sax_parser.c".freeze, "ext/nokogiri/xml_sax_parser.h".freeze, "ext/nokogiri/xml_sax_parser_context.c".freeze, "ext/nokogiri/xml_sax_parser_context.h".freeze, "ext/nokogiri/xml_sax_push_parser.c".freeze, "ext/nokogiri/xml_sax_push_parser.h".freeze, "ext/nokogiri/xml_schema.c".freeze, "ext/nokogiri/xml_schema.h".freeze, "ext/nokogiri/xml_syntax_error.c".freeze, "ext/nokogiri/xml_syntax_error.h".freeze, "ext/nokogiri/xml_text.c".freeze, "ext/nokogiri/xml_text.h".freeze, "ext/nokogiri/xml_xpath_context.c".freeze, "ext/nokogiri/xml_xpath_context.h".freeze, "ext/nokogiri/xslt_stylesheet.c".freeze, "ext/nokogiri/xslt_stylesheet.h".freeze, "lib/nokogiri.rb".freeze, "lib/nokogiri/css.rb".freeze, "lib/nokogiri/css/node.rb".freeze, "lib/nokogiri/css/parser.rb".freeze, "lib/nokogiri/css/parser.y".freeze, "lib/nokogiri/css/parser_extras.rb".freeze, "lib/nokogiri/css/syntax_error.rb".freeze, "lib/nokogiri/css/tokenizer.rb".freeze, "lib/nokogiri/css/tokenizer.rex".freeze, "lib/nokogiri/css/xpath_visitor.rb".freeze, "lib/nokogiri/decorators/slop.rb".freeze, "lib/nokogiri/html.rb".freeze, "lib/nokogiri/html/builder.rb".freeze, "lib/nokogiri/html/document.rb".freeze, "lib/nokogiri/html/document_fragment.rb".freeze, "lib/nokogiri/html/element_description.rb".freeze, "lib/nokogiri/html/element_description_defaults.rb".freeze, "lib/nokogiri/html/entity_lookup.rb".freeze, "lib/nokogiri/html/sax/parser.rb".freeze, "lib/nokogiri/html/sax/parser_context.rb".freeze, "lib/nokogiri/html/sax/push_parser.rb".freeze, "lib/nokogiri/syntax_error.rb".freeze, "lib/nokogiri/version.rb".freeze, "lib/nokogiri/xml.rb".freeze, "lib/nokogiri/xml/attr.rb".freeze, "lib/nokogiri/xml/attribute_decl.rb".freeze, "lib/nokogiri/xml/builder.rb".freeze, "lib/nokogiri/xml/cdata.rb".freeze, "lib/nokogiri/xml/character_data.rb".freeze, "lib/nokogiri/xml/document.rb".freeze, "lib/nokogiri/xml/document_fragment.rb".freeze, "lib/nokogiri/xml/dtd.rb".freeze, "lib/nokogiri/xml/element_content.rb".freeze, "lib/nokogiri/xml/element_decl.rb".freeze, "lib/nokogiri/xml/entity_decl.rb".freeze, "lib/nokogiri/xml/entity_reference.rb".freeze, "lib/nokogiri/xml/namespace.rb".freeze, "lib/nokogiri/xml/node.rb".freeze, "lib/nokogiri/xml/node/save_options.rb".freeze, "lib/nokogiri/xml/node_set.rb".freeze, "lib/nokogiri/xml/notation.rb".freeze, "lib/nokogiri/xml/parse_options.rb".freeze, "lib/nokogiri/xml/pp.rb".freeze, "lib/nokogiri/xml/pp/character_data.rb".freeze, "lib/nokogiri/xml/pp/node.rb".freeze, "lib/nokogiri/xml/processing_instruction.rb".freeze, "lib/nokogiri/xml/reader.rb".freeze, "lib/nokogiri/xml/relax_ng.rb".freeze, "lib/nokogiri/xml/sax.rb".freeze, "lib/nokogiri/xml/sax/document.rb".freeze, "lib/nokogiri/xml/sax/parser.rb".freeze, "lib/nokogiri/xml/sax/parser_context.rb".freeze, "lib/nokogiri/xml/sax/push_parser.rb".freeze, "lib/nokogiri/xml/schema.rb".freeze, "lib/nokogiri/xml/searchable.rb".freeze, "lib/nokogiri/xml/syntax_error.rb".freeze, "lib/nokogiri/xml/text.rb".freeze, "lib/nokogiri/xml/xpath.rb".freeze, "lib/nokogiri/xml/xpath/syntax_error.rb".freeze, "lib/nokogiri/xml/xpath_context.rb".freeze, "lib/nokogiri/xslt.rb".freeze, "lib/nokogiri/xslt/stylesheet.rb".freeze, "lib/xsd/xmlparser/nokogiri.rb".freeze, "patches/libxml2/0001-Revert-Do-not-URI-escape-in-server-side-includes.patch".freeze, "patches/sort-patches-by-date".freeze, "ports/archives/libxml2-2.9.8.tar.gz".freeze, "ports/archives/libxslt-1.1.32.tar.gz".freeze, "suppressions/README.txt".freeze, "suppressions/nokogiri_ruby-2.supp".freeze, "tasks/test.rb".freeze, "test/css/test_nthiness.rb".freeze, "test/css/test_parser.rb".freeze, "test/css/test_tokenizer.rb".freeze, "test/css/test_xpath_visitor.rb".freeze, "test/decorators/test_slop.rb".freeze, "test/files/2ch.html".freeze, "test/files/GH_1042.html".freeze, "test/files/address_book.rlx".freeze, "test/files/address_book.xml".freeze, "test/files/atom.xml".freeze, "test/files/bar/bar.xsd".freeze, "test/files/bogus.xml".freeze, "test/files/dont_hurt_em_why.xml".freeze, "test/files/encoding.html".freeze, "test/files/encoding.xhtml".freeze, "test/files/exslt.xml".freeze, "test/files/exslt.xslt".freeze, "test/files/foo/foo.xsd".freeze, "test/files/metacharset.html".freeze, "test/files/namespace_pressure_test.xml".freeze, "test/files/noencoding.html".freeze, "test/files/po.xml".freeze, "test/files/po.xsd".freeze, "test/files/saml/saml20assertion_schema.xsd".freeze, "test/files/saml/saml20protocol_schema.xsd".freeze, "test/files/saml/xenc_schema.xsd".freeze, "test/files/saml/xmldsig_schema.xsd".freeze, "test/files/shift_jis.html".freeze, "test/files/shift_jis.xml".freeze, "test/files/shift_jis_no_charset.html".freeze, "test/files/slow-xpath.xml".freeze, "test/files/snuggles.xml".freeze, "test/files/staff.dtd".freeze, "test/files/staff.xml".freeze, "test/files/staff.xslt".freeze, "test/files/test_document_url/bar.xml".freeze, "test/files/test_document_url/document.dtd".freeze, "test/files/test_document_url/document.xml".freeze, "test/files/tlm.html".freeze, "test/files/to_be_xincluded.xml".freeze, "test/files/valid_bar.xml".freeze, "test/files/xinclude.xml".freeze, "test/helper.rb".freeze, "test/html/sax/test_parser.rb".freeze, "test/html/sax/test_parser_context.rb".freeze, "test/html/sax/test_parser_text.rb".freeze, "test/html/sax/test_push_parser.rb".freeze, "test/html/test_attributes.rb".freeze, "test/html/test_builder.rb".freeze, "test/html/test_document.rb".freeze, "test/html/test_document_encoding.rb".freeze, "test/html/test_document_fragment.rb".freeze, "test/html/test_element_description.rb".freeze, "test/html/test_named_characters.rb".freeze, "test/html/test_node.rb".freeze, "test/html/test_node_encoding.rb".freeze, "test/namespaces/test_additional_namespaces_in_builder_doc.rb".freeze, "test/namespaces/test_namespaces_aliased_default.rb".freeze, "test/namespaces/test_namespaces_in_builder_doc.rb".freeze, "test/namespaces/test_namespaces_in_cloned_doc.rb".freeze, "test/namespaces/test_namespaces_in_created_doc.rb".freeze, "test/namespaces/test_namespaces_in_parsed_doc.rb".freeze, "test/namespaces/test_namespaces_preservation.rb".freeze, "test/test_convert_xpath.rb".freeze, "test/test_css_cache.rb".freeze, "test/test_encoding_handler.rb".freeze, "test/test_memory_leak.rb".freeze, "test/test_nokogiri.rb".freeze, "test/test_soap4r_sax.rb".freeze, "test/test_xslt_transforms.rb".freeze, "test/xml/node/test_save_options.rb".freeze, "test/xml/node/test_subclass.rb".freeze, "test/xml/sax/test_parser.rb".freeze, "test/xml/sax/test_parser_context.rb".freeze, "test/xml/sax/test_parser_text.rb".freeze, "test/xml/sax/test_push_parser.rb".freeze, "test/xml/test_attr.rb".freeze, "test/xml/test_attribute_decl.rb".freeze, "test/xml/test_builder.rb".freeze, "test/xml/test_c14n.rb".freeze, "test/xml/test_cdata.rb".freeze, "test/xml/test_comment.rb".freeze, "test/xml/test_document.rb".freeze, "test/xml/test_document_encoding.rb".freeze, "test/xml/test_document_fragment.rb".freeze, "test/xml/test_dtd.rb".freeze, "test/xml/test_dtd_encoding.rb".freeze, "test/xml/test_element_content.rb".freeze, "test/xml/test_element_decl.rb".freeze, "test/xml/test_entity_decl.rb".freeze, "test/xml/test_entity_reference.rb".freeze, "test/xml/test_namespace.rb".freeze, "test/xml/test_node.rb".freeze, "test/xml/test_node_attributes.rb".freeze, "test/xml/test_node_encoding.rb".freeze, "test/xml/test_node_inheritance.rb".freeze, "test/xml/test_node_reparenting.rb".freeze, "test/xml/test_node_set.rb".freeze, "test/xml/test_parse_options.rb".freeze, "test/xml/test_processing_instruction.rb".freeze, "test/xml/test_reader.rb".freeze, "test/xml/test_reader_encoding.rb".freeze, "test/xml/test_relax_ng.rb".freeze, "test/xml/test_schema.rb".freeze, "test/xml/test_syntax_error.rb".freeze, "test/xml/test_text.rb".freeze, "test/xml/test_unparented_node.rb".freeze, "test/xml/test_xinclude.rb".freeze, "test/xml/test_xpath.rb".freeze, "test/xslt/test_custom_functions.rb".freeze, "test/xslt/test_exception_handling.rb".freeze]
-  s.licenses = ["MIT".freeze]
-  s.rdoc_options = ["--main".freeze, "README.md".freeze]
-  s.required_ruby_version = Gem::Requirement.new(">= 2.1.0".freeze)
-  s.rubygems_version = "2.5.2.1".freeze
-  s.summary = "Nokogiri (\u{92f8}) is an HTML, XML, SAX, and Reader parser".freeze
-
-  if s.respond_to? :specification_version then
-    s.specification_version = 4
-
-    if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
-      s.add_development_dependency(%q<concourse>.freeze, ["~> 0.15"])
-      s.add_development_dependency(%q<hoe>.freeze, ["~> 3.16"])
-      s.add_development_dependency(%q<hoe-bundler>.freeze, ["~> 1.2"])
-      s.add_development_dependency(%q<hoe-debugging>.freeze, ["~> 1.4"])
-      s.add_development_dependency(%q<hoe-gemspec>.freeze, ["~> 1.0"])
-      s.add_development_dependency(%q<hoe-git>.freeze, ["~> 1.6"])
-      s.add_runtime_dependency(%q<mini_portile2>.freeze, ["~> 2.3.0"])
-      s.add_development_dependency(%q<minitest>.freeze, ["~> 5.8.4"])
-      s.add_development_dependency(%q<racc>.freeze, ["~> 1.4.14"])
-      s.add_development_dependency(%q<rake>.freeze, ["~> 12.0"])
-      s.add_development_dependency(%q<rake-compiler>.freeze, ["~> 1.0.3"])
-      s.add_development_dependency(%q<rake-compiler-dock>.freeze, ["~> 0.6.2"])
-      s.add_development_dependency(%q<rdoc>.freeze, ["~> 4.0"])
-      s.add_development_dependency(%q<rexical>.freeze, ["~> 1.0.5"])
-    else
-      s.add_dependency(%q<concourse>.freeze, ["~> 0.15"])
-      s.add_dependency(%q<hoe>.freeze, ["~> 3.16"])
-      s.add_dependency(%q<hoe-bundler>.freeze, ["~> 1.2"])
-      s.add_dependency(%q<hoe-debugging>.freeze, ["~> 1.4"])
-      s.add_dependency(%q<hoe-gemspec>.freeze, ["~> 1.0"])
-      s.add_dependency(%q<hoe-git>.freeze, ["~> 1.6"])
-      s.add_dependency(%q<mini_portile2>.freeze, ["~> 2.3.0"])
-      s.add_dependency(%q<minitest>.freeze, ["~> 5.8.4"])
-      s.add_dependency(%q<racc>.freeze, ["~> 1.4.14"])
-      s.add_dependency(%q<rake>.freeze, ["~> 12.0"])
-      s.add_dependency(%q<rake-compiler>.freeze, ["~> 1.0.3"])
-      s.add_dependency(%q<rake-compiler-dock>.freeze, ["~> 0.6.2"])
-      s.add_dependency(%q<rdoc>.freeze, ["~> 4.0"])
-      s.add_dependency(%q<rexical>.freeze, ["~> 1.0.5"])
-    end
-  else
-    s.add_dependency(%q<concourse>.freeze, ["~> 0.15"])
-    s.add_dependency(%q<hoe>.freeze, ["~> 3.16"])
-    s.add_dependency(%q<hoe-bundler>.freeze, ["~> 1.2"])
-    s.add_dependency(%q<hoe-debugging>.freeze, ["~> 1.4"])
-    s.add_dependency(%q<hoe-gemspec>.freeze, ["~> 1.0"])
-    s.add_dependency(%q<hoe-git>.freeze, ["~> 1.6"])
-    s.add_dependency(%q<mini_portile2>.freeze, ["~> 2.3.0"])
-    s.add_dependency(%q<minitest>.freeze, ["~> 5.8.4"])
-    s.add_dependency(%q<racc>.freeze, ["~> 1.4.14"])
-    s.add_dependency(%q<rake>.freeze, ["~> 12.0"])
-    s.add_dependency(%q<rake-compiler>.freeze, ["~> 1.0.3"])
-    s.add_dependency(%q<rake-compiler-dock>.freeze, ["~> 0.6.2"])
-    s.add_dependency(%q<rdoc>.freeze, ["~> 4.0"])
-    s.add_dependency(%q<rexical>.freeze, ["~> 1.0.5"])
-  end
-end
diff -pruN 1.8.4-1/Rakefile 1.10.0+dfsg1-2/Rakefile
--- 1.8.4-1/Rakefile	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/Rakefile	2019-01-04 15:20:12.000000000 +0000
@@ -115,8 +115,7 @@ HOE = Hoe.spec 'nokogiri' do
   self.readme_file  = "README.md"
   self.history_file = "CHANGELOG.md"
 
-  self.extra_rdoc_files = FileList['*.rdoc','ext/nokogiri/*.c']
-
+  self.extra_rdoc_files = FileList['ext/nokogiri/*.c']
 
   self.clean_globs += [
     'nokogiri.gemspec',
@@ -127,35 +126,28 @@ HOE = Hoe.spec 'nokogiri' do
 
   unless java?
     self.extra_deps += [
-      ["mini_portile2",    "~> 2.3.0"], # keep version in sync with extconf.rb
+      ["mini_portile2",    "~> 2.4.0"], # keep version in sync with extconf.rb
     ]
   end
 
   self.extra_dev_deps += [
     ["hoe-bundler",        "~> 1.2"],
-    ["hoe-debugging",      "~> 1.4"],
+    ["hoe-debugging",      "~> 2.0"],
     ["hoe-gemspec",        "~> 1.0"],
     ["hoe-git",            "~> 1.6"],
     ["minitest",           "~> 5.8.4"],
     ["rake",               "~> 12.0"],
     ["rake-compiler",      "~> 1.0.3"],
-    ["rake-compiler-dock", "~> 0.6.2"],
+    ["rake-compiler-dock", "~> 0.7.0"],
     ["racc",               "~> 1.4.14"],
     ["rexical",            "~> 1.0.5"],
     ["concourse",          "~> 0.15"],
   ]
 
-  if java?
-    self.spec_extras = {
-        :platform => 'java',
-        :required_ruby_version => '>= 1.9.3' # JRuby >= 1.7
-    }
-  else
-    self.spec_extras = {
-      :extensions => ["ext/nokogiri/extconf.rb"],
-      :required_ruby_version => '>= 2.1.0'
-    }
-  end
+  self.spec_extras = {
+    :extensions => ["ext/nokogiri/extconf.rb"],
+    :required_ruby_version => '>= 2.3.0'
+  }
 
   self.testlib = :minitest
 end
@@ -250,19 +242,9 @@ end
 desc "Generate css/parser.rb and css/tokenizer.rex"
 task 'generate' => [GENERATED_PARSER, GENERATED_TOKENIZER]
 task 'gem:spec' => 'generate' if Rake::Task.task_defined?("gem:spec")
-
-# This is a big hack to make sure that the racc and rexical
-# dependencies in the Gemfile are constrainted to ruby platforms
-# (i.e. MRI and Rubinius). There's no way to do that through hoe,
-# and any solution will require changing hoe and hoe-bundler.
-old_gemfile_task = Rake::Task['bundler:gemfile'] rescue nil
-task 'bundler:gemfile' do
-  old_gemfile_task.invoke if old_gemfile_task
-
-  lines = File.open('Gemfile', 'r') { |f| f.readlines }.map do |line|
-    line =~ /racc|rexical/ ? "#{line.strip}, :platform => [:ruby, :mingw, :x64_mingw]" : line
-  end
-  File.open('Gemfile', 'w') { |f| lines.each { |line| f.puts line } }
+[:compile, :check_manifest].each do |task_name|
+  Rake::Task[task_name].prerequisites << GENERATED_PARSER
+  Rake::Task[task_name].prerequisites << GENERATED_TOKENIZER
 end
 
 file GENERATED_PARSER => "lib/nokogiri/css/parser.y" do |t|
@@ -273,11 +255,6 @@ file GENERATED_TOKENIZER => "lib/nokogir
   sh "rex --independent -o #{t.name} #{t.prerequisites.first}"
 end
 
-[:compile, :check_manifest].each do |task_name|
-  Rake::Task[task_name].prerequisites << GENERATED_PARSER
-  Rake::Task[task_name].prerequisites << GENERATED_TOKENIZER
-end
-
 # ----------------------------------------
 
 desc "set environment variables to build and/or test with debug options"
@@ -339,10 +316,16 @@ task :cross do
   end
 end
 
-desc "build a windows gem without all the ceremony."
+desc "build a windows gem without all the ceremony"
 task "gem:windows" do
   require "rake_compiler_dock"
   RakeCompilerDock.sh "bundle && rake cross native gem MAKE='nice make -j`nproc`' RUBY_CC_VERSION=#{ENV['RUBY_CC_VERSION']}"
 end
 
+desc "build a jruby gem with docker"
+task "gem:jruby" do
+  require "rake_compiler_dock"
+  RakeCompilerDock.sh "bundle && rake java gem", rubyvm: 'jruby'
+end
+
 # vim: syntax=Ruby
diff -pruN 1.8.4-1/README.md 1.10.0+dfsg1-2/README.md
--- 1.8.4-1/README.md	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/README.md	2019-01-04 15:20:12.000000000 +0000
@@ -6,6 +6,7 @@ Nokogiri (鋸) is an HTML, XML, SAX, and
 Nokogiri's many features is the ability to search documents via XPath
 or CSS3 selectors.
 
+
 ## Links
 
 * http://nokogiri.org
@@ -18,8 +19,8 @@ or CSS3 selectors.
 
 [![Concourse CI](https://ci.nokogiri.org/api/v1/teams/nokogiri-core/pipelines/nokogiri/jobs/ruby-2.4-system/badge)](https://ci.nokogiri.org/teams/nokogiri-core/pipelines/nokogiri?groups=master)
 [![Code Climate](https://codeclimate.com/github/sparklemotion/nokogiri.svg)](https://codeclimate.com/github/sparklemotion/nokogiri)
-[![Version Eye](https://www.versioneye.com/ruby/nokogiri/badge.png)](https://www.versioneye.com/ruby/nokogiri)
 [![Join the chat at https://gitter.im/sparklemotion/nokogiri](https://badges.gitter.im/sparklemotion/nokogiri.svg)](https://gitter.im/sparklemotion/nokogiri?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![Tidelift dependencies](https://tidelift.com/badges/github/sparklemotion/nokogiri)](https://tidelift.com/subscription/pkg/rubygems-nokogiri?utm_source=rubygems-nokogiri&utm_medium=referral&utm_campaign=readme)
 
 
 ## Features
@@ -76,10 +77,16 @@ There are open-source tutorials (to whic
 * Before filing a bug report, please read our submission guidelines: http://nokogiri.org/tutorials/getting_help.html
 * The IRC channel is #nokogiri on freenode.
 
+Consider subscribing to [Tidelift][tidelift] which provides license assurances and timely security notifications for your open source dependencies, including Nokogiri. [Tidelift][tidelift] subscriptions also help the Nokogiri maintainers fund our [automated testing](https://ci.nokogiri.org) which in turn allows us to ship releases, bugfixes, and security updates more often.
+
+  [tidelift]: https://tidelift.com/subscription/pkg/rubygems-nokogiri?utm_source=rubygems-nokogiri&utm_medium=referral&utm_campaign=readme
+
 
 ## Security and Vulnerability Reporting
 
-See [`SECURITY.md`](SECURITY.md)
+Please report vulnerabilities at https://hackerone.com/nokogiri
+
+Full information and description of our security policy is in [`SECURITY.md`](SECURITY.md)
 
 
 ## Synopsis
@@ -114,7 +121,7 @@ end
 
 ## Requirements
 
-* Ruby 2.1.0 or higher, including any development packages necessary
+* Ruby 2.3.0 or higher, including any development packages necessary
   to compile native extensions.
 
 * In Nokogiri 1.6.0 and later libxml2 and libxslt are bundled with the
@@ -162,6 +169,7 @@ explicitly setting the encoding to EUC-J
   doc = Nokogiri.XML('<foo><bar /></foo>', nil, 'EUC-JP')
 ```
 
+
 ## Development
 
 ```bash
@@ -171,4 +179,6 @@ explicitly setting the encoding to EUC-J
 
 ## License
 
-MIT. See [`LICENSE.md`](LICENSE.md).
+This project is licensed under the terms of the MIT license.
+
+See this license at [`LICENSE.md`](LICENSE.md).
diff -pruN 1.8.4-1/suppressions/nokogiri_ruby-2.5.supp 1.10.0+dfsg1-2/suppressions/nokogiri_ruby-2.5.supp
--- 1.8.4-1/suppressions/nokogiri_ruby-2.5.supp	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/suppressions/nokogiri_ruby-2.5.supp	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,30 @@
+{
+   <ruby_cleanup_error_1>
+   Memcheck:Addr8
+   fun:mark_locations_array
+   fun:gc_mark_locations
+   fun:rb_gc_mark_locations
+   fun:gc_mark_stacked_objects
+   fun:gc_mark_stacked_objects_*
+   fun:gc_marks_rest
+   ...
+   fun:ruby_finalize_?
+   fun:ruby_cleanup
+   fun:ruby_run_node
+   fun:main
+}
+{
+   <ruby_cleanup_error_2>
+   Memcheck:Addr8
+   fun:mark_locations_array
+   fun:gc_mark_locations
+   fun:rb_gc_mark_locations
+   fun:gc_marks_wb_unprotected_objects
+   fun:gc_marks_finish
+   fun:gc_marks_rest
+   ...
+   fun:ruby_finalize_?
+   fun:ruby_cleanup
+   fun:ruby_run_node
+   fun:main
+}
diff -pruN 1.8.4-1/suppressions/nokogiri_ruby-2.6.supp 1.10.0+dfsg1-2/suppressions/nokogiri_ruby-2.6.supp
--- 1.8.4-1/suppressions/nokogiri_ruby-2.6.supp	1970-01-01 00:00:00.000000000 +0000
+++ 1.10.0+dfsg1-2/suppressions/nokogiri_ruby-2.6.supp	2019-01-04 15:20:12.000000000 +0000
@@ -0,0 +1,26 @@
+{
+   <https://ci.nokogiri.org/teams/nokogiri-core/pipelines/nokogiri/jobs/ruby-2.6-valgrind/builds/1>
+   Memcheck:Addr8
+   fun:mark_locations_array
+   fun:gc_marks_wb_unprotected_objects
+   fun:gc_marks_finish
+   fun:gc_marks_rest
+   ...
+   fun:ruby_finalize_?
+   fun:ruby_cleanup
+   fun:ruby_run_node
+   fun:main
+}
+{
+   <https://ci.nokogiri.org/teams/nokogiri-core/pipelines/nokogiri/jobs/ruby-2.6-valgrind/builds/1>
+   Memcheck:Addr8
+   fun:mark_locations_array
+   fun:gc_mark_stacked_objects
+   fun:gc_mark_stacked_objects_incremental
+   fun:gc_marks_rest
+   ...
+   fun:ruby_finalize_?
+   fun:ruby_cleanup
+   fun:ruby_run_node
+   fun:main
+}
diff -pruN 1.8.4-1/test/helper.rb 1.10.0+dfsg1-2/test/helper.rb
--- 1.8.4-1/test/helper.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/test/helper.rb	2019-01-04 15:20:12.000000000 +0000
@@ -213,7 +213,7 @@ module Nokogiri
         .each do |name|
           define_method name do |*arguments|
             @items << [name, *arguments]
-            super *arguments
+            super(*arguments)
           end
         end
 
diff -pruN 1.8.4-1/test/html/test_document_fragment.rb 1.10.0+dfsg1-2/test/html/test_document_fragment.rb
--- 1.8.4-1/test/html/test_document_fragment.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/test/html/test_document_fragment.rb	2019-01-04 15:20:12.000000000 +0000
@@ -305,6 +305,13 @@ module Nokogiri
         assert_equal original_errors1, frag1.errors
         assert_equal original_errors2, frag2.errors
       end
+
+      def test_dup_should_create_an_html_document_fragment
+        # https://github.com/sparklemotion/nokogiri/issues/1846
+        original = Nokogiri::HTML::DocumentFragment.parse("<div><p>hello</p></div>")
+        duplicate = original.dup
+        assert_instance_of Nokogiri::HTML::DocumentFragment, duplicate
+      end
     end
   end
 end
diff -pruN 1.8.4-1/test/html/test_document.rb 1.10.0+dfsg1-2/test/html/test_document.rb
--- 1.8.4-1/test/html/test_document.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/test/html/test_document.rb	2019-01-04 15:20:12.000000000 +0000
@@ -707,6 +707,16 @@ eohtml
         assert_equal 'ISO-8859-1', html.encoding.name
       end
 
+      def test_leaking_dtd_nodes_after_internal_subset_removal
+        # see https://github.com/sparklemotion/nokogiri/issues/1784
+        #
+        # just checking that this doesn't raise a valgrind error. we
+        # don't otherwise have any test coverage for removing DTDs.
+        #
+        100.times do |i|
+          Nokogiri::HTML::Document.new.internal_subset.remove
+        end
+      end
     end
   end
 end
diff -pruN 1.8.4-1/test/test_memory_leak.rb 1.10.0+dfsg1-2/test/test_memory_leak.rb
--- 1.8.4-1/test/test_memory_leak.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/test/test_memory_leak.rb	2019-01-04 15:20:12.000000000 +0000
@@ -13,6 +13,22 @@ class TestMemoryLeak < Nokogiri::TestCas
 EOF
   end
 
+  #
+  #  this suite is turned off unless the env var NOKOGIRI_GC is non-nil
+  #
+  #  to run any of these tests, do something like this on the commandline:
+  #
+  #    $ NOKOGIRI_GC=t ruby -Ilib:test \
+  #          test/test_memory_leak.rb \
+  #          -n /test_leaking_namespace_node_strings/
+  #
+  #  also see:
+  #
+  #    https://github.com/sparklemotion/nokogiri/issues/1603
+  #
+  #  which is an open issue to resurrect these tests and run them as
+  #  part of the CI pipeline.
+  #
   if ENV['NOKOGIRI_GC'] # turning these off by default for now
     def test_dont_hurt_em_why
       content = File.open("#{File.dirname(__FILE__)}/files/dont_hurt_em_why.xml").read
@@ -126,7 +142,7 @@ EOF
     end
 
     def test_in_context_parser_leak
-      loop do 
+      loop do
         doc = Nokogiri::XML::Document.new
         fragment1 = Nokogiri::XML::DocumentFragment.new(doc, '<foo/>')
         node = fragment1.children[0]
@@ -144,8 +160,62 @@ EOF
         doc.xpath('name(//node())')
       end
     end
+
+    def test_leaking_namespace_node_strings
+      # see https://github.com/sparklemotion/nokogiri/issues/1810 for memory leak report
+      ns = {'xmlns' => 'http://schemas.xmlsoap.org/soap/envelope/'}
+      20.times do
+        10_000.times do
+          Nokogiri::XML::Builder.new do |xml|
+            xml.send 'Envelope', ns do
+              xml.send 'Foobar', ns
+            end
+          end
+        end
+        puts MemInfo.rss
+      end
+    end
+
+    def test_leaking_namespace_node_strings_with_prefix
+      # see https://github.com/sparklemotion/nokogiri/issues/1810 for memory leak report
+      ns = {'xmlns:foo' => 'http://schemas.xmlsoap.org/soap/envelope/'}
+      20.times do
+        10_000.times do
+          Nokogiri::XML::Builder.new do |xml|
+            xml.send 'Envelope', ns do
+              xml.send 'Foobar', ns
+            end
+          end
+        end
+        puts MemInfo.rss
+      end
+    end
+
+    def test_leaking_dtd_nodes_after_internal_subset_removal
+      # see https://github.com/sparklemotion/nokogiri/issues/1784
+      100_000.times do |i|
+        doc = Nokogiri::HTML::Document.new
+        doc.internal_subset.remove
+        puts MemInfo.rss if (i % 1000 == 0)
+      end
+    end
   end # if NOKOGIRI_GC
 
+  module MemInfo
+    # from https://stackoverflow.com/questions/7220896/get-current-ruby-process-memory-usage
+    # this is only going to work on linux
+    PAGE_SIZE = `getconf PAGESIZE`.chomp.to_i rescue 4096
+    STATM_PATH = "/proc/#{Process.pid}/statm"
+    STATM_FOUND = File.exist?(STATM_PATH)
+
+    def self.rss
+      if STATM_FOUND
+        return (File.read(STATM_PATH).split(' ')[1].to_i * PAGE_SIZE) / 1024
+      end
+      return 0
+    end
+  end
+
   private
 
   def count_object_space_documents
diff -pruN 1.8.4-1/test/xml/test_attr.rb 1.10.0+dfsg1-2/test/xml/test_attr.rb
--- 1.8.4-1/test/xml/test_attr.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/test/xml/test_attr.rb	2019-01-04 15:20:12.000000000 +0000
@@ -26,12 +26,34 @@ module Nokogiri
         assert_equal "Y&ent1;", street.value
       end
 
-      def test_value=
+      def test_set_value
         xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
         address = xml.xpath('//address')[3]
         street = address.attributes['street']
         street.value = "Y&ent1;"
         assert_equal "Y&ent1;", street.value
+        assert_includes %Q{ street="Y&amp;ent1;"}, street.to_xml
+      end
+
+      def test_set_value_with_entity_string_in_html_file
+        html = Nokogiri::HTML("<html><body><div foo='asdf'>")
+        foo = html.at_css("div").attributes["foo"]
+        foo.value = "Y&ent1;"
+        assert_includes %Q{ foo="Y&amp;ent1;"}, foo.to_html
+      end
+
+      def test_set_value_with_blank_string_in_html_file
+        html = Nokogiri::HTML("<html><body><div foo='asdf'>")
+        foo = html.at_css("div").attributes["foo"]
+        foo.value = ""
+        assert_includes %Q{ foo=""}, foo.to_html
+      end
+
+      def test_set_value_of_boolean_attr_with_nil_in_html_file
+        html = Nokogiri::HTML("<html><body><div disabled='asdf'>")
+        disabled = html.at_css("div").attributes["disabled"]
+        disabled.value = nil
+        assert_includes %Q{ disabled}, disabled.to_html
       end
 
       def test_unlink # aliased as :remove
diff -pruN 1.8.4-1/test/xml/test_builder.rb 1.10.0+dfsg1-2/test/xml/test_builder.rb
--- 1.8.4-1/test/xml/test_builder.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/test/xml/test_builder.rb	2019-01-04 15:20:12.000000000 +0000
@@ -331,6 +331,18 @@ module Nokogiri
         assert_nil doc.at_xpath("//*[local-name() = 'products']").namespace
       end
 
+      def test_builder_reuses_namespaces
+        # see https://github.com/sparklemotion/nokogiri/issues/1810 for memory leak report
+        builder = Nokogiri::XML::Builder.new
+        builder.send "envelope", {'xmlns' => 'http://schemas.xmlsoap.org/soap/envelope/'} do
+          builder.send "package", {'xmlns' => 'http://schemas.xmlsoap.org/soap/envelope/'}
+        end
+        envelope = builder.doc.at_css("envelope")
+        package = builder.doc.at_css("package")
+        assert_equal envelope.namespace, package.namespace
+        assert_equal envelope.namespace.object_id, package.namespace.object_id
+      end
+
       private
 
       def namespaces_defined_on(node)
diff -pruN 1.8.4-1/test/xml/test_document_fragment.rb 1.10.0+dfsg1-2/test/xml/test_document_fragment.rb
--- 1.8.4-1/test/xml/test_document_fragment.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/test/xml/test_document_fragment.rb	2019-01-04 15:20:12.000000000 +0000
@@ -264,6 +264,36 @@ EOS
       end
 
       if Nokogiri.uses_libxml?
+        def test_dup_should_exist_in_a_new_document
+          # https://github.com/sparklemotion/nokogiri/issues/1063
+          original = Nokogiri::XML::DocumentFragment.parse("<div><p>hello</p></div>")
+          duplicate = original.dup
+          assert_not_equal original.document, duplicate.document
+        end
+      end
+
+      def test_dup_should_create_an_xml_document_fragment
+        # https://github.com/sparklemotion/nokogiri/issues/1846
+        original = Nokogiri::XML::DocumentFragment.parse("<div><p>hello</p></div>")
+        duplicate = original.dup
+        assert_instance_of Nokogiri::XML::DocumentFragment, duplicate
+      end
+
+      def test_dup_creates_tree_with_identical_structure
+        original = Nokogiri::XML::DocumentFragment.parse("<div><p>hello</p></div>")
+        duplicate = original.dup
+        assert_equal original.to_html, duplicate.to_html
+      end
+
+      def test_dup_creates_mutable_tree
+        original = Nokogiri::XML::DocumentFragment.parse("<div><p>hello</p></div>")
+        duplicate = original.dup
+        duplicate.at_css("div").add_child("<b>hello there</b>")
+        assert_nil original.at_css("b")
+        assert_not_nil duplicate.at_css("b")
+      end
+
+      if Nokogiri.uses_libxml?
         def test_for_libxml_in_context_fragment_parsing_bug_workaround
           10.times do
             begin
diff -pruN 1.8.4-1/test/xml/test_node.rb 1.10.0+dfsg1-2/test/xml/test_node.rb
--- 1.8.4-1/test/xml/test_node.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/test/xml/test_node.rb	2019-01-04 15:20:12.000000000 +0000
@@ -165,6 +165,44 @@ module Nokogiri
         assert_equal x.first.name, "span"
       end
 
+      def test_dup_is_deep_copy_by_default
+        doc = XML::Document.parse "<root><div><p>hello</p></div></root>"
+        div = doc.at_css "div"
+        node = div.dup
+        assert_equal 1, node.children.length
+        assert_equal "<p>hello</p>", node.children.first.to_html
+      end
+
+      def test_dup_deep_copy
+        doc = XML::Document.parse "<root><div><p>hello</p></div></root>"
+        div = doc.at_css "div"
+        node = div.dup(1)
+        assert_equal 1, node.children.length
+        assert_equal "<p>hello</p>", node.children.first.to_html
+      end
+
+      def test_dup_shallow_copy
+        doc = XML::Document.parse "<root><div><p>hello</p></div></root>"
+        div = doc.at_css "div"
+        node = div.dup(0)
+        assert_equal 0, node.children.length
+      end
+
+      if Nokogiri.uses_libxml?
+        def test_dup_to_another_document
+          doc1 = HTML::Document.parse "<root><div><p>hello</p></div></root>"
+          doc2 = HTML::Document.parse "<div></div>"
+
+          div = doc1.at_css "div"
+          duplicate_div = div.dup(1, doc2)
+
+          assert_not_nil doc1.at_css("div")
+          assert_equal doc2, duplicate_div.document
+          assert_equal 1, duplicate_div.children.length
+          assert_equal "<p>hello</p>", duplicate_div.children.first.to_html
+        end
+      end
+
       def test_subclass_dup
         subclass = Class.new(Nokogiri::XML::Node)
         node = subclass.new('foo', @xml).dup
@@ -415,12 +453,19 @@ module Nokogiri
         assert_equal ns, ns2
       end
 
-      def test_add_default_ns
+      def test_add_default_namespace
         node = @xml.at('address')
         node.add_namespace(nil, 'http://tenderlovemaking.com')
         assert_equal 'http://tenderlovemaking.com', node.namespaces['xmlns']
       end
 
+      def test_add_default_namespace_twice
+        node = @xml.at('address')
+        ns = node.add_namespace(nil, 'http://tenderlovemaking.com')
+        ns2 = node.add_namespace(nil, 'http://tenderlovemaking.com')
+        assert_equal ns.object_id, ns2.object_id
+      end
+
       def test_add_multiple_namespaces
         node = @xml.at('address')
 
@@ -1320,6 +1365,15 @@ eoxml
           node.add_next_sibling(Nokogiri::XML::Text.new('after', node.document))
         end
       end
+
+      def test_wrap
+        xml = '<root><thing><div class="title">important thing</div></thing></root>'
+        doc = Nokogiri::XML(xml)
+        thing = doc.at_css("thing")
+        thing.wrap("<wrapper/>")
+        assert_equal 'wrapper', thing.parent.name
+        assert_equal 'thing', doc.at_css("wrapper").children.first.name
+      end
     end
   end
 end
diff -pruN 1.8.4-1/test/xml/test_node_reparenting.rb 1.10.0+dfsg1-2/test/xml/test_node_reparenting.rb
--- 1.8.4-1/test/xml/test_node_reparenting.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/test/xml/test_node_reparenting.rb	2019-01-04 15:20:12.000000000 +0000
@@ -197,6 +197,17 @@ module Nokogiri
             end
           end
 
+          describe "given the new document is empty" do
+            it "adds the node to the new document" do
+              doc1 = Nokogiri::XML.parse("<value>3</value>")
+              doc2 = Nokogiri::XML::Document.new
+              node = doc1.at_xpath("//value")
+              node.remove
+              doc2.add_child(node)
+              assert_match(/<value>3<\/value>/, doc2.to_xml)
+            end
+          end
+
           describe "given a parent node with a default namespace" do
             before do
               @doc = Nokogiri::XML(<<-eoxml)
@@ -213,6 +224,59 @@ module Nokogiri
               node.add_child(child)
               assert @doc.at('//xmlns:second')
             end
+
+            describe "and a child node was added to a new doc with the a different namespace using the same prefix" do
+              before do
+                @doc = Nokogiri::XML %Q{<root xmlns:bar="http://tenderlovemaking.com/"><bar:first/></root>}
+                new_doc = Nokogiri::XML %Q{<newroot xmlns:bar="http://flavorjon.es/"/>}
+                assert node = @doc.at("//tenderlove:first", tenderlove: "http://tenderlovemaking.com/")
+                new_doc.root.add_child node
+                @doc = new_doc
+              end
+
+              it "serializes the doc with the proper default namespace" do
+                assert_match(/<bar:first\ xmlns:bar="http:\/\/tenderlovemaking.com\/"\/>/, @doc.to_xml)
+              end
+            end
+
+            describe "and a child node was added to a new doc with the same default namespaces" do
+              before do
+                new_doc = Nokogiri::XML %Q{<newroot xmlns="http://tenderlovemaking.com/"/>}
+                assert node = @doc.at("//tenderlove:first", tenderlove: "http://tenderlovemaking.com/")
+                new_doc.root.add_child node
+                @doc = new_doc
+              end
+
+              it "serializes the doc with the proper default namespace" do
+                assert_match(/<first>/, @doc.to_xml)
+              end
+            end
+
+            describe "and a child node was added to a new doc without any default namespaces" do
+              before do
+                new_doc = Nokogiri::XML "<newroot/>"
+                assert node = @doc.at("//tenderlove:first", tenderlove: "http://tenderlovemaking.com/")
+                new_doc.root.add_child node
+                @doc = new_doc
+              end
+
+              it "serializes the doc with the proper default namespace" do
+                assert_match(/<first xmlns=\"http:\/\/tenderlovemaking.com\/\">/, @doc.to_xml)
+              end
+            end
+
+            describe "and a child node became the root of a new doc" do
+              before do
+                new_doc = Nokogiri::XML::Document.new
+                assert node = @doc.at("//tenderlove:first", tenderlove: "http://tenderlovemaking.com/")
+                new_doc.root = node
+                @doc = new_doc
+              end
+
+              it "serializes the doc with the proper default namespace" do
+                assert_match(/<first xmlns=\"http:\/\/tenderlovemaking.com\/\">/, @doc.to_xml)
+              end
+            end
           end
 
           describe "given a parent node with a default and non-default namespace" do
@@ -238,7 +302,7 @@ module Nokogiri
                   @node.add_child(@child)
                   assert reparented = @doc.at('//bar:second', "bar" => "http://tenderlovemaking.com/")
                   assert reparented.namespace_definitions.empty?
-                  assert_equal @ns, reparented.namespace
+                  assert_equal @doc.root.namespace, reparented.namespace
                   assert_equal(
                     {
                       "xmlns"     => "http://tenderlovemaking.com/",
@@ -271,17 +335,20 @@ module Nokogiri
             end
 
             describe "and a child with a namespace matching the parent's non-default namespace" do
+              before do
+                @root_ns = @doc.root.namespace_definitions.detect { |x| x.prefix == "foo" }
+              end
+
               describe "set by #namespace=" do
                 before do
-                  @ns = @doc.root.namespace_definitions.detect { |x| x.prefix == "foo" }
-                  @child.namespace = @ns
+                  @child.namespace = @root_ns
                 end
 
                 it "inserts a node that inherits the matching parent namespace" do
                   @node.add_child(@child)
                   assert reparented = @doc.at('//bar:second', "bar" => "http://flavorjon.es/")
                   assert reparented.namespace_definitions.empty?
-                  assert_equal @ns, reparented.namespace
+                  assert_equal @root_ns, reparented.namespace
                   assert_equal(
                     {
                       "xmlns"     => "http://tenderlovemaking.com/",
@@ -301,7 +368,7 @@ module Nokogiri
                   @node.add_child(@child)
                   assert reparented = @doc.at('//bar:second', "bar" => "http://flavorjon.es/")
                   assert reparented.namespace_definitions.empty?
-                  assert_equal @ns, reparented.namespace
+                  assert_equal @root_ns, reparented.namespace
                   assert_equal(
                     {
                       "xmlns"     => "http://tenderlovemaking.com/",
@@ -539,6 +606,19 @@ module Nokogiri
           end
         end
 
+        describe "reparenting and preserving a reference to the original ns" do
+          it "should not cause illegal memory access" do
+            # this test will only cause a failure in valgrind. it
+            # drives out the reason why we can't call xmlFreeNs in
+            # relink_namespace and instead have to root the nsdef.
+            doc = Nokogiri::XML '<root xmlns="http://flavorjon.es/"><envelope /></root>'
+            elem = doc.create_element "package", {"xmlns" => "http://flavorjon.es/"}
+            ns = elem.namespace_definitions
+            doc.at_css("envelope").add_child elem
+            ns.inspect
+          end
+        end
+
         describe "reparenting into another document" do
           it "correctly sets default namespace of a reparented node" do
             # issue described in #391
diff -pruN 1.8.4-1/test/xml/test_node_set.rb 1.10.0+dfsg1-2/test/xml/test_node_set.rb
--- 1.8.4-1/test/xml/test_node_set.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/test/xml/test_node_set.rb	2019-01-04 15:20:12.000000000 +0000
@@ -511,6 +511,19 @@ module Nokogiri
         assert_equal 'employee', @xml.search("//wrapper").first.children[0].name
       end
 
+      def test_wrap_various_node_types
+        xml = '<root><foo>contents</foo></root>'
+        doc = Nokogiri::XML xml
+        nodes = doc.at_css("root").xpath(".//* | .//*/text()") # foo and "contents"
+        nodes.wrap("<wrapper/>")
+        wrappers = doc.css("wrapper")
+        assert_equal "root", wrappers.first.parent.name
+        assert_equal "foo", wrappers.first.children.first.name
+        assert_equal "foo", wrappers.last.parent.name
+        assert wrappers.last.children.first.text?
+        assert_equal "contents", wrappers.last.children.first.text
+      end
+
       def test_wrap_a_fragment
         frag = Nokogiri::XML::DocumentFragment.parse <<-EOXML
           <employees>
@@ -804,6 +817,12 @@ module Nokogiri
         assert_equal node_set.document, new_set.document
         assert new_set.respond_to?(:awesome!)
       end
+
+      def test_each_should_return_self
+        node_set1 = @xml.css("address")
+        node_set2 = node_set1.each {}
+        assert_equal node_set1, node_set2
+      end
     end
   end
 end
diff -pruN 1.8.4-1/test/xml/test_text.rb 1.10.0+dfsg1-2/test/xml/test_text.rb
--- 1.8.4-1/test/xml/test_text.rb	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/test/xml/test_text.rb	2019-01-04 15:20:12.000000000 +0000
@@ -55,6 +55,15 @@ module Nokogiri
           node << Text.new('bar', Document.new)
         }
       end
+
+      def test_wrap
+        xml = '<root><thing><div class="title">important thing</div></thing></root>'
+        doc = Nokogiri::XML(xml)
+        text = doc.at_css("div").children.first
+        text.wrap("<wrapper/>")
+        assert_equal 'wrapper', text.parent.name
+        assert_equal 'wrapper', doc.at_css("div").children.first.name
+      end
     end
   end
 end
diff -pruN 1.8.4-1/.travis.yml 1.10.0+dfsg1-2/.travis.yml
--- 1.8.4-1/.travis.yml	2018-07-10 14:00:55.000000000 +0000
+++ 1.10.0+dfsg1-2/.travis.yml	1970-01-01 00:00:00.000000000 +0000
@@ -1,63 +0,0 @@
-language: ruby
-dist: trusty
-group: beta
-sudo: required
-
-matrix:
-  include:
-  - os: linux
-    rvm: 2.0
-  - os: osx
-    rvm: 2.0
-  - os: linux
-    rvm: 2.1
-  - os: osx
-    rvm: 2.1
-  - os: linux
-    rvm: 2.2.5
-  - os: osx
-    rvm: 2.2.5
-  - os: linux
-    rvm: 2.3.5
-  - os: osx
-    rvm: 2.3.5
-  - os: linux
-    rvm: 2.4.2
-    env:
-      - RUBYOPT="--enable-frozen-string-literal --debug=frozen-string-literal"
-  - os: osx
-    rvm: 2.4.2
-    env:
-      - RUBYOPT="--enable-frozen-string-literal --debug=frozen-string-literal"
-  - os: linux
-    rvm: ruby-head
-    env:
-      - RUBYOPT="--enable-frozen-string-literal --debug=frozen-string-literal"
-  - os: osx
-    rvm: ruby-head
-    env:
-      - RUBYOPT="--enable-frozen-string-literal --debug=frozen-string-literal"
-  - os: linux
-    rvm: jruby-1.7
-  - os: linux
-    rvm: jruby-9.1.15.0
-  - os: linux
-    rvm: rbx-3
-  allow_failures:
-  - rvm: ruby-head
-  fast_finish: true
-
-# notifications:
-#   irc:
-#     channels:
-#       - "chat.freenode.net#nokogiri"
-#     on_success: always
-#     on_failure: always
-#     template:
-#       - "%{repository} (%{branch}:%{commit} by %{author}): %{message} (%{build_url})"
-#     skip_join: true
-
-addons:
-  apt:
-    packages:
-    - haveged
