From bbf29b7b78fc9d123310da70a5d8273be307cdf3 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 5 Aug 2023 21:53:45 +0200 Subject: [PATCH 1/2] Add tests with wrong output --- ext/dom/tests/replaceWith_no_parent.phpt | 28 ++++++++++++ .../replaceWith_non_viable_next_sibling.phpt | 45 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 ext/dom/tests/replaceWith_no_parent.phpt create mode 100644 ext/dom/tests/replaceWith_non_viable_next_sibling.phpt diff --git a/ext/dom/tests/replaceWith_no_parent.phpt b/ext/dom/tests/replaceWith_no_parent.phpt new file mode 100644 index 0000000000000..988fd9587830e --- /dev/null +++ b/ext/dom/tests/replaceWith_no_parent.phpt @@ -0,0 +1,28 @@ +--TEST-- +replaceWith() without a parent +--EXTENSIONS-- +dom +--FILE-- +loadXML(<< + + + +XML); + +$container = $doc->documentElement; +$child = $container->firstElementChild; + +$test = $doc->createElement('foo'); +$test->replaceWith($child); +echo $doc->saveXML(); +echo $doc->saveXML($test); +?> +--EXPECTF-- +Fatal error: Uncaught DOMException: Not Found Error in %s:%d +Stack trace: +#0 %s(%d): DOMElement->replaceWith(Object(DOMElement)) +#1 {main} + thrown in %s on line %d diff --git a/ext/dom/tests/replaceWith_non_viable_next_sibling.phpt b/ext/dom/tests/replaceWith_non_viable_next_sibling.phpt new file mode 100644 index 0000000000000..c36e29a0287ca --- /dev/null +++ b/ext/dom/tests/replaceWith_non_viable_next_sibling.phpt @@ -0,0 +1,45 @@ +--TEST-- +replaceWith() with a non-variable next sibling +--EXTENSIONS-- +dom +--FILE-- +loadXML(<< + + + + + +XML); + +$container = $doc->documentElement; +$child = $container->firstElementChild; +$alone = $child->firstElementChild; + +$child->after($alone); +echo $doc->saveXML(); +$child->replaceWith($alone); +echo $doc->saveXML(); +?> +--EXPECT-- + + + + + + + + + + + +================================================================= +==1151863==ERROR: LeakSanitizer: detected memory leaks + +Direct leak of 120 byte(s) in 1 object(s) allocated from: + #0 0x7fc122ee1369 in __interceptor_malloc /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:69 + #1 0x7fc122d02bd0 in xmlNewNodeEatName /home/niels/libxml2/tree.c:2317 + +SUMMARY: AddressSanitizer: 120 byte(s) leaked in 1 allocation(s). From 65efb490d60847f31a08e525f5d20f6edda0af1c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 5 Aug 2023 21:54:04 +0200 Subject: [PATCH 2/2] Fix parent check and viable next sibling search for replaceWith --- ext/dom/parentnode.c | 22 +++++++++++++++++-- ext/dom/tests/replaceWith_no_parent.phpt | 12 +++++----- .../replaceWith_non_viable_next_sibling.phpt | 11 +--------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index 8467fbf0603a4..758014ddedd13 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -545,21 +545,39 @@ void dom_child_node_remove(dom_object *context) void dom_child_replace_with(dom_object *context, zval *nodes, int nodesc) { + /* Spec link: https://dom.spec.whatwg.org/#dom-childnode-replacewith*/ + xmlNodePtr child = dom_object_get_node(context); + + /* Spec step 1 */ xmlNodePtr parentNode = child->parent; + /* Spec step 2 */ + if (!parentNode) { + return; + } int stricterror = dom_get_strict_error(context->document); if (UNEXPECTED(dom_child_removal_preconditions(child, stricterror) != SUCCESS)) { return; } - xmlNodePtr insertion_point = child->next; + /* Spec step 3: find first following child not in nodes; otherwise null */ + xmlNodePtr viable_next_sibling = child->next; + while (viable_next_sibling) { + if (!dom_is_node_in_list(nodes, nodesc, viable_next_sibling)) { + break; + } + viable_next_sibling = viable_next_sibling->next; + } + /* Spec step 4: convert nodes into fragment */ xmlNodePtr fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); if (UNEXPECTED(fragment == NULL)) { return; } + /* Spec step 5: perform the replacement */ + xmlNodePtr newchild = fragment->children; xmlDocPtr doc = parentNode->doc; @@ -571,7 +589,7 @@ void dom_child_replace_with(dom_object *context, zval *nodes, int nodesc) if (newchild) { xmlNodePtr last = fragment->last; - dom_pre_insert(insertion_point, parentNode, newchild, fragment); + dom_pre_insert(viable_next_sibling, parentNode, newchild, fragment); dom_fragment_assign_parent_node(parentNode, fragment); dom_reconcile_ns_list(doc, newchild, last); diff --git a/ext/dom/tests/replaceWith_no_parent.phpt b/ext/dom/tests/replaceWith_no_parent.phpt index 988fd9587830e..923df4affbc5e 100644 --- a/ext/dom/tests/replaceWith_no_parent.phpt +++ b/ext/dom/tests/replaceWith_no_parent.phpt @@ -20,9 +20,9 @@ $test->replaceWith($child); echo $doc->saveXML(); echo $doc->saveXML($test); ?> ---EXPECTF-- -Fatal error: Uncaught DOMException: Not Found Error in %s:%d -Stack trace: -#0 %s(%d): DOMElement->replaceWith(Object(DOMElement)) -#1 {main} - thrown in %s on line %d +--EXPECT-- + + + + + diff --git a/ext/dom/tests/replaceWith_non_viable_next_sibling.phpt b/ext/dom/tests/replaceWith_non_viable_next_sibling.phpt index c36e29a0287ca..61458d6cb72dc 100644 --- a/ext/dom/tests/replaceWith_non_viable_next_sibling.phpt +++ b/ext/dom/tests/replaceWith_non_viable_next_sibling.phpt @@ -32,14 +32,5 @@ echo $doc->saveXML(); - + - -================================================================= -==1151863==ERROR: LeakSanitizer: detected memory leaks - -Direct leak of 120 byte(s) in 1 object(s) allocated from: - #0 0x7fc122ee1369 in __interceptor_malloc /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:69 - #1 0x7fc122d02bd0 in xmlNewNodeEatName /home/niels/libxml2/tree.c:2317 - -SUMMARY: AddressSanitizer: 120 byte(s) leaked in 1 allocation(s).