| /* |
| * 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 |