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 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 } 340