@@ -1359,14 +1359,14 @@ zend_result dom_html_document_encoding_write(dom_object *obj, zval *newval)
1359
1359
return SUCCESS ;
1360
1360
}
1361
1361
1362
- static const xmlNode * dom_html_document_element_read_raw (const xmlDoc * docp , bool (* accept )(const xmlChar * ))
1362
+ static xmlNodePtr dom_html_document_element_read_raw (const xmlDoc * docp , bool (* accept )(const xmlChar * ))
1363
1363
{
1364
1364
const xmlNode * root = xmlDocGetRootElement (docp );
1365
1365
if (root == NULL || !(php_dom_ns_is_fast (root , php_dom_ns_is_html_magic_token ) && xmlStrEqual (root -> name , BAD_CAST "html" ))) {
1366
1366
return NULL ;
1367
1367
}
1368
1368
1369
- const xmlNode * cur = root -> children ;
1369
+ xmlNodePtr cur = root -> children ;
1370
1370
while (cur != NULL ) {
1371
1371
if (cur -> type == XML_ELEMENT_NODE && php_dom_ns_is_fast (cur , php_dom_ns_is_html_magic_token ) && accept (cur -> name )) {
1372
1372
return cur ;
@@ -1476,9 +1476,9 @@ static zend_string *dom_get_child_text_content(const xmlNode *node)
1476
1476
}
1477
1477
1478
1478
/* https://html.spec.whatwg.org/#the-title-element-2 */
1479
- static const xmlNode * dom_get_title_element (const xmlDoc * doc )
1479
+ static xmlNodePtr dom_get_title_element (const xmlDoc * doc )
1480
1480
{
1481
- const xmlNode * node = doc -> children ;
1481
+ xmlNodePtr node = doc -> children ;
1482
1482
1483
1483
while (node != NULL ) {
1484
1484
if (node -> type == XML_ELEMENT_NODE ) {
@@ -1496,11 +1496,28 @@ static const xmlNode *dom_get_title_element(const xmlDoc *doc)
1496
1496
return node ;
1497
1497
}
1498
1498
1499
+ /* The subtle difference is that this is about the direct title descendant of the svg element,
1500
+ * whereas the html variant of this function is about the first in-tree title element. */
1501
+ static xmlNodePtr dom_get_svg_title_element (xmlNodePtr svg )
1502
+ {
1503
+ xmlNodePtr cur = svg -> children ;
1504
+
1505
+ while (cur != NULL ) {
1506
+ if (cur -> type == XML_ELEMENT_NODE
1507
+ && php_dom_ns_is_fast (cur , php_dom_ns_is_svg_magic_token ) && xmlStrEqual (cur -> name , BAD_CAST "title" )) {
1508
+ break ;
1509
+ }
1510
+ cur = cur -> next ;
1511
+ }
1512
+
1513
+ return cur ;
1514
+ }
1515
+
1499
1516
/* https://html.spec.whatwg.org/#document.title */
1500
1517
zend_result dom_html_document_title_read (dom_object * obj , zval * retval )
1501
1518
{
1502
1519
DOM_PROP_NODE (const xmlDoc * , docp , obj );
1503
- const xmlNode * root = xmlDocGetRootElement (docp );
1520
+ xmlNodePtr root = xmlDocGetRootElement (docp );
1504
1521
1505
1522
if (root == NULL ) {
1506
1523
ZVAL_EMPTY_STRING (retval );
@@ -1512,15 +1529,9 @@ zend_result dom_html_document_title_read(dom_object *obj, zval *retval)
1512
1529
/* 1. If the document element is an SVG svg element,
1513
1530
* then let value be the child text content of the first SVG title element that is a child of the document element. */
1514
1531
if (php_dom_ns_is_fast (root , php_dom_ns_is_svg_magic_token ) && xmlStrEqual (root -> name , BAD_CAST "svg" )) {
1515
- const xmlNode * cur = root -> children ;
1516
-
1517
- while (cur != NULL ) {
1518
- if (cur -> type == XML_ELEMENT_NODE
1519
- && php_dom_ns_is_fast (cur , php_dom_ns_is_svg_magic_token ) && xmlStrEqual (cur -> name , BAD_CAST "title" )) {
1520
- value = dom_get_child_text_content (cur );
1521
- break ;
1522
- }
1523
- cur = cur -> next ;
1532
+ const xmlNode * title = dom_get_svg_title_element (root );
1533
+ if (title != NULL ) {
1534
+ value = dom_get_child_text_content (title );
1524
1535
}
1525
1536
} else {
1526
1537
/* 2. Otherwise, let value be the child text content of the title element,
@@ -1540,4 +1551,95 @@ zend_result dom_html_document_title_read(dom_object *obj, zval *retval)
1540
1551
return SUCCESS ;
1541
1552
}
1542
1553
1554
+ static void dom_string_replace_all (xmlDocPtr docp , xmlNodePtr element , zval * zv )
1555
+ {
1556
+ dom_remove_all_children (element );
1557
+ xmlNode * text = xmlNewDocText (docp , BAD_CAST Z_STRVAL_P (zv ));
1558
+ xmlAddChild (element , text );
1559
+ }
1560
+
1561
+ /* https://html.spec.whatwg.org/#document.title */
1562
+ zend_result dom_html_document_title_write (dom_object * obj , zval * newval )
1563
+ {
1564
+ DOM_PROP_NODE (xmlDocPtr , docp , obj );
1565
+ xmlNodePtr root = xmlDocGetRootElement (docp );
1566
+
1567
+ if (root == NULL ) {
1568
+ return SUCCESS ;
1569
+ }
1570
+
1571
+ /* If the document element is an SVG svg element */
1572
+ if (php_dom_ns_is_fast (root , php_dom_ns_is_svg_magic_token ) && xmlStrEqual (root -> name , BAD_CAST "svg" )) {
1573
+ /* 1. If there is an SVG title element that is a child of the document element, let element be the first such element. */
1574
+ xmlNodePtr element = dom_get_svg_title_element (root );
1575
+
1576
+ /* 2. Otherwise: */
1577
+ if (element == NULL ) {
1578
+ /* 2.1. Let element be the result of creating an element given the document element's node document,
1579
+ * title, and the SVG namespace. */
1580
+
1581
+ /* Annoyingly, we must create it in the svg namespace _without_ prefix... */
1582
+ xmlNsPtr ns = root -> ns ;
1583
+ if (ns -> prefix != NULL ) {
1584
+ /* Slow path... */
1585
+ php_dom_libxml_ns_mapper * ns_mapper = php_dom_get_ns_mapper (obj );
1586
+ zend_string * href = ZSTR_INIT_LITERAL (DOM_SVG_NS_URI , false);
1587
+ ns = php_dom_libxml_ns_mapper_get_ns (ns_mapper , zend_empty_string , href );
1588
+ zend_string_release_ex (href , false);
1589
+ }
1590
+
1591
+ element = xmlNewDocNode (docp , ns , BAD_CAST "title" , NULL );
1592
+ if (UNEXPECTED (element == NULL )) {
1593
+ php_dom_throw_error (INVALID_STATE_ERR , true);
1594
+ return FAILURE ;
1595
+ }
1596
+
1597
+ /* 2.2. Insert element as the first child of the document element. */
1598
+ if (root -> children == NULL ) {
1599
+ root -> last = element ;
1600
+ } else {
1601
+ element -> next = root -> children ;
1602
+ root -> children -> prev = element ;
1603
+ }
1604
+ root -> children = element ;
1605
+ element -> parent = root ;
1606
+ }
1607
+
1608
+ /* 3. String replace all with the given value within element. */
1609
+ dom_string_replace_all (docp , element , newval );
1610
+ }
1611
+ /* If the document element is in the HTML namespace */
1612
+ else if (php_dom_ns_is_fast (root , php_dom_ns_is_html_magic_token )) {
1613
+ /* 1. If the title element is null and the head element is null, then return. */
1614
+ xmlNodePtr title = dom_get_title_element (docp );
1615
+ xmlNodePtr head = dom_html_document_element_read_raw (docp , dom_accept_head_name );
1616
+ if (title == NULL && head == NULL ) {
1617
+ return SUCCESS ;
1618
+ }
1619
+
1620
+ /* 2. If the title element is non-null, let element be the title element. */
1621
+ xmlNodePtr element = title ;
1622
+
1623
+ /* 3. Otherwise: */
1624
+ if (element == NULL ) {
1625
+ /* 3.1. Let element be the result of creating an element given the document element's node document, title,
1626
+ * and the HTML namespace. */
1627
+ php_dom_libxml_ns_mapper * ns_mapper = php_dom_get_ns_mapper (obj );
1628
+ element = xmlNewDocNode (docp , php_dom_libxml_ns_mapper_ensure_html_ns (ns_mapper ), BAD_CAST "title" , NULL );
1629
+ if (UNEXPECTED (element == NULL )) {
1630
+ php_dom_throw_error (INVALID_STATE_ERR , true);
1631
+ return FAILURE ;
1632
+ }
1633
+
1634
+ /* 3.2. Append element to the head element. */
1635
+ xmlAddChild (head , element );
1636
+ }
1637
+
1638
+ /* 4. String replace all with the given value within element. */
1639
+ dom_string_replace_all (docp , element , newval );
1640
+ }
1641
+
1642
+ return SUCCESS ;
1643
+ }
1644
+
1543
1645
#endif /* HAVE_LIBXML && HAVE_DOM */
0 commit comments