blob: c393779df044141ba8f9bd4b05d79c762a9b0786 [file] [log] [blame]
/*
* Copyright 2013 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: matterbury@google.com (Matt Atterbury)
#include "net/instaweb/public/global_constants.h"
#include "net/instaweb/rewriter/public/dedup_inlined_images_filter.h"
#include "net/instaweb/rewriter/public/delay_images_filter.h"
#include "net/instaweb/rewriter/public/rewrite_driver.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.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/wildcard.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 {
// Filenames of resource files.
const char kCuppaPngFilename[] = "Cuppa.png";
const char kPuzzleJpgFilename[] = "Puzzle.jpg";
const char kCuppaPngInlineData[] =
"data:image/png;base64,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";
const char kCuppaPngWildcardData[] = "data:image/png;base64*";
const char kInlinedScriptFormat[] =
"<script type=\"text/javascript\""
" id=\"pagespeed_script_%d\" data-pagespeed-no-defer>"
"pagespeed.dedupInlinedImages.inlineImg("
"'pagespeed_img_0%d','pagespeed_img_0%d','pagespeed_script_%d'"
");</script>";
const char kHtmlWrapperFormat[] =
"<head>\n"
" <title>Dedup Inlined Images Test</title>\n"
"%s"
"</head>\n"
"<body>"
"%s"
"</body>\n";
class DedupInlinedImagesTest : public RewriteTestBase,
public ::testing::WithParamInterface<bool> {
protected:
virtual void SetUp() {
RewriteTestBase::SetUp();
SetFiltersAndOptions();
rewrite_driver()->AddFilters();
SetCurrentUserAgent(
UserAgentMatcherTestBase::kChrome18UserAgent);
AddFileToMockFetcher(StrCat(kTestDomain, kCuppaPngFilename),
kCuppaPngFilename, kContentTypePng, 100);
AddFileToMockFetcher(StrCat(kTestDomain, kPuzzleJpgFilename),
kPuzzleJpgFilename, kContentTypeJpeg, 100);
SetHtmlMimetype(); // Don't wrap scripts in <![CDATA[ ]]>
StaticAssetManager* static_asset_manager =
server_context()->static_asset_manager();
dedup_inlined_images_js_ =
StrCat("<script type=\"text/javascript\" data-pagespeed-no-defer>",
static_asset_manager->GetAsset(
StaticAssetEnum::DEDUP_INLINED_IMAGES_JS, options()),
DedupInlinedImagesFilter::kDiiInitializer,
"</script>");
}
virtual void SetFiltersAndOptions() {
options()->EnableFilter(RewriteOptions::kInlineImages);
options()->EnableFilter(RewriteOptions::kDedupInlinedImages);
options()->set_image_inline_max_bytes(2000);
}
void TestDedupImages(const StringPiece& case_id,
const GoogleString& head_html_in,
const GoogleString& head_html_out,
const GoogleString& body_html_in,
const GoogleString& body_html_out) {
GoogleString url(StrCat(
"http://test.com/", case_id, ".html?PageSpeed=noscript"));
GoogleString html_in(StringPrintf(
kHtmlWrapperFormat, head_html_in.c_str(), body_html_in.c_str()));
GoogleString body_out(StrCat(StringPrintf(kNoScriptRedirectFormatter,
url.c_str(), url.c_str()),
body_html_out));
GoogleString html_out(StringPrintf(
kHtmlWrapperFormat, head_html_out.c_str(), body_out.c_str()));
Parse(case_id, html_in);
GoogleString expected_out = doctype_string_ + AddHtmlBody(html_out);
EXPECT_EQ(expected_out, output_buffer_) << "Test id:" << case_id;
output_buffer_.clear();
}
GoogleString InsertScriptBefore(const StringPiece& snippet) const {
return StrCat(dedup_inlined_images_js_, snippet);
}
GoogleString dedup_inlined_images_js_;
};
TEST_F(DedupInlinedImagesTest, Simple) {
TestDedupImages("simple", "", "", "<div/>", "<div/>");
}
TEST_F(DedupInlinedImagesTest, InlineSingleSmallImage) {
// Add an id to the first occurence.
TestDedupImages("inline_single_small_image", "", "",
StrCat("<img src='", kCuppaPngFilename, "'>"),
StrCat("<img src='", kCuppaPngInlineData,
"' id=\"pagespeed_img_01\">"));
}
TEST_F(DedupInlinedImagesTest, DontInlineLargeImage) {
TestDedupImages("dont_inline_large_image", "", "",
StrCat("<img src='", kPuzzleJpgFilename, "'>"),
StrCat("<img src='", kPuzzleJpgFilename, "'>"));
}
TEST_F(DedupInlinedImagesTest, DedupSecondSmallImage) {
// Add an id to the first occurence and convert the second to JavaScript.
TestDedupImages(
"dedup_second_small_image", "", "",
StrCat("<img src='", kCuppaPngFilename, "'>\n",
"<img src='", kCuppaPngFilename, "'>"),
StrCat("<img src='", kCuppaPngInlineData,
"' id=\"pagespeed_img_01\">\n",
InsertScriptBefore(
StrCat("<img id=\"pagespeed_img_02\">",
StringPrintf(kInlinedScriptFormat, 3, 1, 2, 3)))));
}
TEST_F(DedupInlinedImagesTest, DedupManySmallImages) {
// Add an id to the first occurence and convert the following to JavaScript.
GoogleString image = StrCat("<img src='", kCuppaPngFilename, "'>");
TestDedupImages(
"dedup_many_small_images", "", "",
StrCat(image, "\n", image, "\n", image),
StrCat("<img src='", kCuppaPngInlineData, "' id=\"pagespeed_img_01\">\n",
InsertScriptBefore(
StrCat("<img id=\"pagespeed_img_02\">",
StringPrintf(kInlinedScriptFormat, 3, 1, 2, 3), "\n",
"<img id=\"pagespeed_img_04\">",
StringPrintf(kInlinedScriptFormat, 5, 1, 4, 5)))));
}
TEST_F(DedupInlinedImagesTest, DedupSecondSmallImageWithId) {
// Keep the id on the first occurence and convert the second to JavaScript.
TestDedupImages("dedup_second_small_image_with_id", "", "",
StrCat("<img src='", kCuppaPngFilename, "' id='xyzzy'>\n",
"<img src='", kCuppaPngFilename, "'>"),
StrCat("<img src='", kCuppaPngInlineData, "' id='xyzzy'>\n",
InsertScriptBefore(
"<img id=\"pagespeed_img_01\">"
"<script type=\"text/javascript\""
" id=\"pagespeed_script_2\""
" data-pagespeed-no-defer>"
"pagespeed.dedupInlinedImages.inlineImg("
"'xyzzy',"
"'pagespeed_img_01',"
"'pagespeed_script_2');"
"</script>")));
}
TEST_F(DedupInlinedImagesTest, DedupSecondSmallImageWithAttributes) {
// Keep all the attributes.
TestDedupImages("dedup_second_small_image_with_attributes", "", "",
StrCat("<img src='", kCuppaPngFilename, "'>\n",
"<img src='", kCuppaPngFilename,
"' alt='xyzzy' id='plugh'>"),
StrCat("<img src='", kCuppaPngInlineData,
"' id=\"pagespeed_img_01\">\n",
InsertScriptBefore(
"<img alt='xyzzy' id='plugh'>"
"<script type=\"text/javascript\""
" id=\"pagespeed_script_2\""
" data-pagespeed-no-defer>"
"pagespeed.dedupInlinedImages.inlineImg("
"'pagespeed_img_01',"
"'plugh',"
"'pagespeed_script_2');"
"</script>")));
}
TEST_F(DedupInlinedImagesTest, DisabledForOldBlackberry) {
// This UA doesn't support LazyloadImages so nor does it support deduping.
SetCurrentUserAgent(
UserAgentMatcherTestBase::kBlackBerryOS5UserAgent);
GoogleString case_id("disabled_for_old_blackberry");
GoogleString repeated_inlined_image = StrCat(
"<img src='", kCuppaPngFilename, "'>\n",
"<img src='", kCuppaPngFilename, "'>");
GoogleString html_in_out(StringPrintf(
kHtmlWrapperFormat, "", repeated_inlined_image.c_str()));
Parse(case_id, html_in_out);
GoogleString expected_out = doctype_string_ + AddHtmlBody(html_in_out);
EXPECT_EQ(expected_out, output_buffer_) << "Test id:" << case_id;
output_buffer_.clear();
}
class DedupInlinePreviewImagesTest : public DedupInlinedImagesTest {
public:
DedupInlinePreviewImagesTest() : DedupInlinedImagesTest() {}
virtual void SetFiltersAndOptions() {
options()->EnableFilter(RewriteOptions::kDedupInlinedImages);
options()->EnableFilter(RewriteOptions::kDelayImages);
options()->set_min_image_size_low_resolution_bytes(1 * 1024);
options()->set_max_inlined_preview_images_index(-1);
}
GoogleString GetNoscript() const {
return StringPrintf(
kNoScriptRedirectFormatter,
"http://test.com/dedup_inline_preview_images.html?PageSpeed=noscript",
"http://test.com/dedup_inline_preview_images.html?PageSpeed=noscript");
}
GoogleString GetImageOnloadScriptBlock() const {
return StrCat("<script data-pagespeed-no-defer type=\"text/javascript\">",
DelayImagesFilter::kImageOnloadJsSnippet,
"</script>");
}
GoogleString GenerateRewrittenImageTag(const GoogleString& url,
const GoogleString& low_res_src) {
return StrCat("<img data-pagespeed-high-res-src='", url, "'",
" src='", low_res_src, "'"
" onload='", DelayImagesFilter::kImageOnloadCode, "'/>");
}
};
TEST_F(DedupInlinePreviewImagesTest, DedupInlinePreviewImages) {
GoogleString image_filename = StrCat(kTestDomain, kCuppaPngFilename);
GoogleString input_img = StrCat("<img src='", image_filename, "'/>");
GoogleString inlined_img =
StrCat("<img data-pagespeed-high-res-src='", image_filename,
"' src=\"", kCuppaPngWildcardData,
"\" onload=\"", DelayImagesFilter::kImageOnloadCode,
"\" onerror=\"this.onerror=null;",
DelayImagesFilter::kImageOnloadCode,
"\" id=\"pagespeed_img_01\"/>");
GoogleString scripted_img_fmt =
StrCat("<img data-pagespeed-high-res-src='", image_filename,
"' onload=\"", DelayImagesFilter::kImageOnloadCode,
"\" onerror=\"this.onerror=null;",
DelayImagesFilter::kImageOnloadCode,
"\" id=\"pagespeed_img_0%d\"/>");
GoogleString scripted_img_1 = StringPrintf(scripted_img_fmt.c_str(), 2);
GoogleString scripted_img_2 = StringPrintf(scripted_img_fmt.c_str(), 4);
GoogleString script_1 = StringPrintf(kInlinedScriptFormat, 3, 1, 2, 3);
GoogleString script_2 = StringPrintf(kInlinedScriptFormat, 5, 1, 4, 5);
GoogleString input_html = StrCat("<head></head>"
"<body>",
input_img, input_img, input_img,
"</body>");
GoogleString output_html = StrCat("<head></head>"
"<body>",
GetNoscript(),
GetImageOnloadScriptBlock(),
inlined_img,
InsertScriptBefore(
StrCat(scripted_img_1, script_1,
scripted_img_2, script_2)),
"</body>");
// Since the preview image has been resized use a wildcard to match it.
Parse("dedup_inline_preview_images", input_html);
GoogleString full_html = StrCat(doctype_string_, AddHtmlBody(output_html));
EXPECT_TRUE(Wildcard(full_html).Match(output_buffer_))
<< "Output_Html:\n" << full_html << "\n"
<< "Got:\n" << output_buffer_;
}
} // namespace
} // namespace net_instaweb