/*
* 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;
}