diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index b9b98dd..98b7c67 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -52,7 +52,7 @@ function test_wp_lazy_load_content_media() { $content_unfiltered = sprintf( $content, $img, $img_xhtml, $img_html5, $iframe, $img_eager ); $content_filtered = sprintf( $content, $lazy_img, $lazy_img_xhtml, $lazy_img_html5, $iframe, $img_eager ); - $this->assertSame( $content_filtered, wp_add_lazy_load_attributes( $content_unfiltered ) ); + $this->assertSame( $content_filtered, _wp_filter_html_tags( $content_unfiltered ) ); } /** @@ -75,10 +75,22 @@ function test_wp_lazy_load_content_media_opted_in() { $content_unfiltered = sprintf( $content, $img, $iframe ); $content_filtered = sprintf( $content, $lazy_img, $lazy_iframe ); - add_filter( 'wp_get_lazy_load_tags', '__return_true' ); + add_filter( 'wp_add_lazy_loading_to', '__return_true' ); + add_filter( 'wp_get_tags_to_filter', array( $this, 'wp_get_tags_to_filter_callback' ) ); + add_filter( 'wp_filter_iframe_tags', array( $this, 'wp_filter_iframe_tags_callback' ) ); - $this->assertSame( $content_filtered, wp_add_lazy_load_attributes( $content_unfiltered ) ); - remove_filter( 'wp_get_lazy_load_tags', '__return_true' ); + $this->assertSame( $content_filtered, _wp_filter_html_tags( $content_unfiltered ) ); + remove_filter( 'wp_add_lazy_loading_to', '__return_true' ); + remove_filter( 'wp_get_tags_to_filter', array( $this, 'wp_get_tags_to_filter_callback' ) ); + remove_filter( 'wp_filter_iframe_tags', array( $this, 'wp_filter_iframe_tags_callback' ) ); + } + + function wp_get_tags_to_filter_callback() { + return array( 'img', 'iframe' ); + } + + function wp_filter_iframe_tags_callback( $tag_html ) { + return str_replace( 'assertSame( $content, wp_add_lazy_load_attributes( $content ) ); - remove_filter( 'wp_get_lazy_load_tags', '__return_false' ); + $this->assertSame( $content, _wp_filter_html_tags( $content ) ); + remove_filter( 'wp_add_lazy_loading_to', '__return_false' ); } } diff --git a/wp-lazy-loading.php b/wp-lazy-loading.php index 2668eb3..a96961e 100644 --- a/wp-lazy-loading.php +++ b/wp-lazy-loading.php @@ -32,15 +32,30 @@ function _wp_lazy_loading_initialize_filters() { // The following filters would be merged into core. foreach ( array( 'the_content', 'the_excerpt', 'comment_text', 'widget_text_content' ) as $filter ) { - // Before parsing blocks and shortcodes. - // TODO: Comments do not support images. Revisit. - // TODO: This should not exclude images from dynamic blocks and shortcodes. Look at fixing the filter priority. - add_filter( $filter, 'wp_add_lazy_load_attributes', 8 ); + // After parsing blocks and shortcodes. + // TODO: Comments and excerpts do not support images. Revisit? + // TODO: This includes images added from shortcodes. Needs more testing. + add_filter( $filter, '_wp_filter_html_tags', 25 ); } + add_filter( 'wp_filter_img_tags', 'wp_add_lazy_loading_to_img_tags', 10, 3 ); + // The following filters are only needed while this is a feature plugin. add_filter( 'wp_get_attachment_image_attributes', '_wp_lazy_loading_add_attribute_to_attachment_image' ); add_filter( 'get_avatar', '_wp_lazy_loading_add_attribute_to_avatar' ); + + // Exprerimental, testing only. + // To test with iframes: add `?wp-lazy-loading-iframes` to the current URL. + if ( isset( $_GET['wp-lazy-loading-iframes'] ) ) { + // Add to all tags. + add_filter( 'wp_add_lazy_loading_to', '__return_true' ); + + // Add filterig for the iframe tag. + add_filter( 'wp_get_tags_to_filter', 'wp_lazy_loading_add_iframe_tag', 10, 2 ); + + // Add the actual attribute, and filter the tag after that. + add_filter( 'wp_filter_iframe_tags', 'wp_add_lazy_loading_to_iframe_tags', 10, 3 ); + } } add_action( 'plugins_loaded', '_wp_lazy_loading_initialize_filters', 1 ); @@ -62,7 +77,7 @@ function _wp_lazy_loading_initialize_filters() { * @return string Modified tag. */ function _wp_lazy_loading_add_attribute_to_avatar( $avatar ) { - if ( in_array( 'img', wp_get_lazy_load_tags(), true ) && false === strpos( $avatar, ' loading=' ) ) { + if ( wp_add_lazy_loading_to( 'img', 'get_avatar' ) && false === strpos( $avatar, ' loading=' ) ) { $avatar = str_replace( '` tags. * * Currently the "loading" attribute is only supported for `img`, and is enabled by default. - * Support for `iframe` is for testing purposes only. + * Support for `iframe` can be enabled for testing purposes. * * @since (TBD) * - * @param string $content The raw post content to be filtered. + * @param string $tag_html The tag markup. + * @param string $content The (HTML) content where the img tag is. + * @param string $context Optional. Additional context passed to the function. * @return string Converted content with 'loading' attributes added to images. */ -function wp_add_lazy_load_attributes( $content ) { - $tags = wp_get_lazy_load_tags(); +function wp_add_lazy_loading_to_img_tags( $tag_html, $content, $context = null ) { + if ( wp_add_lazy_loading_to( 'img', $context ) && ! preg_match( '/\bloading\s*=/', $tag_html ) ) { + $unfiltered = $tag_html; + $tag_html = str_replace( ' $tag_name ) { + if ( ! has_filter( "wp_filter_{$tag_name}_tags" ) || false === strpos( $content, "<$tag_name" ) ) { + unset( $tags[ $index ] ); + } + } if ( empty( $tags ) ) { return $content; } + // Expects well-formed HTML 5.0. + // Intended for `img` and `iframe`. return preg_replace_callback( - '/<(' . implode( '|', $tags ) . ')(\s)[^>]+>/', - function( array $matches ) { - if ( ! preg_match( '/\sloading\s*=/', $matches[0] ) ) { - $tag = $matches[1]; - $space = $matches[2]; + '/<(' . implode( '|', $tags ) . ')(?:\s[^>]+)?>/', + function( array $matches ) use ( $content, $context ) { + $tag_html = $matches[0]; + $tag_name = $matches[1]; - return str_replace( '<' . $tag . $space, '<' . $tag . $space . 'loading="lazy" ', $matches[0] ); - } - - return $matches[0]; + /** + * Filters each of the HTML tags that were found in $content. + * + * The variable part of the filter is the name of the matched tag. + * + * @since (TBD) + * + * @param string $tag_html The matched HTML markup. + * @param string $content The (HTML) content being filtered. + * @param string $context Optional. Additional context. May be the current filter name or specific context passed to the function. + */ + return apply_filters( "wp_filter_{$tag_name}_tags", $tag_html, $content, $context ); }, $content ); } -