|
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: |
|
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 |
|
139 private: |
|
140 wxWebView* m_webView; |
|
141 |
|
142 }; |
|
143 |
|
144 void notifyDoneFired() |
|
145 { |
|
146 notified = true; |
|
147 if (done) |
|
148 dump(); |
|
149 } |
|
150 |
|
151 LayoutWebViewEventHandler* eventHandler = NULL; |
|
152 |
|
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 |
|
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 |
|
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 |
|
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 } |