Accessibility: Ensure embed iframes have a title attribute.

Screen reader users rely on the iframe title attribute to describe the contents of iframes. A meaningful title attribute allows to quickly identify the iframe content, so users can determine which iframe to enter and explore in detail or skip if desired.
Note: this is the only case where a title attribute is required for compliance with the W3C Web Content Accessibility Guidelines (WCAG).

- checks for oEmbed response of type `video` or `rich`
- checks if they use an iframe
- fetches the title (if any) from the oEmbed response
- adds the title to the embed iframe

Props bamadesigner, TomHarrigan, swissspidy, jrf, afercia.
Fixes #40245.


git-svn-id: https://develop.svn.wordpress.org/trunk@44942 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Andrea Fercia 2019-03-20 17:21:56 +00:00
parent 30c21d2031
commit 7834856fcb
4 changed files with 161 additions and 1 deletions

View File

@ -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 );

View File

@ -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 = '`<iframe[^>]*?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( '<iframe ', sprintf( '<iframe title="%s" ', esc_attr( $title ) ), $result );
}
/**
* Filters the given oEmbed HTML.
*

View File

@ -0,0 +1,110 @@
<?php
/**
* @group oembed
*/
class Tests_Filter_oEmbed_Iframe_Title_Attribute extends WP_UnitTestCase {
public function data_filter_oembed_iframe_title_attribute() {
return array(
array(
'<p>Foo</p><iframe src=""></iframe><b>Bar</b>',
array(
'type' => 'rich',
),
'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
'<p>Foo</p><iframe src=""></iframe><b>Bar</b>',
),
array(
'<p>Foo</p><iframe src="" title="Hello World"></iframe><b>Bar</b>',
array(
'type' => 'rich',
),
'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
'<p>Foo</p><iframe title="Hello World" src=""></iframe><b>Bar</b>',
),
array(
'<p>Foo</p>',
array(
'type' => 'rich',
'title' => 'Hello World',
),
'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
'<p>Foo</p>',
),
array(
'<p title="Foo">Bar</p>',
array(
'type' => 'rich',
'title' => 'Hello World',
),
'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
'<p title="Foo">Bar</p>',
),
array(
'<p>Foo</p><iframe src=""></iframe><b>Bar</b>',
array(
'type' => 'rich',
'title' => 'Hello World',
),
'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
'<p>Foo</p><iframe title="Hello World" src=""></iframe><b>Bar</b>',
),
array(
'<iframe src="" title="Foo"></iframe>',
array(
'type' => 'rich',
'title' => 'Bar',
),
'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
'<iframe title="Foo" src=""></iframe>',
),
);
}
/**
* @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(
'<iframe title="Foo" src=""></iframe>',
(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( '<iframe title="Baz" src=""></iframe>', $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(
'<p title="Bar">Baz</p><iframe title="Foo" src=""></iframe>',
(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( '<p title="Bar">Baz</p><iframe title="Baz" src=""></iframe>', $actual );
}
public function _filter_oembed_iframe_title_attribute() {
return 'Baz';
}
}

View File

@ -4988,7 +4988,7 @@ mockedApiResponse.oembedProxy = {
"thumbnail_width": 480,
"width": 500,
"thumbnail_height": 360,
"html": "<iframe width=\"500\" height=\"375\" src=\"https://www.youtube.com/embed/i_cVJgIz_Cs?feature=oembed\" frameborder=\"0\" allowfullscreen></iframe>",
"html": "<iframe title=\"No te olvides de poner el Where en el Delete From. (Una cancion para programadores)\" width=\"500\" height=\"375\" src=\"https://www.youtube.com/embed/i_cVJgIz_Cs?feature=oembed\" frameborder=\"0\" allowfullscreen></iframe>",
"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)",