1 /* 2 * Copyright (C) 2008 Kevin Ollivier <kevino (at) 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: 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 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 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 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