2010-03-02 Arno Renevier <arno@renevier.net>
[WebKit-https.git] / WebKitTools / DumpRenderTree / gtk / DumpRenderTree.cpp
1 /*
2  * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
3  * Copyright (C) 2008 Alp Toker <alp@nuanti.com>
4  * Copyright (C) 2009 Jan Alonzo <jmalonzo@gmail.com>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1.  Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  * 2.  Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16  *     its contributors may be used to endorse or promote products derived
17  *     from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "DumpRenderTree.h"
33
34 #include "AccessibilityController.h"
35 #include "EventSender.h"
36 #include "GCController.h"
37 #include "LayoutTestController.h"
38 #include "WorkQueue.h"
39 #include "WorkQueueItem.h"
40
41 #include <gtk/gtk.h>
42 #include <webkit/webkit.h>
43 #include <JavaScriptCore/JavaScript.h>
44
45 #include <wtf/Assertions.h>
46
47 #if PLATFORM(X11)
48 #include <fontconfig/fontconfig.h>
49 #endif
50
51 #include <cassert>
52 #include <getopt.h>
53 #include <stdlib.h>
54 #include <string.h>
55
56 using namespace std;
57
58 extern "C" {
59 // This API is not yet public.
60 extern G_CONST_RETURN gchar* webkit_web_history_item_get_target(WebKitWebHistoryItem*);
61 extern gboolean webkit_web_history_item_is_target_item(WebKitWebHistoryItem*);
62 extern GList* webkit_web_history_item_get_children(WebKitWebHistoryItem*);
63 extern GSList* webkit_web_frame_get_children(WebKitWebFrame* frame);
64 extern gchar* webkit_web_frame_get_inner_text(WebKitWebFrame* frame);
65 extern gchar* webkit_web_frame_dump_render_tree(WebKitWebFrame* frame);
66 extern guint webkit_web_frame_get_pending_unload_event_count(WebKitWebFrame* frame);
67 extern void webkit_web_settings_add_extra_plugin_directory(WebKitWebView* view, const gchar* directory);
68 extern gchar* webkit_web_frame_get_response_mime_type(WebKitWebFrame* frame);
69 extern void webkit_web_frame_clear_main_frame_name(WebKitWebFrame* frame);
70 extern void webkit_web_view_set_group_name(WebKitWebView* view, const gchar* groupName);
71 extern void webkit_reset_origin_access_white_lists();
72 }
73
74 volatile bool done;
75 static bool printSeparators;
76 static int dumpPixels;
77 static int dumpTree = 1;
78
79 AccessibilityController* axController = 0;
80 LayoutTestController* gLayoutTestController = 0;
81 static GCController* gcController = 0;
82 static WebKitWebView* webView;
83 static GtkWidget* window;
84 static GtkWidget* container;
85 static GtkWidget* webInspectorWindow;
86 WebKitWebFrame* mainFrame = 0;
87 WebKitWebFrame* topLoadingFrame = 0;
88 guint waitToDumpWatchdog = 0;
89 bool waitForPolicy = false;
90
91 // This is a list of opened webviews
92 GSList* webViewList = 0;
93
94 // current b/f item at the end of the previous test
95 static WebKitWebHistoryItem* prevTestBFItem = NULL;
96
97 const unsigned historyItemIndent = 8;
98
99 static gchar* autocorrectURL(const gchar* url)
100 {
101     if (strncmp("http://", url, 7) != 0 && strncmp("https://", url, 8) != 0) {
102         GString* string = g_string_new("file://");
103         g_string_append(string, url);
104         return g_string_free(string, FALSE);
105     }
106
107     return g_strdup(url);
108 }
109
110 static bool shouldLogFrameLoadDelegates(const char* pathOrURL)
111 {
112     return strstr(pathOrURL, "loading/");
113 }
114
115 static bool shouldOpenWebInspector(const char* pathOrURL)
116 {
117     return strstr(pathOrURL, "inspector/");
118 }
119
120 void dumpFrameScrollPosition(WebKitWebFrame* frame)
121 {
122
123 }
124
125 void displayWebView()
126 {
127     gtk_widget_queue_draw(GTK_WIDGET(webView));
128 }
129
130 static void appendString(gchar*& target, gchar* string)
131 {
132     gchar* oldString = target;
133     target = g_strconcat(target, string, NULL);
134     g_free(oldString);
135 }
136
137 #if PLATFORM(X11)
138 static void initializeFonts()
139 {
140     static int numFonts = -1;
141
142     // Some tests may add or remove fonts via the @font-face rule.
143     // If that happens, font config should be re-created to suppress any unwanted change.
144     FcFontSet* appFontSet = FcConfigGetFonts(0, FcSetApplication);
145     if (appFontSet && numFonts >= 0 && appFontSet->nfont == numFonts)
146         return;
147
148     const char* fontDirEnv = g_getenv("WEBKIT_TESTFONTS");
149     if (!fontDirEnv)
150         g_error("WEBKIT_TESTFONTS environment variable is not set, but it should point to the directory "
151                 "containing the fonts you can clone from git://gitorious.org/qtwebkit/testfonts.git\n");
152
153     GFile* fontDir = g_file_new_for_path(fontDirEnv);
154     if (!fontDir || !g_file_query_exists(fontDir, NULL))
155         g_error("WEBKIT_TESTFONTS environment variable is not set correctly - it should point to the directory "
156                 "containing the fonts you can clone from git://gitorious.org/qtwebkit/testfonts.git\n");
157
158     FcConfig *config = FcConfigCreate();
159     if (!FcConfigParseAndLoad (config, (FcChar8*) FONTS_CONF_FILE, true))
160         g_error("Couldn't load font configuration file");
161     if (!FcConfigAppFontAddDir (config, (FcChar8*) g_file_get_path(fontDir)))
162         g_error("Couldn't add font dir!");
163     FcConfigSetCurrent(config);
164
165     g_object_unref(fontDir);
166
167     appFontSet = FcConfigGetFonts(config, FcSetApplication);
168     numFonts = appFontSet->nfont;
169 }
170 #endif
171
172 static gchar* dumpFramesAsText(WebKitWebFrame* frame)
173 {
174     gchar* result = 0;
175
176     // Add header for all but the main frame.
177     bool isMainFrame = (webkit_web_view_get_main_frame(webView) == frame);
178
179     gchar* innerText = webkit_web_frame_get_inner_text(frame);
180     if (isMainFrame)
181         result = g_strdup_printf("%s\n", innerText);
182     else {
183         const gchar* frameName = webkit_web_frame_get_name(frame);
184         result = g_strdup_printf("\n--------\nFrame: '%s'\n--------\n%s\n", frameName, innerText);
185     }
186     g_free(innerText);
187
188     if (gLayoutTestController->dumpChildFramesAsText()) {
189         GSList* children = webkit_web_frame_get_children(frame);
190         for (GSList* child = children; child; child = g_slist_next(child))
191             appendString(result, dumpFramesAsText(static_cast<WebKitWebFrame* >(child->data)));
192         g_slist_free(children);
193     }
194
195     return result;
196 }
197
198 static gint compareHistoryItems(gpointer* item1, gpointer* item2)
199 {
200     return g_ascii_strcasecmp(webkit_web_history_item_get_target(WEBKIT_WEB_HISTORY_ITEM(item1)),
201                               webkit_web_history_item_get_target(WEBKIT_WEB_HISTORY_ITEM(item2)));
202 }
203
204 static void dumpHistoryItem(WebKitWebHistoryItem* item, int indent, bool current)
205 {
206     ASSERT(item != NULL);
207     int start = 0;
208     g_object_ref(item);
209     if (current) {
210         printf("curr->");
211         start = 6;
212     }
213     for (int i = start; i < indent; i++)
214         putchar(' ');
215
216     // normalize file URLs.
217     const gchar* uri = webkit_web_history_item_get_uri(item);
218     gchar* uriScheme = g_uri_parse_scheme(uri);
219     if (g_strcmp0(uriScheme, "file") == 0) {
220         gchar* pos = g_strstr_len(uri, -1, "/LayoutTests/");
221         if (!pos)
222             return;
223
224         GString* result = g_string_sized_new(strlen(uri));
225         result = g_string_append(result, "(file test):");
226         result = g_string_append(result, pos + strlen("/LayoutTests/"));
227         printf("%s", result->str);
228         g_string_free(result, TRUE);
229     } else
230         printf("%s", uri);
231
232     g_free(uriScheme);
233
234     const gchar* target = webkit_web_history_item_get_target(item);
235     if (target && strlen(target) > 0)
236         printf(" (in frame \"%s\")", target);
237     if (webkit_web_history_item_is_target_item(item))
238         printf("  **nav target**");
239     putchar('\n');
240     GList* kids = webkit_web_history_item_get_children(item);
241     if (kids) {
242         // must sort to eliminate arbitrary result ordering which defeats reproducible testing
243         kids = g_list_sort(kids, (GCompareFunc) compareHistoryItems);
244         for (unsigned i = 0; i < g_list_length(kids); i++)
245             dumpHistoryItem(WEBKIT_WEB_HISTORY_ITEM(g_list_nth_data(kids, i)), indent+4, FALSE);
246     }
247     g_object_unref(item);
248 }
249
250 static void dumpBackForwardListForWebView(WebKitWebView* view)
251 {
252     printf("\n============== Back Forward List ==============\n");
253     WebKitWebBackForwardList* bfList = webkit_web_view_get_back_forward_list(view);
254
255     // Print out all items in the list after prevTestBFItem, which was from the previous test
256     // Gather items from the end of the list, the print them out from oldest to newest
257     GList* itemsToPrint = NULL;
258     gint forwardListCount = webkit_web_back_forward_list_get_forward_length(bfList);
259     for (int i = forwardListCount; i > 0; i--) {
260         WebKitWebHistoryItem* item = webkit_web_back_forward_list_get_nth_item(bfList, i);
261         // something is wrong if the item from the last test is in the forward part of the b/f list
262         ASSERT(item != prevTestBFItem);
263         g_object_ref(item);
264         itemsToPrint = g_list_append(itemsToPrint, item);
265     }
266
267     WebKitWebHistoryItem* currentItem = webkit_web_back_forward_list_get_current_item(bfList);
268
269     g_object_ref(currentItem);
270     itemsToPrint = g_list_append(itemsToPrint, currentItem);
271
272     gint currentItemIndex = g_list_length(itemsToPrint) - 1;
273     gint backListCount = webkit_web_back_forward_list_get_back_length(bfList);
274     for (int i = -1; i >= -(backListCount); i--) {
275         WebKitWebHistoryItem* item = webkit_web_back_forward_list_get_nth_item(bfList, i);
276         if (item == prevTestBFItem)
277             break;
278         g_object_ref(item);
279         itemsToPrint = g_list_append(itemsToPrint, item);
280     }
281
282     for (int i = g_list_length(itemsToPrint) - 1; i >= 0; i--) {
283         WebKitWebHistoryItem* item = WEBKIT_WEB_HISTORY_ITEM(g_list_nth_data(itemsToPrint, i));
284         dumpHistoryItem(item, historyItemIndent, i == currentItemIndex);
285         g_object_unref(item);
286     }
287     g_list_free(itemsToPrint);
288     printf("===============================================\n");
289 }
290
291 static void dumpBackForwardListForAllWebViews()
292 {
293     // Dump the back forward list of the main WebView first
294     dumpBackForwardListForWebView(webView);
295
296     // The view list is prepended. Reverse the list so we get the order right.
297     GSList* viewList = g_slist_reverse(webViewList);
298     for (unsigned i = 0; i < g_slist_length(viewList); ++i)
299         dumpBackForwardListForWebView(WEBKIT_WEB_VIEW(g_slist_nth_data(viewList, i)));
300 }
301
302 static void invalidateAnyPreviousWaitToDumpWatchdog()
303 {
304     if (waitToDumpWatchdog) {
305         g_source_remove(waitToDumpWatchdog);
306         waitToDumpWatchdog = 0;
307     }
308
309     waitForPolicy = false;
310 }
311
312 static void resetDefaultsToConsistentValues()
313 {
314     WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
315     g_object_set(G_OBJECT(settings),
316                  "enable-private-browsing", FALSE,
317                  "enable-developer-extras", FALSE,
318                  "enable-spell-checking", TRUE,
319                  "enable-html5-database", TRUE,
320                  "enable-html5-local-storage", TRUE,
321                  "enable-xss-auditor", FALSE,
322                  "javascript-can-open-windows-automatically", TRUE,
323                  "enable-offline-web-application-cache", TRUE,
324                  "enable-universal-access-from-file-uris", TRUE,
325                  "enable-scripts", TRUE,
326                  "enable-dom-paste", TRUE,
327                  "default-font-family", "Times",
328                  "monospace-font-family", "Courier",
329                  "serif-font-family", "Times",
330                  "sans-serif-font-family", "Helvetica",
331                  "default-font-size", 16,
332                  "default-monospace-font-size", 13,
333                  "minimum-font-size", 1,
334                  "enable-caret-browsing", FALSE,
335                  "enable-page-cache", FALSE,
336                  "auto-resize-window", TRUE,
337                  "enable-java-applet", FALSE,
338                  NULL);
339
340     webkit_web_frame_clear_main_frame_name(mainFrame);
341
342     WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView);
343     g_object_set(G_OBJECT(inspector), "javascript-profiling-enabled", FALSE, NULL);
344
345     webkit_web_view_set_zoom_level(webView, 1.0);
346
347     webkit_reset_origin_access_white_lists();
348
349     setlocale(LC_ALL, "");
350 }
351
352 void dump()
353 {
354     invalidateAnyPreviousWaitToDumpWatchdog();
355
356     bool dumpAsText = gLayoutTestController->dumpAsText();
357     if (dumpTree) {
358         char* result = 0;
359         gchar* responseMimeType = webkit_web_frame_get_response_mime_type(mainFrame);
360
361         dumpAsText = g_str_equal(responseMimeType, "text/plain");
362         g_free(responseMimeType);
363
364         // Test can request controller to be dumped as text even
365         // while test's response mime type is not text/plain.
366         // Overriding this behavior with dumpAsText being false is a bad idea.
367         if (dumpAsText)
368             gLayoutTestController->setDumpAsText(dumpAsText);
369
370         if (gLayoutTestController->dumpAsText())
371             result = dumpFramesAsText(mainFrame);
372         else
373             result = webkit_web_frame_dump_render_tree(mainFrame);
374
375         if (!result) {
376             const char* errorMessage;
377             if (gLayoutTestController->dumpAsText())
378                 errorMessage = "[documentElement innerText]";
379             else if (gLayoutTestController->dumpDOMAsWebArchive())
380                 errorMessage = "[[mainFrame DOMDocument] webArchive]";
381             else if (gLayoutTestController->dumpSourceAsWebArchive())
382                 errorMessage = "[[mainFrame dataSource] webArchive]";
383             else
384                 errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
385             printf("ERROR: nil result from %s", errorMessage);
386         } else {
387             printf("%s", result);
388             g_free(result);
389             if (!gLayoutTestController->dumpAsText() && !gLayoutTestController->dumpDOMAsWebArchive() && !gLayoutTestController->dumpSourceAsWebArchive())
390                 dumpFrameScrollPosition(mainFrame);
391
392             if (gLayoutTestController->dumpBackForwardList())
393                 dumpBackForwardListForAllWebViews();
394         }
395
396         if (printSeparators) {
397             puts("#EOF"); // terminate the content block
398             fputs("#EOF\n", stderr);
399             fflush(stdout);
400             fflush(stderr);
401         }
402     }
403
404     if (dumpPixels) {
405         if (!gLayoutTestController->dumpAsText() && !gLayoutTestController->dumpDOMAsWebArchive() && !gLayoutTestController->dumpSourceAsWebArchive()) {
406             // FIXME: Add support for dumping pixels
407         }
408     }
409
410     // FIXME: call displayWebView here when we support --paint
411
412     done = true;
413     gtk_main_quit();
414 }
415
416 static void setDefaultsToConsistentStateValuesForTesting()
417 {
418     gdk_screen_set_resolution(gdk_screen_get_default(), 72.0);
419
420     resetDefaultsToConsistentValues();
421
422     /* Disable the default auth dialog for testing */
423     SoupSession* session = webkit_get_default_session();
424     soup_session_remove_feature_by_type(session, WEBKIT_TYPE_SOUP_AUTH_DIALOG);
425
426 #if PLATFORM(X11)
427     webkit_web_settings_add_extra_plugin_directory(webView, TEST_PLUGIN_DIR);
428 #endif
429
430     gchar* databaseDirectory = g_build_filename(g_get_user_data_dir(), "gtkwebkitdrt", "databases", NULL);
431     webkit_set_web_database_directory_path(databaseDirectory);
432     g_free(databaseDirectory);
433 }
434
435 static void sendPixelResultsEOF()
436 {
437     puts("#EOF");
438
439     fflush(stdout);
440     fflush(stderr);
441 }
442
443 static void runTest(const string& testPathOrURL)
444 {
445     ASSERT(!testPathOrURL.empty());
446
447     // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows.
448     string pathOrURL(testPathOrURL);
449     string expectedPixelHash;
450
451     size_t separatorPos = pathOrURL.find("'");
452     if (separatorPos != string::npos) {
453         pathOrURL = string(testPathOrURL, 0, separatorPos);
454         expectedPixelHash = string(testPathOrURL, separatorPos + 1);
455     }
456
457     gchar* url = autocorrectURL(pathOrURL.c_str());
458     const string testURL(url);
459
460     resetDefaultsToConsistentValues();
461
462     gLayoutTestController = new LayoutTestController(testURL, expectedPixelHash);
463     topLoadingFrame = 0;
464     done = false;
465
466     gLayoutTestController->setIconDatabaseEnabled(false);
467
468     if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
469         gLayoutTestController->setDumpFrameLoadCallbacks(true);
470
471     if (shouldOpenWebInspector(pathOrURL.c_str()))
472         gLayoutTestController->showWebInspector();
473
474     WorkQueue::shared()->clear();
475     WorkQueue::shared()->setFrozen(false);
476
477     bool isSVGW3CTest = (gLayoutTestController->testPathOrURL().find("svg/W3C-SVG-1.1") != string::npos);
478     GtkAllocation size;
479     size.x = size.y = 0;
480     size.width = isSVGW3CTest ? 480 : LayoutTestController::maxViewWidth;
481     size.height = isSVGW3CTest ? 360 : LayoutTestController::maxViewHeight;
482     gtk_window_resize(GTK_WINDOW(window), size.width, size.height);
483     gtk_widget_size_allocate(container, &size);
484
485     if (prevTestBFItem)
486         g_object_unref(prevTestBFItem);
487     WebKitWebBackForwardList* bfList = webkit_web_view_get_back_forward_list(webView);
488     prevTestBFItem = webkit_web_back_forward_list_get_current_item(bfList);
489     if (prevTestBFItem)
490         g_object_ref(prevTestBFItem);
491
492 #if PLATFORM(X11)
493     initializeFonts();
494 #endif
495
496     // Focus the web view before loading the test to avoid focusing problems
497     gtk_widget_grab_focus(GTK_WIDGET(webView));
498     webkit_web_view_open(webView, url);
499
500     g_free(url);
501     url = NULL;
502
503     gtk_main();
504
505     if (shouldOpenWebInspector(pathOrURL.c_str()))
506         gLayoutTestController->closeWebInspector();
507
508     // Also check if we still have opened webViews and free them.
509     if (gLayoutTestController->closeRemainingWindowsWhenComplete() || webViewList) {
510         while (webViewList) {
511             g_object_unref(WEBKIT_WEB_VIEW(webViewList->data));
512             webViewList = g_slist_next(webViewList);
513         }
514         g_slist_free(webViewList);
515         webViewList = 0;
516     }
517
518     // A blank load seems to be necessary to reset state after certain tests.
519     webkit_web_view_open(webView, "about:blank");
520
521     gLayoutTestController->deref();
522     gLayoutTestController = 0;
523
524     // terminate the (possibly empty) pixels block after all the state reset
525     sendPixelResultsEOF();
526 }
527
528 void webViewLoadStarted(WebKitWebView* view, WebKitWebFrame* frame, void*)
529 {
530     // Make sure we only set this once per test.  If it gets cleared, and then set again, we might
531     // end up doing two dumps for one test.
532     if (!topLoadingFrame && !done)
533         topLoadingFrame = frame;
534 }
535
536 static gboolean processWork(void* data)
537 {
538     // if we finish all the commands, we're ready to dump state
539     if (WorkQueue::shared()->processWork() && !gLayoutTestController->waitToDump())
540         dump();
541
542     return FALSE;
543 }
544
545 static char* getFrameNameSuitableForTestResult(WebKitWebView* view, WebKitWebFrame* frame)
546 {
547     char* frameName = g_strdup(webkit_web_frame_get_name(frame));
548
549     if (frame == webkit_web_view_get_main_frame(view)) {
550         // This is a bit strange. Shouldn't web_frame_get_name return NULL?
551         if (frameName && (frameName[0] != '\0')) {
552             char* tmp = g_strdup_printf("main frame \"%s\"", frameName);
553             g_free(frameName);
554             frameName = tmp;
555         } else {
556             g_free(frameName);
557             frameName = g_strdup("main frame");
558         }
559     } else if (!frameName || (frameName[0] == '\0')) {
560         g_free(frameName);
561         frameName = g_strdup("frame (anonymous)");
562     } else {
563         char* tmp = g_strdup_printf("frame \"%s\"", frameName);
564         g_free(frameName);
565         frameName = tmp;
566     }
567
568     return frameName;
569 }
570
571 static void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
572 {
573     if (frame != topLoadingFrame)
574         return;
575
576     topLoadingFrame = 0;
577     WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
578     if (gLayoutTestController->waitToDump())
579         return;
580
581     if (WorkQueue::shared()->count())
582         g_timeout_add(0, processWork, 0);
583     else
584         dump();
585 }
586
587 static void webViewDocumentLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
588 {
589     if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
590         char* frameName = getFrameNameSuitableForTestResult(view, frame);
591         printf("%s - didFinishDocumentLoadForFrame", frameName);
592         g_free(frameName);
593     } else if (!done) {
594         guint pendingFrameUnloadEvents = webkit_web_frame_get_pending_unload_event_count(frame);
595         if (pendingFrameUnloadEvents) {
596             char* frameName = getFrameNameSuitableForTestResult(view, frame);
597             printf("%s - has %u onunload handler(s)\n", frameName, pendingFrameUnloadEvents);
598             g_free(frameName);
599         }
600     }
601 }
602
603 static void webViewWindowObjectCleared(WebKitWebView* view, WebKitWebFrame* frame, JSGlobalContextRef context, JSObjectRef windowObject, gpointer data)
604 {
605     JSValueRef exception = 0;
606     ASSERT(gLayoutTestController);
607
608     gLayoutTestController->makeWindowObject(context, windowObject, &exception);
609     ASSERT(!exception);
610
611     gcController->makeWindowObject(context, windowObject, &exception);
612     ASSERT(!exception);
613
614     axController->makeWindowObject(context, windowObject, &exception);
615     ASSERT(!exception);
616
617     JSStringRef eventSenderStr = JSStringCreateWithUTF8CString("eventSender");
618     JSValueRef eventSender = makeEventSender(context);
619     JSObjectSetProperty(context, windowObject, eventSenderStr, eventSender, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, 0);
620     JSStringRelease(eventSenderStr);
621 }
622
623 static gboolean webViewConsoleMessage(WebKitWebView* view, const gchar* message, unsigned int line, const gchar* sourceId, gpointer data)
624 {
625     fprintf(stdout, "CONSOLE MESSAGE: line %d: %s\n", line, message);
626     return TRUE;
627 }
628
629
630 static gboolean webViewScriptAlert(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gpointer data)
631 {
632     fprintf(stdout, "ALERT: %s\n", message);
633     return TRUE;
634 }
635
636 static gboolean webViewScriptPrompt(WebKitWebView* webView, WebKitWebFrame* frame, const gchar* message, const gchar* defaultValue, gchar** value, gpointer data)
637 {
638     fprintf(stdout, "PROMPT: %s, default text: %s\n", message, defaultValue);
639     *value = g_strdup(defaultValue);
640     return TRUE;
641 }
642
643 static gboolean webViewScriptConfirm(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gboolean* didConfirm, gpointer data)
644 {
645     fprintf(stdout, "CONFIRM: %s\n", message);
646     *didConfirm = TRUE;
647     return TRUE;
648 }
649
650 static void webViewTitleChanged(WebKitWebView* view, WebKitWebFrame* frame, const gchar* title, gpointer data)
651 {
652     if (gLayoutTestController->dumpTitleChanges() && !done)
653         printf("TITLE CHANGED: %s\n", title ? title : "");
654 }
655
656 static bool webViewNavigationPolicyDecisionRequested(WebKitWebView* view, WebKitWebFrame* frame,
657                                                      WebKitNetworkRequest* request,
658                                                      WebKitWebNavigationAction* navAction,
659                                                      WebKitWebPolicyDecision* policyDecision)
660 {
661     // Use the default handler if we're not waiting for policy,
662     // i.e., LayoutTestController::waitForPolicyDelegate
663     if (!waitForPolicy)
664         return FALSE;
665
666     gchar* typeDescription;
667     WebKitWebNavigationReason reason;
668     g_object_get(G_OBJECT(navAction), "reason", &reason, NULL);
669
670     switch(reason) {
671         case WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED:
672             typeDescription = g_strdup("link clicked");
673             break;
674         case WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED:
675             typeDescription = g_strdup("form submitted");
676             break;
677         case WEBKIT_WEB_NAVIGATION_REASON_BACK_FORWARD:
678             typeDescription = g_strdup("back/forward");
679             break;
680         case WEBKIT_WEB_NAVIGATION_REASON_RELOAD:
681             typeDescription = g_strdup("reload");
682             break;
683         case WEBKIT_WEB_NAVIGATION_REASON_FORM_RESUBMITTED:
684             typeDescription = g_strdup("form resubmitted");
685             break;
686         case WEBKIT_WEB_NAVIGATION_REASON_OTHER:
687             typeDescription = g_strdup("other");
688             break;
689         default:
690             typeDescription = g_strdup("illegal value");
691     }
692
693     printf("Policy delegate: attempt to load %s with navigation type '%s'\n", webkit_network_request_get_uri(request), typeDescription);
694     g_free(typeDescription);
695
696     webkit_web_policy_decision_ignore(policyDecision);
697     gLayoutTestController->notifyDone();
698
699     return TRUE;
700 }
701
702 static void webViewStatusBarTextChanged(WebKitWebView* view, const gchar* message, gpointer data)
703 {
704     // Are we doing anything wrong? One test that does not call
705     // dumpStatusCallbacks gets true here
706     if (gLayoutTestController->dumpStatusCallbacks()) {
707         if (message && strcmp(message, ""))
708             printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", message);
709     }
710 }
711
712 static gboolean webViewClose(WebKitWebView* view)
713 {
714     ASSERT(view);
715
716     webViewList = g_slist_remove(webViewList, view);
717     g_object_unref(view);
718
719     return TRUE;
720 }
721
722 static void databaseQuotaExceeded(WebKitWebView* view, WebKitWebFrame* frame, WebKitWebDatabase *database)
723 {
724     ASSERT(view);
725     ASSERT(frame);
726     ASSERT(database);
727
728     WebKitSecurityOrigin* origin = webkit_web_database_get_security_origin(database);
729     if (gLayoutTestController->dumpDatabaseCallbacks()) {
730         printf("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{%s, %s, %i} database:%s\n",
731             webkit_security_origin_get_protocol(origin),
732             webkit_security_origin_get_host(origin),
733             webkit_security_origin_get_port(origin),
734             webkit_web_database_get_name(database));
735     }
736     webkit_security_origin_set_web_database_quota(origin, 5 * 1024 * 1024);
737 }
738
739 static bool
740 geolocationPolicyDecisionRequested(WebKitWebView*, WebKitWebFrame*, WebKitGeolocationPolicyDecision* decision)
741 {
742     if (!gLayoutTestController->isGeolocationPermissionSet())
743         return FALSE;
744     if (gLayoutTestController->geolocationPermission())
745         webkit_geolocation_policy_allow(decision);
746     else
747         webkit_geolocation_policy_deny(decision);
748
749     return TRUE;
750 }
751
752
753 static WebKitWebView* webViewCreate(WebKitWebView*, WebKitWebFrame*);
754
755 static gboolean webInspectorShowWindow(WebKitWebInspector*, gpointer data)
756 {
757     gtk_window_set_default_size(GTK_WINDOW(webInspectorWindow), 800, 600);
758     gtk_widget_show_all(webInspectorWindow);
759     return TRUE;
760 }
761
762 static gboolean webInspectorCloseWindow(WebKitWebInspector*, gpointer data)
763 {
764     gtk_widget_destroy(webInspectorWindow);
765     webInspectorWindow = 0;
766     return TRUE;
767 }
768
769 static WebKitWebView* webInspectorInspectWebView(WebKitWebInspector*, gpointer data)
770 {
771     webInspectorWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
772
773     GtkWidget* webView = webkit_web_view_new();
774     gtk_container_add(GTK_CONTAINER(webInspectorWindow),
775                       webView);
776
777     return WEBKIT_WEB_VIEW(webView);
778 }
779
780 static WebKitWebView* createWebView()
781 {
782     WebKitWebView* view = WEBKIT_WEB_VIEW(webkit_web_view_new());
783
784     // From bug 11756: Use a frame group name for all WebViews created by
785     // DumpRenderTree to allow testing of cross-page frame lookup.
786     webkit_web_view_set_group_name(view, "org.webkit.gtk.DumpRenderTree");
787
788     g_object_connect(G_OBJECT(view),
789                      "signal::load-started", webViewLoadStarted, 0,
790                      "signal::load-finished", webViewLoadFinished, 0,
791                      "signal::window-object-cleared", webViewWindowObjectCleared, 0,
792                      "signal::console-message", webViewConsoleMessage, 0,
793                      "signal::script-alert", webViewScriptAlert, 0,
794                      "signal::script-prompt", webViewScriptPrompt, 0,
795                      "signal::script-confirm", webViewScriptConfirm, 0,
796                      "signal::title-changed", webViewTitleChanged, 0,
797                      "signal::navigation-policy-decision-requested", webViewNavigationPolicyDecisionRequested, 0,
798                      "signal::status-bar-text-changed", webViewStatusBarTextChanged, 0,
799                      "signal::create-web-view", webViewCreate, 0,
800                      "signal::close-web-view", webViewClose, 0,
801                      "signal::database-quota-exceeded", databaseQuotaExceeded, 0,
802                      "signal::document-load-finished", webViewDocumentLoadFinished, 0,
803                      "signal::geolocation-policy-decision-requested", geolocationPolicyDecisionRequested, 0,
804                      NULL);
805
806     WebKitWebInspector* inspector = webkit_web_view_get_inspector(view);
807     g_object_connect(G_OBJECT(inspector),
808                      "signal::inspect-web-view", webInspectorInspectWebView, 0,
809                      "signal::show-window", webInspectorShowWindow, 0,
810                      "signal::close-window", webInspectorCloseWindow, 0,
811                      NULL);
812
813     if (webView) {
814         WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
815         webkit_web_view_set_settings(view, settings);
816     }
817
818     return view;
819 }
820
821 static WebKitWebView* webViewCreate(WebKitWebView* view, WebKitWebFrame* frame)
822 {
823     if (!gLayoutTestController->canOpenWindows())
824         return 0;
825
826     // Make sure that waitUntilDone has been called.
827     ASSERT(gLayoutTestController->waitToDump());
828
829     WebKitWebView* newWebView = createWebView();
830     g_object_ref_sink(G_OBJECT(newWebView));
831     webViewList = g_slist_prepend(webViewList, newWebView);
832     return newWebView;
833 }
834
835 int main(int argc, char* argv[])
836 {
837     g_thread_init(NULL);
838     gtk_init(&argc, &argv);
839
840 #if PLATFORM(X11)
841     FcInit();
842     initializeFonts();
843 #endif
844
845     struct option options[] = {
846         {"notree", no_argument, &dumpTree, false},
847         {"pixel-tests", no_argument, &dumpPixels, true},
848         {"tree", no_argument, &dumpTree, true},
849         {NULL, 0, NULL, 0}
850     };
851
852     int option;
853     while ((option = getopt_long(argc, (char* const*)argv, "", options, NULL)) != -1)
854         switch (option) {
855             case '?':   // unknown or ambiguous option
856             case ':':   // missing argument
857                 exit(1);
858                 break;
859         }
860
861     window = gtk_window_new(GTK_WINDOW_POPUP);
862     container = GTK_WIDGET(gtk_scrolled_window_new(NULL, NULL));
863     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(container), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
864     gtk_container_add(GTK_CONTAINER(window), container);
865     gtk_widget_show_all(window);
866
867     webView = createWebView();
868     gtk_container_add(GTK_CONTAINER(container), GTK_WIDGET(webView));
869     gtk_widget_realize(GTK_WIDGET(webView));
870     gtk_widget_show_all(container);
871     gtk_widget_grab_focus(GTK_WIDGET(webView));
872     mainFrame = webkit_web_view_get_main_frame(webView);
873
874     setDefaultsToConsistentStateValuesForTesting();
875
876     gcController = new GCController();
877     axController = new AccessibilityController();
878
879     if (argc == optind+1 && strcmp(argv[optind], "-") == 0) {
880         char filenameBuffer[2048];
881         printSeparators = true;
882         while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
883             char* newLineCharacter = strchr(filenameBuffer, '\n');
884             if (newLineCharacter)
885                 *newLineCharacter = '\0';
886
887             if (strlen(filenameBuffer) == 0)
888                 continue;
889
890             runTest(filenameBuffer);
891         }
892     } else {
893         printSeparators = (optind < argc-1 || (dumpPixels && dumpTree));
894         for (int i = optind; i != argc; ++i)
895             runTest(argv[i]);
896     }
897
898     delete gcController;
899     gcController = 0;
900
901     delete axController;
902     axController = 0;
903
904     gtk_widget_destroy(window);
905
906     return 0;
907 }