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 "FrameTestHelpers.h" 34 #include "URLTestHelpers.h" 35 #include "WebCache.h" 36 #include "WebDocument.h" 37 #include "WebElement.h" 38 #include "WebFrame.h" 39 #include "WebNode.h" 40 #include "WebNodeList.h" 41 #include "WebPrerendererClient.h" 42 #include "WebScriptSource.h" 43 #include "WebView.h" 44 #include "WebViewClient.h" 45 46 #include <functional> 47 #include <gtest/gtest.h> 48 #include <list> 49 #include "public/platform/Platform.h" 50 #include "public/platform/WebPrerender.h" 51 #include "public/platform/WebPrerenderingSupport.h" 52 #include "public/platform/WebString.h" 53 #include "public/platform/WebUnitTestSupport.h" 54 #include "wtf/OwnPtr.h" 55 56 using namespace WebKit; 57 using WebKit::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() : m_webView(0) 181 { 182 } 183 184 ~PrerenderingTest() 185 { 186 Platform::current()->unitTestSupport()->unregisterAllMockedURLs(); 187 if (m_webView) 188 close(); 189 } 190 191 void initialize(const char* baseURL, const char* fileName) 192 { 193 ASSERT(!m_webView); 194 URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(baseURL), WebString::fromUTF8(fileName)); 195 const bool RunJavascript = true; 196 m_webView = FrameTestHelpers::createWebView(RunJavascript); 197 m_webView->setPrerendererClient(&m_prerendererClient); 198 199 FrameTestHelpers::loadFrame(m_webView->mainFrame(), std::string(baseURL) + fileName); 200 Platform::current()->unitTestSupport()->serveAsynchronousMockedRequests(); 201 } 202 203 void navigateAway() 204 { 205 FrameTestHelpers::loadFrame(m_webView->mainFrame(), "about:blank"); 206 } 207 208 void close() 209 { 210 ASSERT(m_webView); 211 212 m_webView->mainFrame()->collectGarbage(); 213 214 m_webView->close(); 215 m_webView = 0; 216 217 WebCache::clear(); 218 } 219 220 WebElement console() 221 { 222 WebElement console = m_webView->mainFrame()->document().getElementById("console"); 223 ASSERT(console.nodeName() == "UL"); 224 return console; 225 } 226 227 unsigned consoleLength() 228 { 229 return console().childNodes().length() - 1; 230 } 231 232 std::string consoleAt(unsigned i) 233 { 234 ASSERT(consoleLength() > i); 235 236 WebNode consoleListItem = console().childNodes().item(1 + i); 237 ASSERT(consoleListItem.nodeName() == "LI"); 238 ASSERT(consoleListItem.hasChildNodes()); 239 240 WebNode textNode = consoleListItem.firstChild(); 241 ASSERT(textNode.nodeName() == "#text"); 242 243 return textNode.nodeValue().utf8().data(); 244 } 245 246 void executeScript(const char* code) 247 { 248 m_webView->mainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(code))); 249 } 250 251 TestPrerenderingSupport* prerenderingSupport() 252 { 253 return &m_prerenderingSupport; 254 } 255 256 TestPrerendererClient* prerendererClient() 257 { 258 return &m_prerendererClient; 259 } 260 261 private: 262 TestPrerenderingSupport m_prerenderingSupport; 263 TestPrerendererClient m_prerendererClient; 264 265 WebView* m_webView; 266 }; 267 268 TEST_F(PrerenderingTest, SinglePrerender) 269 { 270 initialize("http://www.foo.com/", "prerender/single_prerender.html"); 271 272 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); 273 EXPECT_FALSE(webPrerender.isNull()); 274 EXPECT_EQ(toWebURL("http://prerender.com/"), webPrerender.url()); 275 276 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); 277 EXPECT_EQ(1u, prerenderingSupport()->totalCount()); 278 279 webPrerender.didStartPrerender(); 280 EXPECT_EQ(1u, consoleLength()); 281 EXPECT_EQ("webkitprerenderstart", consoleAt(0)); 282 283 webPrerender.didSendDOMContentLoadedForPrerender(); 284 EXPECT_EQ(2u, consoleLength()); 285 EXPECT_EQ("webkitprerenderdomcontentloaded", consoleAt(1)); 286 287 webPrerender.didSendLoadForPrerender(); 288 EXPECT_EQ(3u, consoleLength()); 289 EXPECT_EQ("webkitprerenderload", consoleAt(2)); 290 291 webPrerender.didStopPrerender(); 292 EXPECT_EQ(4u, consoleLength()); 293 EXPECT_EQ("webkitprerenderstop", consoleAt(3)); 294 } 295 296 TEST_F(PrerenderingTest, CancelPrerender) 297 { 298 initialize("http://www.foo.com/", "prerender/single_prerender.html"); 299 300 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); 301 EXPECT_FALSE(webPrerender.isNull()); 302 303 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); 304 EXPECT_EQ(1u, prerenderingSupport()->totalCount()); 305 306 executeScript("removePrerender()"); 307 308 EXPECT_EQ(1u, prerenderingSupport()->cancelCount(webPrerender)); 309 EXPECT_EQ(2u, prerenderingSupport()->totalCount()); 310 } 311 312 TEST_F(PrerenderingTest, AbandonPrerender) 313 { 314 initialize("http://www.foo.com/", "prerender/single_prerender.html"); 315 316 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); 317 EXPECT_FALSE(webPrerender.isNull()); 318 319 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); 320 EXPECT_EQ(1u, prerenderingSupport()->totalCount()); 321 322 navigateAway(); 323 324 EXPECT_EQ(1u, prerenderingSupport()->abandonCount(webPrerender)); 325 EXPECT_EQ(2u, prerenderingSupport()->totalCount()); 326 } 327 328 TEST_F(PrerenderingTest, ExtraData) 329 { 330 class TestExtraData : public WebPrerender::ExtraData { 331 public: 332 explicit TestExtraData(bool* alive) : m_alive(alive) 333 { 334 *alive = true; 335 } 336 337 virtual ~TestExtraData() { *m_alive = false; } 338 339 private: 340 bool* m_alive; 341 }; 342 343 bool alive = false; 344 { 345 prerendererClient()->setExtraDataForNextPrerender(new TestExtraData(&alive)); 346 initialize("http://www.foo.com/", "prerender/single_prerender.html"); 347 EXPECT_TRUE(alive); 348 349 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); 350 351 executeScript("removePrerender()"); 352 close(); 353 prerenderingSupport()->clear(); 354 } 355 EXPECT_FALSE(alive); 356 } 357 358 TEST_F(PrerenderingTest, TwoPrerenders) 359 { 360 initialize("http://www.foo.com/", "prerender/multiple_prerenders.html"); 361 362 WebPrerender firstPrerender = prerendererClient()->releaseWebPrerender(); 363 EXPECT_FALSE(firstPrerender.isNull()); 364 EXPECT_EQ(toWebURL("http://first-prerender.com/"), firstPrerender.url()); 365 366 WebPrerender secondPrerender = prerendererClient()->releaseWebPrerender(); 367 EXPECT_FALSE(firstPrerender.isNull()); 368 EXPECT_EQ(toWebURL("http://second-prerender.com/"), secondPrerender.url()); 369 370 EXPECT_EQ(1u, prerenderingSupport()->addCount(firstPrerender)); 371 EXPECT_EQ(1u, prerenderingSupport()->addCount(secondPrerender)); 372 EXPECT_EQ(2u, prerenderingSupport()->totalCount()); 373 374 firstPrerender.didStartPrerender(); 375 EXPECT_EQ(1u, consoleLength()); 376 EXPECT_EQ("first_webkitprerenderstart", consoleAt(0)); 377 378 secondPrerender.didStartPrerender(); 379 EXPECT_EQ(2u, consoleLength()); 380 EXPECT_EQ("second_webkitprerenderstart", consoleAt(1)); 381 } 382 383 TEST_F(PrerenderingTest, TwoPrerendersRemovingFirstThenNavigating) 384 { 385 initialize("http://www.foo.com/", "prerender/multiple_prerenders.html"); 386 387 WebPrerender firstPrerender = prerendererClient()->releaseWebPrerender(); 388 WebPrerender secondPrerender = prerendererClient()->releaseWebPrerender(); 389 390 EXPECT_EQ(1u, prerenderingSupport()->addCount(firstPrerender)); 391 EXPECT_EQ(1u, prerenderingSupport()->addCount(secondPrerender)); 392 EXPECT_EQ(2u, prerenderingSupport()->totalCount()); 393 394 executeScript("removeFirstPrerender()"); 395 396 EXPECT_EQ(1u, prerenderingSupport()->cancelCount(firstPrerender)); 397 EXPECT_EQ(3u, prerenderingSupport()->totalCount()); 398 399 navigateAway(); 400 401 EXPECT_EQ(1u, prerenderingSupport()->abandonCount(secondPrerender)); 402 403 // FIXME: After we fix prerenders such that they don't send redundant events, assert that totalCount() == 4u. 404 } 405 406 TEST_F(PrerenderingTest, TwoPrerendersAddingThird) 407 { 408 initialize("http://www.foo.com/", "prerender/multiple_prerenders.html"); 409 410 WebPrerender firstPrerender = prerendererClient()->releaseWebPrerender(); 411 WebPrerender secondPrerender = prerendererClient()->releaseWebPrerender(); 412 413 EXPECT_EQ(1u, prerenderingSupport()->addCount(firstPrerender)); 414 EXPECT_EQ(1u, prerenderingSupport()->addCount(secondPrerender)); 415 EXPECT_EQ(2u, prerenderingSupport()->totalCount()); 416 417 executeScript("addThirdPrerender()"); 418 419 WebPrerender thirdPrerender = prerendererClient()->releaseWebPrerender(); 420 EXPECT_EQ(1u, prerenderingSupport()->addCount(thirdPrerender)); 421 EXPECT_EQ(3u, prerenderingSupport()->totalCount()); 422 } 423 424 TEST_F(PrerenderingTest, ShortLivedClient) 425 { 426 initialize("http://www.foo.com/", "prerender/single_prerender.html"); 427 428 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); 429 EXPECT_FALSE(webPrerender.isNull()); 430 431 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); 432 EXPECT_EQ(1u, prerenderingSupport()->totalCount()); 433 434 navigateAway(); 435 close(); 436 437 // This test passes if this next line doesn't crash. 438 webPrerender.didStartPrerender(); 439 } 440 441 TEST_F(PrerenderingTest, FastRemoveElement) 442 { 443 initialize("http://www.foo.com/", "prerender/single_prerender.html"); 444 445 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); 446 EXPECT_FALSE(webPrerender.isNull()); 447 448 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); 449 EXPECT_EQ(1u, prerenderingSupport()->totalCount()); 450 451 // Race removing & starting the prerender against each other, as if the element was removed very quickly. 452 executeScript("removePrerender()"); 453 EXPECT_FALSE(webPrerender.isNull()); 454 webPrerender.didStartPrerender(); 455 456 // The page should be totally disconnected from the Prerender at this point, so the console should not have updated. 457 EXPECT_EQ(0u, consoleLength()); 458 } 459 460 TEST_F(PrerenderingTest, MutateTarget) 461 { 462 initialize("http://www.foo.com/", "prerender/single_prerender.html"); 463 464 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); 465 EXPECT_FALSE(webPrerender.isNull()); 466 EXPECT_EQ(toWebURL("http://prerender.com/"), webPrerender.url()); 467 468 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); 469 EXPECT_EQ(0u, prerenderingSupport()->cancelCount(webPrerender)); 470 EXPECT_EQ(1u, prerenderingSupport()->totalCount()); 471 472 // Change the href of this prerender, make sure this is treated as a remove and add. 473 executeScript("mutateTarget()"); 474 EXPECT_EQ(1u, prerenderingSupport()->cancelCount(webPrerender)); 475 476 WebPrerender mutatedPrerender = prerendererClient()->releaseWebPrerender(); 477 EXPECT_EQ(toWebURL("http://mutated.com/"), mutatedPrerender.url()); 478 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); 479 EXPECT_EQ(1u, prerenderingSupport()->addCount(mutatedPrerender)); 480 EXPECT_EQ(3u, prerenderingSupport()->totalCount()); 481 } 482 483 TEST_F(PrerenderingTest, MutateRel) 484 { 485 initialize("http://www.foo.com/", "prerender/single_prerender.html"); 486 487 WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); 488 EXPECT_FALSE(webPrerender.isNull()); 489 EXPECT_EQ(toWebURL("http://prerender.com/"), webPrerender.url()); 490 491 EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); 492 EXPECT_EQ(0u, prerenderingSupport()->cancelCount(webPrerender)); 493 EXPECT_EQ(1u, prerenderingSupport()->totalCount()); 494 495 // Change the rel of this prerender, make sure this is treated as a remove. 496 executeScript("mutateRel()"); 497 EXPECT_EQ(1u, prerenderingSupport()->cancelCount(webPrerender)); 498 EXPECT_EQ(2u, prerenderingSupport()->totalCount()); 499 } 500 501 } // namespace 502