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 61 WebURL toWebURL(const char* url) 62 { 63 return WebURL(toKURL(url)); 64 } 65 66 class TestPrerendererClient : public WebPrerendererClient { 67 public: 68 TestPrerendererClient() { } 69 virtual ~TestPrerendererClient() { } 70 71 void setExtraDataForNextPrerender(WebPrerender::ExtraData* extraData) 72 { 73 ASSERT(!m_extraData); 74 m_extraData = adoptPtr(extraData); 75 } 76 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 85 bool empty() const 86 { 87 return m_webPrerenders.empty(); 88 } 89 90 void clear() 91 { 92 m_webPrerenders.clear(); 93 } 94 95 private: 96 // From WebPrerendererClient: 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: 111 TestPrerenderingSupport() 112 { 113 initialize(this); 114 } 115 116 virtual ~TestPrerenderingSupport() 117 { 118 shutdown(); 119 } 120 121 void clear() 122 { 123 m_addedPrerenders.clear(); 124 m_canceledPrerenders.clear(); 125 m_abandonedPrerenders.clear(); 126 } 127 128 size_t totalCount() const 129 { 130 return m_addedPrerenders.size() + m_canceledPrerenders.size() + m_abandonedPrerenders.size(); 131 } 132 133 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 138 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 143 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: 151 bool operator()(const WebPrerender& first, const WebPrerender& second) const 152 { 153 return first.toPrerender() == second.toPrerender(); 154 } 155 }; 156 157 // From WebPrerenderingSupport: 158 virtual void add(const WebPrerender& prerender) OVERRIDE 159 { 160 m_addedPrerenders.push_back(prerender); 161 } 162 163 virtual void cancel(const WebPrerender& prerender) OVERRIDE 164 { 165 m_canceledPrerenders.push_back(prerender); 166 } 167 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: 180 ~PrerenderingTest() 181 { 182 Platform::current()->unitTestSupport()->unregisterAllMockedURLs(); 183 } 184 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 195 void navigateAway() 196 { 197 FrameTestHelpers::loadFrame(m_webViewHelper.webView()->mainFrame(), "about:blank"); 198 } 199 200 void close() 201 { 202 m_webViewHelper.webView()->mainFrame()->collectGarbage(); 203 m_webViewHelper.reset(); 204 205 WebCache::clear(); 206 } 207 208 WebElement console() 209 { 210 WebElement console = m_webViewHelper.webView()->mainFrame()->document().getElementById("console"); 211 ASSERT(console.nodeName() == "UL"); 212 return console; 213 } 214 215 unsigned consoleLength() 216 { 217 return console().childNodes().length() - 1; 218 } 219 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 234 void executeScript(const char* code) 235 { 236 m_webViewHelper.webView()->mainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(code))); 237 } 238 239 TestPrerenderingSupport* prerenderingSupport() 240 { 241 return &m_prerenderingSupport; 242 } 243 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 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 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 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 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 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 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 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 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 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 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 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 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