1/*
2 * Copyright (C) 2011 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#include "web/tests/FrameTestHelpers.h"
33
34#include "core/testing/URLTestHelpers.h"
35#include "public/platform/Platform.h"
36#include "public/platform/WebData.h"
37#include "public/platform/WebString.h"
38#include "public/platform/WebThread.h"
39#include "public/platform/WebURLRequest.h"
40#include "public/platform/WebURLResponse.h"
41#include "public/platform/WebUnitTestSupport.h"
42#include "public/web/WebSettings.h"
43#include "public/web/WebViewClient.h"
44#include "web/WebLocalFrameImpl.h"
45#include "wtf/StdLibExtras.h"
46
47namespace blink {
48namespace FrameTestHelpers {
49
50namespace {
51
52// The frame test helpers coordinate frame loads in a carefully choreographed
53// dance. Since the parser is threaded, simply spinning the run loop once is not
54// enough to ensure completion of a load. Instead, the following pattern is
55// used to ensure that tests see the final state:
56// 1. Post a task to trigger a load (LoadTask/LoadHTMLStringTask/ReloadTask).
57// 2. Enter the run loop.
58// 3. Posted task triggers the load, and starts pumping pending resource
59//    requests using ServeAsyncRequestsTask.
60// 4. TestWebFrameClient watches for didStartLoading/didStopLoading calls,
61//    keeping track of how many loads it thinks are in flight.
62// 5. While ServeAsyncRequestsTask observes TestWebFrameClient to still have
63//    loads in progress, it posts itself back to the run loop.
64// 6. When ServeAsyncRequestsTask notices there are no more loads in progress,
65//    it exits the run loop.
66// 7. At this point, all parsing, resource loads, and layout should be finished.
67TestWebFrameClient* testClientForFrame(WebFrame* frame)
68{
69    return static_cast<TestWebFrameClient*>(toWebLocalFrameImpl(frame)->client());
70}
71
72class QuitTask : public WebThread::Task {
73public:
74    void PostThis(Timer<QuitTask>*)
75    {
76        // We don't just quit here because the SharedTimer may be part-way
77        // through the current queue of tasks when runPendingTasks was called,
78        // and we can't miss the tasks that were behind it.
79        // Takes ownership of |this|.
80        Platform::current()->currentThread()->postTask(this);
81    }
82
83    virtual void run()
84    {
85        Platform::current()->currentThread()->exitRunLoop();
86    }
87};
88
89class ServeAsyncRequestsTask : public WebThread::Task {
90public:
91    explicit ServeAsyncRequestsTask(TestWebFrameClient* client)
92        : m_client(client)
93    {
94    }
95
96    virtual void run() OVERRIDE
97    {
98        Platform::current()->unitTestSupport()->serveAsynchronousMockedRequests();
99        if (m_client->isLoading())
100            Platform::current()->currentThread()->postTask(new ServeAsyncRequestsTask(m_client));
101        else
102            Platform::current()->currentThread()->exitRunLoop();
103    }
104
105private:
106    TestWebFrameClient* const m_client;
107};
108
109void pumpPendingRequests(WebFrame* frame)
110{
111    Platform::current()->currentThread()->postTask(new ServeAsyncRequestsTask(testClientForFrame(frame)));
112    Platform::current()->currentThread()->enterRunLoop();
113}
114
115class LoadTask : public WebThread::Task {
116public:
117    LoadTask(WebFrame* frame, const WebURLRequest& request)
118        : m_frame(frame)
119        , m_request(request)
120    {
121    }
122
123    virtual void run() OVERRIDE
124    {
125        m_frame->loadRequest(m_request);
126    }
127
128private:
129    WebFrame* const m_frame;
130    const WebURLRequest m_request;
131};
132
133class LoadHTMLStringTask : public WebThread::Task {
134public:
135    LoadHTMLStringTask(WebFrame* frame, const std::string& html, const WebURL& baseURL)
136        : m_frame(frame)
137        , m_html(html)
138        , m_baseURL(baseURL)
139    {
140    }
141
142    virtual void run() OVERRIDE
143    {
144        m_frame->loadHTMLString(WebData(m_html.data(), m_html.size()), m_baseURL);
145    }
146
147private:
148    WebFrame* const m_frame;
149    const std::string m_html;
150    const WebURL m_baseURL;
151};
152
153class LoadHistoryItemTask : public WebThread::Task {
154public:
155    LoadHistoryItemTask(WebFrame* frame, const WebHistoryItem& item, WebHistoryLoadType loadType, WebURLRequest::CachePolicy cachePolicy)
156        : m_frame(frame)
157        , m_item(item)
158        , m_loadType(loadType)
159        , m_cachePolicy(cachePolicy)
160    {
161    }
162
163    virtual void run() OVERRIDE
164    {
165        m_frame->loadHistoryItem(m_item, m_loadType, m_cachePolicy);
166    }
167
168private:
169    WebFrame* const m_frame;
170    const WebHistoryItem m_item;
171    const WebHistoryLoadType m_loadType;
172    const WebURLRequest::CachePolicy m_cachePolicy;
173};
174
175class ReloadTask : public WebThread::Task {
176public:
177    ReloadTask(WebFrame* frame, bool ignoreCache)
178        : m_frame(frame)
179        , m_ignoreCache(ignoreCache)
180    {
181    }
182
183    virtual void run() OVERRIDE
184    {
185        m_frame->reload(m_ignoreCache);
186    }
187
188private:
189    WebFrame* const m_frame;
190    const bool m_ignoreCache;
191};
192
193TestWebFrameClient* defaultWebFrameClient()
194{
195    DEFINE_STATIC_LOCAL(TestWebFrameClient, client, ());
196    return &client;
197}
198
199WebViewClient* defaultWebViewClient()
200{
201    DEFINE_STATIC_LOCAL(TestWebViewClient,  client, ());
202    return &client;
203}
204
205} // namespace
206
207void loadFrame(WebFrame* frame, const std::string& url)
208{
209    WebURLRequest urlRequest;
210    urlRequest.initialize();
211    urlRequest.setURL(URLTestHelpers::toKURL(url));
212
213    Platform::current()->currentThread()->postTask(new LoadTask(frame, urlRequest));
214    pumpPendingRequests(frame);
215}
216
217void loadHTMLString(WebFrame* frame, const std::string& html, const WebURL& baseURL)
218{
219    Platform::current()->currentThread()->postTask(new LoadHTMLStringTask(frame, html, baseURL));
220    pumpPendingRequests(frame);
221}
222
223void loadHistoryItem(WebFrame* frame, const WebHistoryItem& item, WebHistoryLoadType loadType, WebURLRequest::CachePolicy cachePolicy)
224{
225    Platform::current()->currentThread()->postTask(new LoadHistoryItemTask(frame, item, loadType, cachePolicy));
226    pumpPendingRequests(frame);
227}
228
229void reloadFrame(WebFrame* frame)
230{
231    Platform::current()->currentThread()->postTask(new ReloadTask(frame, false));
232    pumpPendingRequests(frame);
233}
234
235void reloadFrameIgnoringCache(WebFrame* frame)
236{
237    Platform::current()->currentThread()->postTask(new ReloadTask(frame, true));
238    pumpPendingRequests(frame);
239}
240
241void pumpPendingRequestsDoNotUse(WebFrame* frame)
242{
243    pumpPendingRequests(frame);
244}
245
246// FIXME: There's a duplicate implementation in UnitTestHelpers.cpp. Remove one.
247void runPendingTasks()
248{
249    // Pending tasks include Timers that have been scheduled.
250    Timer<QuitTask> quitOnTimeout(new QuitTask, &QuitTask::PostThis);
251    quitOnTimeout.startOneShot(0, FROM_HERE);
252    Platform::current()->currentThread()->enterRunLoop();
253}
254
255WebViewHelper::WebViewHelper()
256    : m_webView(0)
257{
258}
259
260WebViewHelper::~WebViewHelper()
261{
262    reset();
263}
264
265WebViewImpl* WebViewHelper::initialize(bool enableJavascript, TestWebFrameClient* webFrameClient, WebViewClient* webViewClient, void (*updateSettingsFunc)(WebSettings*))
266{
267    reset();
268
269    if (!webFrameClient)
270        webFrameClient = defaultWebFrameClient();
271    if (!webViewClient)
272        webViewClient = defaultWebViewClient();
273    m_webView = WebViewImpl::create(webViewClient);
274    m_webView->settings()->setJavaScriptEnabled(enableJavascript);
275    if (updateSettingsFunc)
276        updateSettingsFunc(m_webView->settings());
277    else
278        m_webView->settings()->setDeviceSupportsMouse(false);
279
280    m_webView->setMainFrame(WebLocalFrameImpl::create(webFrameClient));
281
282    return m_webView;
283}
284
285WebViewImpl* WebViewHelper::initializeAndLoad(const std::string& url, bool enableJavascript, TestWebFrameClient* webFrameClient, WebViewClient* webViewClient, void (*updateSettingsFunc)(WebSettings*))
286{
287    initialize(enableJavascript, webFrameClient, webViewClient, updateSettingsFunc);
288
289    loadFrame(webView()->mainFrame(), url);
290
291    return webViewImpl();
292}
293
294void WebViewHelper::reset()
295{
296    if (m_webView) {
297        ASSERT(!testClientForFrame(m_webView->mainFrame())->isLoading());
298        m_webView->close();
299        m_webView = 0;
300    }
301}
302
303TestWebFrameClient::TestWebFrameClient() : m_loadsInProgress(0)
304{
305}
306
307WebFrame* TestWebFrameClient::createChildFrame(WebLocalFrame* parent, const WebString& frameName)
308{
309    WebFrame* frame = WebLocalFrame::create(this);
310    parent->appendChild(frame);
311    return frame;
312}
313
314void TestWebFrameClient::frameDetached(WebFrame* frame)
315{
316    if (frame->parent())
317        frame->parent()->removeChild(frame);
318    frame->close();
319}
320
321void TestWebFrameClient::didStartLoading(bool)
322{
323    ++m_loadsInProgress;
324}
325
326void TestWebFrameClient::didStopLoading()
327{
328    ASSERT(m_loadsInProgress > 0);
329    --m_loadsInProgress;
330}
331
332void TestWebViewClient::initializeLayerTreeView()
333{
334    m_layerTreeView = adoptPtr(Platform::current()->unitTestSupport()->createLayerTreeViewForTesting());
335    ASSERT(m_layerTreeView);
336}
337
338} // namespace FrameTestHelpers
339} // namespace blink
340