Home | History | Annotate | Download | only in tests
      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