Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 10 additions & 23 deletions plugins/optimization-detective/class-od-link-collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,39 +260,26 @@ public function get_html(): string {
* Constructs the Link HTTP response header.
*
* @since 0.4.0
* @since n.e.x.t Excludes links with `imagesrcset` since responsive image preloads are sent via `<link>` tags in the HTML instead.
*
* @return non-empty-string|null Link HTTP response header, or null if there are none.
*/
public function get_response_header(): ?string {
$link_headers = array();

foreach ( $this->get_prepared_links() as $link ) {
if ( isset( $link['href'] ) ) {
$link['href'] = $this->encode_url_for_response_header( $link['href'] );
} else {
// The about:blank is present since a Link without a reference-uri is invalid so any imagesrcset would otherwise not get downloaded.
$link['href'] = 'about:blank';
}

// Encode the URLs in the srcset.
// Responsive image preloads are sent via <link> tags in the HTML (see get_html()) instead of the Link
// response header, since imagesrcset/imagesizes can make the header exceed common reverse-proxy header
// size limits. See <https://web.dev/articles/preload-responsive-images#imagesrcset-imagesizes>.
if ( isset( $link['imagesrcset'] ) ) {
$link['imagesrcset'] = join(
', ',
array_map(
function ( $image_candidate ) {
// Parse out the URL to separate it from the descriptor.
$image_candidate_parts = (array) preg_split( '/\s+/', (string) $image_candidate, 2 );

// Encode the URL.
$image_candidate_parts[0] = $this->encode_url_for_response_header( (string) $image_candidate_parts[0] );
continue;
}

// Re-join the URL with the descriptor.
return implode( ' ', $image_candidate_parts );
},
(array) preg_split( '/\s*,\s*/', $link['imagesrcset'] )
)
);
if ( ! isset( $link['href'] ) ) {
// Not reachable: imagesrcset-only links are skipped above, and add_link() requires either href or imagesrcset.
continue;
}
$link['href'] = $this->encode_url_for_response_header( $link['href'] );

$link_header = '<' . $link['href'] . '>';
unset( $link['href'] );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function data_provider_to_test_add_link(): array {
'expected_html' => '
<link data-od-added-tag rel="preload" href="https://example.com/foo.jpg" imagesrcset="https://example.com/foo-400.jpg 400w, https://example.com/foo-800.jpg 800w" imagesizes="100vw" crossorigin="anonymous" fetchpriority="high" as="image" media="screen" integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC" referrerpolicy="origin" type="image/jpeg">
',
'expected_header' => 'Link: <https://example.com/foo.jpg>; rel="preload"; imagesrcset="https://example.com/foo-400.jpg 400w, https://example.com/foo-800.jpg 800w"; imagesizes="100vw"; crossorigin="anonymous"; fetchpriority="high"; as="image"; media="screen"; integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"; referrerpolicy="origin"; type="image/jpeg"',
'expected_header' => null,
'expected_count' => 1,
'error' => '',
),
Expand All @@ -56,10 +56,37 @@ public function data_provider_to_test_add_link(): array {
'expected_html' => '
<link data-od-added-tag rel="preload" imagesrcset="https://example.com/foo-400.jpg 400w, https://example.com/foo-800.jpg 800w" imagesizes="(max-width: 600px) 480px, 800px" as="image" media="screen">
',
'expected_header' => 'Link: <about:blank>; rel="preload"; imagesrcset="https://example.com/foo-400.jpg 400w, https://example.com/foo-800.jpg 800w"; imagesizes="(max-width: 600px) 480px, 800px"; as="image"; media="screen"',
'expected_header' => null,
'expected_count' => 1,
'error' => '',
),
'preload_imagesrcset_excluded_from_header_when_mixed_with_plain_link' => array(
'links_args' => array(
array(
array(
'rel' => 'preload',
'href' => 'https://example.com/foo.jpg',
'imagesrcset' => 'https://example.com/foo-400.jpg 400w, https://example.com/foo-800.jpg 800w',
'imagesizes' => '100vw',
'as' => 'image',
),
),
array(
array(
'rel' => 'preload',
'href' => 'https://example.com/bar.jpg',
'as' => 'image',
),
),
),
'expected_html' => '
<link data-od-added-tag rel="preload" href="https://example.com/bar.jpg" as="image">
<link data-od-added-tag rel="preload" href="https://example.com/foo.jpg" imagesrcset="https://example.com/foo-400.jpg 400w, https://example.com/foo-800.jpg 800w" imagesizes="100vw" as="image">
',
'expected_header' => 'Link: <https://example.com/bar.jpg>; rel="preload"; as="image"',
'expected_count' => 2,
'error' => '',
),
'preload_with_min0_max_viewport_widths' => array(
'links_args' => array(
array(
Expand Down Expand Up @@ -268,7 +295,7 @@ public function data_provider_to_test_add_link(): array {
'expected_html' => '
<link data-od-added-tag rel="preload" href="https://example.com/bar.jpg" as="image" fetchpriority="high" imagesrcset="https://example.com/&quot;bar&quot;-480w.jpg 480w, https://example.com/&quot;bar&quot;-800w.jpg 800w" imagesizes="(max-width: 600px) 480px, 800px" crossorigin="anonymous">
',
'expected_header' => 'Link: <https://example.com/bar.jpg>; rel="preload"; as="image"; fetchpriority="high"; imagesrcset="https://example.com/%22bar%22-480w.jpg 480w, https://example.com/%22bar%22-800w.jpg 800w"; imagesizes="(max-width: 600px) 480px, 800px"; crossorigin="anonymous"',
'expected_header' => null,
'expected_count' => 1,
'error' => '',
),
Expand Down Expand Up @@ -469,7 +496,7 @@ public function data_provider_to_test_add_link(): array {
'expected_html' => '
<link data-od-added-tag href="https://example.com/wp-content/uploads/2025/02/البيسون-1024x668-jpg.webp" rel="preload" as="image" imagesizes="(width &lt;= 480px) 316px, (480px &lt; width &lt;= 600px) 489px, (600px &lt; width &lt;= 782px) 644px, (782px &lt; width) 644px" imagesrcset="https://example.com/wp-content/uploads/2025/02/البيسون-1024x668-jpg.webp 1024w, https://example.com/wp-content/uploads/2025/02/البيسون-300x196-jpg.webp 300w, https://example.com/wp-content/uploads/2025/02/البيسون-768x501-jpg.webp 768w, https://example.com/wp-content/uploads/2025/02/البيسون-1536x1002-jpg.webp 1536w, https://example.com/wp-content/uploads/2025/02/البيسون-2048x1336-jpg.webp 2048w">
',
'expected_header' => 'Link: <https://example.com/wp-content/uploads/2025/02/%D8%A7%D9%84%D8%A8%D9%8A%D8%B3%D9%88%D9%86-1024x668-jpg.webp>; rel="preload"; as="image"; imagesizes="(width <= 480px) 316px, (480px < width <= 600px) 489px, (600px < width <= 782px) 644px, (782px < width) 644px"; imagesrcset="https://example.com/wp-content/uploads/2025/02/%D8%A7%D9%84%D8%A8%D9%8A%D8%B3%D9%88%D9%86-1024x668-jpg.webp 1024w, https://example.com/wp-content/uploads/2025/02/%D8%A7%D9%84%D8%A8%D9%8A%D8%B3%D9%88%D9%86-300x196-jpg.webp 300w, https://example.com/wp-content/uploads/2025/02/%D8%A7%D9%84%D8%A8%D9%8A%D8%B3%D9%88%D9%86-768x501-jpg.webp 768w, https://example.com/wp-content/uploads/2025/02/%D8%A7%D9%84%D8%A8%D9%8A%D8%B3%D9%88%D9%86-1536x1002-jpg.webp 1536w, https://example.com/wp-content/uploads/2025/02/%D8%A7%D9%84%D8%A8%D9%8A%D8%B3%D9%88%D9%86-2048x1336-jpg.webp 2048w"',
'expected_header' => null,
'expected_count' => 1,
'error' => '',
),
Expand Down Expand Up @@ -576,11 +603,11 @@ public function data_provider_to_test_add_link(): array {
*
* @param array<string, mixed> $links_args Links args.
* @param string $expected_html Expected HTML.
* @param string $expected_header Expected Link header.
* @param string|null $expected_header Expected Link header.
* @param int $expected_count Expected count of links.
* @param string $error Error.
*/
public function test_add_link( array $links_args, string $expected_html, string $expected_header, int $expected_count, string $error = '' ): void {
public function test_add_link( array $links_args, string $expected_html, ?string $expected_header, int $expected_count, string $error = '' ): void {
if ( '' !== $error ) {
$this->expectException( InvalidArgumentException::class );
$this->expectExceptionMessage( $error );
Expand Down
Loading