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 "core/testing/URLTestHelpers.h"
33 #include "public/platform/Platform.h"
34 #include "public/platform/WebString.h"
35 #include "public/platform/WebThread.h"
36 #include "public/platform/WebURL.h"
37 #include "public/platform/WebURLRequest.h"
38 #include "public/platform/WebURLResponse.h"
39 #include "public/platform/WebUnitTestSupport.h"
40 #include "public/web/WebDocument.h"
41 #include "public/web/WebFrame.h"
42 #include "public/web/WebPageSerializer.h"
43 #include "public/web/WebPageSerializerClient.h"
44 #include "public/web/WebScriptSource.h"
45 #include "public/web/WebSettings.h"
46 #include "public/web/WebView.h"
47 #include "web/tests/FrameTestHelpers.h"
48 #include <gtest/gtest.h>
52 using blink::FrameTestHelpers::runPendingTasks;
53 using blink::URLTestHelpers::toKURL;
54 using blink::URLTestHelpers::registerMockedURLLoad;
55 using namespace blink;
59 LineReader(const std::string& text) : m_text(text), m_index(0) { }
60 bool getNextLine(std::string* line)
63 if (m_index >= m_text.length())
66 size_t endOfLineIndex = m_text.find("\r\n", m_index);
67 if (endOfLineIndex == std::string::npos) {
68 *line = m_text.substr(m_index);
69 m_index = m_text.length();
71 *line = m_text.substr(m_index, endOfLineIndex - m_index);
72 m_index = endOfLineIndex + 2;
82 class LengthCountingWebPageSerializerClient : public WebPageSerializerClient {
84 LengthCountingWebPageSerializerClient(size_t* counter)
89 virtual void didSerializeDataForFrame(const WebURL& frameURL, const WebCString& data, PageSerializationStatus status) {
90 *m_counter += data.length();
97 class FrameDataWebPageSerializerClient : public WebPageSerializerClient {
99 FrameDataWebPageSerializerClient(const WebURL& frameURL, WebString* serializationData)
100 : m_frameURL(frameURL)
101 , m_serializationData(serializationData)
105 virtual void didSerializeDataForFrame(const WebURL& frameURL, const WebCString& data, PageSerializationStatus status)
107 if (frameURL != m_frameURL)
109 *m_serializationData = data.utf16();
114 WebString* m_serializationData;
117 class WebPageNewSerializeTest : public testing::Test {
119 WebPageNewSerializeTest()
120 : m_baseURL("http://internal.test/")
121 , m_htmlMimeType(WebString::fromUTF8("text/html"))
122 , m_xhtmlMimeType(WebString::fromUTF8("application/xhtml+xml"))
123 , m_cssMimeType(WebString::fromUTF8("text/css"))
124 , m_pngMimeType(WebString::fromUTF8("image/png"))
125 , m_svgMimeType(WebString::fromUTF8("image/svg+xml"))
132 // We want the images to load and JavaScript to be on.
133 m_helper.initialize(true, 0, 0, &configureSettings);
136 virtual void TearDown()
138 Platform::current()->unitTestSupport()->unregisterAllMockedURLs();
141 KURL toTestURL(std::string relativeURL)
143 return toKURL(m_baseURL + relativeURL);
146 WebURL setUpCSSTestPage()
148 registerMockedURLLoad(toTestURL(""), WebString::fromUTF8("css_test_page.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
149 registerMockedURLLoad(toTestURL("link_styles.css"), WebString::fromUTF8("link_styles.css"), WebString::fromUTF8("pageserializer/"), cssMimeType());
150 registerMockedURLLoad(toTestURL("import_style_from_link.css"), WebString::fromUTF8("import_style_from_link.css"), WebString::fromUTF8("pageserializer/"), cssMimeType());
151 registerMockedURLLoad(toTestURL("import_styles.css"), WebString::fromUTF8("import_styles.css"), WebString::fromUTF8("pageserializer/"), cssMimeType());
152 registerMockedURLLoad(toTestURL("red_background.png"), WebString::fromUTF8("red_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
153 registerMockedURLLoad(toTestURL("orange_background.png"), WebString::fromUTF8("orange_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
154 registerMockedURLLoad(toTestURL("yellow_background.png"), WebString::fromUTF8("yellow_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
155 registerMockedURLLoad(toTestURL("green_background.png"), WebString::fromUTF8("green_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
156 registerMockedURLLoad(toTestURL("blue_background.png"), WebString::fromUTF8("blue_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
157 registerMockedURLLoad(toTestURL("purple_background.png"), WebString::fromUTF8("purple_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
158 registerMockedURLLoad(toTestURL("ul-dot.png"), WebString::fromUTF8("ul-dot.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
159 registerMockedURLLoad(toTestURL("ol-dot.png"), WebString::fromUTF8("ol-dot.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
161 return toKURL(m_baseURL); // Top frame URL.
164 void loadURLInTopFrame(const WebURL& url)
166 FrameTestHelpers::loadFrame(m_helper.webView()->mainFrame(), url.string().utf8());
169 const WebString& htmlMimeType() const { return m_htmlMimeType; }
170 const WebString& xhtmlMimeType() const { return m_xhtmlMimeType; }
171 const WebString& cssMimeType() const { return m_cssMimeType; }
172 const WebString& pngMimeType() const { return m_pngMimeType; }
173 const WebString& svgMimeType() const { return m_svgMimeType; }
175 static bool resourceVectorContains(const WebVector<WebPageSerializer::Resource>& resources, std::string url, const char* mimeType)
177 WebURL webURL = WebURL(toKURL(url));
178 for (size_t i = 0; i < resources.size(); ++i) {
179 const WebPageSerializer::Resource& resource = resources[i];
180 if (resource.url == webURL && !resource.data.isEmpty() && !resource.mimeType.compare(WebCString(mimeType)))
186 WebView* webView() const { return m_helper.webView(); }
188 std::string m_baseURL;
191 static void configureSettings(WebSettings* settings)
193 settings->setImagesEnabled(true);
194 settings->setLoadsImagesAutomatically(true);
195 settings->setJavaScriptEnabled(true);
198 FrameTestHelpers::WebViewHelper m_helper;
200 WebString m_htmlMimeType;
201 WebString m_xhtmlMimeType;
202 WebString m_cssMimeType;
203 WebString m_pngMimeType;
204 WebString m_svgMimeType;
207 // Tests that a page with resources and sub-frame is reported with all its resources.
208 TEST_F(WebPageNewSerializeTest, PageWithFrames)
210 // Register the mocked frames.
211 registerMockedURLLoad(toTestURL(""), WebString::fromUTF8("top_frame.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
212 registerMockedURLLoad(toTestURL("iframe.html"), WebString::fromUTF8("iframe.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
213 registerMockedURLLoad(toTestURL("iframe2.html"), WebString::fromUTF8("iframe2.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
214 registerMockedURLLoad(toTestURL("red_background.png"), WebString::fromUTF8("red_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
215 registerMockedURLLoad(toTestURL("green_background.png"), WebString::fromUTF8("green_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
216 registerMockedURLLoad(toTestURL("blue_background.png"), WebString::fromUTF8("blue_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
218 loadURLInTopFrame(toKURL(m_baseURL));
220 WebVector<WebPageSerializer::Resource> resources;
221 WebPageSerializer::serialize(webView(), &resources);
222 ASSERT_FALSE(resources.isEmpty());
224 // The first resource should be the main-frame.
225 const WebPageSerializer::Resource& resource = resources[0];
226 EXPECT_TRUE(resource.url == WebURL(toKURL(m_baseURL)));
227 EXPECT_EQ(0, resource.mimeType.compare(WebCString("text/html")));
228 EXPECT_FALSE(resource.data.isEmpty());
230 EXPECT_EQ(6U, resources.size()); // There should be no duplicates.
231 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "red_background.png", "image/png"));
232 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "green_background.png", "image/png"));
233 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "blue_background.png", "image/png"));
234 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "iframe.html", "text/html"));
235 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "iframe2.html", "text/html"));
238 // Test that when serializing a page, all CSS resources are reported, including url()'s
239 // and imports and links. Note that we don't test the resources contents, we only make sure
240 // they are all reported with the right mime type and that they contain some data.
241 TEST_F(WebPageNewSerializeTest, FAILS_CSSResources)
243 // Register the mocked frame and load it.
244 WebURL topFrameURL = setUpCSSTestPage();
245 loadURLInTopFrame(topFrameURL);
247 WebVector<WebPageSerializer::Resource> resources;
248 WebPageSerializer::serialize(webView(), &resources);
249 ASSERT_FALSE(resources.isEmpty());
251 // The first resource should be the main-frame.
252 const WebPageSerializer::Resource& resource = resources[0];
253 EXPECT_TRUE(resource.url == WebURL(toKURL(m_baseURL)));
254 EXPECT_EQ(0, resource.mimeType.compare(WebCString("text/html")));
255 EXPECT_FALSE(resource.data.isEmpty());
257 EXPECT_EQ(12U, resources.size()); // There should be no duplicates.
258 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "link_styles.css", "text/css"));
259 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "import_styles.css", "text/css"));
260 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "import_style_from_link.css", "text/css"));
261 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "red_background.png", "image/png"));
262 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "orange_background.png", "image/png"));
263 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "yellow_background.png", "image/png"));
264 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "green_background.png", "image/png"));
265 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "blue_background.png", "image/png"));
266 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "purple_background.png", "image/png"));
267 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "ul-dot.png", "image/png"));
268 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "ol-dot.png", "image/png"));
271 // Tests that when serializing a page with blank frames these are reported with their resources.
272 TEST_F(WebPageNewSerializeTest, BlankFrames)
274 // Register the mocked frame and load it.
275 WebURL topFrameURL = toKURL(m_baseURL);
276 registerMockedURLLoad(topFrameURL, WebString::fromUTF8("blank_frames.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
277 registerMockedURLLoad(toTestURL("red_background.png"), WebString::fromUTF8("red_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
278 registerMockedURLLoad(toTestURL("orange_background.png"), WebString::fromUTF8("orange_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
279 registerMockedURLLoad(toTestURL("blue_background.png"), WebString::fromUTF8("blue_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
281 loadURLInTopFrame(topFrameURL);
283 WebVector<WebPageSerializer::Resource> resources;
284 WebPageSerializer::serialize(webView(), &resources);
285 ASSERT_FALSE(resources.isEmpty());
287 // The first resource should be the main-frame.
288 const WebPageSerializer::Resource& resource = resources[0];
289 EXPECT_TRUE(resource.url == WebURL(toKURL(m_baseURL)));
290 EXPECT_EQ(0, resource.mimeType.compare(WebCString("text/html")));
291 EXPECT_FALSE(resource.data.isEmpty());
293 EXPECT_EQ(7U, resources.size()); // There should be no duplicates.
294 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "red_background.png", "image/png"));
295 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "orange_background.png", "image/png"));
296 EXPECT_TRUE(resourceVectorContains(resources, m_baseURL + "blue_background.png", "image/png"));
297 // The blank frames should have got a magic URL.
298 EXPECT_TRUE(resourceVectorContains(resources, "wyciwyg://frame/0", "text/html"));
299 EXPECT_TRUE(resourceVectorContains(resources, "wyciwyg://frame/1", "text/html"));
300 EXPECT_TRUE(resourceVectorContains(resources, "wyciwyg://frame/2", "text/html"));
303 TEST_F(WebPageNewSerializeTest, SerializeXMLHasRightDeclaration)
305 WebURL topFrameURL = toTestURL("simple.xhtml");
306 registerMockedURLLoad(topFrameURL, WebString::fromUTF8("simple.xhtml"), WebString::fromUTF8("pageserializer/"), xhtmlMimeType());
308 loadURLInTopFrame(topFrameURL);
310 WebVector<WebPageSerializer::Resource> resources;
311 WebPageSerializer::serialize(webView(), &resources);
312 ASSERT_FALSE(resources.isEmpty());
314 // We expect only one resource, the XML.
315 ASSERT_EQ(1U, resources.size());
316 std::string xml = std::string(resources[0].data.data());
318 // We should have one and only one instance of the XML declaration.
319 size_t pos = xml.find("<?xml version=");
320 ASSERT_TRUE(pos != std::string::npos);
322 pos = xml.find("<?xml version=", pos + 1);
323 ASSERT_TRUE(pos == std::string::npos);
326 TEST_F(WebPageNewSerializeTest, FAILS_TestMHTMLEncoding)
328 // Load a page with some CSS and some images.
329 WebURL topFrameURL = setUpCSSTestPage();
330 loadURLInTopFrame(topFrameURL);
332 WebCString mhtmlData = WebPageSerializer::serializeToMHTML(webView());
333 ASSERT_FALSE(mhtmlData.isEmpty());
335 // Read the MHTML data line per line and do some pseudo-parsing to make sure the right encoding is used for the different sections.
336 LineReader lineReader(std::string(mhtmlData.data()));
337 int sectionCheckedCount = 0;
338 const char* expectedEncoding = 0;
340 while (lineReader.getNextLine(&line)) {
341 if (!line.find("Content-Type:")) {
342 ASSERT_FALSE(expectedEncoding);
343 if (line.find("multipart/related;") != std::string::npos) {
344 // Skip this one, it's part of the MHTML header.
347 if (line.find("text/") != std::string::npos)
348 expectedEncoding = "quoted-printable";
349 else if (line.find("image/") != std::string::npos)
350 expectedEncoding = "base64";
352 FAIL() << "Unexpected Content-Type: " << line;
355 if (!line.find("Content-Transfer-Encoding:")) {
356 ASSERT_TRUE(expectedEncoding);
357 EXPECT_TRUE(line.find(expectedEncoding) != std::string::npos);
358 expectedEncoding = 0;
359 sectionCheckedCount++;
362 EXPECT_EQ(12, sectionCheckedCount);
365 // Test that we don't regress https://bugs.webkit.org/show_bug.cgi?id=99105
366 TEST_F(WebPageNewSerializeTest, SVGImageDontCrash)
368 WebURL pageUrl = toKURL(m_baseURL);
369 WebURL imageUrl = toTestURL("green_rectangle.svg");
371 registerMockedURLLoad(pageUrl, WebString::fromUTF8("page_with_svg_image.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
372 registerMockedURLLoad(imageUrl, WebString::fromUTF8("green_rectangle.svg"), WebString::fromUTF8("pageserializer/"), svgMimeType());
374 loadURLInTopFrame(pageUrl);
376 WebCString mhtml = WebPageSerializer::serializeToMHTML(webView());
377 // We expect some data to be generated.
378 EXPECT_GT(mhtml.length(), 50U);
382 TEST_F(WebPageNewSerializeTest, DontIncludeErrorImage)
384 WebURL pageUrl = toKURL(m_baseURL);
385 WebURL imageUrl = toTestURL("error_image.png");
387 registerMockedURLLoad(pageUrl, WebString::fromUTF8("page_with_img_error.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
388 registerMockedURLLoad(imageUrl, WebString::fromUTF8("error_image.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
390 loadURLInTopFrame(pageUrl);
392 WebCString mhtmlData = WebPageSerializer::serializeToMHTML(webView());
393 ASSERT_FALSE(mhtmlData.isEmpty());
395 // Sniff the MHTML data to make sure image content is excluded.
396 LineReader lineReader(std::string(mhtmlData.data()));
398 while (lineReader.getNextLine(&line)) {
399 if (line.find("image/") != std::string::npos)
400 FAIL() << "Error Image was not excluded " << line;
405 TEST_F(WebPageNewSerializeTest, NamespaceElementsDontCrash)
407 WebURL pageUrl = toKURL(m_baseURL);
408 registerMockedURLLoad(pageUrl, WebString::fromUTF8("namespace_element.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
410 loadURLInTopFrame(pageUrl);
412 WebVector<WebURL> localLinks(static_cast<size_t>(1));
413 WebVector<WebString> localPaths(static_cast<size_t>(1));
414 localLinks[0] = pageUrl;
415 localPaths[0] = WebString("/");
418 LengthCountingWebPageSerializerClient client(&counter);
420 // We just want to make sure nothing crazy happens, namely that no
421 // assertions are hit. As a sanity check, we also make sure that some data
423 WebPageSerializer::serialize(webView()->mainFrame()->toWebLocalFrame(), true, &client, localLinks, localPaths, WebString(""));
425 EXPECT_GT(counter, 0U);
428 TEST_F(WebPageNewSerializeTest, SubFrameSerialization)
430 WebURL pageUrl = toKURL(m_baseURL);
431 registerMockedURLLoad(pageUrl, WebString::fromUTF8("top_frame.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
432 registerMockedURLLoad(toTestURL("iframe.html"), WebString::fromUTF8("iframe.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
433 registerMockedURLLoad(toTestURL("iframe2.html"), WebString::fromUTF8("iframe2.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
434 registerMockedURLLoad(toTestURL("red_background.png"), WebString::fromUTF8("red_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
435 registerMockedURLLoad(toTestURL("green_background.png"), WebString::fromUTF8("green_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
436 registerMockedURLLoad(toTestURL("blue_background.png"), WebString::fromUTF8("blue_background.png"), WebString::fromUTF8("pageserializer/"), pngMimeType());
438 loadURLInTopFrame(pageUrl);
440 WebVector<WebURL> localLinks(static_cast<size_t>(2));
441 WebVector<WebString> localPaths(static_cast<size_t>(2));
442 localLinks[0] = pageUrl;
443 localPaths[0] = WebString("/");
444 localLinks[1] = toTestURL("iframe.html");
445 localPaths[1] = WebString("SavedFiles/iframe.html");
447 WebString serializedData;
448 FrameDataWebPageSerializerClient client(pageUrl, &serializedData);
450 // We just want to make sure nothing crazy happens, namely that no
451 // assertions are hit. As a sanity check, we also make sure that some data
453 WebPageSerializer::serialize(webView()->mainFrame()->toWebLocalFrame(), true, &client, localLinks, localPaths, WebString(""));
456 EXPECT_TRUE(static_cast<String>(serializedData).contains("src=\"SavedFiles/iframe.html\""));
461 TEST_F(WebPageNewSerializeTest, TestMHTMLEncodingWithDataURL)
463 // Load a page with some data urls.
464 WebURL topFrameURL = toKURL(m_baseURL);
465 registerMockedURLLoad(topFrameURL, WebString::fromUTF8("page_with_data.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
466 loadURLInTopFrame(topFrameURL);
468 WebCString mhtmlData = WebPageSerializer::serializeToMHTML(webView());
469 ASSERT_FALSE(mhtmlData.isEmpty());
471 // Read the MHTML data line and check that the string data:image is found
473 size_t nbDataURLs = 0;
474 LineReader lineReader(std::string(mhtmlData.data()));
476 while (lineReader.getNextLine(&line)) {
477 if (line.find("data:image") != std::string::npos)
480 EXPECT_EQ(1u, nbDataURLs);
484 TEST_F(WebPageNewSerializeTest, TestMHTMLEncodingWithMorphingDataURL)
486 // Load a page with some data urls.
487 WebURL topFrameURL = toKURL(m_baseURL);
488 registerMockedURLLoad(topFrameURL, WebString::fromUTF8("page_with_morphing_data.html"), WebString::fromUTF8("pageserializer/"), htmlMimeType());
489 loadURLInTopFrame(topFrameURL);
491 WebCString mhtmlData = WebPageSerializer::serializeToMHTML(webView());
492 ASSERT_FALSE(mhtmlData.isEmpty());
494 // Read the MHTML data line and check that the string data:image is found
495 // exactly two times.
496 size_t nbDataURLs = 0;
497 LineReader lineReader(std::string(mhtmlData.data()));
499 while (lineReader.getNextLine(&line)) {
500 if (line.find("data:text") != std::string::npos)
503 EXPECT_EQ(2u, nbDataURLs);