2 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #include "LayoutTestController.h"
30 #include "WorkQueue.h"
31 #include "WorkQueueItem.h"
34 #include <webkit/webkit.h>
36 #include <JavaScriptCore/JSBase.h>
37 #include <JavaScriptCore/JSContextRef.h>
38 #include <JavaScriptCore/JSObjectRef.h>
39 #include <JavaScriptCore/JSStringRef.h>
40 #include <JavaScriptCore/JSValueRef.h>
42 #include <wtf/Assertions.h>
50 // This API is not yet public.
51 extern GSList* webkit_web_frame_get_children(WebKitWebFrame* frame);
52 extern gchar* webkit_web_frame_get_inner_text(WebKitWebFrame* frame);
56 static bool printSeparators;
57 static int testRepaintDefault;
58 static int repaintSweepHorizontallyDefault;
59 static int dumpPixels;
60 static int dumpTree = 1;
61 static gchar* currentTest;
63 LayoutTestController* layoutTestController = 0;
64 static WebKitWebView* webView;
65 WebKitWebFrame* mainFrame = 0;
66 WebKitWebFrame* topLoadingFrame = 0;
67 guint waitToDumpWatchdog = 0;
69 const unsigned maxViewHeight = 600;
70 const unsigned maxViewWidth = 800;
72 static gchar* autocorrectURL(const gchar* url)
74 if (strncmp("http://", url, 7) != 0 && strncmp("https://", url, 8) != 0) {
75 GString* string = g_string_new("file://");
76 g_string_append(string, url);
77 return g_string_free(string, FALSE);
83 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
85 return strstr(pathOrURL, "loading/");
88 void dumpFrameScrollPosition(WebKitWebFrame* frame)
98 static void appendString(gchar*& target, gchar* string)
100 gchar* oldString = target;
101 target = g_strconcat(target, string, NULL);
105 static gchar* dumpFramesAsText(WebKitWebFrame* frame)
109 // Add header for all but the main frame.
110 bool isMainFrame = (webkit_web_view_get_main_frame(webView) == frame);
113 gchar* innerText = webkit_web_frame_get_inner_text(frame);
114 result = g_strdup_printf("%s\n", innerText);
117 const gchar* frameName = webkit_web_frame_get_name(frame);
118 gchar* innerText = webkit_web_frame_get_inner_text(frame);
120 result = g_strdup_printf("\n--------\nFrame: '%s'\n--------\n%s\n", frameName, innerText);
125 if (layoutTestController->dumpChildFramesAsText()) {
126 GSList* children = webkit_web_frame_get_children(frame);
127 for (;children; children = g_slist_next(children))
128 appendString(result, dumpFramesAsText((WebKitWebFrame*)children->data));
134 static gchar* dumpRenderTreeAsText(WebKitWebFrame* frame)
136 // FIXME: this will require new WebKitGtk SPI
137 return strdup("foo");
140 static void invalidateAnyPreviousWaitToDumpWatchdog()
142 if (waitToDumpWatchdog) {
143 g_source_remove(waitToDumpWatchdog);
144 waitToDumpWatchdog = 0;
150 invalidateAnyPreviousWaitToDumpWatchdog();
154 bool dumpAsText = layoutTestController->dumpAsText();
155 // FIXME: Also dump text resuls as text.
156 layoutTestController->setDumpAsText(dumpAsText);
157 if (layoutTestController->dumpAsText())
158 result = dumpFramesAsText(mainFrame);
160 bool isSVGW3CTest = (g_strrstr(currentTest, "svg/W3C-SVG-1.1"));
162 gtk_widget_set_size_request(GTK_WIDGET(webView), 480, 360);
164 gtk_widget_set_size_request(GTK_WIDGET(webView), maxViewWidth, maxViewHeight);
165 result = dumpRenderTreeAsText(mainFrame);
169 const char* errorMessage;
170 if (layoutTestController->dumpAsText())
171 errorMessage = "[documentElement innerText]";
172 else if (layoutTestController->dumpDOMAsWebArchive())
173 errorMessage = "[[mainFrame DOMDocument] webArchive]";
174 else if (layoutTestController->dumpSourceAsWebArchive())
175 errorMessage = "[[mainFrame dataSource] webArchive]";
177 errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
178 printf("ERROR: nil result from %s", errorMessage);
180 printf("%s", result);
182 if (!layoutTestController->dumpAsText() && !layoutTestController->dumpDOMAsWebArchive() && !layoutTestController->dumpSourceAsWebArchive())
183 dumpFrameScrollPosition(mainFrame);
186 if (layoutTestController->dumpBackForwardList()) {
187 // FIXME: not implemented
195 if (!layoutTestController->dumpAsText() && !layoutTestController->dumpDOMAsWebArchive() && !layoutTestController->dumpSourceAsWebArchive()) {
196 // FIXME: Add support for dumping pixels
202 // FIXME: call displayWebView here when we support --paint
207 static void runTest(const char* pathOrURL)
209 gchar* url = autocorrectURL(pathOrURL);
211 layoutTestController = new LayoutTestController(testRepaintDefault, repaintSweepHorizontallyDefault);
216 if (shouldLogFrameLoadDelegates(pathOrURL))
217 layoutTestController->setDumpFrameLoadCallbacks(true);
223 WorkQueue::shared()->clear();
224 WorkQueue::shared()->setFrozen(false);
226 webkit_web_view_open(webView, url);
229 g_main_context_iteration(NULL, true);
231 WorkQueue::shared()->clear();
233 delete layoutTestController;
234 layoutTestController = 0;
237 void webViewLoadStarted(WebKitWebView* view, WebKitWebFrame* frame, void*)
239 // Make sure we only set this once per test. If it gets cleared, and then set again, we might
240 // end up doing two dumps for one test.
241 if (!topLoadingFrame && !done)
242 topLoadingFrame = frame;
245 static gboolean processWork(void* data)
247 // quit doing work once a load is in progress
248 while (WorkQueue::shared()->count() > 0 && !topLoadingFrame) {
249 WorkQueueItem* item = WorkQueue::shared()->dequeue();
255 // if we didn't start a new load, then we finished all the commands, so we're ready to dump state
256 if (!topLoadingFrame && !layoutTestController->waitToDump())
262 void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
264 if (frame != topLoadingFrame)
268 WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
269 if (layoutTestController->waitToDump())
272 if (WorkQueue::shared()->count())
273 g_timeout_add(0, processWork, 0);
278 void webViewWindowObjectCleared(WebKitWebView* view, WebKitWebFrame* frame, JSGlobalContextRef context, JSObjectRef globalObject)
280 JSValueRef exception = 0;
281 assert(layoutTestController);
282 layoutTestController->makeWindowObject(context, globalObject, &exception);
286 gboolean webViewConsoleMessage(WebKitWebView* view, const gchar* message, unsigned int line, const gchar* sourceId)
288 fprintf(stdout, "CONSOLE MESSAGE: line %d: %s\n", line, message);
293 gboolean webViewScriptAlert(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message)
295 fprintf(stdout, "ALERT: %s\n", message);
299 gboolean webViewScriptPrompt(WebKitWebView* webView, WebKitWebFrame* frame, const gchar* message, const gchar* defaultValue, gchar** value)
301 fprintf(stdout, "PROMPT: %s, default text: %s\n", message, defaultValue);
302 *value = g_strdup(defaultValue);
306 gboolean webViewScriptConfirm(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gboolean* didConfirm)
308 fprintf(stdout, "CONFIRM: %s\n", message);
314 int main(int argc, char* argv[])
316 struct option options[] = {
317 {"horizontal-sweep", no_argument, &repaintSweepHorizontallyDefault, true},
318 {"notree", no_argument, &dumpTree, false},
319 {"pixel-tests", no_argument, &dumpPixels, true},
320 {"repaint", no_argument, &testRepaintDefault, true},
321 {"tree", no_argument, &dumpTree, true},
326 while ((option = getopt_long(argc, (char* const*)argv, "", options, NULL)) != -1)
328 case '?': // unknown or ambiguous option
329 case ':': // missing argument
334 gtk_init(&argc, &argv);
336 GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
337 GtkContainer* container = GTK_CONTAINER(gtk_fixed_new());
338 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(container));
339 gtk_widget_realize(window);
341 webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
342 gtk_container_add(container, GTK_WIDGET(webView));
343 gtk_widget_realize(GTK_WIDGET(webView));
344 mainFrame = webkit_web_view_get_main_frame(webView);
346 g_signal_connect(G_OBJECT(webView), "load-started", G_CALLBACK(webViewLoadStarted), 0);
347 g_signal_connect(G_OBJECT(webView), "load-finished", G_CALLBACK(webViewLoadFinished), 0);
348 g_signal_connect(G_OBJECT(webView), "window-object-cleared", G_CALLBACK(webViewWindowObjectCleared), 0);
349 g_signal_connect(G_OBJECT(webView), "console-message", G_CALLBACK(webViewConsoleMessage), 0);
350 g_signal_connect(G_OBJECT(webView), "script-alert", G_CALLBACK(webViewScriptAlert), 0);
351 g_signal_connect(G_OBJECT(webView), "script-prompt", G_CALLBACK(webViewScriptPrompt), 0);
352 g_signal_connect(G_OBJECT(webView), "script-confirm", G_CALLBACK(webViewScriptConfirm), 0);
354 if (argc == optind+1 && strcmp(argv[optind], "-") == 0) {
355 char filenameBuffer[2048];
356 printSeparators = true;
357 while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
358 char* newLineCharacter = strchr(filenameBuffer, '\n');
359 if (newLineCharacter)
360 *newLineCharacter = '\0';
362 if (strlen(filenameBuffer) == 0)
365 runTest(filenameBuffer);
368 printSeparators = (optind < argc-1 || (dumpPixels && dumpTree));
369 for (int i = optind; i != argc; ++i)