2008-01-31 Alp Toker <alp@atoker.com>
[WebKit-https.git] / WebKitTools / DumpRenderTree / gtk / DumpRenderTree.cpp
1 /*
2  * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
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 "LayoutTestController.h"
30 #include "WorkQueue.h"
31 #include "WorkQueueItem.h"
32
33 #include <gtk/gtk.h>
34 #include <webkit/webkit.h>
35
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>
41
42 #include <wtf/Assertions.h>
43
44 #include <cassert>
45 #include <getopt.h>
46 #include <stdlib.h>
47 #include <string.h>
48
49 extern "C" {
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);
53 }
54
55 volatile bool done;
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;
62
63 LayoutTestController* layoutTestController = 0;
64 static WebKitWebView* webView;
65 WebKitWebFrame* mainFrame = 0;
66 WebKitWebFrame* topLoadingFrame = 0;
67 guint waitToDumpWatchdog = 0;
68
69 const unsigned maxViewHeight = 600;
70 const unsigned maxViewWidth = 800;
71
72 static gchar* autocorrectURL(const gchar* url)
73 {
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);
78     }
79
80     return g_strdup(url);
81 }
82
83 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
84 {
85     return strstr(pathOrURL, "loading/");
86 }
87
88 void dumpFrameScrollPosition(WebKitWebFrame* frame)
89 {
90
91 }
92
93 void displayWebView()
94 {
95
96 }
97
98 static void appendString(gchar*& target, gchar* string)
99 {
100     gchar* oldString = target;
101     target = g_strconcat(target, string, NULL);
102     g_free(oldString);
103 }
104
105 static gchar* dumpFramesAsText(WebKitWebFrame* frame)
106 {
107     gchar* result = 0;
108
109     // Add header for all but the main frame.
110     bool isMainFrame = (webkit_web_view_get_main_frame(webView) == frame);
111
112     if (isMainFrame) {
113         gchar* innerText = webkit_web_frame_get_inner_text(frame);
114         result = g_strdup_printf("%s\n", innerText);
115         g_free(innerText);
116     } else {
117         const gchar* frameName = webkit_web_frame_get_name(frame);
118         gchar* innerText = webkit_web_frame_get_inner_text(frame);
119
120         result = g_strdup_printf("\n--------\nFrame: '%s'\n--------\n%s\n", frameName, innerText);
121
122         g_free(innerText);
123     }
124
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));
129     }
130
131     return result;
132 }
133
134 static gchar* dumpRenderTreeAsText(WebKitWebFrame* frame)
135 {
136     // FIXME: this will require new WebKitGtk SPI
137     return strdup("foo");
138 }
139
140 static void invalidateAnyPreviousWaitToDumpWatchdog()
141 {
142     if (waitToDumpWatchdog) {
143         g_source_remove(waitToDumpWatchdog);
144         waitToDumpWatchdog = 0;
145     }
146 }
147
148 void dump()
149 {
150     invalidateAnyPreviousWaitToDumpWatchdog();
151     if (dumpTree) {
152         char* result = 0;
153
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);
159         else {
160             bool isSVGW3CTest = (g_strrstr(currentTest, "svg/W3C-SVG-1.1"));
161             if (isSVGW3CTest)
162                 gtk_widget_set_size_request(GTK_WIDGET(webView), 480, 360);
163             else
164                 gtk_widget_set_size_request(GTK_WIDGET(webView), maxViewWidth, maxViewHeight);
165             result = dumpRenderTreeAsText(mainFrame);
166         }
167
168         if (!result) {
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]";
176             else
177                 errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
178             printf("ERROR: nil result from %s", errorMessage);
179         } else {
180             printf("%s", result);
181             g_free(result);
182             if (!layoutTestController->dumpAsText() && !layoutTestController->dumpDOMAsWebArchive() && !layoutTestController->dumpSourceAsWebArchive())
183                 dumpFrameScrollPosition(mainFrame);
184         }
185
186         if (layoutTestController->dumpBackForwardList()) {
187             // FIXME: not implemented
188         }
189
190         if (printSeparators)
191             puts("#EOF");
192     }
193
194     if (dumpPixels) {
195         if (!layoutTestController->dumpAsText() && !layoutTestController->dumpDOMAsWebArchive() && !layoutTestController->dumpSourceAsWebArchive()) {
196             // FIXME: Add support for dumping pixels
197         }
198     }
199
200     fflush(stdout);
201
202     // FIXME: call displayWebView here when we support --paint
203
204     done = true;
205 }
206
207 static void runTest(const char* pathOrURL)
208 {
209     gchar* url = autocorrectURL(pathOrURL);
210
211     layoutTestController = new LayoutTestController(testRepaintDefault, repaintSweepHorizontallyDefault);
212
213     done = false;
214     topLoadingFrame = 0;
215
216     if (shouldLogFrameLoadDelegates(pathOrURL))
217         layoutTestController->setDumpFrameLoadCallbacks(true);
218
219     if (currentTest)
220         g_free(currentTest);
221     currentTest = url;
222
223     WorkQueue::shared()->clear();
224     WorkQueue::shared()->setFrozen(false);
225
226     webkit_web_view_open(webView, url);
227
228     while (!done)
229         g_main_context_iteration(NULL, true);
230
231     WorkQueue::shared()->clear();
232
233     delete layoutTestController;
234     layoutTestController = 0;
235 }
236
237 void webViewLoadStarted(WebKitWebView* view, WebKitWebFrame* frame, void*)
238 {
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;
243 }
244
245 static gboolean processWork(void* data)
246 {
247     // quit doing work once a load is in progress
248     while (WorkQueue::shared()->count() > 0 && !topLoadingFrame) {
249         WorkQueueItem* item = WorkQueue::shared()->dequeue();
250         ASSERT(item);
251         item->invoke();
252         delete item;
253     }
254
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())
257         dump();
258
259     return FALSE;
260 }
261
262 void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
263 {
264     if (frame != topLoadingFrame)
265         return;
266
267     topLoadingFrame = 0;
268     WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
269     if (layoutTestController->waitToDump())
270         return;
271
272     if (WorkQueue::shared()->count())
273         g_timeout_add(0, processWork, 0);
274      else
275         dump();
276 }
277
278 void webViewWindowObjectCleared(WebKitWebView* view, WebKitWebFrame* frame, JSGlobalContextRef context, JSObjectRef globalObject)
279 {
280     JSValueRef exception = 0;
281     assert(layoutTestController);
282     layoutTestController->makeWindowObject(context, globalObject, &exception);
283     assert(!exception);
284 }
285
286 gboolean webViewConsoleMessage(WebKitWebView* view, const gchar* message, unsigned int line, const gchar* sourceId)
287 {
288     fprintf(stdout, "CONSOLE MESSAGE: line %d: %s\n", line, message);
289     return TRUE;
290 }
291
292
293 gboolean webViewScriptAlert(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message)
294 {
295     fprintf(stdout, "ALERT: %s\n", message);
296     return TRUE;
297 }
298
299 gboolean webViewScriptPrompt(WebKitWebView* webView, WebKitWebFrame* frame, const gchar* message, const gchar* defaultValue, gchar** value)
300 {
301     fprintf(stdout, "PROMPT: %s, default text: %s\n", message, defaultValue);
302     *value = g_strdup(defaultValue);
303     return TRUE;
304 }
305
306 gboolean webViewScriptConfirm(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gboolean* didConfirm)
307 {
308     fprintf(stdout, "CONFIRM: %s\n", message);
309     *didConfirm = TRUE;
310     return TRUE;
311 }
312
313
314 int main(int argc, char* argv[])
315 {
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},
322         {NULL, 0, NULL, 0}
323     };
324
325     int option;
326     while ((option = getopt_long(argc, (char* const*)argv, "", options, NULL)) != -1)
327         switch (option) {
328             case '?':   // unknown or ambiguous option
329             case ':':   // missing argument
330                 exit(1);
331                 break;
332         }
333
334     gtk_init(&argc, &argv);
335
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);
340
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);
345
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);
353
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';
361
362             if (strlen(filenameBuffer) == 0)
363                 continue;
364
365             runTest(filenameBuffer);
366         }
367     } else {
368         printSeparators = (optind < argc-1 || (dumpPixels && dumpTree));
369         for (int i = optind; i != argc; ++i)
370             runTest(argv[i]);
371     }
372
373     return 0;
374 }