blob: 8818167f8f2260e081240547efa826dbd0148c9d [file] [log] [blame]
/*
* Copyright 2012 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/local_storage_cache_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/html/html_parse_test_base.h"
#include "pagespeed/kernel/http/content_type.h"
#include "pagespeed/kernel/http/http_names.h"
#include "pagespeed/kernel/http/request_headers.h"
namespace net_instaweb {
namespace {
// Filenames of resource files.
const char kJunkCssFilename[] = "http://example.com/junk.css"; // NOT test.com
const char kStylesCssFilename[] = "styles.css";
const char kCuppaPngFilename[] = "Cuppa.png";
const char kPuzzleJpgFilename[] = "Puzzle.jpg";
// Contents of resource files.
const char kJunkCssContents[] =
"@import url(junk://junk.com);";
const char kStylesCssContents[] =
".background_cyan{background-color:#0ff}"
".foreground_pink{color:#ffc0cb}";
const char kCuppaPngInlineData[] =
"data:image/png;base64,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";
const char kCuppaPng30sqInlineData[] =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAAAAAAeW/F+AAABLU"
"lEQVQoz2P4jxcwUEv6z4H//99u+/7/1U2s0pv0//+tMP74vzUbm/Qvm6D/TyRj/v3XzsIm/U"
"iy+P8Zxtz/f1nzsUl/VdH/91xe681/G9U32OyOYbz0N4tx+f/FzIuxSc9m7Pm/nSHx3wOGYm"
"zScxnn/N/M0PR/O+sqbNIJUh/+psjf/x/o8R2L9De19f9faK//f0fmHjaX/5vy4//z5t//7y"
"3ECNTXxyDg6CEQAcTHPyBLr2FAB72rTv2ES+/DkFZ3NCz4DZO+hCF94fd1jRsw6W8saLIyH/"
"+/MbsEd7kmmrTfr38bTN7DpQvRpCf+WyYzG+HvSxyo0k56egv/IaT/1DAiSXKlrjv1HSXUPq"
"Uxw2XZp/zGCNR/m+WhsvpbsCbFi7Mg4BUtsgEZ0gD3t6kusa+ehQAAAABJRU5ErkJggg==";
const char kCuppaPng150sqInlineData[] =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEEAAABGCAAAAAC2maYhAAACyU"
"lEQVRYw+3XXWjVdRzH8ffZXE5wK4T0SnFCzJYXSlghofiADwQmEyy6G01KSBEKDNMlgXMYyI"
"aRj3SRESlOnIweoDJR1FLQkEQsrTnFqQg+rrkdz7uLc86F+j/n/H4EQbjvze/m/3vx//8ePl"
"/++G+LQeFxED7vyI4dH3Wq2tWSjhOOMVvVsxW8r2oTh6OEzIvUq7oAvlDNjGN9lLAP3lM9nY"
"KTqj/BO1HCy/CZ6mKoSqu+BR/GCL8DZ9RMNcxU9XlojxH2QLXqH0CDaroSDsUIrVB+V+0Cpq"
"r2lcMnMUIn8L3qaBh6VbUOpsQI3cBi1TeAZtVV5DYldC9GwIh+9RtgzH21qwzejhEWAd9mDx"
"J0qs6FqtsRQgfQqLoSeFX1K+DLCOEiMFr1F+DJ/NKsiLkXwyD1t3oL4JJ6G1gWczfHAldVn4"
"Bh93IvszPmHSqhRvU8MEN1G5RfjhD+ApbnF3BD7q4tjdnNjTD0ouo8qLunnoBRN2KEabBS9c"
"8UqYOqDbAj5lRfKaO2T/XT3EnsqmB+VNKegt2q1lN7J/tVT/dECTeZlFF1PD+rup6tkWn/Qi"
"5O5r2SHXc92x8pDORDvzs79nb/Vz1r+wcBtfpYEWE6IbWuiDA/SGje39qy5Vqy0EBwVTf3Jg"
"nvBgPlMD1JWBs0e8mPZ65nGuFogrApSDiiOlDDwgRhdwhQdlfV1UxMEH4LEWqzz37M5AShvy"
"JAeD37bFu+Cz54qp8LEFryGfRmkvBa6EJ6EA4kCetKA0+lVS/XUZNJEs6WFupVLzwDm5Nv9+"
"SSwibtaxsJSwrkQ2tJ4bv2pjHAnIECQs+QoGM5YUe6YEY1lZxdNbFhf7GUG3ip6PSpe3tK5u"
"S5qiJXoi0oafdWFhQ2BGb10bGFgiE47W81Jn7CmvsR/aJ9+CPArF/jOs7XqYeA5dE96/gPD1"
"bv4P/moPC/Ef4BwgJ0BoZbWwQAAAAASUVORK5CYII=";
class LocalStorageCacheTest : public RewriteTestBase,
public ::testing::WithParamInterface<bool> {
protected:
virtual void SetUp() {
RewriteTestBase::SetUp();
MySetUp();
}
void MySetUp() {
options()->EnableFilter(RewriteOptions::kInlineCss);
options()->EnableFilter(RewriteOptions::kInlineImages);
options()->EnableFilter(RewriteOptions::kLocalStorageCache);
options()->set_image_inline_max_bytes(2000);
rewrite_driver()->AddFilters();
SetResponseWithDefaultHeaders(kJunkCssFilename, kContentTypeCss,
kJunkCssContents, 100);
SetResponseWithDefaultHeaders(kStylesCssFilename, kContentTypeCss,
kStylesCssContents, 100);
AddFileToMockFetcher(StrCat(kTestDomain, kCuppaPngFilename),
kCuppaPngFilename, kContentTypePng, 100);
AddFileToMockFetcher(StrCat(kTestDomain, kPuzzleJpgFilename),
kPuzzleJpgFilename, kContentTypeJpeg, 100);
StaticAssetManager* static_asset_manager =
server_context()->static_asset_manager();
local_storage_cache_js_ =
StrCat("<script type=\"text/javascript\" data-pagespeed-no-defer>"
"//<![CDATA[\n",
static_asset_manager->GetAsset(
StaticAssetEnum::LOCAL_STORAGE_CACHE_JS, options()),
LocalStorageCacheFilter::kLscInitializer,
"\n//]]></script>");
}
void TestLocalStorage(const StringPiece& case_id,
const GoogleString& head_html_in,
const GoogleString& head_html_out,
const GoogleString& body_html_in,
const GoogleString& body_html_out) {
const char kInWrapperFormat[] =
"<head>\n"
" <title>Local Storage Cache Test</title>\n"
"%s"
"</head>\n"
"<body>\n"
"%s"
"</body>\n";
const GoogleString out_wrapper_format = StrCat(
"<head>\n"
" <title>Local Storage Cache Test</title>\n"
"%s"
"</head>\n"
"<body>",
kNoScriptRedirectFormatter, "\n"
"%s"
"</body>\n");
GoogleString url = StrCat(
"http://test.com/", case_id, ".html?PageSpeed=noscript");
GoogleString html_in(StringPrintf(
kInWrapperFormat, head_html_in.c_str(), body_html_in.c_str()));
GoogleString html_out(StringPrintf(
out_wrapper_format.c_str(), head_html_out.c_str(), url.c_str(),
url.c_str(), body_html_out.c_str()));
// Clear request_headers and set them afresh for every test.
ClearRewriteDriver();
rewrite_driver()->SetRequestHeaders(request_headers_);
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(local_storage_cache_js_, snippet);
}
RequestHeaders request_headers_;
GoogleString local_storage_cache_js_;
};
TEST_F(LocalStorageCacheTest, Simple) {
TestLocalStorage("simple", "", "", "<div/>", "<div/>");
}
TEST_F(LocalStorageCacheTest, Link) {
TestLocalStorage(
"link",
"<link rel='stylesheet' href='styles.css'>",
InsertScriptBefore(
"<style "
"data-pagespeed-lsc-url=\"http://test.com/styles.css\" "
"data-pagespeed-lsc-hash=\"0\" "
"data-pagespeed-lsc-expiry=\"Tue, 02 Feb 2010 18:53:06 GMT\">"
".background_cyan{background-color:#0ff}"
".foreground_pink{color:#ffc0cb}"
"</style>"),
"<div/>", "<div/>");
}
TEST_F(LocalStorageCacheTest, LinkRewriteContextNotExecuted) {
// The domains are different so the RewriteContext is never kicked off,
// which should result in no local storage cache changes at all.
TestLocalStorage("link_rewrite_context_not_executed",
"<link rel='stylesheet' href='http://example.com/junk.css'>",
"<link rel='stylesheet' href='http://example.com/junk.css'>",
"<div/>", "<div/>");
}
TEST_F(LocalStorageCacheTest, LinkUrlTransormationFails) {
// The CSS rewriting fails so the local storage cache attributes are omitted
// but because the CSS rewriting is asynchronous we still insert the JS even
// though it ends up not being used. C'est la vie!
AddDomain("example.com");
TestLocalStorage("link_url_transormation_fails",
"<link rel='stylesheet' href='http://example.com/junk.css'>",
InsertScriptBefore(
"<link rel='stylesheet' "
"href='http://example.com/junk.css'>"),
"<div/>", "<div/>");
}
class LocalStorageCacheTinyTest : public LocalStorageCacheTest {
protected:
virtual void SetUp() {
RewriteTestBase::SetUp();
options()->set_css_inline_max_bytes(10); // An arbitrary tiny value.
MySetUp();
}
};
TEST_F(LocalStorageCacheTinyTest, LinkDontInline) {
// The CSS inlining fails because we've turned the limit down low. We expect
// no LSC attributes in the result but because the CSS rewriting is
// asynchronous we still insert the JS even though it ends up not being used.
TestLocalStorage("link_dont_inline",
"<link rel='stylesheet' href='styles.css'>",
InsertScriptBefore(
"<link rel='stylesheet' href='styles.css'>"),
"<div/>", "<div/>");
}
TEST_F(LocalStorageCacheTest, Img) {
TestLocalStorage("img", "", "",
StrCat("<img src='", kCuppaPngFilename, "'>"),
InsertScriptBefore(
StrCat("<img src='", kCuppaPngInlineData,
"' data-pagespeed-lsc-url="
"\"", kTestDomain, kCuppaPngFilename, "\""
" data-pagespeed-lsc-hash=\"0\""
" data-pagespeed-lsc-expiry="
"\"Tue, 02 Feb 2010 18:53:06 GMT\""
">")));
}
TEST_F(LocalStorageCacheTest, ImgTooBig) {
TestLocalStorage("img_too_big", "", "",
StrCat("<img src='", kPuzzleJpgFilename, "'>"),
InsertScriptBefore(
StrCat("<img src='", kPuzzleJpgFilename, "'>")));
}
TEST_F(LocalStorageCacheTest, ImgLocalStorageDisabled) {
options()->ClearSignatureForTesting();
// Enabling another filter that triggers the NOSCRIPT tag-insertion in HTML.
options()->EnableFilter(RewriteOptions::kDeferIframe);
options()->DisableFilter(RewriteOptions::kLocalStorageCache);
options()->set_in_place_rewriting_enabled(true);
server_context()->ComputeSignature(options());
TestLocalStorage("img_local_storage_disabled", "", "",
StrCat("<img src='", kPuzzleJpgFilename, "'>"),
StrCat("<img src='", kPuzzleJpgFilename, "'>"));
}
TEST_F(LocalStorageCacheTest, CookieSet) {
// The 2 hash values are Fe1SLPZ14c and du_OhARrJl. Only suppress the first.
UseMd5Hasher();
GoogleString cookie = StrCat(LocalStorageCacheFilter::kLscCookieName,
"=Fe1SLPZ14c");
request_headers_.Add(HttpAttributes::kCookie, cookie);
TestLocalStorage("cookie_set",
StrCat("<link rel='stylesheet' href='",
kStylesCssFilename,
"'>"),
InsertScriptBefore(
StrCat("<script data-pagespeed-no-defer>"
"pagespeed.localStorageCache.inlineCss("
"\"", kTestDomain, kStylesCssFilename, "\""
");</script>")),
StrCat("<img src='", kCuppaPngFilename, "'>"),
StrCat("<img src='", kCuppaPngInlineData,
"' data-pagespeed-lsc-url="
"\"", kTestDomain, kCuppaPngFilename, "\""
" data-pagespeed-lsc-hash=\"du_OhARrJl\""
" data-pagespeed-lsc-expiry="
"\"Tue, 02 Feb 2010 18:53:06 GMT\""
">"));
}
TEST_F(LocalStorageCacheTest, RepeatViews) {
// The 2 hash values are Fe1SLPZ14c (CSS) and du_OhARrJl (image).
UseMd5Hasher();
GoogleString css = StrCat("<link rel='stylesheet' href='",
kStylesCssFilename,
"'>");
GoogleString img = StrCat("<img src='", kCuppaPngFilename, "'>");
// First view shouldn't rewrite anything though lsc_url attributes are added.
// Don't rewrite because in the real world the fetch and processing of the
// resource could take longer than the rewriting timeout, and we want to
// simulate that here. We redo it below with the rewriting completing in time.
GoogleString external_css = StrCat("<link rel='stylesheet' href="
"'", kStylesCssFilename, "'"
" data-pagespeed-lsc-url="
"\"", kTestDomain, kStylesCssFilename, "\""
">");
GoogleString external_img = StrCat("<img src="
"'", kCuppaPngFilename, "'"
" data-pagespeed-lsc-url="
"\"", kTestDomain, kCuppaPngFilename, "\""
">");
SetupWaitFetcher();
TestLocalStorage("first_view",
css, InsertScriptBefore(external_css),
img, external_img);
CallFetcherCallbacks();
// Second view will inline them both and add an expiry to both.
GoogleString inlined_css = StrCat("<style data-pagespeed-lsc-url="
"\"", kTestDomain, kStylesCssFilename, "\""
" data-pagespeed-lsc-hash=\"Fe1SLPZ14c\""
" data-pagespeed-lsc-expiry="
"\"Tue, 02 Feb 2010 18:53:06 GMT\""
">",
kStylesCssContents,
"</style>");
GoogleString inlined_img = StrCat("<img src='", kCuppaPngInlineData,
"' data-pagespeed-lsc-url="
"\"", kTestDomain, kCuppaPngFilename, "\""
" data-pagespeed-lsc-hash=\"du_OhARrJl\""
" data-pagespeed-lsc-expiry="
"\"Tue, 02 Feb 2010 18:53:06 GMT\""
">");
TestLocalStorage("second_view",
css, InsertScriptBefore(inlined_css),
img, inlined_img);
// The JavaScript would set these cookies for the next request.
GoogleString cookie = StrCat(LocalStorageCacheFilter::kLscCookieName,
"=", "Fe1SLPZ14c", "!", "du_OhARrJl");
request_headers_.Add(HttpAttributes::kCookie, cookie);
// Third view will not send the inlined data and will send scripts in place
// of the link and img elements.
GoogleString scripted_css = StrCat("<script data-pagespeed-no-defer>"
"pagespeed.localStorageCache.inlineCss("
"\"", kTestDomain, kStylesCssFilename, "\""
");</script>");
GoogleString scripted_img = StrCat("<script data-pagespeed-no-defer>"
"pagespeed.localStorageCache.inlineImg("
"\"", kTestDomain, kCuppaPngFilename, "\""
", \"du_OhARrJl\");</script>");
TestLocalStorage("third_view",
css, InsertScriptBefore(scripted_css),
img, scripted_img);
}
TEST_F(LocalStorageCacheTest, RepeatViewsWithOtherAttributes) {
// The 2 hash values are Fe1SLPZ14c (CSS) and du_OhARrJl (image).
UseMd5Hasher();
GoogleString css = StrCat("<link rel='stylesheet' href='",
kStylesCssFilename,
"'>");
GoogleString img = StrCat("<img src='", kCuppaPngFilename, "'"
" alt='A cup of joe'"
" alt=\"A cup of joe\""
" alt='A cup of joe&#39;s \"joe\"'"
" alt=\"A cup of joe's &quot;joe&quot;\">");
// First view shouldn't rewrite anything though lsc_url attributes are added.
// Don't rewrite because in the real world the fetch and processing of the
// resource could take longer than the rewriting timeout, and we want to
// simulate that here. We redo it below with the rewriting completing in time.
GoogleString external_css = StrCat("<link rel='stylesheet' href="
"'", kStylesCssFilename, "'"
" data-pagespeed-lsc-url="
"\"", kTestDomain, kStylesCssFilename, "\""
">");
GoogleString external_img = StrCat("<img src="
"'", kCuppaPngFilename, "'"
" alt='A cup of joe'"
" alt=\"A cup of joe\""
" alt='A cup of joe&#39;s \"joe\"'"
" alt=\"A cup of joe's &quot;joe&quot;\""
" data-pagespeed-lsc-url="
"\"", kTestDomain, kCuppaPngFilename, "\""
">");
SetupWaitFetcher();
TestLocalStorage("first_view",
css, InsertScriptBefore(external_css),
img, external_img);
CallFetcherCallbacks();
// Second view will inline them both and add an expiry to both.
GoogleString inlined_css = StrCat("<style"
" data-pagespeed-lsc-url="
"\"", kTestDomain, kStylesCssFilename, "\""
" data-pagespeed-lsc-hash=\"Fe1SLPZ14c\""
" data-pagespeed-lsc-expiry="
"\"Tue, 02 Feb 2010 18:53:06 GMT\""
">",
kStylesCssContents,
"</style>");
GoogleString inlined_img = StrCat("<img src='", kCuppaPngInlineData, "'"
" alt='A cup of joe'"
" alt=\"A cup of joe\""
" alt='A cup of joe&#39;s \"joe\"'"
" alt=\"A cup of joe's &quot;joe&quot;\""
" data-pagespeed-lsc-url="
"\"", kTestDomain, kCuppaPngFilename, "\""
" data-pagespeed-lsc-hash=\"du_OhARrJl\""
" data-pagespeed-lsc-expiry="
"\"Tue, 02 Feb 2010 18:53:06 GMT\""
">");
TestLocalStorage("second_view",
css, InsertScriptBefore(inlined_css),
img, inlined_img);
// The JavaScript would set these cookies for the next request.
GoogleString cookie = StrCat(LocalStorageCacheFilter::kLscCookieName,
"=", "Fe1SLPZ14c", "!", "du_OhARrJl");
request_headers_.Add(HttpAttributes::kCookie, cookie);
// Third view will not send the inlined data and will send scripts in place
// of the link and img elements.
GoogleString scripted_css = StrCat("<script data-pagespeed-no-defer>"
"pagespeed.localStorageCache.inlineCss("
"\"", kTestDomain, kStylesCssFilename, "\""
");</script>");
GoogleString scripted_img = StrCat("<script data-pagespeed-no-defer>"
"pagespeed.localStorageCache.inlineImg("
"\"", kTestDomain, kCuppaPngFilename, "\""
", \"du_OhARrJl\""
", \"alt=A cup of joe\""
", \"alt=A cup of joe\""
", \"alt=A cup of joe\\'s \\\"joe\\\"\""
", \"alt=A cup of joe\\'s \\\"joe\\\"\""
");</script>");
TestLocalStorage("third_view",
css, InsertScriptBefore(scripted_css),
img, scripted_img);
}
TEST_F(LocalStorageCacheTest, RepeatViewsOfSameImageAtDifferentSizes) {
// Enable "rewrite_images" so that the first instance of the image is
// rescaled.
options()->ClearSignatureForTesting();
options()->EnableFilter(RewriteOptions::kConvertGifToPng);
options()->EnableFilter(RewriteOptions::kConvertJpegToProgressive);
options()->EnableFilter(RewriteOptions::kInlineImages);
options()->EnableFilter(RewriteOptions::kJpegSubsampling);
options()->EnableFilter(RewriteOptions::kRecompressJpeg);
options()->EnableFilter(RewriteOptions::kRecompressPng);
options()->EnableFilter(RewriteOptions::kRecompressWebp);
options()->EnableFilter(RewriteOptions::kResizeImages);
options()->EnableFilter(RewriteOptions::kStripImageColorProfile);
options()->EnableFilter(RewriteOptions::kStripImageMetaData);
server_context()->ComputeSignature(options());
UseMd5Hasher();
const char kHash30x30[] = "07FPv8sBor";
const char kHash150x150[] = "jSr1gEyima";
GoogleString imgs = StrCat("<img src='", kCuppaPngFilename, "'"
"width=\"30\" height=\"30\">"
"<img src='", kCuppaPngFilename, "'"
"width=\"150\" height=\"150\">");
// First view shouldn't rewrite anything though lsc_url attributes are added.
// Don't rewrite because in the real world the fetch and processing of the
// resource could take longer than the rewriting timeout, and we want to
// simulate that here. We redo it below with the rewriting completing in time.
GoogleString external_imgs =
StrCat("<img src='", kCuppaPngFilename, "'"
" width=\"30\" height=\"30\""
" data-pagespeed-lsc-url=""\"",
kTestDomain, kCuppaPngFilename, "\">",
"<img src='", kCuppaPngFilename, "'"
" width=\"150\" height=\"150\""
" data-pagespeed-lsc-url=\"",
kTestDomain, kCuppaPngFilename, "\">");
SetupWaitFetcher();
TestLocalStorage("first_view", "", "",
imgs, InsertScriptBefore(external_imgs));
CallFetcherCallbacks();
// Second view will inline them and add an expiry.
GoogleString inlined_imgs =
StrCat("<img src='", kCuppaPng30sqInlineData, "'"
// This is dropped; see below for why.
// " width=\"30\" height=\"30\""
" data-pagespeed-lsc-url=\"",
kTestDomain, kCuppaPngFilename, "\""
" data-pagespeed-lsc-hash=\"", kHash30x30, "\""
" data-pagespeed-lsc-expiry="
"\"Tue, 02 Feb 2010 18:53:06 GMT\">",
"<img src='", kCuppaPng150sqInlineData, "'"
" width=\"150\" height=\"150\""
" data-pagespeed-lsc-url=\"",
kTestDomain, kCuppaPngFilename, "\""
" data-pagespeed-lsc-hash=\"", kHash150x150, "\""
" data-pagespeed-lsc-expiry="
"\"Tue, 02 Feb 2010 18:53:06 GMT\">");
// NOTE: Why are width=30 and height=30 dropped from the first img tag?
// Because the image rewriter calls DeleteMatchingImageDimsAfterInline for
// each inlined image, and at this point the cached version of Cuppa.png is
// the 30x30 version, so the attributes are stripped, but the 150x150 version
// is different so its attributes are kept.
// TODO(matterbury): Work out if the image rewriter needs to be smarter about
// cached versions on inline images in this situation: same image, inlined
// at different resolutions.
TestLocalStorage("second_view", "", "",
imgs, InsertScriptBefore(inlined_imgs));
// The JavaScript would set this cookie for the next request.
GoogleString cookie = StrCat(LocalStorageCacheFilter::kLscCookieName,
"=", kHash30x30, "!", kHash150x150);
request_headers_.Add(HttpAttributes::kCookie, cookie);
// Third view will not send the inlined data and will send scripts in place
// of the link and img elements.
GoogleString scripted_imgs =
StrCat("<script data-pagespeed-no-defer>"
"pagespeed.localStorageCache.inlineImg("
"\"", kTestDomain, kCuppaPngFilename,
"\", \"", kHash30x30, "\""
", \"width=30\", \"height=30\""
");</script>",
"<script data-pagespeed-no-defer>"
"pagespeed.localStorageCache.inlineImg("
"\"", kTestDomain, kCuppaPngFilename,
"\", \"", kHash150x150, "\""
", \"width=150\", \"height=150\""
");</script>");
TestLocalStorage("third_view", "", "",
imgs, InsertScriptBefore(scripted_imgs));
}
} // namespace
} // namespace net_instaweb