• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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
13  * distribution.
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.
17  *
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.
29  */
30 
31 #include "config.h"
32 
33 #include "public/web/WebCache.h"
34 #include "public/web/WebDocument.h"
35 #include "public/web/WebElement.h"
36 #include "public/web/WebFrame.h"
37 #include "public/web/WebNode.h"
38 #include "public/web/WebNodeList.h"
39 #include "public/web/WebPrerendererClient.h"
40 #include "public/web/WebScriptSource.h"
41 #include "public/web/WebView.h"
42 #include "public/web/WebViewClient.h"
43 #include "web/tests/FrameTestHelpers.h"
44 #include "web/tests/URLTestHelpers.h"
45 
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"
52 #include <functional>
53 #include <gtest/gtest.h>
54 #include <list>
55 
56 using namespace blink;
57 using blink::URLTestHelpers::toKURL;
58 
59 namespace {
60 
toWebURL(const char * url)61 WebURL toWebURL(const char* url)
62 {
63     return WebURL(toKURL(url));
64 }
65 
66 class TestPrerendererClient : public WebPrerendererClient {
67 public:
TestPrerendererClient()68     TestPrerendererClient() { }
~TestPrerendererClient()69     virtual ~TestPrerendererClient() { }
70 
setExtraDataForNextPrerender(WebPrerender::ExtraData * extraData)71     void setExtraDataForNextPrerender(WebPrerender::ExtraData* extraData)
72     {
73         ASSERT(!m_extraData);
74         m_extraData = adoptPtr(extraData);
75     }
76 
releaseWebPrerender()77     WebPrerender releaseWebPrerender()
78     {
79         ASSERT(!m_webPrerenders.empty());
80         WebPrerender retval(m_webPrerenders.front());
81         m_webPrerenders.pop_front();
82         return retval;
83     }
84 
empty() const85     bool empty() const
86     {
87         return m_webPrerenders.empty();
88     }
89 
clear()90     void clear()
91     {
92         m_webPrerenders.clear();
93     }
94 
95 private:
96     // From WebPrerendererClient:
willAddPrerender(WebPrerender * prerender)97     virtual void willAddPrerender(WebPrerender* prerender) OVERRIDE
98     {
99         prerender->setExtraData(m_extraData.leakPtr());
100 
101         ASSERT(!prerender->isNull());
102         m_webPrerenders.push_back(*prerender);
103     }
104 
105     OwnPtr<WebPrerender::ExtraData> m_extraData;
106     std::list<WebPrerender> m_webPrerenders;
107 };
108 
109 class TestPrerenderingSupport : public WebPrerenderingSupport {
110 public:
TestPrerenderingSupport()111     TestPrerenderingSupport()
112     {
113         initialize(this);
114     }
115 
~TestPrerenderingSupport()116     virtual ~TestPrerenderingSupport()
117     {
118         shutdown();
119     }
120 
clear()121     void clear()
122     {
123         m_addedPrerenders.clear();
124         m_canceledPrerenders.clear();
125         m_abandonedPrerenders.clear();
126     }
127 
totalCount() const128     size_t totalCount() const
129     {
130         return m_addedPrerenders.size() + m_canceledPrerenders.size() + m_abandonedPrerenders.size();
131     }
132 
addCount(const WebPrerender & prerender) const133     size_t addCount(const WebPrerender& prerender) const
134     {
135         return std::count_if(m_addedPrerenders.begin(), m_addedPrerenders.end(), std::bind1st(WebPrerenderEqual(), prerender));
136     }
137 
cancelCount(const WebPrerender & prerender) const138     size_t cancelCount(const WebPrerender& prerender) const
139     {
140         return std::count_if(m_canceledPrerenders.begin(), m_canceledPrerenders.end(), std::bind1st(WebPrerenderEqual(), prerender));
141     }
142 
abandonCount(const WebPrerender & prerender) const143     size_t abandonCount(const WebPrerender& prerender) const
144     {
145         return std::count_if(m_abandonedPrerenders.begin(), m_abandonedPrerenders.end(), std::bind1st(WebPrerenderEqual(), prerender));
146     }
147 
148 private:
149     class WebPrerenderEqual : public std::binary_function<WebPrerender, WebPrerender, bool> {
150     public:
operator ()(const WebPrerender & first,const WebPrerender & second) const151         bool operator()(const WebPrerender& first, const WebPrerender& second) const
152         {
153             return first.toPrerender() == second.toPrerender();
154         }
155     };
156 
157     // From WebPrerenderingSupport:
add(const WebPrerender & prerender)158     virtual void add(const WebPrerender& prerender) OVERRIDE
159     {
160         m_addedPrerenders.push_back(prerender);
161     }
162 
cancel(const WebPrerender & prerender)163     virtual void cancel(const WebPrerender& prerender) OVERRIDE
164     {
165         m_canceledPrerenders.push_back(prerender);
166     }
167 
abandon(const WebPrerender & prerender)168     virtual void abandon(const WebPrerender& prerender) OVERRIDE
169     {
170         m_abandonedPrerenders.push_back(prerender);
171     }
172 
173     std::vector<WebPrerender> m_addedPrerenders;
174     std::vector<WebPrerender> m_canceledPrerenders;
175     std::vector<WebPrerender> m_abandonedPrerenders;
176 };
177 
178 class PrerenderingTest : public testing::Test {
179 public:
~PrerenderingTest()180     ~PrerenderingTest()
181     {
182         Platform::current()->unitTestSupport()->unregisterAllMockedURLs();
183     }
184 
initialize(const char * baseURL,const char * fileName)185     void initialize(const char* baseURL, const char* fileName)
186     {
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);
191 
192         FrameTestHelpers::loadFrame(m_webViewHelper.webView()->mainFrame(), std::string(baseURL) + fileName);
193     }
194 
navigateAway()195     void navigateAway()
196     {
197         FrameTestHelpers::loadFrame(m_webViewHelper.webView()->mainFrame(), "about:blank");
198     }
199 
close()200     void close()
201     {
202         m_webViewHelper.webView()->mainFrame()->collectGarbage();
203         m_webViewHelper.reset();
204 
205         WebCache::clear();
206     }
207 
console()208     WebElement console()
209     {
210         WebElement console = m_webViewHelper.webView()->mainFrame()->document().getElementById("console");
211         ASSERT(console.nodeName() == "UL");
212         return console;
213     }
214 
consoleLength()215     unsigned consoleLength()
216     {
217         return console().childNodes().length() - 1;
218     }
219 
consoleAt(unsigned i)220     std::string consoleAt(unsigned i)
221     {
222         ASSERT(consoleLength() > i);
223 
224         WebNode consoleListItem = console().childNodes().item(1 + i);
225         ASSERT(consoleListItem.nodeName() == "LI");
226         ASSERT(consoleListItem.hasChildNodes());
227 
228         WebNode textNode = consoleListItem.firstChild();
229         ASSERT(textNode.nodeName() == "#text");
230 
231         return textNode.nodeValue().utf8().data();
232     }
233 
executeScript(const char * code)234     void executeScript(const char* code)
235     {
236         m_webViewHelper.webView()->mainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(code)));
237     }
238 
prerenderingSupport()239     TestPrerenderingSupport* prerenderingSupport()
240     {
241         return &m_prerenderingSupport;
242     }
243 
prerendererClient()244     TestPrerendererClient* prerendererClient()
245     {
246         return &m_prerendererClient;
247     }
248 
249 private:
250     TestPrerenderingSupport m_prerenderingSupport;
251     TestPrerendererClient m_prerendererClient;
252 
253     FrameTestHelpers::WebViewHelper m_webViewHelper;
254 };
255 
TEST_F(PrerenderingTest,SinglePrerender)256 TEST_F(PrerenderingTest, SinglePrerender)
257 {
258     initialize("http://www.foo.com/", "prerender/single_prerender.html");
259 
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());
264 
265     EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
266     EXPECT_EQ(1u, prerenderingSupport()->totalCount());
267 
268     webPrerender.didStartPrerender();
269     EXPECT_EQ(1u, consoleLength());
270     EXPECT_EQ("webkitprerenderstart", consoleAt(0));
271 
272     webPrerender.didSendDOMContentLoadedForPrerender();
273     EXPECT_EQ(2u, consoleLength());
274     EXPECT_EQ("webkitprerenderdomcontentloaded", consoleAt(1));
275 
276     webPrerender.didSendLoadForPrerender();
277     EXPECT_EQ(3u, consoleLength());
278     EXPECT_EQ("webkitprerenderload", consoleAt(2));
279 
280     webPrerender.didStopPrerender();
281     EXPECT_EQ(4u, consoleLength());
282     EXPECT_EQ("webkitprerenderstop", consoleAt(3));
283 }
284 
TEST_F(PrerenderingTest,CancelPrerender)285 TEST_F(PrerenderingTest, CancelPrerender)
286 {
287     initialize("http://www.foo.com/", "prerender/single_prerender.html");
288 
289     WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
290     EXPECT_FALSE(webPrerender.isNull());
291 
292     EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
293     EXPECT_EQ(1u, prerenderingSupport()->totalCount());
294 
295     executeScript("removePrerender()");
296 
297     EXPECT_EQ(1u, prerenderingSupport()->cancelCount(webPrerender));
298     EXPECT_EQ(2u, prerenderingSupport()->totalCount());
299 }
300 
TEST_F(PrerenderingTest,AbandonPrerender)301 TEST_F(PrerenderingTest, AbandonPrerender)
302 {
303     initialize("http://www.foo.com/", "prerender/single_prerender.html");
304 
305     WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
306     EXPECT_FALSE(webPrerender.isNull());
307 
308     EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
309     EXPECT_EQ(1u, prerenderingSupport()->totalCount());
310 
311     navigateAway();
312 
313     EXPECT_EQ(1u, prerenderingSupport()->abandonCount(webPrerender));
314     EXPECT_EQ(2u, prerenderingSupport()->totalCount());
315 
316     // Check that the prerender does not emit an extra cancel when garbage-collecting everything.
317     close();
318 
319     EXPECT_EQ(2u, prerenderingSupport()->totalCount());
320 }
321 
TEST_F(PrerenderingTest,ExtraData)322 TEST_F(PrerenderingTest, ExtraData)
323 {
324     class TestExtraData : public WebPrerender::ExtraData {
325     public:
326         explicit TestExtraData(bool* alive) : m_alive(alive)
327         {
328             *alive = true;
329         }
330 
331         virtual ~TestExtraData() { *m_alive = false; }
332 
333     private:
334         bool* m_alive;
335     };
336 
337     bool alive = false;
338     {
339         prerendererClient()->setExtraDataForNextPrerender(new TestExtraData(&alive));
340         initialize("http://www.foo.com/", "prerender/single_prerender.html");
341         EXPECT_TRUE(alive);
342 
343         WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
344 
345         executeScript("removePrerender()");
346         close();
347         prerenderingSupport()->clear();
348     }
349     EXPECT_FALSE(alive);
350 }
351 
TEST_F(PrerenderingTest,TwoPrerenders)352 TEST_F(PrerenderingTest, TwoPrerenders)
353 {
354     initialize("http://www.foo.com/", "prerender/multiple_prerenders.html");
355 
356     WebPrerender firstPrerender = prerendererClient()->releaseWebPrerender();
357     EXPECT_FALSE(firstPrerender.isNull());
358     EXPECT_EQ(toWebURL("http://first-prerender.com/"), firstPrerender.url());
359 
360     WebPrerender secondPrerender = prerendererClient()->releaseWebPrerender();
361     EXPECT_FALSE(firstPrerender.isNull());
362     EXPECT_EQ(toWebURL("http://second-prerender.com/"), secondPrerender.url());
363 
364     EXPECT_EQ(1u, prerenderingSupport()->addCount(firstPrerender));
365     EXPECT_EQ(1u, prerenderingSupport()->addCount(secondPrerender));
366     EXPECT_EQ(2u, prerenderingSupport()->totalCount());
367 
368     firstPrerender.didStartPrerender();
369     EXPECT_EQ(1u, consoleLength());
370     EXPECT_EQ("first_webkitprerenderstart", consoleAt(0));
371 
372     secondPrerender.didStartPrerender();
373     EXPECT_EQ(2u, consoleLength());
374     EXPECT_EQ("second_webkitprerenderstart", consoleAt(1));
375 }
376 
TEST_F(PrerenderingTest,TwoPrerendersRemovingFirstThenNavigating)377 TEST_F(PrerenderingTest, TwoPrerendersRemovingFirstThenNavigating)
378 {
379     initialize("http://www.foo.com/", "prerender/multiple_prerenders.html");
380 
381     WebPrerender firstPrerender = prerendererClient()->releaseWebPrerender();
382     WebPrerender secondPrerender = prerendererClient()->releaseWebPrerender();
383 
384     EXPECT_EQ(1u, prerenderingSupport()->addCount(firstPrerender));
385     EXPECT_EQ(1u, prerenderingSupport()->addCount(secondPrerender));
386     EXPECT_EQ(2u, prerenderingSupport()->totalCount());
387 
388     executeScript("removeFirstPrerender()");
389 
390     EXPECT_EQ(1u, prerenderingSupport()->cancelCount(firstPrerender));
391     EXPECT_EQ(3u, prerenderingSupport()->totalCount());
392 
393     navigateAway();
394 
395     EXPECT_EQ(1u, prerenderingSupport()->abandonCount(secondPrerender));
396     EXPECT_EQ(4u, prerenderingSupport()->totalCount());
397 }
398 
TEST_F(PrerenderingTest,TwoPrerendersAddingThird)399 TEST_F(PrerenderingTest, TwoPrerendersAddingThird)
400 {
401     initialize("http://www.foo.com/", "prerender/multiple_prerenders.html");
402 
403     WebPrerender firstPrerender = prerendererClient()->releaseWebPrerender();
404     WebPrerender secondPrerender = prerendererClient()->releaseWebPrerender();
405 
406     EXPECT_EQ(1u, prerenderingSupport()->addCount(firstPrerender));
407     EXPECT_EQ(1u, prerenderingSupport()->addCount(secondPrerender));
408     EXPECT_EQ(2u, prerenderingSupport()->totalCount());
409 
410     executeScript("addThirdPrerender()");
411 
412     WebPrerender thirdPrerender = prerendererClient()->releaseWebPrerender();
413     EXPECT_EQ(1u, prerenderingSupport()->addCount(thirdPrerender));
414     EXPECT_EQ(3u, prerenderingSupport()->totalCount());
415 }
416 
TEST_F(PrerenderingTest,ShortLivedClient)417 TEST_F(PrerenderingTest, ShortLivedClient)
418 {
419     initialize("http://www.foo.com/", "prerender/single_prerender.html");
420 
421     WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
422     EXPECT_FALSE(webPrerender.isNull());
423 
424     EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
425     EXPECT_EQ(1u, prerenderingSupport()->totalCount());
426 
427     navigateAway();
428     close();
429 
430     // This test passes if this next line doesn't crash.
431     webPrerender.didStartPrerender();
432 }
433 
TEST_F(PrerenderingTest,FastRemoveElement)434 TEST_F(PrerenderingTest, FastRemoveElement)
435 {
436     initialize("http://www.foo.com/", "prerender/single_prerender.html");
437 
438     WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
439     EXPECT_FALSE(webPrerender.isNull());
440 
441     EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
442     EXPECT_EQ(1u, prerenderingSupport()->totalCount());
443 
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();
448 
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());
451 }
452 
TEST_F(PrerenderingTest,MutateTarget)453 TEST_F(PrerenderingTest, MutateTarget)
454 {
455     initialize("http://www.foo.com/", "prerender/single_prerender.html");
456 
457     WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
458     EXPECT_FALSE(webPrerender.isNull());
459     EXPECT_EQ(toWebURL("http://prerender.com/"), webPrerender.url());
460 
461     EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
462     EXPECT_EQ(0u, prerenderingSupport()->cancelCount(webPrerender));
463     EXPECT_EQ(1u, prerenderingSupport()->totalCount());
464 
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));
468 
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());
474 }
475 
TEST_F(PrerenderingTest,MutateRel)476 TEST_F(PrerenderingTest, MutateRel)
477 {
478     initialize("http://www.foo.com/", "prerender/single_prerender.html");
479 
480     WebPrerender webPrerender = prerendererClient()->releaseWebPrerender();
481     EXPECT_FALSE(webPrerender.isNull());
482     EXPECT_EQ(toWebURL("http://prerender.com/"), webPrerender.url());
483 
484     EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender));
485     EXPECT_EQ(0u, prerenderingSupport()->cancelCount(webPrerender));
486     EXPECT_EQ(1u, prerenderingSupport()->totalCount());
487 
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());
492 }
493 
TEST_F(PrerenderingTest,RelNext)494 TEST_F(PrerenderingTest, RelNext)
495 {
496     initialize("http://www.foo.com/", "prerender/rel_next_prerender.html");
497 
498     WebPrerender relNextOnly = prerendererClient()->releaseWebPrerender();
499     EXPECT_EQ(toWebURL("http://rel-next-only.com/"), relNextOnly.url());
500     EXPECT_EQ(PrerenderRelTypeNext, relNextOnly.relTypes());
501 
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());
505 }
506 
507 } // namespace
508