Fix viable next sibling search for replaceWith

Closes GH-11888.
This commit is contained in:
Niels Dossche 2023-08-05 21:53:45 +02:00
parent dddd309da4
commit 815b5ad501
3 changed files with 59 additions and 2 deletions

1
NEWS
View File

@ -20,6 +20,7 @@ PHP NEWS
. Fix manually calling __construct() on DOM classes. (nielsdos)
. Fixed bug GH-11830 (ParentNode methods should perform their checks
upfront). (nielsdos)
. Fix viable next sibling search for replaceWith. (nielsdos)
- FFI:
. Fix leaking definitions when using FFI::cdef()->new(...). (ilutov)

View File

@ -550,25 +550,45 @@ 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) {
int stricterror = dom_get_strict_error(context->document);
php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
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;
}
if (UNEXPECTED(dom_sanity_check_node_list_for_insertion(context->document, parentNode, nodes, nodesc) != SUCCESS)) {
return;
}
/* 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;
@ -580,7 +600,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);

View File

@ -0,0 +1,36 @@
--TEST--
replaceWith() with a non-viable next sibling
--EXTENSIONS--
dom
--FILE--
<?php
$doc = new DOMDocument;
$doc->loadXML(<<<XML
<?xml version="1.0"?>
<container>
<child>
<alone/>
</child>
</container>
XML);
$container = $doc->documentElement;
$child = $container->firstElementChild;
$alone = $child->firstElementChild;
$child->after($alone);
echo $doc->saveXML();
$child->replaceWith($alone);
echo $doc->saveXML();
?>
--EXPECT--
<?xml version="1.0"?>
<container>
<child>
</child><alone/>
</container>
<?xml version="1.0"?>
<container>
<alone/>
</container>