blob: cdf683946f46dd415e3402a2a7ddcbdeebaabab0 [file] [log] [blame]
/*
* Copyright 2015 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Author: sligocki@google.com (Shawn Ligocki)
#include "net/instaweb/rewriter/public/responsive_image_filter.h"
#include "net/instaweb/rewriter/public/delay_images_filter.h"
#include "net/instaweb/rewriter/public/local_storage_cache_filter.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/rewriter/public/rewrite_test_base.h"
#include "net/instaweb/rewriter/public/server_context.h"
#include "net/instaweb/rewriter/public/static_asset_manager.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/html/html_parse_test_base.h"
#include "pagespeed/kernel/http/content_type.h"
#include "pagespeed/kernel/http/user_agent_matcher_test_base.h"
namespace net_instaweb {
namespace {
const char kPuzzleJpgFile[] = "Puzzle.jpg"; // 1023 x 766
const char kCuppaPngFile[] = "Cuppa.png"; // 65 x 70
const char kIronChefGifFile[] = "IronChef2.gif"; // 192 x 256
const char k1x1GifFile[] = "another-blank.gif"; // 1 x 1
const char k16x16PngFile[] = "small_16x16.png"; // 16 x 16
static const char* k16x16PngDataUrl =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAA"
"BGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAH"
"UwAADqYAAAOpgAABdwnLpRPAAAAAZQTFRFAAD/////e9yZLAAAAAFiS0dEAf8CLd4AAAAJ"
"cEhZcwAAAEgAAABIAEbJaz4AAAAMSURBVAjXY2AgDQAAADAAAceqhY4AAAAldEVYdGRhdG"
"U6Y3JlYXRlADIwMTUtMDMtMjVUMTg6MDA6MTctMDQ6MDCr+Rs2AAAAJXRFWHRkYXRlOm1v"
"ZGlmeQAyMDE1LTAzLTI1VDE4OjAwOjE3LTA0OjAw2qSjigAAAABJRU5ErkJggg==";
class ResponsiveImageFilterTest : public RewriteTestBase {
protected:
ResponsiveImageFilterTest() {
AddFileToMockFetcher(StrCat(kTestDomain, "a.jpg"), kPuzzleJpgFile,
kContentTypeJpeg, 100);
AddFileToMockFetcher(StrCat(kTestDomain, "b.png"), kCuppaPngFile,
kContentTypePng, 100);
AddFileToMockFetcher(StrCat(kTestDomain, "c.gif"), kIronChefGifFile,
kContentTypeGif, 100);
AddFileToMockFetcher(StrCat(kTestDomain, "small_1x1.gif"), k1x1GifFile,
kContentTypeGif, 100);
AddFileToMockFetcher(StrCat(kTestDomain, "small_16x16.png"), k16x16PngFile,
kContentTypePng, 100);
}
virtual bool AddHtmlTags() const { return false; }
void TestSimple(int width, int height, StringPiece filename,
StringPiece full_density, StringPiece final_ext,
bool include_zoom_script) {
GoogleString width_str = IntegerToString(width);
GoogleString height_str = IntegerToString(height);
GoogleString input_html = StrCat(
"<img src=", filename, " width=", width_str, " height=", height_str,
">");
GoogleString output_html = StrCat(
"<img src=", EncodeImage(width, height, filename, "0", final_ext),
" width=", width_str, " height=", height_str);
StrAppend(
&output_html, " srcset=\"",
EncodeImage(1.5 * width, 1.5 * height, filename, "0", final_ext),
" 1.5x,",
EncodeImage(2 * width, 2 * height, filename, "0", final_ext), " 2x,",
EncodeImage(3 * width, 3 * height, filename, "0", final_ext), " 3x,");
StrAppend(
&output_html, EncodeImage(-1, -1, filename, "0", final_ext),
" ", full_density, "x\">");
if (include_zoom_script) {
StrAppend(&output_html,
"<script src=\"/psajs/responsive.0.js\"></script>");
}
ValidateExpected("test_simple", input_html, output_html);
}
};
TEST_F(ResponsiveImageFilterTest, SimpleJpg) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kRecompressJpeg);
rewrite_driver()->AddFilters();
TestSimple(100, 100, "a.jpg", "10.23", "jpg", false);
}
TEST_F(ResponsiveImageFilterTest, SimplePng) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kRecompressPng);
rewrite_driver()->AddFilters();
TestSimple(10, 10, "b.png", "6.5", "png", false);
}
TEST_F(ResponsiveImageFilterTest, SimpleGif) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kRecompressPng);
options()->EnableFilter(RewriteOptions::kConvertGifToPng);
rewrite_driver()->AddFilters();
TestSimple(10, 10, "c.gif", "19.2", "png", false);
}
TEST_F(ResponsiveImageFilterTest, SimpleWebp) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kConvertJpegToWebp);
rewrite_driver()->AddFilters();
SetupForWebp();
TestSimple(100, 100, "a.jpg", "10.23", "webp", false);
}
TEST_F(ResponsiveImageFilterTest, Zoom) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResponsiveImagesZoom);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kRecompressJpeg);
rewrite_driver()->AddFilters();
// Add zoom script.
TestSimple(100, 100, "a.jpg", "10.23", "jpg", true);
}
TEST_F(ResponsiveImageFilterTest, OddRatio) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kConvertJpegToWebp);
rewrite_driver()->AddFilters();
SetupForWebp();
// Important, only 2 digits after decimal.
TestSimple(99, 99, "a.jpg", "10.33", "webp", false);
}
// Nothing happens if we do not enable image resizing.
TEST_F(ResponsiveImageFilterTest, NoResizeImages) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
rewrite_driver()->AddFilters();
const char input_html[] = "<img src=a.jpg width=100 height=100>";
ValidateNoChanges("no_resize_images", input_html);
}
TEST_F(ResponsiveImageFilterTest, NoResizeLarger) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
// Note: This is the native size of a.jpg.
const char input_html[] = "<img src=a.jpg width=1023 height=766>";
// We do not add a srcset because this is already native size.
ValidateNoChanges("no_resize_larger", input_html);
}
TEST_F(ResponsiveImageFilterTest, RecompressLarger) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kRecompressJpeg);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
// Note: This is the native size of a.jpg.
const char input_html[] = "<img src=a.jpg width=1023 height=766>";
GoogleString output_html = StrCat(
"<img src=", EncodeImage(-1, -1, "a.jpg", "0", "jpg"),
" width=1023 height=766>");
// We do not add a srcset because this is already native size.
ValidateExpected("recompress_larger", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, RecompressLarger2) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kRecompressJpeg);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
// Note: This is 2/3 the native size of a.jpg.
const char input_html[] = "<img src=a.jpg width=682 height=511>";
GoogleString output_html = StrCat(
"<img src=", EncodeImage(682, 511, "a.jpg", "0", "jpg"),
" width=682 height=511 srcset=\"",
EncodeImage(-1, -1, "a.jpg", "0", "jpg"), " 1.5x\">");
// We do not add a 2x version because the 1.5x is already native size.
ValidateExpected("recompress_larger2", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, RecompressLarger3) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kRecompressJpeg);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
// Note: This is slightly less than 2/3 the native size of a.jpg.
const char input_html[] = "<img src=a.jpg width=682 height=510>";
GoogleString output_html = StrCat(
"<img src=", EncodeImage(682, 510, "a.jpg", "0", "jpg"),
" width=682 height=510 srcset=\"",
EncodeImage(1023, 765, "a.jpg", "0", "jpg"), " 1.5x,",
// Note this 2x version is actually only 1023x766.
EncodeImage(-1, -1, "a.jpg", "0", "jpg"), " 2x\">");
// TODO(sligocki): We shouldn't include the 1.5x version because it's so
// close to the 2x version. Update this test when that is fixed.
ValidateExpected("recompress_larger3", input_html, output_html);
}
// Do not do any responsive rewriting if there are no dimensions.
TEST_F(ResponsiveImageFilterTest, NoDims) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
const char input_html[] = "<img src=a.jpg>";
ValidateNoChanges("no_dims", input_html);
}
// Do not do any responsive rewriting if image dimensions are inserted.
// This triggered an early bug where the filter thought that it was in the
// first pass during the second pass.
TEST_F(ResponsiveImageFilterTest, InsertDims) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kInsertImageDimensions);
rewrite_driver()->AddFilters();
const char input_html[] = "<img src=a.jpg>";
const char output_html[] = "<img src=a.jpg width=\"1023\" height=\"766\">";
ValidateExpected("insert_dims", input_html, output_html);
}
// Do not do any responsive rewriting if there is only one dimension.
// TODO(sligocki): Maybe we should allow rewriting with one dim. Seems like
// it would work fine.
TEST_F(ResponsiveImageFilterTest, OneDim) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
const char input_html[] = "<img src=a.jpg width=100>";
GoogleString output_html = StrCat(
"<img src=", EncodeImage(100, -1, "a.jpg", "0", "jpg"), " width=100>");
ValidateExpected("one_dim", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, InlineNativeSize) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kInlineImages);
rewrite_driver()->AddFilters();
// This image is displayed at native resolution, therefore we will not add
// a srcset to make it responsive. Instead we just inline it.
const char input_html[] = "<img src=small_16x16.png width=16 height=16>";
GoogleString output_html =
StrCat("<img width=16 height=16 src=\"", k16x16PngDataUrl, "\">");
ValidateExpected("inline_native", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, InlineSmall) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kInlineImages);
rewrite_driver()->AddFilters();
// This image is displayed at 1/2 native resolution, we could add a srcset
// to allow multiple resolutions, but instead we just inline the largest
// version because even the largest one is pretty small.
const char input_html[] = "<img src=small_16x16.png width=8 height=8>";
GoogleString output_html =
StrCat("<img width=8 height=8 src=\"", k16x16PngDataUrl, "\">");
ValidateExpected("inline_small", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, InlineSmall2) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kInlineImages);
rewrite_driver()->AddFilters();
// Like InlineSmall test, but with at an odd resolution.
const char input_html[] = "<img src=small_16x16.png width=11 height=11>";
GoogleString output_html =
StrCat("<img width=11 height=11 src=\"", k16x16PngDataUrl, "\">");
ValidateExpected("inline_small2", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, NoPartialInline) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kInlineImages);
// Original image is 292 bytes, 8x8 resize is 83 bytes. So we choose a
// value between.
options()->set_image_inline_max_bytes(200);
rewrite_driver()->AddFilters();
const char input_html[] = "<img src=small_16x16.png width=8 height=8>";
GoogleString output_html = StrCat(
"<img src=", EncodeImage(8, 8, "small_16x16.png", "0", "png"),
" width=8 height=8 srcset=\"",
EncodeImage(12, 12, "small_16x16.png", "0", "png"), " 1.5x,"
"small_16x16.png 2x\">");
ValidateExpected("no_partial_inline", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, NoPartialInline2) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kInlineImages);
// Original image is 292 bytes, 11x11 resize is 83 bytes. So we choose a
// value between.
options()->set_image_inline_max_bytes(200);
rewrite_driver()->AddFilters();
const char input_html[] = "<img src=small_16x16.png width=11 height=11>";
GoogleString output_html = StrCat(
"<img src=", EncodeImage(11, 11, "small_16x16.png", "0", "png"),
" width=11 height=11 srcset=\"small_16x16.png 1.5x\">");
ValidateExpected("no_partial_inline2", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, LocalStorageFilter) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kInlineImages);
options()->EnableFilter(RewriteOptions::kLocalStorageCache);
rewrite_driver()->AddFilters();
SetHtmlMimetype();
GoogleString local_storage_cache_js =
StrCat("<script type=\"text/javascript\" data-pagespeed-no-defer>",
server_context()->static_asset_manager()->GetAsset(
StaticAssetEnum::LOCAL_STORAGE_CACHE_JS, options()),
LocalStorageCacheFilter::kLscInitializer,
"</script>");
// Note: Currently images used by the responsive filter do not get
// local storage attributes and thus cannot be saved into local storage.
// If we figure out a way (and think it's worth the effort) to make these
// work together, we will need to update this test.
const char input_html[] = "<img src=small_16x16.png width=16 height=16>";
GoogleString output_html =
StrCat(local_storage_cache_js,
"<img width=16 height=16 src=\"", k16x16PngDataUrl, "\">");
ValidateExpected("local_storage", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, DataUrl) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
// Don't mess with data URLs.
GoogleString input_html = StrCat("<img src=\"", k16x16PngDataUrl, "\">");
ValidateNoChanges("data_url", input_html);
}
TEST_F(ResponsiveImageFilterTest, CommasInUrls) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
AddFileToMockFetcher(StrCat(kTestDomain, "comma,middle"), kPuzzleJpgFile,
kContentTypeJpeg, 100);
AddFileToMockFetcher(StrCat(kTestDomain, "comma,end,"), kPuzzleJpgFile,
kContentTypeJpeg, 100);
AddFileToMockFetcher(StrCat(kTestDomain, ",comma,begin"), kPuzzleJpgFile,
kContentTypeJpeg, 100);
// srcset added. Commas are allowed in the middle of URLs in srcsets.
ValidateExpected(
"comma_middle",
"<img src='comma,middle' width=682 height=511>",
StrCat("<img src='", EncodeImage(682, 511, "comma,middle", "0", "jpg"),
"' width=682 height=511 srcset=\"comma,middle 1.5x\">"));
// No srcset added. Commas are not allowed at end of URLs in srcset.
ValidateExpected(
"comma_end",
"<img src='comma,end,' width=682 height=511>",
StrCat("<img src='", EncodeImage(682, 511, "comma,end,", "0", "jpg"),
"' width=682 height=511>"));
// No srcset added. Commas are not allowed at beginning of URLs in srcset.
ValidateExpected(
"comma_begin",
"<img src=',comma,begin' width=682 height=511>",
StrCat("<img src='", EncodeImage(682, 511, ",comma,begin", "0", "jpg"),
"' width=682 height=511>"));
}
TEST_F(ResponsiveImageFilterTest, SpacesInUrls) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
AddFileToMockFetcher(StrCat(kTestDomain, "space%20%20in%20%0C%20URL"),
kPuzzleJpgFile, kContentTypeJpeg, 100);
// All whitespace chars should be escaped in srcset.
ValidateExpected(
"comma_middle",
"<img src='space \t in \n\r\f URL' width=682 height=511>",
StrCat("<img src='",
EncodeImage(682, 511, "space%20%20in%20%0C%20URL", "0", "jpg"),
"' width=682 height=511 "
"srcset=\"space%20%09%20in%20%0A%0D%0C%20URL 1.5x\">"));
}
TEST_F(ResponsiveImageFilterTest, NoTransform) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
const char input_html[] =
"<img src=a.jpg width=100 height=100 pagespeed_no_transform>";
const char output_html[] = "<img src=a.jpg width=100 height=100>";
ValidateExpected("no_transform", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, DataNoTransform) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
const char input_html[] =
"<img src=a.jpg width=100 height=100 data-pagespeed-no-transform>";
const char output_html[] = "<img src=a.jpg width=100 height=100>";
ValidateExpected("data-no-transform", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, TrackingPixel) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
// Don't add srcset for 1x1 tracking pixels.
const char input_html[] = "<img src=small_1x1.gif width=1 height=1>";
ValidateNoChanges("tracking_pixel", input_html);
}
TEST_F(ResponsiveImageFilterTest, InputSrcSet) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
const char input_html[] =
"<img src=a.jpg width=100 height=100 srcset='a.jpg 1x, b.png 2x'>";
GoogleString output_html = StrCat(
"<img src=", EncodeImage(100, 100, "a.jpg", "0", "jpg"),
" width=100 height=100 srcset='a.jpg 1x, b.png 2x'>");
ValidateExpected("input_srcset", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, CustomDensities) {
RewriteOptions::ResponsiveDensities densities;
EXPECT_TRUE(RewriteOptions::ParseFromString("2, 4.7, 0.5", &densities));
options()->set_responsive_image_densities(densities);
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
const char input_html[] = "<img src=a.jpg width=100 height=100>";
GoogleString output_html = StrCat(
"<img src=", EncodeImage(100, 100, "a.jpg", "0", "jpg"),
" width=100 height=100 srcset=\"",
// Note: Resolutions are sorted.
EncodeImage(50, 50, "a.jpg", "0", "jpg"), " 0.5x,",
EncodeImage(200, 200, "a.jpg", "0", "jpg"), " 2x,",
EncodeImage(470, 470, "a.jpg", "0", "jpg"), " 4.7x,"
"a.jpg 10.23x\">");
ValidateExpected("custom_densities", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, Debug) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kRecompressJpeg);
options()->EnableFilter(RewriteOptions::kResizeImages);
rewrite_driver()->AddFilters();
EnableDebug();
ValidateExpected(
"no_transform",
"<img src=a.jpg width=100 height=100 data-pagespeed-no-transform>",
"<img src=a.jpg width=100 height=100>"
"<!--ResponsiveImageFilter: Not adding srcset because of "
"data-pagespeed-no-transform attribute.-->");
ValidateExpected(
"with_srcset",
"<img src=a.jpg width=100 height=100 srcset='a.jpg 1x, b.png 2x'>",
StrCat("<img src=", EncodeImage(100, 100, "a.jpg", "0", "jpg"),
" width=100 height=100 srcset='a.jpg 1x, b.png 2x'>"
"<!--Resized image from 1023x766 to 100x100-->"
"<!--ResponsiveImageFilter: Not adding srcset because image "
"already has one.-->"));
ValidateExpected(
"no_dims",
"<img src=a.jpg>",
StrCat("<img src=", EncodeImage(-1, -1, "a.jpg", "0", "jpg"), ">"
"<!--Image does not appear to need resizing.-->"
"<!--ResponsiveImageFilter: Not adding srcset because image does "
"not have dimensions (or a src URL).-->"));
ValidateExpected(
"tracking_pixel",
"<img src=small_1x1.gif width=1 height=1>",
"<img src=small_1x1.gif width=1 height=1>"
"<!--Image does not appear to need resizing.-->"
"<!--ResponsiveImageFilter: Not adding srcset to tracking pixel.-->");
ValidateExpected(
"native_size",
// Input
"<img src=a.jpg width=1023 height=766>",
// Expected output
// 1.5x virtual image debug messages:
StrCat("<!--ResponsiveImageFilter: Any debug messages after this refer "
"to the virtual 1.5x image with src=",
EncodeImage(-1, -1, "a.jpg", "0", "jpg"),
" width=1534 height=1149-->"
"<!--Image does not appear to need resizing.-->"
// 2x virtual image debug messages:
"<!--ResponsiveImageFilter: Any debug messages after this refer "
"to the virtual 2x image with src=",
EncodeImage(-1, -1, "a.jpg", "0", "jpg"),
" width=2046 height=1532-->"
"<!--Image does not appear to need resizing.-->"
// 3x virtual image debug messages:
"<!--ResponsiveImageFilter: Any debug messages after this refer "
"to the virtual 3x image with src=",
EncodeImage(-1, -1, "a.jpg", "0", "jpg"),
" width=3069 height=2298-->"
"<!--Image does not appear to need resizing.-->"
// Inlinable virtual image debug messages:
"<!--ResponsiveImageFilter: Any debug messages after this refer "
"to the virtual inlinable 3x image with src=",
EncodeImage(-1, -1, "a.jpg", "0", "jpg"),
" width=3069 height=2298-->"
"<!--Image does not appear to need resizing.-->"
// Full virtual image debug messages:
"<!--ResponsiveImageFilter: Any debug messages after this refer "
"to the virtual full-sized image with src=",
EncodeImage(-1, -1, "a.jpg", "0", "jpg"),
" width= height=-->"
"<!--Image does not appear to need resizing.-->"
// Actual image + debug messages:
"<img src=", EncodeImage(-1, -1, "a.jpg", "0", "jpg"),
" width=1023 height=766>"
"<!--ResponsiveImageFilter: Not adding 1x candidate to srcset "
"because it is the same as previous candidate.-->"
"<!--ResponsiveImageFilter: Not adding 3x candidate to srcset "
"because it is the same as previous candidate.-->"
"<!--ResponsiveImageFilter: Not adding 2x candidate to srcset "
"because it is the same as previous candidate.-->"
"<!--ResponsiveImageFilter: Not adding 1.5x candidate to srcset "
"because it is the same as previous candidate.-->"
"<!--Image does not appear to need resizing.-->"));
ValidateExpected(
"same_src",
// Input
"<img src=http://other-domain.com/a.jpg width=100 height=100>",
// Expected output
// 1.5x virtual image debug messages:
"<!--ResponsiveImageFilter: Any debug messages after this refer "
"to the virtual 1.5x image with "
"src=http://other-domain.com/a.jpg width=150 height=150-->"
"<!--The preceding resource was not rewritten because its domain "
"(other-domain.com) is not authorized-->"
// 2x virtual image debug messages:
"<!--ResponsiveImageFilter: Any debug messages after this refer "
"to the virtual 2x image with "
"src=http://other-domain.com/a.jpg width=200 height=200-->"
"<!--The preceding resource was not rewritten because its domain "
"(other-domain.com) is not authorized-->"
// 3x virtual image debug messages:
"<!--ResponsiveImageFilter: Any debug messages after this refer "
"to the virtual 3x image with "
"src=http://other-domain.com/a.jpg width=300 height=300-->"
"<!--The preceding resource was not rewritten because its domain "
"(other-domain.com) is not authorized-->"
// Inlinable virtual image debug messages:
"<!--ResponsiveImageFilter: Any debug messages after this refer "
"to the virtual inlinable 3x image with "
"src=http://other-domain.com/a.jpg width=300 height=300-->"
"<!--The preceding resource was not rewritten because its domain "
"(other-domain.com) is not authorized-->"
// Full virtual image debug messages:
"<!--ResponsiveImageFilter: Any debug messages after this refer "
"to the virtual full-sized image with "
"src=http://other-domain.com/a.jpg width= height=-->"
"<!--The preceding resource was not rewritten because its domain "
"(other-domain.com) is not authorized-->"
// Actual image + debug messages:
"<img src=http://other-domain.com/a.jpg width=100 height=100>"
"<!--ResponsiveImageFilter: Not adding 3x candidate to srcset "
"because it is the same as previous candidate.-->"
"<!--ResponsiveImageFilter: Not adding 2x candidate to srcset "
"because it is the same as previous candidate.-->"
"<!--ResponsiveImageFilter: Not adding 1.5x candidate to srcset "
"because it is the same as previous candidate.-->"
"<!--The preceding resource was not rewritten because its domain "
"(other-domain.com) is not authorized-->");
}
TEST_F(ResponsiveImageFilterTest, InlinePreview) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
// inline_preview_images -> kDelayImages.
options()->EnableFilter(RewriteOptions::kDelayImages);
SetHtmlMimetype(); // Prevent insertion of CDATA tags to static JS.
rewrite_driver()->AddFilters();
const GoogleString kInlinePreviewScript = StrCat(
"<script data-pagespeed-no-defer type=\"text/javascript\">",
DelayImagesFilter::kImageOnloadJsSnippet, "</script>");
const char kLowResSource[] = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAA"
"AQABAAD/2wBDAFA3PEY8MlBGQUZaVVBfeMiCeG5uePWvuZHI////////////////////"
"////////////////////////////////2wBDAVVaWnhpeOuCguv/////////////////"
"////////////////////////////////////////////////////////wAARCABkAGQD"
"ASIAAhEBAxEB/8QAGQABAQEBAQEAAAAAAAAAAAAAAAECAwQF/8QAKRAAAgIBAwQABQUA"
"AAAAAAAAAAECEQMhMUEEElFhcYGRofAUI0LR4f/EABYBAQEBAAAAAAAAAAAAAAAAAAAB"
"Av/EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAwDAQACEQMRAD8A9oBLSVgUEKAAAAAg"
"FBwfURU3Fp0uTrGcZq4tMDQIZjkjNtRlbQGwABynkrRHCeRtVwJSM72Z1qR1xZe3R7Ho"
"TTVo8RuGSUNvuXSx6wefDmlPI4y8HoKy5ZpuELjued9RNrVr6DqcjeSk9EcQK23JkTad"
"p17M2RgdJZpyVOTozGTjJSi6aMgD6GPqISinJqL5QPngDvuVEQMtqAUCwl2yUvB3nkXZ"
"e2h5XLtklydOobUa8ljNeeUr1vcw9Q7RCoaotoWAJsUi3oq0YAHtw4scsad2AOJeSSfb"
"qE01a1Mtr4JKXZG+S2tzhOXfL0BE252z1dTN9sEktVex5Unex3yu8UH6o0y4Mhrfgy1q"
"EUiBQKmBHcbegKvr8waUJSVpfYAdckaRiGKcXxXtnqy429Uc6vVMlWMOHctWVYorg6KD"
"9lWOVf2RWFFJ6JGc8f2o6cnoWP2cupgli08lSvJ+asya/NjPJUC8EKtv9AI3jg5zUUbw"
"41PTVa736PVhwrEn5YG4RUIqK2QNACCl4BQIUAAZnBTjTNADiumx8pv4sj6XG/K+DO4A"
"8/6SPEmZfSX/AD+x6gBwxYHjknafyO4AAAAQoAAAAAAAAAAAAAAAAAH/2Q==";
const char input_html[] = "<img src=a.jpg width=100 height=100>";
GoogleString output_html = StrCat(
kInlinePreviewScript,
"<img data-pagespeed-high-res-src=",
EncodeImage(100, 100, "a.jpg", "0", "jpg"),
" width=100 height=100 data-pagespeed-high-res-srcset=\"",
EncodeImage(150, 150, "a.jpg", "0", "jpg"), " 1.5x,",
EncodeImage(200, 200, "a.jpg", "0", "jpg"), " 2x,",
EncodeImage(300, 300, "a.jpg", "0", "jpg"), " 3x,"
"a.jpg 10.23x\" src=\"", kLowResSource,
"\" onload=\"pagespeed.switchToHighResAndMaybeBeacon(this);\" onerror=\""
"this.onerror=null;pagespeed.switchToHighResAndMaybeBeacon(this);\">");
ValidateExpected("inline_preview", input_html, output_html);
}
TEST_F(ResponsiveImageFilterTest, Lazyload) {
options()->EnableFilter(RewriteOptions::kResponsiveImages);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kLazyloadImages);
// Disable beaconing so that the image is automatically lazyloaded.
options()->set_critical_images_beacon_enabled(false);
// Set User-Agent so that Lazyload will work.
SetCurrentUserAgent(
UserAgentMatcherTestBase::kChrome18UserAgent);
SetHtmlMimetype(); // Prevent insertion of CDATA tags to static JS.
rewrite_driver()->AddFilters();
const char input_html[] = "<img src=a.jpg width=100 height=100>";
GoogleString output_html = StrCat(
GetLazyloadScriptHtml(),
"<img data-pagespeed-lazy-src=",
EncodeImage(100, 100, "a.jpg", "0", "jpg"),
" width=100 height=100 data-pagespeed-lazy-srcset=\"",
EncodeImage(150, 150, "a.jpg", "0", "jpg"), " 1.5x,",
EncodeImage(200, 200, "a.jpg", "0", "jpg"), " 2x,",
EncodeImage(300, 300, "a.jpg", "0", "jpg"), " 3x,"
"a.jpg 10.23x\" src=\"/psajs/1.0.gif\" "
"onload=\"pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);\" "
"onerror=\"this.onerror=null;pagespeed.lazyLoadImages."
"loadIfVisibleAndMaybeBeacon(this);\">");
ValidateExpected("lazyload", input_html, output_html);
}
} // namespace
} // namespace net_instaweb