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 LayoutTestController* gLayoutTestController = 0;
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 if (!gLayoutTestController->dumpAsText() &&
206 !gLayoutTestController->dumpDOMAsWebArchive() &&
207 !gLayoutTestController->dumpSourceAsWebArchive()) {
208 // FIXME: Add support for dumping pixels
209 }
210
211 fflush(stdout);
212 }
213
214 puts("#EOF");
215 fflush(stdout);
216 fflush(stderr);
217
218 gLayoutTestController->deref();
219 gLayoutTestController = 0;
220 }
221
runTest(const wxString testPathOrURL)222 static void runTest(const wxString testPathOrURL)
223 {
224 done = false;
225 time(&startTime);
226 string pathOrURLString(testPathOrURL.char_str());
227 string pathOrURL(pathOrURLString);
228 string expectedPixelHash;
229
230 size_t separatorPos = pathOrURL.find("'");
231 if (separatorPos != string::npos) {
232 pathOrURL = string(pathOrURLString, 0, separatorPos);
233 expectedPixelHash = string(pathOrURLString, separatorPos + 1);
234 }
235
236 // CURL isn't happy if we don't have a protocol.
237 size_t http = pathOrURL.find("http://");
238 if (http == string::npos)
239 pathOrURL.insert(0, "file://");
240
241 gLayoutTestController = new LayoutTestController(pathOrURL, expectedPixelHash);
242 if (!gLayoutTestController) {
243 wxTheApp->ExitMainLoop();
244 }
245
246 WorkQueue::shared()->clear();
247 WorkQueue::shared()->setFrozen(false);
248
249 webView->LoadURL(wxString(pathOrURL.c_str(), wxConvUTF8));
250
251 // wait until load completes and the results are dumped
252 while (!done)
253 wxSafeYield();
254 }
255
256 class MyApp : public wxApp
257 {
258 public:
259
260 virtual bool OnInit();
261
262 private:
263 wxLog* logger;
264 };
265
266
IMPLEMENT_APP(MyApp)267 IMPLEMENT_APP(MyApp)
268
269 bool MyApp::OnInit()
270 {
271 logOutput = fopen("output.txt", "ab");
272 if (logOutput) {
273 logger = new wxLogStderr(logOutput);
274 wxLog::SetActiveTarget(logger);
275 }
276
277 wxLogMessage(wxT("Starting DumpRenderTool, %d args.\n"), argc);
278
279 for (int i = 1; i < argc; ++i) {
280 wxString option = wxString(argv[i]);
281 if (!option.CmpNoCase(_T("--notree"))) {
282 dumpTree = false;
283 continue;
284 }
285
286 if (!option.CmpNoCase(_T("--pixel-tests"))) {
287 dumpPixels = true;
288 continue;
289 }
290
291 if (!option.CmpNoCase(_T("--tree"))) {
292 dumpTree = true;
293 continue;
294 }
295 }
296 wxInitAllImageHandlers();
297
298 // create the main application window
299 wxWebBrowserShell* webFrame = new wxWebBrowserShell(_T("wxWebKit DumpRenderTree App"));
300 SetTopWindow(webFrame);
301 webView = webFrame->webview;
302 webView->SetSize(wxSize(maxViewWidth, maxViewHeight));
303
304 if (!eventHandler) {
305 eventHandler = new LayoutWebViewEventHandler(webView);
306 eventHandler->bindEvents();
307 }
308
309 int optind = 1;
310 time(&startTime);
311 wxString option_str = wxString(argv[optind]);
312 if (argc == optind+1 && option_str.Find(_T("-")) == 0) {
313 char filenameBuffer[2048];
314 while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
315 wxString filename = wxString::FromUTF8(filenameBuffer);
316 char* newLineCharacter = strchr(filenameBuffer, '\n');
317 if (newLineCharacter)
318 *newLineCharacter = '\0';
319
320 if (strlen(filenameBuffer) == 0)
321 return 0;
322 wxLogMessage(wxT("Running test %S.\n"), filenameBuffer);
323 runTest(filename);
324 }
325
326 } else {
327 printSeparators = (optind < argc-1 || (dumpPixels && dumpTree));
328 for (int i = optind; i != argc; ++i) {
329 runTest(wxTheApp->argv[1]);
330 }
331 }
332
333 webFrame->Close();
334 delete eventHandler;
335
336 wxLog::SetActiveTarget(NULL);
337 delete logger;
338 fclose(logOutput);
339
340 delete gLayoutTestController;
341 gLayoutTestController = 0;
342
343 // returning false shuts the app down
344 return false;
345 }
346