Skip to content

Commit 96d718a

Browse files
authored
fix(dom): implement cloneNode() correctly for DocumentFragment (#249)
1 parent a48f9f0 commit 96d718a

File tree

4 files changed

+86
-0
lines changed

4 files changed

+86
-0
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<html>
2+
<head>
3+
<meta charset="utf-8" />
4+
<title>DocumentFragment cloneNode Test</title>
5+
<style>
6+
body { font-family: monospace; padding: 20px; }
7+
.result { background: #f0f0f0; padding: 10px; margin: 10px 0; border-radius: 5px; }
8+
.pass { background: #e6ffe6; }
9+
.fail { background: #ffe6e6; }
10+
</style>
11+
</head>
12+
<body>
13+
<h1>DocumentFragment cloneNode Test</h1>
14+
<div id="results"></div>
15+
16+
<script>
17+
function test(name, condition) {
18+
const div = document.createElement('div');
19+
div.className = `result ${condition ? 'pass' : 'fail'}`;
20+
div.textContent = `${condition ? 'PASS' : 'FAIL'}: ${name}`;
21+
document.getElementById('results').appendChild(div);
22+
console.log(`${condition ? 'PASS' : 'FAIL'}: ${name}`);
23+
}
24+
25+
console.log('Testing DocumentFragment cloneNode...');
26+
27+
// Create a DocumentFragment
28+
const fragment = document.createDocumentFragment();
29+
console.log('Fragment nodeType:', fragment.nodeType);
30+
console.log('Fragment nodeName:', fragment.nodeName);
31+
32+
// Add some content to the fragment
33+
const div = document.createElement('div');
34+
div.textContent = 'Hello';
35+
const span = document.createElement('span');
36+
span.textContent = 'World';
37+
fragment.appendChild(div);
38+
fragment.appendChild(span);
39+
40+
console.log('Original fragment children count:', fragment.childNodes.length);
41+
42+
// Test shallow clone
43+
const shallowClone = fragment.cloneNode(false);
44+
test('Shallow clone preserves DocumentFragment type', shallowClone.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
45+
test('Shallow clone has correct nodeName', shallowClone.nodeName === '#document-fragment');
46+
test('Shallow clone has no children', shallowClone.childNodes.length === 0);
47+
test('Shallow clone is different instance', shallowClone !== fragment);
48+
49+
// Test deep clone
50+
const deepClone = fragment.cloneNode(true);
51+
test('Deep clone preserves DocumentFragment type', deepClone.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
52+
test('Deep clone has correct nodeName', deepClone.nodeName === '#document-fragment');
53+
test('Deep clone has same number of children', deepClone.childNodes.length === fragment.childNodes.length);
54+
test('Deep clone children are different instances', deepClone.childNodes[0] !== fragment.childNodes[0]);
55+
test('Deep clone children have same content', deepClone.childNodes[0].textContent === fragment.childNodes[0].textContent);
56+
57+
// Test DocumentFragment specific methods on cloned fragment
58+
if (deepClone.querySelector) {
59+
test('Cloned fragment supports querySelector', typeof deepClone.querySelector === 'function');
60+
}
61+
if (deepClone.childElementCount !== undefined) {
62+
test('Cloned fragment supports childElementCount', deepClone.childElementCount === 2);
63+
}
64+
65+
console.log('DocumentFragment cloneNode tests completed');
66+
</script>
67+
</body>
68+
</html>

src/client/dom/document_fragment.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ namespace dom
2020
{
2121
}
2222

23+
shared_ptr<Node> DocumentFragment::CloneDocumentFragment(shared_ptr<Node> srcFragment)
24+
{
25+
auto fragmentNode = dynamic_pointer_cast<DocumentFragment>(srcFragment);
26+
assert(fragmentNode != nullptr && "The source node is not a document fragment.");
27+
return make_shared<DocumentFragment>(*fragmentNode);
28+
}
29+
2330
size_t DocumentFragment::childElementCount() const
2431
{
2532
size_t count = 0;

src/client/dom/document_fragment.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ namespace dom
1717
DocumentFragment(const DocumentFragment &other);
1818
~DocumentFragment() = default;
1919

20+
/**
21+
* Clone the given document fragment and return a new document fragment with the same properties.
22+
*
23+
* @param srcFragment The document fragment to clone.
24+
* @returns The cloned document fragment in `std::shared_ptr<Node>`.
25+
*/
26+
static std::shared_ptr<Node> CloneDocumentFragment(std::shared_ptr<Node> srcFragment);
27+
2028
public:
2129
bool isDocumentFragment() const override
2230
{

src/client/dom/node.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ namespace dom
7575
, connected(false)
7676
, nodeName(other.nodeName)
7777
, nodeType(other.nodeType)
78+
, nodeValue_(other.nodeValue_)
7879
, ownerDocument(other.ownerDocument)
7980
, parentNode(weak_ptr<Node>())
8081
, childNodes({})
@@ -210,6 +211,8 @@ namespace dom
210211
cloned = Comment::CloneComment(shared_from_this());
211212
else if (nodeType == NodeType::TEXT_NODE)
212213
cloned = Text::CloneText(shared_from_this());
214+
else if (nodeType == NodeType::DOCUMENT_FRAGMENT_NODE)
215+
cloned = DocumentFragment::CloneDocumentFragment(shared_from_this());
213216
else
214217
cloned = make_shared<Node>(*this);
215218

0 commit comments

Comments
 (0)