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