Flesh out DumpRenderTree for Gtk. After these changes, the majority of the tests...
[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.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 <cassert>
43 #include <getopt.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 extern "C" {
48 // This API is not yet public.
49 extern GSList* webkit_web_frame_get_children(WebKitWebFrame* frame);
50 extern gchar* webkit_web_frame_get_inner_text(WebKitWebFrame* frame);
51 }
52
53 volatile bool done;
54 static bool printSeparators;
55 static int testRepaintDefault;
56 static int repaintSweepHorizontallyDefault;
57 static int dumpPixels;
58 static int dumpTree = 1;
59 static gchar* currentTest;
60
61 LayoutTestController* layoutTestController = 0;
62 static WebKitWebView* webView;
63 WebKitWebFrame* mainFrame = 0;
64 WebKitWebFrame* topLoadingFrame = 0;
65
66 const unsigned maxViewHeight = 600;
67 const unsigned maxViewWidth = 800;
68
69 static gchar* autocorrectURL(const gchar* url)
70 {
71     if (strncmp("http://", url, 7) != 0 && strncmp("https://", url, 8) != 0) {
72         GString* string = g_string_new("file://");
73         g_string_append(string, url);
74         return g_string_free(string, FALSE);
75     }
76
77     return g_strdup(url);
78 }
79
80 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
81 {
82     return strstr(pathOrURL, "loading/");
83 }
84
85 void dumpFrameScrollPosition(WebKitWebFrame* frame)
86 {
87
88 }
89
90 void displayWebView()
91 {
92
93 }
94
95 static void appendString(gchar*& target, gchar* string)
96 {
97     gchar* oldString = target;
98     target = g_strconcat(target, string, NULL);
99     g_free(oldString);
100 }
101
102 static gchar* dumpFramesAsText(WebKitWebFrame* frame)
103 {
104     gchar* result = 0;
105
106     // Add header for all but the main frame.
107     bool isMainFrame = (webkit_web_view_get_main_frame(webView) == frame);
108
109     if (isMainFrame) {
110         gchar* innerText = webkit_web_frame_get_inner_text(frame);
111         result = g_strdup_printf("%s\n", innerText);
112         g_free(innerText);
113     } else {
114         const gchar* frameName = webkit_web_frame_get_name(frame);
115         gchar* innerText = webkit_web_frame_get_inner_text(frame);
116
117         result = g_strdup_printf("\n--------\nFrame: '%s'\n--------\n%s\n", frameName, innerText);
118
119         g_free(innerText);
120     }
121
122     if (layoutTestController->dumpChildFramesAsText()) {
123         GSList* children = webkit_web_frame_get_children(frame);
124         for (;children; children = g_slist_next(children))
125            appendString(result, dumpFramesAsText((WebKitWebFrame*)children->data));
126     }
127
128     return result;
129 }
130
131 static gchar* dumpRenderTreeAsText(WebKitWebFrame* frame)
132 {
133     // FIXME: this will require new WebKitGtk SPI
134     return strdup("foo");
135 }
136
137 void dump()
138 {
139     if (dumpTree) {
140         char* result = 0;
141
142         bool dumpAsText = layoutTestController->dumpAsText();
143         // FIXME: Also dump text resuls as text.
144         layoutTestController->setDumpAsText(dumpAsText);
145         if (layoutTestController->dumpAsText())
146             result = dumpFramesAsText(mainFrame);
147         else {
148             bool isSVGW3CTest = (g_strrstr(currentTest, "svg/W3C-SVG-1.1"));
149             if (isSVGW3CTest)
150                 gtk_widget_set_size_request(GTK_WIDGET(webView), 480, 360);
151             else
152                 gtk_widget_set_size_request(GTK_WIDGET(webView), maxViewWidth, maxViewHeight);
153             result = dumpRenderTreeAsText(mainFrame);
154         }
155
156         if (!result) {
157             const char* errorMessage;
158             if (layoutTestController->dumpAsText())
159                 errorMessage = "[documentElement innerText]";
160             else if (layoutTestController->dumpDOMAsWebArchive())
161                 errorMessage = "[[mainFrame DOMDocument] webArchive]";
162             else if (layoutTestController->dumpSourceAsWebArchive())
163                 errorMessage = "[[mainFrame dataSource] webArchive]";
164             else
165                 errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
166             printf("ERROR: nil result from %s", errorMessage);
167         } else {
168             printf("%s", result);
169             g_free(result);
170             if (!layoutTestController->dumpAsText() && !layoutTestController->dumpDOMAsWebArchive() && !layoutTestController->dumpSourceAsWebArchive())
171                 dumpFrameScrollPosition(mainFrame);
172         }
173
174         if (layoutTestController->dumpBackForwardList()) {
175             // FIXME: not implemented
176         }
177
178         if (printSeparators)
179             puts("#EOF");
180     }
181
182     if (dumpPixels) {
183         if (!layoutTestController->dumpAsText() && !layoutTestController->dumpDOMAsWebArchive() && !layoutTestController->dumpSourceAsWebArchive()) {
184             // FIXME: Add support for dumping pixels
185         }
186     }
187
188     fflush(stdout);
189
190     // FIXME: call displayWebView here when we support --paint
191
192     done = true;
193 }
194
195 static void runTest(const char* pathOrURL)
196 {
197     gchar* url = autocorrectURL(pathOrURL);
198
199     layoutTestController = new LayoutTestController(testRepaintDefault, repaintSweepHorizontallyDefault);
200
201     done = false;
202     topLoadingFrame = 0;
203
204     if (shouldLogFrameLoadDelegates(pathOrURL))
205         layoutTestController->setDumpFrameLoadCallbacks(true);
206
207     if (currentTest)
208         g_free(currentTest);
209     currentTest = url;
210
211     WorkQueue::shared()->clear();
212     WorkQueue::shared()->setFrozen(false);
213
214     webkit_web_view_open(webView, url);
215
216     while (!done)
217         g_main_context_iteration(NULL, true);
218
219     WorkQueue::shared()->clear();
220
221     delete layoutTestController;
222     layoutTestController = 0;
223 }
224
225 void webViewLoadStarted(WebKitWebView* view, WebKitWebFrame* frame, void*)
226 {
227     // Make sure we only set this once per test.  If it gets cleared, and then set again, we might
228     // end up doing two dumps for one test.
229     if (!topLoadingFrame && !done)
230         topLoadingFrame = frame;
231 }
232
233 void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
234 {
235     if (frame != topLoadingFrame)
236         return;
237
238     topLoadingFrame = 0;
239     WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
240     if (layoutTestController->waitToDump())
241         return;
242
243     if (WorkQueue::shared()->count())
244         fprintf(stderr, "FIXME: [self performSelector:@selector(processWork:) withObject:nil afterDelay:0];\n");
245      else
246         dump();
247 }
248
249 void webViewWindowObjectCleared(WebKitWebView* view, WebKitWebFrame* frame, JSGlobalContextRef context, JSObjectRef globalObject)
250 {
251     JSValueRef exception = 0;
252     assert(layoutTestController);
253     layoutTestController->makeWindowObject(context, globalObject, &exception);
254     assert(!exception);
255 }
256
257 gboolean webViewConsoleMessage(WebKitWebView* view, const gchar* message, unsigned int line, const gchar* sourceId)
258 {
259     fprintf(stdout, "CONSOLE MESSAGE: line %d: %s\n", line, message);
260     return TRUE;
261 }
262
263 int main(int argc, char* argv[])
264 {
265     struct option options[] = {
266         {"horizontal-sweep", no_argument, &repaintSweepHorizontallyDefault, true},
267         {"notree", no_argument, &dumpTree, false},
268         {"pixel-tests", no_argument, &dumpPixels, true},
269         {"repaint", no_argument, &testRepaintDefault, true},
270         {"tree", no_argument, &dumpTree, true},
271         {NULL, 0, NULL, 0}
272     };
273
274     int option;
275     while ((option = getopt_long(argc, (char* const*)argv, "", options, NULL)) != -1)
276         switch (option) {
277             case '?':   // unknown or ambiguous option
278             case ':':   // missing argument
279                 exit(1);
280                 break;
281         }
282
283     gtk_init(&argc, &argv);
284     webkit_init();
285
286     GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
287     GtkContainer* container = GTK_CONTAINER(gtk_fixed_new());
288     gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(container));
289     gtk_widget_realize(window);
290
291     webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
292     gtk_container_add(container, GTK_WIDGET(webView));
293     gtk_widget_realize(GTK_WIDGET(webView));
294     mainFrame = webkit_web_view_get_main_frame(webView);
295
296     g_signal_connect(G_OBJECT(webView), "load-started", G_CALLBACK(webViewLoadStarted), 0);
297     g_signal_connect(G_OBJECT(webView), "load-finished", G_CALLBACK(webViewLoadFinished), 0);
298     g_signal_connect(G_OBJECT(webView), "window-object-cleared", G_CALLBACK(webViewWindowObjectCleared), 0);
299     g_signal_connect(G_OBJECT(webView), "console-message", G_CALLBACK(webViewConsoleMessage), 0);
300
301     if (argc == optind+1 && strcmp(argv[optind], "-") == 0) {
302         char filenameBuffer[2048];
303         printSeparators = true;
304         while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
305             char* newLineCharacter = strchr(filenameBuffer, '\n');
306             if (newLineCharacter)
307                 *newLineCharacter = '\0';
308
309             if (strlen(filenameBuffer) == 0)
310                 continue;
311
312             runTest(filenameBuffer);
313         }
314     } else {
315         printSeparators = (optind < argc-1 || (dumpPixels && dumpTree));
316         for (int i = optind; i != argc; ++i)
317             runTest(argv[i]);
318     }
319
320     return 0;
321 }