| /* |
| * Copyright (C) 2012 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #include "FrameTestHelpers.h" |
| #include "URLTestHelpers.h" |
| #include "WebCache.h" |
| #include "WebDocument.h" |
| #include "WebElement.h" |
| #include "WebFrame.h" |
| #include "WebNode.h" |
| #include "WebNodeList.h" |
| #include "WebPrerendererClient.h" |
| #include "WebScriptSource.h" |
| #include "WebView.h" |
| #include "WebViewClient.h" |
| |
| #include <functional> |
| #include <gtest/gtest.h> |
| #include <list> |
| #include "public/platform/Platform.h" |
| #include "public/platform/WebPrerender.h" |
| #include "public/platform/WebPrerenderingSupport.h" |
| #include "public/platform/WebString.h" |
| #include "public/platform/WebUnitTestSupport.h" |
| #include "wtf/OwnPtr.h" |
| |
| using namespace blink; |
| using blink::URLTestHelpers::toKURL; |
| |
| namespace { |
| |
| WebURL toWebURL(const char* url) |
| { |
| return WebURL(toKURL(url)); |
| } |
| |
| class TestPrerendererClient : public WebPrerendererClient { |
| public: |
| TestPrerendererClient() { } |
| virtual ~TestPrerendererClient() { } |
| |
| void setExtraDataForNextPrerender(WebPrerender::ExtraData* extraData) |
| { |
| ASSERT(!m_extraData); |
| m_extraData = adoptPtr(extraData); |
| } |
| |
| WebPrerender releaseWebPrerender() |
| { |
| ASSERT(!m_webPrerenders.empty()); |
| WebPrerender retval(m_webPrerenders.front()); |
| m_webPrerenders.pop_front(); |
| return retval; |
| } |
| |
| bool empty() const |
| { |
| return m_webPrerenders.empty(); |
| } |
| |
| void clear() |
| { |
| m_webPrerenders.clear(); |
| } |
| |
| private: |
| // From WebPrerendererClient: |
| virtual void willAddPrerender(WebPrerender* prerender) OVERRIDE |
| { |
| prerender->setExtraData(m_extraData.leakPtr()); |
| |
| ASSERT(!prerender->isNull()); |
| m_webPrerenders.push_back(*prerender); |
| } |
| |
| OwnPtr<WebPrerender::ExtraData> m_extraData; |
| std::list<WebPrerender> m_webPrerenders; |
| }; |
| |
| class TestPrerenderingSupport : public WebPrerenderingSupport { |
| public: |
| TestPrerenderingSupport() |
| { |
| initialize(this); |
| } |
| |
| virtual ~TestPrerenderingSupport() |
| { |
| shutdown(); |
| } |
| |
| void clear() |
| { |
| m_addedPrerenders.clear(); |
| m_canceledPrerenders.clear(); |
| m_abandonedPrerenders.clear(); |
| } |
| |
| size_t totalCount() const |
| { |
| return m_addedPrerenders.size() + m_canceledPrerenders.size() + m_abandonedPrerenders.size(); |
| } |
| |
| size_t addCount(const WebPrerender& prerender) const |
| { |
| return std::count_if(m_addedPrerenders.begin(), m_addedPrerenders.end(), std::bind1st(WebPrerenderEqual(), prerender)); |
| } |
| |
| size_t cancelCount(const WebPrerender& prerender) const |
| { |
| return std::count_if(m_canceledPrerenders.begin(), m_canceledPrerenders.end(), std::bind1st(WebPrerenderEqual(), prerender)); |
| } |
| |
| size_t abandonCount(const WebPrerender& prerender) const |
| { |
| return std::count_if(m_abandonedPrerenders.begin(), m_abandonedPrerenders.end(), std::bind1st(WebPrerenderEqual(), prerender)); |
| } |
| |
| private: |
| class WebPrerenderEqual : public std::binary_function<WebPrerender, WebPrerender, bool> { |
| public: |
| bool operator()(const WebPrerender& first, const WebPrerender& second) const |
| { |
| return first.toPrerender() == second.toPrerender(); |
| } |
| }; |
| |
| // From WebPrerenderingSupport: |
| virtual void add(const WebPrerender& prerender) OVERRIDE |
| { |
| m_addedPrerenders.push_back(prerender); |
| } |
| |
| virtual void cancel(const WebPrerender& prerender) OVERRIDE |
| { |
| m_canceledPrerenders.push_back(prerender); |
| } |
| |
| virtual void abandon(const WebPrerender& prerender) OVERRIDE |
| { |
| m_abandonedPrerenders.push_back(prerender); |
| } |
| |
| std::vector<WebPrerender> m_addedPrerenders; |
| std::vector<WebPrerender> m_canceledPrerenders; |
| std::vector<WebPrerender> m_abandonedPrerenders; |
| }; |
| |
| class PrerenderingTest : public testing::Test { |
| public: |
| ~PrerenderingTest() |
| { |
| Platform::current()->unitTestSupport()->unregisterAllMockedURLs(); |
| } |
| |
| void initialize(const char* baseURL, const char* fileName) |
| { |
| URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(baseURL), WebString::fromUTF8(fileName)); |
| const bool RunJavascript = true; |
| m_webViewHelper.initialize(RunJavascript); |
| m_webViewHelper.webView()->setPrerendererClient(&m_prerendererClient); |
| |
| FrameTestHelpers::loadFrame(m_webViewHelper.webView()->mainFrame(), std::string(baseURL) + fileName); |
| Platform::current()->unitTestSupport()->serveAsynchronousMockedRequests(); |
| } |
| |
| void navigateAway() |
| { |
| FrameTestHelpers::loadFrame(m_webViewHelper.webView()->mainFrame(), "about:blank"); |
| } |
| |
| void close() |
| { |
| m_webViewHelper.webView()->mainFrame()->collectGarbage(); |
| m_webViewHelper.reset(); |
| |
| WebCache::clear(); |
| } |
| |
| WebElement console() |
| { |
| WebElement console = m_webViewHelper.webView()->mainFrame()->document().getElementById("console"); |
| ASSERT(console.nodeName() == "UL"); |
| return console; |
| } |
| |
| unsigned consoleLength() |
| { |
| return console().childNodes().length() - 1; |
| } |
| |
| std::string consoleAt(unsigned i) |
| { |
| ASSERT(consoleLength() > i); |
| |
| WebNode consoleListItem = console().childNodes().item(1 + i); |
| ASSERT(consoleListItem.nodeName() == "LI"); |
| ASSERT(consoleListItem.hasChildNodes()); |
| |
| WebNode textNode = consoleListItem.firstChild(); |
| ASSERT(textNode.nodeName() == "#text"); |
| |
| return textNode.nodeValue().utf8().data(); |
| } |
| |
| void executeScript(const char* code) |
| { |
| m_webViewHelper.webView()->mainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(code))); |
| } |
| |
| TestPrerenderingSupport* prerenderingSupport() |
| { |
| return &m_prerenderingSupport; |
| } |
| |
| TestPrerendererClient* prerendererClient() |
| { |
| return &m_prerendererClient; |
| } |
| |
| private: |
| TestPrerenderingSupport m_prerenderingSupport; |
| TestPrerendererClient m_prerendererClient; |
| |
| FrameTestHelpers::WebViewHelper m_webViewHelper; |
| }; |
| |
| TEST_F(PrerenderingTest, SinglePrerender) |
| { |
| initialize("http://www.foo.com/", "prerender/single_prerender.html"); |
| |
| WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); |
| EXPECT_FALSE(webPrerender.isNull()); |
| EXPECT_EQ(toWebURL("http://prerender.com/"), webPrerender.url()); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); |
| EXPECT_EQ(1u, prerenderingSupport()->totalCount()); |
| |
| webPrerender.didStartPrerender(); |
| EXPECT_EQ(1u, consoleLength()); |
| EXPECT_EQ("webkitprerenderstart", consoleAt(0)); |
| |
| webPrerender.didSendDOMContentLoadedForPrerender(); |
| EXPECT_EQ(2u, consoleLength()); |
| EXPECT_EQ("webkitprerenderdomcontentloaded", consoleAt(1)); |
| |
| webPrerender.didSendLoadForPrerender(); |
| EXPECT_EQ(3u, consoleLength()); |
| EXPECT_EQ("webkitprerenderload", consoleAt(2)); |
| |
| webPrerender.didStopPrerender(); |
| EXPECT_EQ(4u, consoleLength()); |
| EXPECT_EQ("webkitprerenderstop", consoleAt(3)); |
| } |
| |
| TEST_F(PrerenderingTest, CancelPrerender) |
| { |
| initialize("http://www.foo.com/", "prerender/single_prerender.html"); |
| |
| WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); |
| EXPECT_FALSE(webPrerender.isNull()); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); |
| EXPECT_EQ(1u, prerenderingSupport()->totalCount()); |
| |
| executeScript("removePrerender()"); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->cancelCount(webPrerender)); |
| EXPECT_EQ(2u, prerenderingSupport()->totalCount()); |
| } |
| |
| TEST_F(PrerenderingTest, AbandonPrerender) |
| { |
| initialize("http://www.foo.com/", "prerender/single_prerender.html"); |
| |
| WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); |
| EXPECT_FALSE(webPrerender.isNull()); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); |
| EXPECT_EQ(1u, prerenderingSupport()->totalCount()); |
| |
| navigateAway(); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->abandonCount(webPrerender)); |
| EXPECT_EQ(2u, prerenderingSupport()->totalCount()); |
| |
| // Check that the prerender does not emit an extra cancel when garbage-collecting everything. |
| close(); |
| |
| EXPECT_EQ(2u, prerenderingSupport()->totalCount()); |
| } |
| |
| TEST_F(PrerenderingTest, ExtraData) |
| { |
| class TestExtraData : public WebPrerender::ExtraData { |
| public: |
| explicit TestExtraData(bool* alive) : m_alive(alive) |
| { |
| *alive = true; |
| } |
| |
| virtual ~TestExtraData() { *m_alive = false; } |
| |
| private: |
| bool* m_alive; |
| }; |
| |
| bool alive = false; |
| { |
| prerendererClient()->setExtraDataForNextPrerender(new TestExtraData(&alive)); |
| initialize("http://www.foo.com/", "prerender/single_prerender.html"); |
| EXPECT_TRUE(alive); |
| |
| WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); |
| |
| executeScript("removePrerender()"); |
| close(); |
| prerenderingSupport()->clear(); |
| } |
| EXPECT_FALSE(alive); |
| } |
| |
| TEST_F(PrerenderingTest, TwoPrerenders) |
| { |
| initialize("http://www.foo.com/", "prerender/multiple_prerenders.html"); |
| |
| WebPrerender firstPrerender = prerendererClient()->releaseWebPrerender(); |
| EXPECT_FALSE(firstPrerender.isNull()); |
| EXPECT_EQ(toWebURL("http://first-prerender.com/"), firstPrerender.url()); |
| |
| WebPrerender secondPrerender = prerendererClient()->releaseWebPrerender(); |
| EXPECT_FALSE(firstPrerender.isNull()); |
| EXPECT_EQ(toWebURL("http://second-prerender.com/"), secondPrerender.url()); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(firstPrerender)); |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(secondPrerender)); |
| EXPECT_EQ(2u, prerenderingSupport()->totalCount()); |
| |
| firstPrerender.didStartPrerender(); |
| EXPECT_EQ(1u, consoleLength()); |
| EXPECT_EQ("first_webkitprerenderstart", consoleAt(0)); |
| |
| secondPrerender.didStartPrerender(); |
| EXPECT_EQ(2u, consoleLength()); |
| EXPECT_EQ("second_webkitprerenderstart", consoleAt(1)); |
| } |
| |
| TEST_F(PrerenderingTest, TwoPrerendersRemovingFirstThenNavigating) |
| { |
| initialize("http://www.foo.com/", "prerender/multiple_prerenders.html"); |
| |
| WebPrerender firstPrerender = prerendererClient()->releaseWebPrerender(); |
| WebPrerender secondPrerender = prerendererClient()->releaseWebPrerender(); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(firstPrerender)); |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(secondPrerender)); |
| EXPECT_EQ(2u, prerenderingSupport()->totalCount()); |
| |
| executeScript("removeFirstPrerender()"); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->cancelCount(firstPrerender)); |
| EXPECT_EQ(3u, prerenderingSupport()->totalCount()); |
| |
| navigateAway(); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->abandonCount(secondPrerender)); |
| EXPECT_EQ(4u, prerenderingSupport()->totalCount()); |
| } |
| |
| TEST_F(PrerenderingTest, TwoPrerendersAddingThird) |
| { |
| initialize("http://www.foo.com/", "prerender/multiple_prerenders.html"); |
| |
| WebPrerender firstPrerender = prerendererClient()->releaseWebPrerender(); |
| WebPrerender secondPrerender = prerendererClient()->releaseWebPrerender(); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(firstPrerender)); |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(secondPrerender)); |
| EXPECT_EQ(2u, prerenderingSupport()->totalCount()); |
| |
| executeScript("addThirdPrerender()"); |
| |
| WebPrerender thirdPrerender = prerendererClient()->releaseWebPrerender(); |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(thirdPrerender)); |
| EXPECT_EQ(3u, prerenderingSupport()->totalCount()); |
| } |
| |
| TEST_F(PrerenderingTest, ShortLivedClient) |
| { |
| initialize("http://www.foo.com/", "prerender/single_prerender.html"); |
| |
| WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); |
| EXPECT_FALSE(webPrerender.isNull()); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); |
| EXPECT_EQ(1u, prerenderingSupport()->totalCount()); |
| |
| navigateAway(); |
| close(); |
| |
| // This test passes if this next line doesn't crash. |
| webPrerender.didStartPrerender(); |
| } |
| |
| TEST_F(PrerenderingTest, FastRemoveElement) |
| { |
| initialize("http://www.foo.com/", "prerender/single_prerender.html"); |
| |
| WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); |
| EXPECT_FALSE(webPrerender.isNull()); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); |
| EXPECT_EQ(1u, prerenderingSupport()->totalCount()); |
| |
| // Race removing & starting the prerender against each other, as if the element was removed very quickly. |
| executeScript("removePrerender()"); |
| EXPECT_FALSE(webPrerender.isNull()); |
| webPrerender.didStartPrerender(); |
| |
| // The page should be totally disconnected from the Prerender at this point, so the console should not have updated. |
| EXPECT_EQ(0u, consoleLength()); |
| } |
| |
| TEST_F(PrerenderingTest, MutateTarget) |
| { |
| initialize("http://www.foo.com/", "prerender/single_prerender.html"); |
| |
| WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); |
| EXPECT_FALSE(webPrerender.isNull()); |
| EXPECT_EQ(toWebURL("http://prerender.com/"), webPrerender.url()); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); |
| EXPECT_EQ(0u, prerenderingSupport()->cancelCount(webPrerender)); |
| EXPECT_EQ(1u, prerenderingSupport()->totalCount()); |
| |
| // Change the href of this prerender, make sure this is treated as a remove and add. |
| executeScript("mutateTarget()"); |
| EXPECT_EQ(1u, prerenderingSupport()->cancelCount(webPrerender)); |
| |
| WebPrerender mutatedPrerender = prerendererClient()->releaseWebPrerender(); |
| EXPECT_EQ(toWebURL("http://mutated.com/"), mutatedPrerender.url()); |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(mutatedPrerender)); |
| EXPECT_EQ(3u, prerenderingSupport()->totalCount()); |
| } |
| |
| TEST_F(PrerenderingTest, MutateRel) |
| { |
| initialize("http://www.foo.com/", "prerender/single_prerender.html"); |
| |
| WebPrerender webPrerender = prerendererClient()->releaseWebPrerender(); |
| EXPECT_FALSE(webPrerender.isNull()); |
| EXPECT_EQ(toWebURL("http://prerender.com/"), webPrerender.url()); |
| |
| EXPECT_EQ(1u, prerenderingSupport()->addCount(webPrerender)); |
| EXPECT_EQ(0u, prerenderingSupport()->cancelCount(webPrerender)); |
| EXPECT_EQ(1u, prerenderingSupport()->totalCount()); |
| |
| // Change the rel of this prerender, make sure this is treated as a remove. |
| executeScript("mutateRel()"); |
| EXPECT_EQ(1u, prerenderingSupport()->cancelCount(webPrerender)); |
| EXPECT_EQ(2u, prerenderingSupport()->totalCount()); |
| } |
| |
| } // namespace |