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
50 volatile bool done = true;
51 volatile bool notified = false;
52 static bool printSeparators = true;
53 static int dumpPixels;
54 static int dumpTree = 1;
55 time_t startTime; // to detect timeouts / failed tests
56
57 using namespace std;
58
59 FILE* logOutput;
60
61 RefPtr<LayoutTestController> gLayoutTestController;
62 static wxWebView* webView;
63 static wxTimer* idleTimer;
64
65 const unsigned timeOut = 10;
66 const unsigned maxViewHeight = 600;
67 const unsigned maxViewWidth = 800;
68
69 class LayoutWebViewEventHandler : public wxEvtHandler {
70
71 public:
LayoutWebViewEventHandler(wxWebView * webView)72 LayoutWebViewEventHandler(wxWebView* webView)
73 : m_webView(webView)
74 {
75 }
76
bindEvents()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
OnLoadEvent(wxWebViewLoadEvent & event)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
OnAlertEvent(wxWebViewAlertEvent & event)103 void OnAlertEvent(wxWebViewAlertEvent& event)
104 {
105 fprintf(stdout, "ALERT: %S\n", event.GetMessage().c_str());
106 }
107
OnConfirmEvent(wxWebViewConfirmEvent & event)108 void OnConfirmEvent(wxWebViewConfirmEvent& event)
109 {
110 fprintf(stdout, "CONFIRM: %S\n", event.GetMessage().c_str());
111 event.SetReturnCode(1);
112 }
113
OnPromptEvent(wxWebViewPromptEvent & event)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
OnConsoleMessageEvent(wxWebViewConsoleMessageEvent & event)120 void OnConsoleMessageEvent(wxWebViewConsoleMessageEvent& event)
121 {
122 fprintf(stdout, "CONSOLE MESSAGE: line %d: %S\n", event.GetLineNumber(), event.GetMessage().c_str());
123 }
124
OnReceivedTitleEvent(wxWebViewReceivedTitleEvent & event)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
OnWindowObjectClearedEvent(wxWebViewWindowObjectClearedEvent & event)133 void OnWindowObjectClearedEvent(wxWebViewWindowObjectClearedEvent& event)
134 {
135 JSValueRef exception = 0;
136 gLayoutTestController->makeWindowObject(event.GetJSContext(), event.GetWindowObject(), &exception);
137 }
138
139 private:
140 wxWebView* m_webView;
141
142 };
143
notifyDoneFired()144 void notifyDoneFired()
145 {
146 notified = true;
147 if (done)
148 dump();
149 }
150
151 LayoutWebViewEventHandler* eventHandler = NULL;
152
dumpFramesAsText(wxWebFrame * frame)153 static 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
dump()162 void 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
runTest(const wxString testPathOrURL)219 static 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
253 class MyApp : public wxApp
254 {
255 public:
256
257 virtual bool OnInit();
258
259 private:
260 wxLog* logger;
261 };
262
263
IMPLEMENT_APP(MyApp)264 IMPLEMENT_APP(MyApp)
265
266 bool 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