/* * Copyright (C) 2008 Kevin Ollivier <kevino@theolliviers.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "DumpRenderTree.h" #include "LayoutTestController.h" #include "WorkQueue.h" #include "WorkQueueItem.h" #include <JavaScriptCore/JavaScript.h> #include <wx/wx.h> #include "WebView.h" #include "WebFrame.h" #include "WebBrowserShell.h" #include <wtf/Assertions.h> #include <cassert> #include <stdlib.h> #include <string.h> #include <time.h> volatile bool done = true; volatile bool notified = false; static bool printSeparators = true; static int dumpPixels; static int dumpTree = 1; time_t startTime; // to detect timeouts / failed tests using namespace std; FILE* logOutput; RefPtr<LayoutTestController> gLayoutTestController; static wxWebView* webView; static wxTimer* idleTimer; const unsigned timeOut = 10; const unsigned maxViewHeight = 600; const unsigned maxViewWidth = 800; class LayoutWebViewEventHandler : public wxEvtHandler { public: LayoutWebViewEventHandler(wxWebView* webView) : m_webView(webView) { } void bindEvents() { m_webView->Connect(wxEVT_WEBVIEW_LOAD, wxWebViewLoadEventHandler(LayoutWebViewEventHandler::OnLoadEvent), NULL, this); m_webView->Connect(wxEVT_WEBVIEW_JS_ALERT, wxWebViewAlertEventHandler(LayoutWebViewEventHandler::OnAlertEvent), NULL, this); m_webView->Connect(wxEVT_WEBVIEW_JS_CONFIRM, wxWebViewConfirmEventHandler(LayoutWebViewEventHandler::OnConfirmEvent), NULL, this); m_webView->Connect(wxEVT_WEBVIEW_JS_PROMPT, wxWebViewPromptEventHandler(LayoutWebViewEventHandler::OnPromptEvent), NULL, this); m_webView->Connect(wxEVT_WEBVIEW_CONSOLE_MESSAGE, wxWebViewConsoleMessageEventHandler(LayoutWebViewEventHandler::OnConsoleMessageEvent), NULL, this); m_webView->Connect(wxEVT_WEBVIEW_RECEIVED_TITLE, wxWebViewReceivedTitleEventHandler(LayoutWebViewEventHandler::OnReceivedTitleEvent), NULL, this); m_webView->Connect(wxEVT_WEBVIEW_WINDOW_OBJECT_CLEARED, wxWebViewWindowObjectClearedEventHandler(LayoutWebViewEventHandler::OnWindowObjectClearedEvent), NULL, this); } void OnLoadEvent(wxWebViewLoadEvent& event) { if (event.GetState() == wxWEBVIEW_LOAD_FAILED || event.GetState() == wxWEBVIEW_LOAD_STOPPED) done = true; if (event.GetState() == wxWEBVIEW_LOAD_ONLOAD_HANDLED) { done = true; if (!gLayoutTestController->waitToDump() || notified) { dump(); } } } void OnAlertEvent(wxWebViewAlertEvent& event) { fprintf(stdout, "ALERT: %S\n", event.GetMessage().c_str()); } void OnConfirmEvent(wxWebViewConfirmEvent& event) { fprintf(stdout, "CONFIRM: %S\n", event.GetMessage().c_str()); event.SetReturnCode(1); } void OnPromptEvent(wxWebViewPromptEvent& event) { fprintf(stdout, "PROMPT: %S, default text: %S\n", event.GetMessage().c_str(), event.GetResponse().c_str()); event.SetReturnCode(1); } void OnConsoleMessageEvent(wxWebViewConsoleMessageEvent& event) { fprintf(stdout, "CONSOLE MESSAGE: line %d: %S\n", event.GetLineNumber(), event.GetMessage().c_str()); } void OnReceivedTitleEvent(wxWebViewReceivedTitleEvent& event) { if (gLayoutTestController->dumpTitleChanges() && !done) { const char* title = event.GetTitle().mb_str(wxConvUTF8); printf("TITLE CHANGED: %S\n", title ? title : ""); } } void OnWindowObjectClearedEvent(wxWebViewWindowObjectClearedEvent& event) { JSValueRef exception = 0; gLayoutTestController->makeWindowObject(event.GetJSContext(), event.GetWindowObject(), &exception); } private: wxWebView* m_webView; }; void notifyDoneFired() { notified = true; if (done) dump(); } LayoutWebViewEventHandler* eventHandler = NULL; static wxString dumpFramesAsText(wxWebFrame* frame) { // TODO: implement this. leaving this here so we don't forget this case. if (gLayoutTestController->dumpChildFramesAsText()) { } return frame->GetInnerText(); } void dump() { if (!done) return; if (gLayoutTestController->waitToDump() && !notified) return; if (dumpTree) { const char* result = 0; bool dumpAsText = gLayoutTestController->dumpAsText(); wxString str; if (gLayoutTestController->dumpAsText()) str = dumpFramesAsText(webView->GetMainFrame()); else str = webView->GetMainFrame()->GetExternalRepresentation(); result = str.ToUTF8(); if (!result) { const char* errorMessage; if (gLayoutTestController->dumpAsText()) errorMessage = "WebFrame::GetInnerText"; else errorMessage = "WebFrame::GetExternalRepresentation"; printf("ERROR: NULL result from %s", errorMessage); } else { printf("%s\n", result); } if (gLayoutTestController->dumpBackForwardList()) { // FIXME: not implemented } if (printSeparators) { puts("#EOF"); fputs("#EOF\n", stderr); fflush(stdout); fflush(stderr); } } if (dumpPixels && gLayoutTestController->generatePixelResults() && !gLayoutTestController->dumpDOMAsWebArchive() && !gLayoutTestController->dumpSourceAsWebArchive()) { // FIXME: Add support for dumping pixels fflush(stdout); } puts("#EOF"); fflush(stdout); fflush(stderr); gLayoutTestController.clear(); } static void runTest(const wxString testPathOrURL) { done = false; time(&startTime); string pathOrURLString(testPathOrURL.char_str()); string pathOrURL(pathOrURLString); string expectedPixelHash; size_t separatorPos = pathOrURL.find("'"); if (separatorPos != string::npos) { pathOrURL = string(pathOrURLString, 0, separatorPos); expectedPixelHash = string(pathOrURLString, separatorPos + 1); } // CURL isn't happy if we don't have a protocol. size_t http = pathOrURL.find("http://"); if (http == string::npos) pathOrURL.insert(0, "file://"); gLayoutTestController = LayoutTestController::create(pathOrURL, expectedPixelHash); if (!gLayoutTestController) { wxTheApp->ExitMainLoop(); } WorkQueue::shared()->clear(); WorkQueue::shared()->setFrozen(false); webView->LoadURL(wxString(pathOrURL.c_str(), wxConvUTF8)); // wait until load completes and the results are dumped while (!done) wxSafeYield(); } class MyApp : public wxApp { public: virtual bool OnInit(); private: wxLog* logger; }; IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { logOutput = fopen("output.txt", "ab"); if (logOutput) { logger = new wxLogStderr(logOutput); wxLog::SetActiveTarget(logger); } wxLogMessage(wxT("Starting DumpRenderTool, %d args.\n"), argc); for (int i = 1; i < argc; ++i) { wxString option = wxString(argv[i]); if (!option.CmpNoCase(_T("--notree"))) { dumpTree = false; continue; } if (!option.CmpNoCase(_T("--pixel-tests"))) { dumpPixels = true; continue; } if (!option.CmpNoCase(_T("--tree"))) { dumpTree = true; continue; } } wxInitAllImageHandlers(); // create the main application window wxWebBrowserShell* webFrame = new wxWebBrowserShell(_T("wxWebKit DumpRenderTree App")); SetTopWindow(webFrame); webView = webFrame->webview; webView->SetSize(wxSize(maxViewWidth, maxViewHeight)); if (!eventHandler) { eventHandler = new LayoutWebViewEventHandler(webView); eventHandler->bindEvents(); } int optind = 1; time(&startTime); wxString option_str = wxString(argv[optind]); if (argc == optind+1 && option_str.Find(_T("-")) == 0) { char filenameBuffer[2048]; while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) { wxString filename = wxString::FromUTF8(filenameBuffer); char* newLineCharacter = strchr(filenameBuffer, '\n'); if (newLineCharacter) *newLineCharacter = '\0'; if (strlen(filenameBuffer) == 0) return 0; wxLogMessage(wxT("Running test %S.\n"), filenameBuffer); runTest(filename); } } else { printSeparators = (optind < argc-1 || (dumpPixels && dumpTree)); for (int i = optind; i != argc; ++i) { runTest(wxTheApp->argv[1]); } } webFrame->Close(); delete eventHandler; wxLog::SetActiveTarget(NULL); delete logger; fclose(logOutput); // returning false shuts the app down return false; }