1/*
2 * Copyright (C) 2008 Kevin Ollivier <kevino@theolliviers.com>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "DumpRenderTree.h"
31
32#include "LayoutTestController.h"
33#include "WorkQueue.h"
34#include "WorkQueueItem.h"
35
36#include <JavaScriptCore/JavaScript.h>
37
38#include <wx/wx.h>
39#include "WebView.h"
40#include "WebFrame.h"
41#include "WebBrowserShell.h"
42
43#include <wtf/Assertions.h>
44
45#include <cassert>
46#include <stdlib.h>
47#include <string.h>
48#include <time.h>
49
50volatile bool done = true;
51volatile bool notified = false;
52static bool printSeparators = true;
53static int dumpPixels;
54static int dumpTree = 1;
55time_t startTime; // to detect timeouts / failed tests
56
57using namespace std;
58
59FILE* logOutput;
60
61RefPtr<LayoutTestController> gLayoutTestController;
62static wxWebView* webView;
63static wxTimer* idleTimer;
64
65const unsigned timeOut = 10;
66const unsigned maxViewHeight = 600;
67const unsigned maxViewWidth = 800;
68
69class LayoutWebViewEventHandler : public wxEvtHandler {
70
71public:
72    LayoutWebViewEventHandler(wxWebView* webView)
73        : m_webView(webView)
74    {
75    }
76
77    void bindEvents()
78    {
79        m_webView->Connect(wxEVT_WEBVIEW_LOAD, wxWebViewLoadEventHandler(LayoutWebViewEventHandler::OnLoadEvent), NULL, this);
80        m_webView->Connect(wxEVT_WEBVIEW_JS_ALERT, wxWebViewAlertEventHandler(LayoutWebViewEventHandler::OnAlertEvent), NULL, this);
81        m_webView->Connect(wxEVT_WEBVIEW_JS_CONFIRM, wxWebViewConfirmEventHandler(LayoutWebViewEventHandler::OnConfirmEvent), NULL, this);
82        m_webView->Connect(wxEVT_WEBVIEW_JS_PROMPT, wxWebViewPromptEventHandler(LayoutWebViewEventHandler::OnPromptEvent), NULL, this);
83        m_webView->Connect(wxEVT_WEBVIEW_CONSOLE_MESSAGE, wxWebViewConsoleMessageEventHandler(LayoutWebViewEventHandler::OnConsoleMessageEvent), NULL, this);
84        m_webView->Connect(wxEVT_WEBVIEW_RECEIVED_TITLE, wxWebViewReceivedTitleEventHandler(LayoutWebViewEventHandler::OnReceivedTitleEvent), NULL, this);
85        m_webView->Connect(wxEVT_WEBVIEW_WINDOW_OBJECT_CLEARED, wxWebViewWindowObjectClearedEventHandler(LayoutWebViewEventHandler::OnWindowObjectClearedEvent), NULL, this);
86    }
87
88    void OnLoadEvent(wxWebViewLoadEvent& event)
89    {
90
91        if (event.GetState() == wxWEBVIEW_LOAD_FAILED || event.GetState() == wxWEBVIEW_LOAD_STOPPED)
92            done = true;
93
94        if (event.GetState() == wxWEBVIEW_LOAD_ONLOAD_HANDLED) {
95            done = true;
96
97            if (!gLayoutTestController->waitToDump() || notified) {
98                dump();
99            }
100        }
101    }
102
103    void OnAlertEvent(wxWebViewAlertEvent& event)
104    {
105        fprintf(stdout, "ALERT: %S\n", event.GetMessage().c_str());
106    }
107
108    void OnConfirmEvent(wxWebViewConfirmEvent& event)
109    {
110        fprintf(stdout, "CONFIRM: %S\n", event.GetMessage().c_str());
111        event.SetReturnCode(1);
112    }
113
114    void OnPromptEvent(wxWebViewPromptEvent& event)
115    {
116        fprintf(stdout, "PROMPT: %S, default text: %S\n", event.GetMessage().c_str(), event.GetResponse().c_str());
117        event.SetReturnCode(1);
118    }
119
120    void OnConsoleMessageEvent(wxWebViewConsoleMessageEvent& event)
121    {
122        fprintf(stdout, "CONSOLE MESSAGE: line %d: %S\n", event.GetLineNumber(), event.GetMessage().c_str());
123    }
124
125    void OnReceivedTitleEvent(wxWebViewReceivedTitleEvent& event)
126    {
127        if (gLayoutTestController->dumpTitleChanges() && !done) {
128            const char* title = event.GetTitle().mb_str(wxConvUTF8);
129            printf("TITLE CHANGED: %S\n", title ? title : "");
130        }
131    }
132
133    void OnWindowObjectClearedEvent(wxWebViewWindowObjectClearedEvent& event)
134    {
135        JSValueRef exception = 0;
136        gLayoutTestController->makeWindowObject(event.GetJSContext(), event.GetWindowObject(), &exception);
137    }
138
139private:
140    wxWebView* m_webView;
141
142};
143
144void notifyDoneFired()
145{
146    notified = true;
147    if (done)
148        dump();
149}
150
151LayoutWebViewEventHandler* eventHandler = NULL;
152
153static wxString dumpFramesAsText(wxWebFrame* frame)
154{
155    // TODO: implement this. leaving this here so we don't forget this case.
156    if (gLayoutTestController->dumpChildFramesAsText()) {
157    }
158
159    return frame->GetInnerText();
160}
161
162void dump()
163{
164    if (!done)
165        return;
166
167    if (gLayoutTestController->waitToDump() && !notified)
168        return;
169
170    if (dumpTree) {
171        const char* result = 0;
172
173        bool dumpAsText = gLayoutTestController->dumpAsText();
174        wxString str;
175        if (gLayoutTestController->dumpAsText())
176            str = dumpFramesAsText(webView->GetMainFrame());
177        else
178            str = webView->GetMainFrame()->GetExternalRepresentation();
179
180        result = str.ToUTF8();
181        if (!result) {
182            const char* errorMessage;
183            if (gLayoutTestController->dumpAsText())
184                errorMessage = "WebFrame::GetInnerText";
185            else
186                errorMessage = "WebFrame::GetExternalRepresentation";
187            printf("ERROR: NULL result from %s", errorMessage);
188        } else {
189            printf("%s\n", result);
190        }
191
192        if (gLayoutTestController->dumpBackForwardList()) {
193            // FIXME: not implemented
194        }
195
196        if (printSeparators) {
197            puts("#EOF");
198            fputs("#EOF\n", stderr);
199            fflush(stdout);
200            fflush(stderr);
201        }
202    }
203
204    if (dumpPixels
205        && gLayoutTestController->generatePixelResults()
206        && !gLayoutTestController->dumpDOMAsWebArchive()
207        && !gLayoutTestController->dumpSourceAsWebArchive()) {
208        // FIXME: Add support for dumping pixels
209        fflush(stdout);
210    }
211
212    puts("#EOF");
213    fflush(stdout);
214    fflush(stderr);
215
216    gLayoutTestController.clear();
217}
218
219static void runTest(const wxString testPathOrURL)
220{
221    done = false;
222    time(&startTime);
223    string pathOrURLString(testPathOrURL.char_str());
224    string pathOrURL(pathOrURLString);
225    string expectedPixelHash;
226
227    size_t separatorPos = pathOrURL.find("'");
228    if (separatorPos != string::npos) {
229        pathOrURL = string(pathOrURLString, 0, separatorPos);
230        expectedPixelHash = string(pathOrURLString, separatorPos + 1);
231    }
232
233    // CURL isn't happy if we don't have a protocol.
234    size_t http = pathOrURL.find("http://");
235    if (http == string::npos)
236        pathOrURL.insert(0, "file://");
237
238    gLayoutTestController = LayoutTestController::create(pathOrURL, expectedPixelHash);
239    if (!gLayoutTestController) {
240        wxTheApp->ExitMainLoop();
241    }
242
243    WorkQueue::shared()->clear();
244    WorkQueue::shared()->setFrozen(false);
245
246    webView->LoadURL(wxString(pathOrURL.c_str(), wxConvUTF8));
247
248    // wait until load completes and the results are dumped
249    while (!done)
250        wxSafeYield();
251}
252
253class MyApp : public wxApp
254{
255public:
256
257    virtual bool OnInit();
258
259private:
260    wxLog* logger;
261};
262
263
264IMPLEMENT_APP(MyApp)
265
266bool MyApp::OnInit()
267{
268    logOutput = fopen("output.txt", "ab");
269    if (logOutput) {
270        logger = new wxLogStderr(logOutput);
271        wxLog::SetActiveTarget(logger);
272    }
273
274    wxLogMessage(wxT("Starting DumpRenderTool, %d args.\n"), argc);
275
276    for (int i = 1; i < argc; ++i) {
277        wxString option = wxString(argv[i]);
278        if (!option.CmpNoCase(_T("--notree"))) {
279            dumpTree = false;
280            continue;
281        }
282
283        if (!option.CmpNoCase(_T("--pixel-tests"))) {
284            dumpPixels = true;
285            continue;
286        }
287
288        if (!option.CmpNoCase(_T("--tree"))) {
289            dumpTree = true;
290            continue;
291        }
292    }
293    wxInitAllImageHandlers();
294
295    // create the main application window
296    wxWebBrowserShell* webFrame = new wxWebBrowserShell(_T("wxWebKit DumpRenderTree App"));
297    SetTopWindow(webFrame);
298    webView = webFrame->webview;
299    webView->SetSize(wxSize(maxViewWidth, maxViewHeight));
300
301    if (!eventHandler) {
302        eventHandler = new LayoutWebViewEventHandler(webView);
303        eventHandler->bindEvents();
304    }
305
306    int optind = 1;
307    time(&startTime);
308    wxString option_str = wxString(argv[optind]);
309    if (argc == optind+1 && option_str.Find(_T("-")) == 0) {
310        char filenameBuffer[2048];
311        while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
312            wxString filename = wxString::FromUTF8(filenameBuffer);
313            char* newLineCharacter = strchr(filenameBuffer, '\n');
314            if (newLineCharacter)
315                *newLineCharacter = '\0';
316
317            if (strlen(filenameBuffer) == 0)
318                return 0;
319            wxLogMessage(wxT("Running test %S.\n"), filenameBuffer);
320            runTest(filename);
321        }
322
323    } else {
324        printSeparators = (optind < argc-1 || (dumpPixels && dumpTree));
325        for (int i = optind; i != argc; ++i) {
326            runTest(wxTheApp->argv[1]);
327        }
328    }
329
330    webFrame->Close();
331    delete eventHandler;
332
333    wxLog::SetActiveTarget(NULL);
334    delete logger;
335    fclose(logOutput);
336
337    // returning false shuts the app down
338    return false;
339}
340