Skip to content

Commit 4129a3a

Browse files
committed
HTML API: Check tag namespace in ::set_modifiable_text().
The method should only apply to special "atomic" HTML tags like `SCRIPT` or `TEXTAREA`. `::set_modifiable_text()` should not apply to tags with the same name in other namespaces. Developed in #11083. Props jonsurrell, dmsnell, westonruter. Fixes #64751. git-svn-id: https://develop.svn.wordpress.org/trunk@61796 602fd350-edb4-49c9-b593-d223f7449a82
1 parent b5d48d4 commit 4129a3a

3 files changed

Lines changed: 123 additions & 5 deletions

File tree

src/wp-includes/html-api/class-wp-html-tag-processor.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3817,7 +3817,14 @@ public function set_modifiable_text( string $plaintext_content ): bool {
38173817
return true;
38183818
}
38193819

3820-
if ( self::STATE_MATCHED_TAG !== $this->parser_state ) {
3820+
/*
3821+
* The rest of this function handles modifiable text for special "atomic" HTML elements.
3822+
* Only tags in the HTML namespace should be processed.
3823+
*/
3824+
if (
3825+
self::STATE_MATCHED_TAG !== $this->parser_state ||
3826+
'html' !== $this->get_namespace()
3827+
) {
38213828
return false;
38223829
}
38233830

tests/phpunit/tests/html-api/wpHtmlProcessorModifiableText.php

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class Tests_HtmlApi_WpHtmlProcessorModifiableText extends WP_UnitTestCase {
2424
* @param string $set_text Text to set.
2525
* @param string $expected_html Expected HTML output.
2626
*/
27-
public function test_modifiable_text_special_textarea( string $set_text, string $expected_html ) {
27+
public function test_modifiable_text_special_textarea( string $set_text, string $expected_html ): void {
2828
$processor = WP_HTML_Processor::create_fragment( '<textarea></textarea>' );
2929
$processor->next_token();
3030
$processor->set_modifiable_text( $set_text );
@@ -50,9 +50,9 @@ public function test_modifiable_text_special_textarea( string $set_text, string
5050
/**
5151
* Data provider.
5252
*
53-
* @return array[]
53+
* @return array<string, array{0: string, 1: string}>
5454
*/
55-
public static function data_modifiable_text_special_textarea() {
55+
public static function data_modifiable_text_special_textarea(): array {
5656
return array(
5757
'Leading newline' => array(
5858
"\nAFTER NEWLINE",
@@ -68,4 +68,59 @@ public static function data_modifiable_text_special_textarea() {
6868
),
6969
);
7070
}
71+
72+
/**
73+
* Ensures that `set_modifiable_text()` returns false for elements that are not special "atomic" elements.
74+
*
75+
* This includes atomic-like foreign elements (`<svg><textarea>`) as well as arbitrary HTML
76+
* elements (`<div>`).
77+
*
78+
* @ticket 64751
79+
* @dataProvider data_set_modifiable_fails_non_atomic_tags
80+
*/
81+
public function test_set_modifiable_fails_non_atomic_tags(
82+
string $html,
83+
string $target_tag
84+
): void {
85+
$processor = WP_HTML_Processor::create_fragment( $html );
86+
$this->assertNotNull( $processor, 'Failed to create a processor.' );
87+
$this->assertTrue( $processor->next_tag( $target_tag ), 'Failed to find target tag.' );
88+
$this->assertFalse(
89+
$processor->set_modifiable_text( 'test' ),
90+
"set_modifiable_text() should return false on {$processor->get_namespace()}:{$processor->get_qualified_tag_name()}."
91+
);
92+
$this->assertSame(
93+
$html,
94+
$processor->get_updated_html(),
95+
'HTML should be unchanged after rejected set_modifiable_text().'
96+
);
97+
}
98+
99+
/**
100+
* Data provider.
101+
*
102+
* @return array<string, array{0: string, 1: string, 2: string}>
103+
*/
104+
public static function data_set_modifiable_fails_non_atomic_tags(): array {
105+
return array(
106+
// Plain HTML tags.
107+
'html DIV' => array( '<div>', 'DIV' ),
108+
109+
// Foreign elements with non-atomic tags.
110+
'svg PATH' => array( '<svg><path></path></svg>', 'PATH' ),
111+
'svg PATH (self-closing)' => array( '<svg><path /></svg>', 'PATH' ),
112+
'math MTEXT' => array( '<math><mtext></mtext></math>', 'MTEXT' ),
113+
'math MSPACE (self-closing)' => array( '<math><mspace /></math>', 'MSPACE' ),
114+
115+
// Foreign elements with atomic-like tags.
116+
'svg TEXTAREA' => array( '<svg><textarea></textarea></svg>', 'TEXTAREA' ),
117+
'svg TITLE' => array( '<svg><title></title></svg>', 'TITLE' ),
118+
'svg STYLE' => array( '<svg><style></style></svg>', 'STYLE' ),
119+
'svg SCRIPT' => array( '<svg><script></script></svg>', 'SCRIPT' ),
120+
'math TEXTAREA' => array( '<math><textarea></textarea></math>', 'TEXTAREA' ),
121+
'math TITLE' => array( '<math><title></title></math>', 'TITLE' ),
122+
'math STYLE' => array( '<math><style></style></math>', 'STYLE' ),
123+
'math SCRIPT' => array( '<math><script></script></math>', 'SCRIPT' ),
124+
);
125+
}
71126
}

tests/phpunit/tests/html-api/wpHtmlTagProcessorModifiableText.php

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ public function test_json_auto_escaping() {
644644
*
645645
* @ticket 64609
646646
*/
647-
public function test_modifiable_text_special_textarea() {
647+
public function test_modifiable_text_special_textarea(): void {
648648
$processor = new WP_HTML_Tag_Processor( '<textarea></textarea>' );
649649
$processor->next_token();
650650
$processor->set_modifiable_text( "\nAFTER NEWLINE" );
@@ -664,4 +664,60 @@ public function test_modifiable_text_special_textarea() {
664664
'Should have preserved the leading newline in the content.'
665665
);
666666
}
667+
668+
/**
669+
* Ensures that `set_modifiable_text()` returns false for elements that are
670+
* not special "atomic" elements.
671+
*
672+
* This includes atomic-like foreign elements as well as non-atomic foreign elements.
673+
*
674+
* @ticket 64751
675+
* @dataProvider data_set_modifiable_fails_non_atomic_tags
676+
*/
677+
public function test_set_modifiable_fails_non_atomic_tags(
678+
string $html,
679+
string $parsing_namespace,
680+
string $target_tag
681+
): void {
682+
$processor = new WP_HTML_Tag_Processor( $html );
683+
$processor->change_parsing_namespace( $parsing_namespace );
684+
$this->assertTrue( $processor->next_tag( $target_tag ), 'Failed to find target tag.' );
685+
$this->assertFalse(
686+
$processor->set_modifiable_text( 'test' ),
687+
"set_modifiable_text() should return false for {$parsing_namespace}:{$target_tag}."
688+
);
689+
$this->assertSame(
690+
$html,
691+
$processor->get_updated_html(),
692+
'HTML should be unchanged after rejected set_modifiable_text().'
693+
);
694+
}
695+
696+
/**
697+
* Data provider.
698+
*
699+
* @return array<string, array{0: string, 1: string, 2: string}>
700+
*/
701+
public static function data_set_modifiable_fails_non_atomic_tags(): array {
702+
return array(
703+
// Plain HTML tags.
704+
'html DIV' => array( '<div>', 'html', 'DIV' ),
705+
706+
// Foreign elements with non-atomic tags.
707+
'svg PATH' => array( '<path></path>', 'svg', 'PATH' ),
708+
'svg PATH (self-closing)' => array( '<path />', 'svg', 'PATH' ),
709+
'math MTEXT' => array( '<mtext></mtext>', 'math', 'MTEXT' ),
710+
'math MSPACE (self-closing)' => array( '<mspace />', 'math', 'MSPACE' ),
711+
712+
// Foreign elements with atomic-like tags.
713+
'svg TEXTAREA' => array( '<textarea></textarea>', 'svg', 'TEXTAREA' ),
714+
'svg TITLE' => array( '<title></title>', 'svg', 'TITLE' ),
715+
'svg STYLE' => array( '<style></style>', 'svg', 'STYLE' ),
716+
'svg SCRIPT' => array( '<script></script>', 'svg', 'SCRIPT' ),
717+
'math TEXTAREA' => array( '<textarea></textarea>', 'math', 'TEXTAREA' ),
718+
'math TITLE' => array( '<title></title>', 'math', 'TITLE' ),
719+
'math STYLE' => array( '<style></style>', 'math', 'STYLE' ),
720+
'math SCRIPT' => array( '<script></script>', 'math', 'SCRIPT' ),
721+
);
722+
}
667723
}

0 commit comments

Comments
 (0)