2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "FrameTestHelpers.h"
33 #include "URLTestHelpers.h"
35 #include "WebPageSerializer.h"
36 #include "WebPageSerializerClient.h"
37 #include "WebScriptSource.h"
38 #include "WebSettings.h"
40 #include "core/dom/Document.h"
41 #include "public/platform/Platform.h"
42 #include "public/platform/WebString.h"
43 #include "public/platform/WebThread.h"
44 #include "public/platform/WebURL.h"
45 #include "public/platform/WebURLRequest.h"
46 #include "public/platform/WebURLResponse.h"
47 #include "public/platform/WebUnitTestSupport.h"
48 #include "public/web/WebDocument.h"
49 #include <gtest/gtest.h>
51 using namespace blink;
52 using WebCore::Document;
53 using blink::FrameTestHelpers::runPendingTasks;
54 using blink::URLTestHelpers::toKURL;
55 using blink::URLTestHelpers::registerMockedURLLoad;
61 LineReader(const std::string& text) : m_text(text), m_index(0) { }
62 bool getNextLine(std::string* line)
65 if (m_index >= m_text.length())
68 size_t endOfLineIndex = m_text.find("\r\n", m_index);
69 if (endOfLineIndex == std::string::npos) {
70 *line = m_text.substr(m_index);
71 m_index = m_text.length();
73 *line = m_text.substr(m_index, endOfLineIndex - m_index);
74 m_index = endOfLineIndex + 2;
84 class LengthCountingWebPageSerializerClient : public WebPageSerializerClient {
86 LengthCountingWebPageSerializerClient(size_t* counter)
91 virtual void didSerializeDataForFrame(const WebURL& frameURL, const WebCString& data, PageSerializationStatus status) {
92 *m_counter += data.length();
99 class WebPageNewSerializeTest : public testing::Test {
101 WebPageNewSerializeTest()
102 : m_htmlMimeType(WebString::fromUTF8("text/html"))
103 , m_xhtmlMimeType(WebString::fromUTF8("application/xhtml+xml"))
104 , m_cssMimeType(WebString::fromUTF8("text/css"))
105 , m_pngMimeType(WebString::fromUTF8("image/png"))
106 , m_svgMimeType(WebString::fromUTF8("image/svg+xml"))
113 // We want the images to load and JavaScript to be on.
114 m_helper.initialize(true, 0, 0, &configureSettings);
117 virtual void TearDown()
119 Platform::current()->unitTestSupport()->unregisterAllMockedURLs();
122 WebURL setUpCSSTestPage()
124 WebURL topFrameURL = toKURL("http://www.test.com");
125 registerMockedURLLoad(topFrameURL, WebString::fromUTF8("css_test_page.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
126 registerMockedURLLoad(toKURL("http://www.test.com/link_styles.css"), WebString::fromUTF8("link_styles.css"), WebString::fromUTF8("pageserializer/"), cssMimeType());
127 registerMockedURLLoad(toKURL("http://www.test.com/import_style_from_link.css"), WebString::fromUTF8("import_style_from_link.css"), WebString::fromUTF8("pageserializer/"), cssMimeType());
128 registerMockedURLLoad(toKURL("http://www.test.com/import_styles.css"), WebString::fromUTF8("import_styles.css"), WebString::fromUTF8("pageserializer/"), cssMimeType());
129 registerMockedURLLoad(toKURL("http://www.test.com/red_background.png"), WebString::fromUTF8("red_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
130 registerMockedURLLoad(toKURL("http://www.test.com/orange_background.png"), WebString::fromUTF8("orange_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
131 registerMockedURLLoad(toKURL("http://www.test.com/yellow_background.png"), WebString::fromUTF8("yellow_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
132 registerMockedURLLoad(toKURL("http://www.test.com/green_background.png"), WebString::fromUTF8("green_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
133 registerMockedURLLoad(toKURL("http://www.test.com/blue_background.png"), WebString::fromUTF8("blue_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
134 registerMockedURLLoad(toKURL("http://www.test.com/purple_background.png"), WebString::fromUTF8("purple_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
135 registerMockedURLLoad(toKURL("http://www.test.com/ul-dot.png"), WebString::fromUTF8("ul-dot.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
136 registerMockedURLLoad(toKURL("http://www.test.com/ol-dot.png"), WebString::fromUTF8("ol-dot.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
140 void loadURLInTopFrame(const WebURL& url)
142 WebURLRequest urlRequest;
143 urlRequest.initialize();
144 urlRequest.setURL(url);
145 m_helper.webView()->mainFrame()->loadRequest(urlRequest);
146 // Make sure any pending request get served.
147 Platform::current()->unitTestSupport()->serveAsynchronousMockedRequests();
148 // Some requests get delayed, run the timer.
150 // Server the delayed resources.
151 Platform::current()->unitTestSupport()->serveAsynchronousMockedRequests();
154 const WebString& htmlMimeType() const { return m_htmlMimeType; }
155 const WebString& xhtmlMimeType() const { return m_xhtmlMimeType; }
156 const WebString& cssMimeType() const { return m_cssMimeType; }
157 const WebString& pngMimeType() const { return m_pngMimeType; }
158 const WebString& svgMimeType() const { return m_svgMimeType; }
160 static bool resourceVectorContains(const WebVector<WebPageSerializer::Resource>& resources, const char* url, const char* mimeType)
162 WebURL webURL = WebURL(toKURL(url));
163 for (size_t i = 0; i < resources.size(); ++i) {
164 const WebPageSerializer::Resource& resource = resources[i];
165 if (resource.url == webURL && !resource.data.isEmpty() && !resource.mimeType.compare(WebCString(mimeType)))
171 WebView* webView() const { return m_helper.webView(); }
174 static void configureSettings(WebSettings* settings)
176 settings->setImagesEnabled(true);
177 settings->setLoadsImagesAutomatically(true);
178 settings->setJavaScriptEnabled(true);
181 FrameTestHelpers::WebViewHelper m_helper;
182 WebString m_htmlMimeType;
183 WebString m_xhtmlMimeType;
184 WebString m_cssMimeType;
185 WebString m_pngMimeType;
186 WebString m_svgMimeType;
189 // Tests that a page with resources and sub-frame is reported with all its resources.
190 TEST_F(WebPageNewSerializeTest, PageWithFrames)
192 // Register the mocked frames.
193 WebURL topFrameURL = toKURL("http://www.test.com");
194 registerMockedURLLoad(topFrameURL, WebString::fromUTF8("top_frame.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
195 registerMockedURLLoad(toKURL("http://www.test.com/iframe.html"), WebString::fromUTF8("iframe.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
196 registerMockedURLLoad(toKURL("http://www.test.com/iframe2.html"), WebString::fromUTF8("iframe2.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
197 registerMockedURLLoad(toKURL("http://www.test.com/red_background.png"), WebString::fromUTF8("red_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
198 registerMockedURLLoad(toKURL("http://www.test.com/green_background.png"), WebString::fromUTF8("green_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
199 registerMockedURLLoad(toKURL("http://www.test.com/blue_background.png"), WebString::fromUTF8("blue_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
201 loadURLInTopFrame(topFrameURL);
202 // OBJECT/EMBED have some delay to start to load their content. The first
203 // serveAsynchronousMockedRequests call in loadURLInTopFrame() finishes
205 RefPtr<Document> document = static_cast<PassRefPtr<Document> >(webView()->mainFrame()->document());
206 document->updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasksSynchronously);
207 Platform::current()->unitTestSupport()->serveAsynchronousMockedRequests();
209 WebVector<WebPageSerializer::Resource> resources;
210 WebPageSerializer::serialize(webView(), &resources);
211 ASSERT_FALSE(resources.isEmpty());
213 // The first resource should be the main-frame.
214 const WebPageSerializer::Resource& resource = resources[0];
215 EXPECT_TRUE(resource.url == WebURL(toKURL("http://www.test.com")));
216 EXPECT_EQ(0, resource.mimeType.compare(WebCString("text/html")));
217 EXPECT_FALSE(resource.data.isEmpty());
219 EXPECT_EQ(6U, resources.size()); // There should be no duplicates.
220 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/red_background.png", "image/png"));
221 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/green_background.png", "image/png"));
222 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/blue_background.png", "image/png"));
223 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/iframe.html", "text/html"));
224 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/iframe2.html", "text/html"));
227 // Test that when serializing a page, all CSS resources are reported, including url()'s
228 // and imports and links. Note that we don't test the resources contents, we only make sure
229 // they are all reported with the right mime type and that they contain some data.
230 TEST_F(WebPageNewSerializeTest, FAILS_CSSResources)
232 // Register the mocked frame and load it.
233 WebURL topFrameURL = setUpCSSTestPage();
234 loadURLInTopFrame(topFrameURL);
236 WebVector<WebPageSerializer::Resource> resources;
237 WebPageSerializer::serialize(webView(), &resources);
238 ASSERT_FALSE(resources.isEmpty());
240 // The first resource should be the main-frame.
241 const WebPageSerializer::Resource& resource = resources[0];
242 EXPECT_TRUE(resource.url == WebURL(toKURL("http://www.test.com")));
243 EXPECT_EQ(0, resource.mimeType.compare(WebCString("text/html")));
244 EXPECT_FALSE(resource.data.isEmpty());
246 EXPECT_EQ(12U, resources.size()); // There should be no duplicates.
247 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/link_styles.css", "text/css"));
248 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/import_styles.css", "text/css"));
249 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/import_style_from_link.css", "text/css"));
250 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/red_background.png", "image/png"));
251 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/orange_background.png", "image/png"));
252 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/yellow_background.png", "image/png"));
253 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/green_background.png", "image/png"));
254 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/blue_background.png", "image/png"));
255 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/purple_background.png", "image/png"));
256 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/ul-dot.png", "image/png"));
257 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/ol-dot.png", "image/png"));
260 // Tests that when serializing a page with blank frames these are reported with their resources.
261 TEST_F(WebPageNewSerializeTest, BlankFrames)
263 // Register the mocked frame and load it.
264 WebURL topFrameURL = toKURL("http://www.test.com");
265 registerMockedURLLoad(topFrameURL, WebString::fromUTF8("blank_frames.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
266 registerMockedURLLoad(toKURL("http://www.test.com/red_background.png"), WebString::fromUTF8("red_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
267 registerMockedURLLoad(toKURL("http://www.test.com/orange_background.png"), WebString::fromUTF8("orange_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
268 registerMockedURLLoad(toKURL("http://www.test.com/blue_background.png"), WebString::fromUTF8("blue_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
270 loadURLInTopFrame(topFrameURL);
272 WebVector<WebPageSerializer::Resource> resources;
273 WebPageSerializer::serialize(webView(), &resources);
274 ASSERT_FALSE(resources.isEmpty());
276 // The first resource should be the main-frame.
277 const WebPageSerializer::Resource& resource = resources[0];
278 EXPECT_TRUE(resource.url == WebURL(toKURL("http://www.test.com")));
279 EXPECT_EQ(0, resource.mimeType.compare(WebCString("text/html")));
280 EXPECT_FALSE(resource.data.isEmpty());
282 EXPECT_EQ(7U, resources.size()); // There should be no duplicates.
283 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/red_background.png", "image/png"));
284 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/orange_background.png", "image/png"));
285 EXPECT_TRUE(resourceVectorContains(resources, "http://www.test.com/blue_background.png", "image/png"));
286 // The blank frames should have got a magic URL.
287 EXPECT_TRUE(resourceVectorContains(resources, "wyciwyg://frame/0", "text/html"));
288 EXPECT_TRUE(resourceVectorContains(resources, "wyciwyg://frame/1", "text/html"));
289 EXPECT_TRUE(resourceVectorContains(resources, "wyciwyg://frame/2", "text/html"));
292 TEST_F(WebPageNewSerializeTest, SerializeXMLHasRightDeclaration)
294 WebURL topFrameURL = toKURL("http://www.test.com/simple.xhtml");
295 registerMockedURLLoad(topFrameURL, WebString::fromUTF8("simple.xhtml"), WebString::fromUTF8("pageserializer/"), xhtmlMimeType());
297 loadURLInTopFrame(topFrameURL);
299 WebVector<WebPageSerializer::Resource> resources;
300 WebPageSerializer::serialize(webView(), &resources);
301 ASSERT_FALSE(resources.isEmpty());
303 // We expect only one resource, the XML.
304 ASSERT_EQ(1U, resources.size());
305 std::string xml = std::string(resources[0].data.data());
307 // We should have one and only one instance of the XML declaration.
308 size_t pos = xml.find("<?xml version=");
309 ASSERT_TRUE(pos != std::string::npos);
311 pos = xml.find("<?xml version=", pos + 1);
312 ASSERT_TRUE(pos == std::string::npos);
315 TEST_F(WebPageNewSerializeTest, FAILS_TestMHTMLEncoding)
317 // Load a page with some CSS and some images.
318 WebURL topFrameURL = setUpCSSTestPage();
319 loadURLInTopFrame(topFrameURL);
321 WebCString mhtmlData = WebPageSerializer::serializeToMHTML(webView());
322 ASSERT_FALSE(mhtmlData.isEmpty());
324 // Read the MHTML data line per line and do some pseudo-parsing to make sure the right encoding is used for the different sections.
325 LineReader lineReader(std::string(mhtmlData.data()));
326 int sectionCheckedCount = 0;
327 const char* expectedEncoding = 0;
329 while (lineReader.getNextLine(&line)) {
330 if (!line.find("Content-Type:")) {
331 ASSERT_FALSE(expectedEncoding);
332 if (line.find("multipart/related;") != std::string::npos) {
333 // Skip this one, it's part of the MHTML header.
336 if (line.find("text/") != std::string::npos)
337 expectedEncoding = "quoted-printable";
338 else if (line.find("image/") != std::string::npos)
339 expectedEncoding = "base64";
341 FAIL() << "Unexpected Content-Type: " << line;
344 if (!line.find("Content-Transfer-Encoding:")) {
345 ASSERT_TRUE(expectedEncoding);
346 EXPECT_TRUE(line.find(expectedEncoding) != std::string::npos);
347 expectedEncoding = 0;
348 sectionCheckedCount++;
351 EXPECT_EQ(12, sectionCheckedCount);
354 // Test that we don't regress https://bugs.webkit.org/show_bug.cgi?id=99105
355 TEST_F(WebPageNewSerializeTest, SVGImageDontCrash)
357 WebURL pageUrl = toKURL("http://www.test.com");
358 WebURL imageUrl = toKURL("http://www.test.com/green_rectangle.svg");
360 registerMockedURLLoad(pageUrl, WebString::fromUTF8("page_with_svg_image.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
361 registerMockedURLLoad(imageUrl, WebString::fromUTF8("green_rectangle.svg"), WebString::fromUTF8("pageserializer/"), svgMimeType());
363 loadURLInTopFrame(pageUrl);
365 WebCString mhtml = WebPageSerializer::serializeToMHTML(webView());
366 // We expect some data to be generated.
367 EXPECT_GT(mhtml.length(), 50U);
371 TEST_F(WebPageNewSerializeTest, DontIncludeErrorImage)
373 WebURL pageUrl = toKURL("http://www.test.com");
374 WebURL imageUrl = toKURL("http://www.test.com/error_image.png");
376 registerMockedURLLoad(pageUrl, WebString::fromUTF8("page_with_img_error.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
377 registerMockedURLLoad(imageUrl, WebString::fromUTF8("error_image.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
379 loadURLInTopFrame(pageUrl);
381 WebCString mhtmlData = WebPageSerializer::serializeToMHTML(webView());
382 ASSERT_FALSE(mhtmlData.isEmpty());
384 // Sniff the MHTML data to make sure image content is excluded.
385 LineReader lineReader(std::string(mhtmlData.data()));
387 while (lineReader.getNextLine(&line)) {
388 if (line.find("image/") != std::string::npos)
389 FAIL() << "Error Image was not excluded " << line;
394 TEST_F(WebPageNewSerializeTest, NamespaceElementsDontCrash)
396 WebURL pageUrl = toKURL("http://www.test.com");
397 registerMockedURLLoad(pageUrl, WebString::fromUTF8("namespace_element.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
399 loadURLInTopFrame(pageUrl);
401 WebVector<WebURL> localLinks(static_cast<size_t>(1));
402 WebVector<WebString> localPaths(static_cast<size_t>(1));
403 localLinks[0] = pageUrl;
404 localPaths[0] = WebString("/");
407 LengthCountingWebPageSerializerClient client(&counter);
409 // We just want to make sure nothing crazy happens, namely that no
410 // assertions are hit. As a sanity check, we also make sure that some data
412 WebPageSerializer::serialize(webView()->mainFrame(), true, &client, localLinks, localPaths, WebString(""));
414 EXPECT_GT(counter, 0U);
419 TEST_F(WebPageNewSerializeTest, TestMHTMLEncodingWithDataURL)
421 // Load a page with some data urls.
422 WebURL topFrameURL = toKURL("http://www.test.com");
423 registerMockedURLLoad(topFrameURL, WebString::fromUTF8("page_with_data.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
424 loadURLInTopFrame(topFrameURL);
426 WebCString mhtmlData = WebPageSerializer::serializeToMHTML(webView());
427 ASSERT_FALSE(mhtmlData.isEmpty());
429 // Read the MHTML data line and check that the string data:image is found
431 size_t nbDataURLs = 0;
432 LineReader lineReader(std::string(mhtmlData.data()));
434 while (lineReader.getNextLine(&line)) {
435 if (line.find("data:image") != std::string::npos)
438 EXPECT_EQ(1u, nbDataURLs);
442 TEST_F(WebPageNewSerializeTest, TestMHTMLEncodingWithMorphingDataURL)
444 // Load a page with some data urls.
445 WebURL topFrameURL = toKURL("http://www.test.com");
446 registerMockedURLLoad(topFrameURL, WebString::fromUTF8("page_with_morphing_data.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
447 loadURLInTopFrame(topFrameURL);
449 WebCString mhtmlData = WebPageSerializer::serializeToMHTML(webView());
450 ASSERT_FALSE(mhtmlData.isEmpty());
452 // Read the MHTML data line and check that the string data:image is found
453 // exactly two times.
454 size_t nbDataURLs = 0;
455 LineReader lineReader(std::string(mhtmlData.data()));
457 while (lineReader.getNextLine(&line)) {
458 if (line.find("data:text") != std::string::npos)
461 EXPECT_EQ(2u, nbDataURLs);