2 * Copyright (C) 2012 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.
33 #include "core/testing/URLTestHelpers.h"
34 #include "public/web/WebCache.h"
35 #include "public/web/WebDocument.h"
36 #include "public/web/WebElement.h"
37 #include "public/web/WebFrame.h"
38 #include "public/web/WebNode.h"
39 #include "public/web/WebNodeList.h"
40 #include "public/web/WebPrerendererClient.h"
41 #include "public/web/WebScriptSource.h"
42 #include "public/web/WebView.h"
43 #include "public/web/WebViewClient.h"
44 #include "web/tests/FrameTestHelpers.h"
46 #include "public/platform/Platform.h"
47 #include "public/platform/WebPrerender.h"
48 #include "public/platform/WebPrerenderingSupport.h"
49 #include "public/platform/WebString.h"
50 #include "public/platform/WebUnitTestSupport.h"
51 #include "wtf/OwnPtr.h"
53 #include <gtest/gtest.h>
56 using namespace blink;
57 using blink::URLTestHelpers::toKURL;
61 WebURL toWebURL(const char* url)
63 return WebURL(toKURL(url));
66 class TestPrerendererClient : public WebPrerendererClient {
68 TestPrerendererClient() { }
69 virtual ~TestPrerendererClient() { }
71 void setExtraDataForNextPrerender(WebPrerender::ExtraData* extraData)
74 m_extraData = adoptPtr(extraData);
77 WebPrerender releaseWebPrerender()
79 ASSERT(!m_webPrerenders.empty());
80 WebPrerender retval(m_webPrerenders.front());
81 m_webPrerenders.pop_front();
87 return m_webPrerenders.empty();
92 m_webPrerenders.clear();
96 // From WebPrerendererClient:
97 virtual void willAddPrerender(WebPrerender* prerender) override
99 prerender->setExtraData(m_extraData.leakPtr());
101 ASSERT(!prerender->isNull());
102 m_webPrerenders.push_back(*prerender);
105 OwnPtr<WebPrerender::ExtraData> m_extraData;
106 std::list<WebPrerender> m_webPrerenders;
109 class TestPrerenderingSupport : public WebPrerenderingSupport {
111 TestPrerenderingSupport()
116 virtual ~TestPrerenderingSupport()
123 m_addedPrerenders.clear();
124 m_canceledPrerenders.clear();
125 m_abandonedPrerenders.clear();
128 size_t totalCount() const
130 return m_addedPrerenders.size() + m_canceledPrerenders.size() + m_abandonedPrerenders.size();
133 size_t addCount(const WebPrerender& prerender) const
135 return std::count_if(m_addedPrerenders.begin(), m_addedPrerenders.end(), std::bind1st(WebPrerenderEqual(), prerender));
138 size_t cancelCount(const WebPrerender& prerender) const
140 return std::count_if(m_canceledPrerenders.begin(), m_canceledPrerenders.end(), std::bind1st(WebPrerenderEqual(), prerender));
143 size_t abandonCount(const WebPrerender& prerender) const
145 return std::count_if(m_abandonedPrerenders.begin(), m_abandonedPrerenders.end(), std::bind1st(WebPrerenderEqual(), prerender));
149 class WebPrerenderEqual : public std::binary_function<WebPrerender, WebPrerender, bool> {
151 bool operator()(const WebPrerender& first, const WebPrerender& second) const
153 return first.toPrerender() == second.toPrerender();
157 // From WebPrerenderingSupport:
158 virtual void add(const WebPrerender& prerender) override
160 m_addedPrerenders.push_back(prerender);
163 virtual void cancel(const WebPrerender& prerender) override
165 m_canceledPrerenders.push_back(prerender);
168 virtual void abandon(const WebPrerender& prerender) override
170 m_abandonedPrerenders.push_back(prerender);
173 std::vector<WebPrerender> m_addedPrerenders;
174 std::vector<WebPrerender> m_canceledPrerenders;
175 std::vector<WebPrerender> m_abandonedPrerenders;
178 class PrerenderingTest : public testing::Test {
182 Platform::current()->unitTestSupport()->unregisterAllMockedURLs();
185 void initialize(const char* baseURL, const char* fileName)
187 URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(baseURL), WebString::fromUTF8(fileName));
188 const bool RunJavascript = true;
189 m_webViewHelper.initialize(RunJavascript);
190 m_webViewHelper.webView()->setPrerendererClient(&m_prerendererClient);
192 FrameTestHelpers::loadFrame(m_webViewHelper.webView()->mainFrame(), std::string(baseURL) + fileName);
197 FrameTestHelpers::loadFrame(m_webViewHelper.webView()->mainFrame(), "about:blank");
202 m_webViewHelper.webView()->mainFrame()->collectGarbage();
203 m_webViewHelper.reset();
210 WebElement console = m_webViewHelper.webView()->mainFrame()->document().getElementById("console");
211 ASSERT(console.nodeName() == "UL");
215 unsigned consoleLength()
217 return console().childNodes().length() - 1;
220 std::string consoleAt(unsigned i)
222 ASSERT(consoleLength() > i);
224 WebNode consoleListItem = console().childNodes().item(1 + i);
225 ASSERT(consoleListItem.nodeName() == "LI");
226 ASSERT(consoleListItem.hasChildNodes());
228 WebNode textNode = consoleListItem.firstChild();
229 ASSERT(textNode.nodeName() == "#text");
231 return textNode.nodeValue().utf8().data();
234 void executeScript(const char* code)
236 m_webViewHelper.webView()->mainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(code)));
239 TestPrerenderingSupport* prerenderingSupport()
241 return &m_prerenderingSupport;
244 TestPrerendererClient* prerendererClient()
246 return &m_prerendererClient;
250 TestPrerenderingSupport m_prerenderingSupport;
251 TestPrerendererClient m_prerendererClient;
253 FrameTestHelpers::WebViewHelper m_webViewHelper;
256 TEST_F(PrerenderingTest, SinglePrerender)
258 initialize("http://www.foo.com/", "prerender/single_prerender.html");
260 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
261 EXPECT_FALSE(webPrerender.isNull());
262 EXPECT_EQ(toWebURL("http://prerender.com/"), webPrerender.url());
263 EXPECT_EQ(PrerenderRelTypePrerender, webPrerender.relTypes());
265 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
266 EXPECT_EQ(1u, prerenderingSupport()->totalCount());
268 webPrerender.didStartPrerender();
269 EXPECT_EQ(1u, consoleLength());
270 EXPECT_EQ("webkitprerenderstart", consoleAt(0));
272 webPrerender.didSendDOMContentLoadedForPrerender();
273 EXPECT_EQ(2u, consoleLength());
274 EXPECT_EQ("webkitprerenderdomcontentloaded", consoleAt(1));
276 webPrerender.didSendLoadForPrerender();
277 EXPECT_EQ(3u, consoleLength());
278 EXPECT_EQ("webkitprerenderload", consoleAt(2));
280 webPrerender.didStopPrerender();
281 EXPECT_EQ(4u, consoleLength());
282 EXPECT_EQ("webkitprerenderstop", consoleAt(3));
285 TEST_F(PrerenderingTest, CancelPrerender)
287 initialize("http://www.foo.com/", "prerender/single_prerender.html");
289 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
290 EXPECT_FALSE(webPrerender.isNull());
292 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
293 EXPECT_EQ(1u, prerenderingSupport()->totalCount());
295 executeScript("removePrerender()");
297 EXPECT_EQ(1u, prerenderingSupport()->cancelCount(webPrerender));
298 EXPECT_EQ(2u, prerenderingSupport()->totalCount());
301 TEST_F(PrerenderingTest, AbandonPrerender)
303 initialize("http://www.foo.com/", "prerender/single_prerender.html");
305 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
306 EXPECT_FALSE(webPrerender.isNull());
308 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
309 EXPECT_EQ(1u, prerenderingSupport()->totalCount());
313 EXPECT_EQ(1u, prerenderingSupport()->abandonCount(webPrerender));
314 EXPECT_EQ(2u, prerenderingSupport()->totalCount());
316 // Check that the prerender does not emit an extra cancel when garbage-collecting everything.
319 EXPECT_EQ(2u, prerenderingSupport()->totalCount());
322 TEST_F(PrerenderingTest, ExtraData)
324 class TestExtraData : public WebPrerender::ExtraData {
326 explicit TestExtraData(bool* alive) : m_alive(alive)
331 virtual ~TestExtraData() { *m_alive = false; }
339 prerendererClient()->setExtraDataForNextPrerender(new TestExtraData(&alive));
340 initialize("http://www.foo.com/", "prerender/single_prerender.html");
343 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
345 executeScript("removePrerender()");
347 prerenderingSupport()->clear();
352 TEST_F(PrerenderingTest, TwoPrerenders)
354 initialize("http://www.foo.com/", "prerender/multiple_prerenders.html");
356 WebPrerender firstPrerender = prerendererClient()->releaseWebPrerender();
357 EXPECT_FALSE(firstPrerender.isNull());
358 EXPECT_EQ(toWebURL("http://first-prerender.com/"), firstPrerender.url());
360 WebPrerender secondPrerender = prerendererClient()->releaseWebPrerender();
361 EXPECT_FALSE(firstPrerender.isNull());
362 EXPECT_EQ(toWebURL("http://second-prerender.com/"), secondPrerender.url());
364 EXPECT_EQ(1u, prerenderingSupport()->addCount(firstPrerender));
365 EXPECT_EQ(1u, prerenderingSupport()->addCount(secondPrerender));
366 EXPECT_EQ(2u, prerenderingSupport()->totalCount());
368 firstPrerender.didStartPrerender();
369 EXPECT_EQ(1u, consoleLength());
370 EXPECT_EQ("first_webkitprerenderstart", consoleAt(0));
372 secondPrerender.didStartPrerender();
373 EXPECT_EQ(2u, consoleLength());
374 EXPECT_EQ("second_webkitprerenderstart", consoleAt(1));
377 TEST_F(PrerenderingTest, TwoPrerendersRemovingFirstThenNavigating)
379 initialize("http://www.foo.com/", "prerender/multiple_prerenders.html");
381 WebPrerender firstPrerender = prerendererClient()->releaseWebPrerender();
382 WebPrerender secondPrerender = prerendererClient()->releaseWebPrerender();
384 EXPECT_EQ(1u, prerenderingSupport()->addCount(firstPrerender));
385 EXPECT_EQ(1u, prerenderingSupport()->addCount(secondPrerender));
386 EXPECT_EQ(2u, prerenderingSupport()->totalCount());
388 executeScript("removeFirstPrerender()");
390 EXPECT_EQ(1u, prerenderingSupport()->cancelCount(firstPrerender));
391 EXPECT_EQ(3u, prerenderingSupport()->totalCount());
395 EXPECT_EQ(1u, prerenderingSupport()->abandonCount(secondPrerender));
396 EXPECT_EQ(4u, prerenderingSupport()->totalCount());
399 TEST_F(PrerenderingTest, TwoPrerendersAddingThird)
401 initialize("http://www.foo.com/", "prerender/multiple_prerenders.html");
403 WebPrerender firstPrerender = prerendererClient()->releaseWebPrerender();
404 WebPrerender secondPrerender = prerendererClient()->releaseWebPrerender();
406 EXPECT_EQ(1u, prerenderingSupport()->addCount(firstPrerender));
407 EXPECT_EQ(1u, prerenderingSupport()->addCount(secondPrerender));
408 EXPECT_EQ(2u, prerenderingSupport()->totalCount());
410 executeScript("addThirdPrerender()");
412 WebPrerender thirdPrerender = prerendererClient()->releaseWebPrerender();
413 EXPECT_EQ(1u, prerenderingSupport()->addCount(thirdPrerender));
414 EXPECT_EQ(3u, prerenderingSupport()->totalCount());
417 TEST_F(PrerenderingTest, ShortLivedClient)
419 initialize("http://www.foo.com/", "prerender/single_prerender.html");
421 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
422 EXPECT_FALSE(webPrerender.isNull());
424 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
425 EXPECT_EQ(1u, prerenderingSupport()->totalCount());
430 // This test passes if this next line doesn't crash.
431 webPrerender.didStartPrerender();
434 TEST_F(PrerenderingTest, FastRemoveElement)
436 initialize("http://www.foo.com/", "prerender/single_prerender.html");
438 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
439 EXPECT_FALSE(webPrerender.isNull());
441 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
442 EXPECT_EQ(1u, prerenderingSupport()->totalCount());
444 // Race removing & starting the prerender against each other, as if the element was removed very quickly.
445 executeScript("removePrerender()");
446 EXPECT_FALSE(webPrerender.isNull());
447 webPrerender.didStartPrerender();
449 // The page should be totally disconnected from the Prerender at this point, so the console should not have updated.
450 EXPECT_EQ(0u, consoleLength());
453 TEST_F(PrerenderingTest, MutateTarget)
455 initialize("http://www.foo.com/", "prerender/single_prerender.html");
457 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
458 EXPECT_FALSE(webPrerender.isNull());
459 EXPECT_EQ(toWebURL("http://prerender.com/"), webPrerender.url());
461 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
462 EXPECT_EQ(0u, prerenderingSupport()->cancelCount(webPrerender));
463 EXPECT_EQ(1u, prerenderingSupport()->totalCount());
465 // Change the href of this prerender, make sure this is treated as a remove and add.
466 executeScript("mutateTarget()");
467 EXPECT_EQ(1u, prerenderingSupport()->cancelCount(webPrerender));
469 WebPrerender mutatedPrerender = prerendererClient()->releaseWebPrerender();
470 EXPECT_EQ(toWebURL("http://mutated.com/"), mutatedPrerender.url());
471 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
472 EXPECT_EQ(1u, prerenderingSupport()->addCount(mutatedPrerender));
473 EXPECT_EQ(3u, prerenderingSupport()->totalCount());
476 TEST_F(PrerenderingTest, MutateRel)
478 initialize("http://www.foo.com/", "prerender/single_prerender.html");
480 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
481 EXPECT_FALSE(webPrerender.isNull());
482 EXPECT_EQ(toWebURL("http://prerender.com/"), webPrerender.url());
484 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
485 EXPECT_EQ(0u, prerenderingSupport()->cancelCount(webPrerender));
486 EXPECT_EQ(1u, prerenderingSupport()->totalCount());
488 // Change the rel of this prerender, make sure this is treated as a remove.
489 executeScript("mutateRel()");
490 EXPECT_EQ(1u, prerenderingSupport()->cancelCount(webPrerender));
491 EXPECT_EQ(2u, prerenderingSupport()->totalCount());
494 TEST_F(PrerenderingTest, RelNext)
496 initialize("http://www.foo.com/", "prerender/rel_next_prerender.html");
498 WebPrerender relNextOnly = prerendererClient()->releaseWebPrerender();
499 EXPECT_EQ(toWebURL("http://rel-next-only.com/"), relNextOnly.url());
500 EXPECT_EQ(PrerenderRelTypeNext, relNextOnly.relTypes());
502 WebPrerender relNextAndPrerender = prerendererClient()->releaseWebPrerender();
503 EXPECT_EQ(toWebURL("http://rel-next-and-prerender.com/"), relNextAndPrerender.url());
504 EXPECT_EQ(static_cast<unsigned>(PrerenderRelTypeNext | PrerenderRelTypePrerender), relNextAndPrerender.relTypes());