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