diff --git a/CHANGELOG.md b/CHANGELOG.md index 92f31f4370..e6ea177b53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * [MRI] OpenBSD installation should be a bit easier now. [#1685] (Thanks, @jeremyevans!) * [MRI] Cross-built Windows gems now support Ruby 2.5 * 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] ## Bug fixes diff --git a/lib/nokogiri/xml/document_fragment.rb b/lib/nokogiri/xml/document_fragment.rb index c2c5bec735..57c266179d 100644 --- a/lib/nokogiri/xml/document_fragment.rb +++ b/lib/nokogiri/xml/document_fragment.rb @@ -25,6 +25,15 @@ def initialize document, tags = nil, ctx = nil children.each { |child| child.parent = self } end + def dup + new_document = document.dup + new_fragment = XML::DocumentFragment.new(new_document) + children.each do |child| + child.dup(1, new_document).parent = new_fragment + end + new_fragment + end + ### # return the name for DocumentFragment def name diff --git a/test/xml/test_document_fragment.rb b/test/xml/test_document_fragment.rb index c919ee2068..3ff801e847 100644 --- a/test/xml/test_document_fragment.rb +++ b/test/xml/test_document_fragment.rb @@ -263,6 +263,20 @@ def test_issue_1077_parsing_of_frozen_strings Nokogiri::XML::DocumentFragment.parse(input) # assert_nothing_raised end + def test_dup_creates_tree_with_identical_structure + original = Nokogiri::XML::DocumentFragment.parse("

hello

") + duplicate = original.dup + assert_equal original.to_html, duplicate.to_html + end + + def test_dup_creates_mutable_tree + original = Nokogiri::XML::DocumentFragment.parse("

hello

") + duplicate = original.dup + duplicate.at_css("div").add_child("hello there") + 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