diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 631525d0e2..4ab87de558 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -573,6 +573,7 @@ add_filter( 'the_excerpt_embed', 'shortcode_unautop' ); add_filter( 'the_excerpt_embed', 'wp_embed_excerpt_attachment' ); add_filter( 'oembed_dataparse', 'wp_filter_oembed_result', 10, 3 ); +add_filter( 'oembed_dataparse', 'wp_filter_oembed_iframe_title_attribute', 20, 3 ); add_filter( 'oembed_response_data', 'get_oembed_response_data_rich', 10, 4 ); add_filter( 'pre_oembed_result', 'wp_filter_pre_oembed_result', 10, 3 ); diff --git a/src/wp-includes/embed.php b/src/wp-includes/embed.php index 921b693c43..b8c8e5149a 100644 --- a/src/wp-includes/embed.php +++ b/src/wp-includes/embed.php @@ -780,6 +780,55 @@ function _oembed_create_xml( $data, $node = null ) { return $node->asXML(); } +/** + * Filters the given oEmbed HTML to make sure iframes have a title attribute. + * + * @since 5.2.0 + * + * @param string $result The oEmbed HTML result. + * @param object $data A data object result from an oEmbed provider. + * @param string $url The URL of the content to be embedded. + * @return string The filtered oEmbed result. + */ +function wp_filter_oembed_iframe_title_attribute( $result, $data, $url ) { + if ( false === $result || ! in_array( $data->type, array( 'rich', 'video' ) ) ) { + return $result; + } + + $title = ! empty( $data->title ) ? $data->title : ''; + + $pattern = '`]*?title=(\\\\\'|\\\\"|[\'"])([^>]*?)\1`i'; + $has_title_attr = preg_match( $pattern, $result, $matches ); + + if ( $has_title_attr && ! empty( $matches[2] ) ) { + $title = $matches[2]; + } + + /** + * Filters the title attribute of the given oEmbed HTML iframe. + * + * @since 5.2.0 + * + * @param string $title The title attribute. + * @param string $result The oEmbed HTML result. + * @param object $data A data object result from an oEmbed provider. + * @param string $url The URL of the content to be embedded. + */ + $title = apply_filters( 'oembed_iframe_title_attribute', $title, $result, $data, $url ); + + if ( '' === $title ) { + return $result; + } + + if ( $has_title_attr ) { + // Remove the old title, $matches[1]: quote, $matches[2]: title attribute value. + $result = str_replace( ' title=' . $matches[1] . $matches[2] . $matches[1], '', $result ); + } + + return str_ireplace( 'Bar', + array( + 'type' => 'rich', + ), + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + '

Foo

Bar', + ), + array( + '

Foo

Bar', + array( + 'type' => 'rich', + ), + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + '

Foo

Bar', + ), + array( + '

Foo

', + array( + 'type' => 'rich', + 'title' => 'Hello World', + ), + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + '

Foo

', + ), + array( + '

Bar

', + array( + 'type' => 'rich', + 'title' => 'Hello World', + ), + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + '

Bar

', + ), + array( + '

Foo

Bar', + array( + 'type' => 'rich', + 'title' => 'Hello World', + ), + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + '

Foo

Bar', + ), + array( + '', + array( + 'type' => 'rich', + 'title' => 'Bar', + ), + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + '', + ), + ); + } + + /** + * @dataProvider data_filter_oembed_iframe_title_attribute + */ + public function test_oembed_iframe_title_attribute( $html, $oembed_data, $url, $expected ) { + $actual = wp_filter_oembed_iframe_title_attribute( $html, (object) $oembed_data, $url ); + + $this->assertSame( $expected, $actual ); + } + + public function test_filter_oembed_iframe_title_attribute() { + add_filter( 'oembed_iframe_title_attribute', array( $this, '_filter_oembed_iframe_title_attribute' ) ); + + $actual = wp_filter_oembed_iframe_title_attribute( + '', + (object) array( + 'type' => 'rich', + 'title' => 'Bar', + ), + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' + ); + + remove_filter( 'oembed_iframe_title_attribute', array( $this, '_filter_oembed_iframe_title_attribute' ) ); + + $this->assertSame( '', $actual ); + } + + public function test_filter_oembed_iframe_title_attribute_does_not_modify_other_tags() { + add_filter( 'oembed_iframe_title_attribute', array( $this, '_filter_oembed_iframe_title_attribute' ) ); + + $actual = wp_filter_oembed_iframe_title_attribute( + '

Baz

', + (object) array( + 'type' => 'rich', + 'title' => 'Bar', + ), + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' + ); + + remove_filter( 'oembed_iframe_title_attribute', array( $this, '_filter_oembed_iframe_title_attribute' ) ); + + $this->assertSame( '

Baz

', $actual ); + } + + public function _filter_oembed_iframe_title_attribute() { + return 'Baz'; + } +} diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 2ad1feb047..d895d750dc 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -4988,7 +4988,7 @@ mockedApiResponse.oembedProxy = { "thumbnail_width": 480, "width": 500, "thumbnail_height": 360, - "html": "", + "html": "", "author_name": "Jorge Rubira Santos", "thumbnail_url": "https://i.ytimg.com/vi/i_cVJgIz_Cs/hqdefault.jpg", "title": "No te olvides de poner el Where en el Delete From. (Una cancion para programadores)",