REGRESSION (r129211-r129218): http/tests/loading/redirect-with-no-location-crash...
[WebKit-https.git] / Tools / 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  * Copyright (C) 2010, 2011 Igalia S.L.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1.  Redistributions of source code must retain the above copyright
12  *     notice, this list of conditions and the following disclaimer.
13  * 2.  Redistributions in binary form must reproduce the above copyright
14  *     notice, this list of conditions and the following disclaimer in the
15  *     documentation and/or other materials provided with the distribution.
16  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
17  *     its contributors may be used to endorse or promote products derived
18  *     from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "DumpRenderTree.h"
34
35 #include "AccessibilityController.h"
36 #include "EditingCallbacks.h"
37 #include "EventSender.h"
38 #include "GCController.h"
39 #include "PixelDumpSupport.h"
40 #include "SelfScrollingWebKitWebView.h"
41 #include "TestRunner.h"
42 #include "TextInputController.h"
43 #include "WebCoreSupport/DumpRenderTreeSupportGtk.h"
44 #include "WebCoreTestSupport.h"
45 #include "WorkQueue.h"
46 #include "WorkQueueItem.h"
47 #include <JavaScriptCore/JavaScript.h>
48 #include <cassert>
49 #include <cstdlib>
50 #include <cstring>
51 #include <getopt.h>
52 #include <gtk/gtk.h>
53 #include <webkit/webkit.h>
54 #include <wtf/Assertions.h>
55 #include <wtf/gobject/GOwnPtr.h>
56 #include <wtf/gobject/GlibUtilities.h>
57
58 #if PLATFORM(X11)
59 #include <fontconfig/fontconfig.h>
60 #endif
61
62
63 using namespace std;
64
65 extern "C" {
66 // This API is not yet public.
67 extern gchar* webkit_web_history_item_get_target(WebKitWebHistoryItem*);
68 extern gboolean webkit_web_history_item_is_target_item(WebKitWebHistoryItem*);
69 extern GList* webkit_web_history_item_get_children(WebKitWebHistoryItem*);
70 extern void webkit_web_settings_add_extra_plugin_directory(WebKitWebView* view, const gchar* directory);
71 extern gchar* webkit_web_frame_get_response_mime_type(WebKitWebFrame* frame);
72 }
73
74 volatile bool done;
75 static bool printSeparators;
76 static bool dumpPixelsForCurrentTest;
77 static int dumpTree = 1;
78 static int useTimeoutWatchdog = 1;
79
80 AccessibilityController* axController = 0;
81 RefPtr<TestRunner> gTestRunner;
82 static GCController* gcController = 0;
83 static WebKitWebView* webView;
84 static GtkWidget* window;
85 static GtkWidget* container;
86 static GtkWidget* webInspectorWindow;
87 WebKitWebFrame* mainFrame = 0;
88 WebKitWebFrame* topLoadingFrame = 0;
89 guint waitToDumpWatchdog = 0;
90 bool waitForPolicy = false;
91
92 // This is a list of opened webviews
93 GSList* webViewList = 0;
94
95 // current b/f item at the end of the previous test
96 static WebKitWebHistoryItem* prevTestBFItem = NULL;
97
98 const unsigned historyItemIndent = 8;
99
100 static void runTest(const string& inputLine);
101
102 static void didRunInsecureContent(WebKitWebFrame*, WebKitSecurityOrigin*, const char* url);
103
104 static bool shouldLogFrameLoadDelegates(const string& pathOrURL)
105 {
106     return pathOrURL.find("loading/") != string::npos;
107 }
108
109 static bool shouldOpenWebInspector(const string& pathOrURL)
110 {
111     return pathOrURL.find("inspector/") != string::npos;
112 }
113
114 static bool shouldDumpAsText(const string& pathOrURL)
115 {
116     return pathOrURL.find("dumpAsText/") != string::npos;
117 }
118
119 static bool shouldEnableDeveloperExtras(const string& pathOrURL)
120 {
121     return true;
122 }
123
124 void dumpFrameScrollPosition(WebKitWebFrame* frame)
125 {
126     WebKitDOMDocument* document = webkit_web_frame_get_dom_document(frame);
127     if (!document)
128         return;
129
130     WebKitDOMDOMWindow* domWindow = webkit_dom_document_get_default_view(document);
131     if (!domWindow)
132         return;
133
134     glong x = webkit_dom_dom_window_get_page_x_offset(domWindow);
135     glong y = webkit_dom_dom_window_get_page_y_offset(domWindow);
136
137     if (abs(x) > 0 || abs(y) > 0) {
138         if (webkit_web_frame_get_parent(frame))
139             printf("frame '%s' ", webkit_web_frame_get_name(frame));
140         printf("scrolled to %ld,%ld\n", x, y);
141     }
142
143     if (gTestRunner->dumpChildFrameScrollPositions()) {
144         GSList* children = DumpRenderTreeSupportGtk::getFrameChildren(frame);
145         for (GSList* child = children; child; child = g_slist_next(child))
146             dumpFrameScrollPosition(static_cast<WebKitWebFrame*>(child->data));
147         g_slist_free(children);
148     }
149 }
150
151 void displayWebView()
152 {
153     DumpRenderTreeSupportGtk::forceWebViewPaint(webView);
154     DumpRenderTreeSupportGtk::setTracksRepaints(mainFrame, true);
155     DumpRenderTreeSupportGtk::resetTrackedRepaints(mainFrame);
156 }
157
158 static void appendString(gchar*& target, const gchar* string)
159 {
160     gchar* oldString = target;
161     target = g_strconcat(target, string, NULL);
162     g_free(oldString);
163 }
164
165 static void initializeGtkFontSettings(const char* testURL)
166 {
167     GtkSettings* settings = gtk_settings_get_default();
168     if (!settings)
169         return;
170     g_object_set(settings,
171                  "gtk-xft-dpi", 98304, // This is 96 * 1024 or 96 DPI according to the GTK+ docs.
172                  "gtk-xft-antialias", 1,
173                  "gtk-xft-hinting", 0,
174                  "gtk-font-name", "Liberation Sans 12",
175                  NULL);
176     gdk_screen_set_resolution(gdk_screen_get_default(), 96.0);
177
178     // One test needs subpixel anti-aliasing turned on, but generally we
179     // want all text in other tests to use to grayscale anti-aliasing.
180     if (testURL && strstr(testURL, "xsettings_antialias_settings.html"))
181         g_object_set(settings, "gtk-xft-rgba", "rgb", NULL);
182     else
183         g_object_set(settings, "gtk-xft-rgba", "none", NULL);
184 }
185
186 CString getTopLevelPath()
187 {
188     if (!g_getenv("WEBKIT_TOP_LEVEL"))
189         g_setenv("WEBKIT_TOP_LEVEL", TOP_LEVEL_DIR, FALSE);
190
191     return TOP_LEVEL_DIR;
192 }
193
194 CString getOutputDir()
195 {
196     const char* webkitOutputDir = g_getenv("WEBKITOUTPUTDIR");
197     if (webkitOutputDir)
198         return webkitOutputDir;
199
200     CString topLevelPath = getTopLevelPath();
201     GOwnPtr<char> outputDir(g_build_filename(topLevelPath.data(), "WebKitBuild", NULL));
202     return outputDir.get();
203 }
204
205 static CString getFontsPath()
206 {
207     CString webkitOutputDir = getOutputDir();
208     GOwnPtr<char> fontsPath(g_build_filename(webkitOutputDir.data(), "Dependencies", "Root", "webkitgtk-test-fonts", NULL));
209     if (g_file_test(fontsPath.get(), static_cast<GFileTest>(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
210         return fontsPath.get();
211
212     // Try alternative fonts path.
213     fontsPath.set(g_build_filename(webkitOutputDir.data(), "webkitgtk-test-fonts", NULL));
214     if (g_file_test(fontsPath.get(), static_cast<GFileTest>(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
215         return fontsPath.get();
216
217     return CString();
218 }
219
220 static void initializeFonts(const char* testURL = 0)
221 {
222 #if PLATFORM(X11)
223     initializeGtkFontSettings(testURL);
224
225     FcInit();
226
227     // If a test resulted a font being added or removed via the @font-face rule, then
228     // we want to reset the FontConfig configuration to prevent it from affecting other tests.
229     static int numFonts = 0;
230     FcFontSet* appFontSet = FcConfigGetFonts(0, FcSetApplication);
231     if (appFontSet && numFonts && appFontSet->nfont == numFonts)
232         return;
233
234     // Load our configuration file, which sets up proper aliases for family
235     // names like sans, serif and monospace.
236     FcConfig* config = FcConfigCreate();
237     GOwnPtr<gchar> fontConfigFilename(g_build_filename(FONTS_CONF_DIR, "fonts.conf", NULL));
238     if (!FcConfigParseAndLoad(config, reinterpret_cast<FcChar8*>(fontConfigFilename.get()), true))
239         g_error("Couldn't load font configuration file from: %s", fontConfigFilename.get());
240
241     CString fontsPath = getFontsPath();
242     if (fontsPath.isNull())
243         g_error("Could not locate test fonts at %s. Is WEBKIT_TOP_LEVEL set?", fontsPath.data());
244
245     GOwnPtr<GError> error;
246     GOwnPtr<GDir> fontsDirectory(g_dir_open(fontsPath.data(), 0, &error.outPtr()));
247     while (const char* directoryEntry = g_dir_read_name(fontsDirectory.get())) {
248         if (!g_str_has_suffix(directoryEntry, ".ttf") && !g_str_has_suffix(directoryEntry, ".otf"))
249             continue;
250         GOwnPtr<gchar> fontPath(g_build_filename(fontsPath.data(), directoryEntry, NULL));
251         if (!FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(fontPath.get())))
252             g_error("Could not load font at %s!", fontPath.get());
253
254     }
255
256     // Ahem is used by many layout tests.
257     GOwnPtr<gchar> ahemFontFilename(g_build_filename(FONTS_CONF_DIR, "AHEM____.TTF", NULL));
258     if (!FcConfigAppFontAddFile(config, reinterpret_cast<FcChar8*>(ahemFontFilename.get())))
259         g_error("Could not load font at %s!", ahemFontFilename.get()); 
260
261     for (int i = 1; i <= 9; i++) {
262         GOwnPtr<gchar> fontFilename(g_strdup_printf("WebKitWeightWatcher%i00.ttf", i));
263         GOwnPtr<gchar> fontPath(g_build_filename(FONTS_CONF_DIR, "..", "..", "fonts", fontFilename.get(), NULL));
264         if (!FcConfigAppFontAddFile(config, reinterpret_cast<FcChar8*>(fontPath.get())))
265             g_error("Could not load font at %s!", fontPath.get()); 
266     }
267
268     // A font with no valid Fontconfig encoding to test https://bugs.webkit.org/show_bug.cgi?id=47452
269     GOwnPtr<gchar> fontWithNoValidEncodingFilename(g_build_filename(FONTS_CONF_DIR, "FontWithNoValidEncoding.fon", NULL));
270     if (!FcConfigAppFontAddFile(config, reinterpret_cast<FcChar8*>(fontWithNoValidEncodingFilename.get())))
271         g_error("Could not load font at %s!", fontWithNoValidEncodingFilename.get()); 
272
273     if (!FcConfigSetCurrent(config))
274         g_error("Could not set the current font configuration!");
275
276     numFonts = FcConfigGetFonts(config, FcSetApplication)->nfont;
277 #endif
278 }
279
280 static gchar* dumpFramesAsText(WebKitWebFrame* frame)
281 {
282     gchar* result = 0;
283
284     // Add header for all but the main frame.
285     bool isMainFrame = (webkit_web_view_get_main_frame(webView) == frame);
286
287     CString innerText = DumpRenderTreeSupportGtk::getInnerText(frame);
288     if (isMainFrame)
289         result = g_strdup_printf("%s\n", innerText.data());
290     else {
291         const gchar* frameName = webkit_web_frame_get_name(frame);
292         result = g_strdup_printf("\n--------\nFrame: '%s'\n--------\n%s\n", frameName, innerText.data());
293     }
294
295     if (gTestRunner->dumpChildFramesAsText()) {
296         GSList* children = DumpRenderTreeSupportGtk::getFrameChildren(frame);
297         for (GSList* child = children; child; child = g_slist_next(child)) {
298             GOwnPtr<gchar> childData(dumpFramesAsText(static_cast<WebKitWebFrame*>(child->data)));
299             appendString(result, childData.get());
300         }
301         g_slist_free(children);
302     }
303
304     return result;
305 }
306
307 static gint compareHistoryItems(gpointer* item1, gpointer* item2)
308 {
309     GOwnPtr<gchar> firstItemTarget(webkit_web_history_item_get_target(WEBKIT_WEB_HISTORY_ITEM(item1)));
310     GOwnPtr<gchar> secondItemTarget(webkit_web_history_item_get_target(WEBKIT_WEB_HISTORY_ITEM(item2)));
311     return g_ascii_strcasecmp(firstItemTarget.get(), secondItemTarget.get());
312 }
313
314 static void dumpHistoryItem(WebKitWebHistoryItem* item, int indent, bool current)
315 {
316     ASSERT(item != NULL);
317     int start = 0;
318     g_object_ref(item);
319     if (current) {
320         printf("curr->");
321         start = 6;
322     }
323     for (int i = start; i < indent; i++)
324         putchar(' ');
325
326     // normalize file URLs.
327     const gchar* uri = webkit_web_history_item_get_uri(item);
328     gchar* uriScheme = g_uri_parse_scheme(uri);
329     if (g_strcmp0(uriScheme, "file") == 0) {
330         gchar* pos = g_strstr_len(uri, -1, "/LayoutTests/");
331         if (!pos) {
332             g_free(uriScheme);
333             return;
334         }
335
336         GString* result = g_string_sized_new(strlen(uri));
337         result = g_string_append(result, "(file test):");
338         result = g_string_append(result, pos + strlen("/LayoutTests/"));
339         printf("%s", result->str);
340         g_string_free(result, TRUE);
341     } else
342         printf("%s", uri);
343
344     g_free(uriScheme);
345
346     GOwnPtr<gchar> target(webkit_web_history_item_get_target(item));
347     if (target.get() && strlen(target.get()) > 0)
348         printf(" (in frame \"%s\")", target.get());
349     if (webkit_web_history_item_is_target_item(item))
350         printf("  **nav target**");
351     putchar('\n');
352
353     if (GList* kids = webkit_web_history_item_get_children(item)) {
354         // must sort to eliminate arbitrary result ordering which defeats reproducible testing
355         for (GList* kid = g_list_sort(kids, (GCompareFunc) compareHistoryItems); kid; kid = g_list_next(kid)) {
356             WebKitWebHistoryItem* item = WEBKIT_WEB_HISTORY_ITEM(kid->data);
357             dumpHistoryItem(item, indent + 4, FALSE);
358             g_object_unref(item);
359         }
360         g_list_free(kids);
361     }
362     g_object_unref(item);
363 }
364
365 static void dumpBackForwardListForWebView(WebKitWebView* view)
366 {
367     printf("\n============== Back Forward List ==============\n");
368     WebKitWebBackForwardList* bfList = webkit_web_view_get_back_forward_list(view);
369
370     // Print out all items in the list after prevTestBFItem, which was from the previous test
371     // Gather items from the end of the list, the print them out from oldest to newest
372     GList* itemsToPrint = NULL;
373     gint forwardListCount = webkit_web_back_forward_list_get_forward_length(bfList);
374     for (int i = forwardListCount; i > 0; i--) {
375         WebKitWebHistoryItem* item = webkit_web_back_forward_list_get_nth_item(bfList, i);
376         // something is wrong if the item from the last test is in the forward part of the b/f list
377         ASSERT(item != prevTestBFItem);
378         g_object_ref(item);
379         itemsToPrint = g_list_prepend(itemsToPrint, item);
380     }
381
382     WebKitWebHistoryItem* currentItem = webkit_web_back_forward_list_get_current_item(bfList);
383     g_object_ref(currentItem);
384     itemsToPrint = g_list_prepend(itemsToPrint, currentItem);
385
386     gint backListCount = webkit_web_back_forward_list_get_back_length(bfList);
387     for (int i = -1; i >= -(backListCount); i--) {
388         WebKitWebHistoryItem* item = webkit_web_back_forward_list_get_nth_item(bfList, i);
389         if (item == prevTestBFItem)
390             break;
391         g_object_ref(item);
392         itemsToPrint = g_list_prepend(itemsToPrint, item);
393     }
394
395     for (GList* itemToPrint = itemsToPrint; itemToPrint; itemToPrint = g_list_next(itemToPrint)) {
396         WebKitWebHistoryItem* item = WEBKIT_WEB_HISTORY_ITEM(itemToPrint->data);
397         dumpHistoryItem(item, historyItemIndent, item == currentItem);
398         g_object_unref(item);
399     }
400
401     g_list_free(itemsToPrint);
402     printf("===============================================\n");
403 }
404
405 static void dumpBackForwardListForAllWebViews()
406 {
407     // Dump the back forward list of the main WebView first
408     dumpBackForwardListForWebView(webView);
409
410     // The view list is prepended. Reverse the list so we get the order right.
411     for (GSList* currentView = g_slist_reverse(webViewList); currentView; currentView = g_slist_next(currentView))
412         dumpBackForwardListForWebView(WEBKIT_WEB_VIEW(currentView->data));
413 }
414
415 void setWaitToDumpWatchdog(guint timer)
416 {
417     waitToDumpWatchdog = timer;
418 }
419
420 bool shouldSetWaitToDumpWatchdog()
421 {
422     return !waitToDumpWatchdog && useTimeoutWatchdog;
423 }
424
425 static void invalidateAnyPreviousWaitToDumpWatchdog()
426 {
427     if (waitToDumpWatchdog) {
428         g_source_remove(waitToDumpWatchdog);
429         waitToDumpWatchdog = 0;
430     }
431
432     waitForPolicy = false;
433 }
434
435 static void resetDefaultsToConsistentValues()
436 {
437     WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
438     GOwnPtr<gchar> localStoragePath(g_build_filename(g_get_user_data_dir(), "DumpRenderTreeGtk", "databases", NULL));
439     g_object_set(G_OBJECT(settings),
440                  "enable-private-browsing", FALSE,
441                  "enable-developer-extras", FALSE,
442                  "enable-spell-checking", TRUE,
443                  "enable-html5-database", TRUE,
444                  "enable-html5-local-storage", TRUE,
445                  "html5-local-storage-database-path", localStoragePath.get(),
446                  "enable-xss-auditor", FALSE,
447                  "enable-spatial-navigation", FALSE,
448                  "enable-frame-flattening", FALSE,
449                  "javascript-can-access-clipboard", TRUE,
450                  "javascript-can-open-windows-automatically", TRUE,
451                  "enable-offline-web-application-cache", TRUE,
452                  "enable-universal-access-from-file-uris", TRUE,
453                  "enable-file-access-from-file-uris", TRUE,
454                  "enable-scripts", TRUE,
455                  "enable-dom-paste", TRUE,
456                  "default-font-family", "Times",
457                  "monospace-font-family", "Courier",
458                  "serif-font-family", "Times",
459                  "sans-serif-font-family", "Helvetica",
460                  "cursive-font-family", "cursive",
461                  "fantasy-font-family", "fantasy",
462                  "default-font-size", 12,
463                  "default-monospace-font-size", 10,
464                  "minimum-font-size", 0,
465                  "enable-caret-browsing", FALSE,
466                  "enable-page-cache", FALSE,
467                  "auto-resize-window", TRUE,
468                  "auto-load-images", TRUE,
469                  "enable-java-applet", FALSE,
470                  "enable-plugins", TRUE,
471                  "enable-hyperlink-auditing", FALSE,
472                  "editing-behavior", WEBKIT_EDITING_BEHAVIOR_UNIX,
473                  "enable-fullscreen", TRUE,
474                  NULL);
475     webkit_web_view_set_settings(webView, settings);
476     webkit_set_cache_model(WEBKIT_CACHE_MODEL_DOCUMENT_BROWSER);
477
478     DumpRenderTreeSupportGtk::clearMainFrameName(mainFrame);
479     DumpRenderTreeSupportGtk::scalePageBy(webView, 1, 0, 0);
480
481     WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView);
482     g_object_set(G_OBJECT(inspector), "javascript-profiling-enabled", FALSE, NULL);
483
484     webkit_web_view_set_zoom_level(webView, 1.0);
485     DumpRenderTreeSupportGtk::setMinimumTimerInterval(webView, DumpRenderTreeSupportGtk::defaultMinimumTimerInterval());
486
487     DumpRenderTreeSupportGtk::resetOriginAccessWhiteLists();
488
489     WebKitWebBackForwardList* list = webkit_web_view_get_back_forward_list(webView);
490     webkit_web_back_forward_list_clear(list);
491
492     SoupSession* session = webkit_get_default_session();
493     SoupCookieJar* jar = reinterpret_cast<SoupCookieJar*>(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR));
494
495     // We only create the jar when the soup backend needs to do
496     // HTTP. Should we initialize it earlier, perhaps?
497     if (jar)
498         g_object_set(G_OBJECT(jar), SOUP_COOKIE_JAR_ACCEPT_POLICY, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY, NULL);
499
500     setlocale(LC_ALL, "");
501
502     DumpRenderTreeSupportGtk::setLinksIncludedInFocusChain(true);
503     webkit_icon_database_set_path(webkit_get_icon_database(), 0);
504     DumpRenderTreeSupportGtk::setSelectTrailingWhitespaceEnabled(false);
505     DumpRenderTreeSupportGtk::setSmartInsertDeleteEnabled(webView, true);
506     DumpRenderTreeSupportGtk::setDefersLoading(webView, false);
507     DumpRenderTreeSupportGtk::setSerializeHTTPLoads(false);
508
509     if (axController)
510         axController->resetToConsistentState();
511
512     DumpRenderTreeSupportGtk::clearOpener(mainFrame);
513     DumpRenderTreeSupportGtk::setTracksRepaints(mainFrame, false);
514
515     DumpRenderTreeSupportGtk::resetGeolocationClientMock(webView);
516
517     DumpRenderTreeSupportGtk::setCSSGridLayoutEnabled(webView, false);
518     DumpRenderTreeSupportGtk::setCSSRegionsEnabled(webView, true);
519 }
520
521 static bool useLongRunningServerMode(int argc, char *argv[])
522 {
523     // This assumes you've already called getopt_long
524     return (argc == optind+1 && !strcmp(argv[optind], "-"));
525 }
526
527 static void runTestingServerLoop()
528 {
529     // When DumpRenderTree runs in server mode, we just wait around for file names
530     // to be passed to us and read each in turn, passing the results back to the client
531     char filenameBuffer[2048];
532     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
533         char* newLineCharacter = strchr(filenameBuffer, '\n');
534         if (newLineCharacter)
535             *newLineCharacter = '\0';
536
537         if (!strlen(filenameBuffer))
538             continue;
539
540         runTest(filenameBuffer);
541     }
542 }
543
544 static void initializeGlobalsFromCommandLineOptions(int argc, char *argv[])
545 {
546     struct option options[] = {
547         {"notree", no_argument, &dumpTree, false},
548         {"tree", no_argument, &dumpTree, true},
549         {"no-timeout", no_argument, &useTimeoutWatchdog, false},
550         {NULL, 0, NULL, 0}
551     };
552     
553     int option;
554     while ((option = getopt_long(argc, (char * const *)argv, "", options, NULL)) != -1) {
555         switch (option) {
556         case '?': // unknown or ambiguous option
557         case ':': // missing argument
558             exit(1);
559             break;
560         }
561     }
562 }
563
564
565 void dump()
566 {
567     invalidateAnyPreviousWaitToDumpWatchdog();
568
569     // Grab widget focus before dumping the contents of a widget, in
570     // case it was lost in the course of the test.
571     gtk_widget_grab_focus(GTK_WIDGET(webView));
572
573     if (dumpTree) {
574         char* result = 0;
575         gchar* responseMimeType = webkit_web_frame_get_response_mime_type(mainFrame);
576
577         if (g_str_equal(responseMimeType, "text/plain")) {
578             gTestRunner->setDumpAsText(true);
579             gTestRunner->setGeneratePixelResults(false);
580         }
581         g_free(responseMimeType);
582
583         if (gTestRunner->dumpAsText())
584             result = dumpFramesAsText(mainFrame);
585         else {
586             // Widget resizing is done asynchronously in GTK+. We pump the main
587             // loop here, to flush any pending resize requests. This prevents
588             // timing issues which affect the size of elements in the output.
589             // We only enable this workaround for tests that print the render tree
590             // because this seems to break some dumpAsText tests: see bug 39988
591             // After fixing that test, we should apply this approach to all dumps.
592             while (gtk_events_pending())
593                 gtk_main_iteration();
594
595             result = g_strdup(DumpRenderTreeSupportGtk::dumpRenderTree(mainFrame).data());
596         }
597
598         if (!result) {
599             const char* errorMessage;
600             if (gTestRunner->dumpAsText())
601                 errorMessage = "[documentElement innerText]";
602             else if (gTestRunner->dumpDOMAsWebArchive())
603                 errorMessage = "[[mainFrame DOMDocument] webArchive]";
604             else if (gTestRunner->dumpSourceAsWebArchive())
605                 errorMessage = "[[mainFrame dataSource] webArchive]";
606             else
607                 errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
608             printf("ERROR: nil result from %s", errorMessage);
609         } else {
610             printf("%s", result);
611             g_free(result);
612             if (!gTestRunner->dumpAsText() && !gTestRunner->dumpDOMAsWebArchive() && !gTestRunner->dumpSourceAsWebArchive())
613                 dumpFrameScrollPosition(mainFrame);
614
615             if (gTestRunner->dumpBackForwardList())
616                 dumpBackForwardListForAllWebViews();
617         }
618
619         if (printSeparators) {
620             puts("#EOF"); // terminate the content block
621             fputs("#EOF\n", stderr);
622             fflush(stdout);
623             fflush(stderr);
624         }
625     }
626
627     if (dumpPixelsForCurrentTest
628      && gTestRunner->generatePixelResults()
629      && !gTestRunner->dumpDOMAsWebArchive()
630      && !gTestRunner->dumpSourceAsWebArchive()) {
631         DumpRenderTreeSupportGtk::forceWebViewPaint(webView);
632         dumpWebViewAsPixelsAndCompareWithExpected(gTestRunner->expectedPixelHash());
633     }
634
635     // FIXME: call displayWebView here when we support --paint
636
637     done = true;
638     gtk_main_quit();
639 }
640
641 static void setDefaultsToConsistentStateValuesForTesting()
642 {
643     resetDefaultsToConsistentValues();
644
645     /* Disable the default auth dialog for testing */
646     SoupSession* session = webkit_get_default_session();
647     soup_session_remove_feature_by_type(session, WEBKIT_TYPE_SOUP_AUTH_DIALOG);
648
649 #if PLATFORM(X11)
650     webkit_web_settings_add_extra_plugin_directory(webView, TEST_PLUGIN_DIR);
651 #endif
652
653     gchar* databaseDirectory = g_build_filename(g_get_user_data_dir(), "gtkwebkitdrt", "databases", NULL);
654     webkit_set_web_database_directory_path(databaseDirectory);
655     g_free(databaseDirectory);
656
657 #if defined(GTK_API_VERSION_2)
658     gtk_rc_parse_string("style \"nix_scrollbar_spacing\"                    "
659                         "{                                                  "
660                         "    GtkScrolledWindow::scrollbar-spacing = 0       "
661                         "}                                                  "
662                         "class \"GtkWidget\" style \"nix_scrollbar_spacing\"");
663
664 #else
665     GtkCssProvider* cssProvider = gtk_css_provider_new();
666     gtk_css_provider_load_from_data(cssProvider,
667                                     "@binding-set NoKeyboardNavigation {        "
668                                     "   unbind \"<shift>F10\";                  "
669                                     "}                                          "
670                                     " * {                                       "
671                                     "   -GtkScrolledWindow-scrollbar-spacing: 0;"
672                                     "   gtk-key-bindings: NoKeyboardNavigation; "
673                                     "}                                          ",
674                                     -1, 0);
675     gtk_style_context_add_provider_for_screen(gdk_display_get_default_screen(gdk_display_get_default()),
676                                               GTK_STYLE_PROVIDER(cssProvider),
677                                               GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
678     g_object_unref(cssProvider);
679 #endif
680 }
681
682 static void sendPixelResultsEOF()
683 {
684     puts("#EOF");
685
686     fflush(stdout);
687     fflush(stderr);
688 }
689
690 static void runTest(const string& inputLine)
691 {
692     ASSERT(!inputLine.empty());
693
694     TestCommand command = parseInputLine(inputLine);
695     string& testURL = command.pathOrURL;
696     dumpPixelsForCurrentTest = command.shouldDumpPixels;
697
698     // Convert the path into a full file URL if it does not look
699     // like an HTTP/S URL (doesn't start with http:// or https://).
700     if (testURL.find("http://") && testURL.find("https://")) {
701         GFile* testFile = g_file_new_for_path(testURL.c_str());
702         gchar* testURLCString = g_file_get_uri(testFile);
703         testURL = testURLCString;
704         g_free(testURLCString);
705         g_object_unref(testFile);
706     }
707
708     resetDefaultsToConsistentValues();
709
710     gTestRunner = TestRunner::create(testURL, command.expectedPixelHash);
711     topLoadingFrame = 0;
712     done = false;
713
714     gTestRunner->setIconDatabaseEnabled(false);
715
716     if (shouldLogFrameLoadDelegates(testURL))
717         gTestRunner->setDumpFrameLoadCallbacks(true);
718
719     if (shouldEnableDeveloperExtras(testURL)) {
720         gTestRunner->setDeveloperExtrasEnabled(true);
721         if (shouldOpenWebInspector(testURL))
722             gTestRunner->showWebInspector();
723         if (shouldDumpAsText(testURL)) {
724             gTestRunner->setDumpAsText(true);
725             gTestRunner->setGeneratePixelResults(false);
726         }
727     }
728
729     WorkQueue::shared()->clear();
730     WorkQueue::shared()->setFrozen(false);
731
732     bool isSVGW3CTest = (testURL.find("svg/W3C-SVG-1.1") != string::npos);
733     GtkAllocation size;
734     size.x = size.y = 0;
735     size.width = isSVGW3CTest ? 480 : TestRunner::maxViewWidth;
736     size.height = isSVGW3CTest ? 360 : TestRunner::maxViewHeight;
737     gtk_window_resize(GTK_WINDOW(window), size.width, size.height);
738     gtk_widget_size_allocate(container, &size);
739
740     if (prevTestBFItem)
741         g_object_unref(prevTestBFItem);
742     WebKitWebBackForwardList* bfList = webkit_web_view_get_back_forward_list(webView);
743     prevTestBFItem = webkit_web_back_forward_list_get_current_item(bfList);
744     if (prevTestBFItem)
745         g_object_ref(prevTestBFItem);
746
747     initializeFonts(testURL.c_str());
748
749     // Focus the web view before loading the test to avoid focusing problems
750     gtk_widget_grab_focus(GTK_WIDGET(webView));
751     webkit_web_view_open(webView, testURL.c_str());
752
753     gtk_main();
754
755     // If developer extras enabled Web Inspector may have been open by the test.
756     if (shouldEnableDeveloperExtras(testURL)) {
757         gTestRunner->closeWebInspector();
758         gTestRunner->setDeveloperExtrasEnabled(false);
759     }
760
761     // Also check if we still have opened webViews and free them.
762     if (gTestRunner->closeRemainingWindowsWhenComplete() || webViewList) {
763         while (webViewList) {
764             g_object_unref(WEBKIT_WEB_VIEW(webViewList->data));
765             webViewList = g_slist_next(webViewList);
766         }
767         g_slist_free(webViewList);
768         webViewList = 0;
769     }
770
771     WebCoreTestSupport::resetInternalsObject(webkit_web_frame_get_global_context(mainFrame));
772     DumpRenderTreeSupportGtk::clearMemoryCache();
773     DumpRenderTreeSupportGtk::clearApplicationCache();
774
775     // A blank load seems to be necessary to reset state after certain tests.
776     webkit_web_view_open(webView, "about:blank");
777
778     gTestRunner.clear();
779
780     // terminate the (possibly empty) pixels block after all the state reset
781     sendPixelResultsEOF();
782 }
783
784 void webViewLoadStarted(WebKitWebView* view, WebKitWebFrame* frame, void*)
785 {
786     // Make sure we only set this once per test.  If it gets cleared, and then set again, we might
787     // end up doing two dumps for one test.
788     if (!topLoadingFrame && !done)
789         topLoadingFrame = frame;
790 }
791
792 static gboolean processWork(void* data)
793 {
794     // if we finish all the commands, we're ready to dump state
795     if (WorkQueue::shared()->processWork() && !gTestRunner->waitToDump())
796         dump();
797
798     return FALSE;
799 }
800
801 static char* getFrameNameSuitableForTestResult(WebKitWebView* view, WebKitWebFrame* frame)
802 {
803     char* frameName = g_strdup(webkit_web_frame_get_name(frame));
804
805     if (frame == webkit_web_view_get_main_frame(view)) {
806         // This is a bit strange. Shouldn't web_frame_get_name return NULL?
807         if (frameName && (frameName[0] != '\0')) {
808             char* tmp = g_strdup_printf("main frame \"%s\"", frameName);
809             g_free(frameName);
810             frameName = tmp;
811         } else {
812             g_free(frameName);
813             frameName = g_strdup("main frame");
814         }
815     } else if (!frameName || (frameName[0] == '\0')) {
816         g_free(frameName);
817         frameName = g_strdup("frame (anonymous)");
818     } else {
819         char* tmp = g_strdup_printf("frame \"%s\"", frameName);
820         g_free(frameName);
821         frameName = tmp;
822     }
823
824     return frameName;
825 }
826
827 static void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
828 {
829     // The deprecated "load-finished" signal is triggered by postProgressFinishedNotification(),
830     // so we can use it here in the DRT to provide the correct dump.
831     if (frame != topLoadingFrame)
832         return;
833     if (gTestRunner->dumpProgressFinishedCallback())
834         printf("postProgressFinishedNotification\n");
835 }
836
837 static gboolean webViewLoadError(WebKitWebView*, WebKitWebFrame*, gchar*, gpointer, gpointer)
838 {
839     return TRUE; // Return true here to disable the default error page.
840 }
841
842 static void webViewDocumentLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
843 {
844     if (!done && gTestRunner->dumpFrameLoadCallbacks()) {
845         char* frameName = getFrameNameSuitableForTestResult(view, frame);
846         printf("%s - didFinishDocumentLoadForFrame\n", frameName);
847         g_free(frameName);
848     } else if (!done) {
849         guint pendingFrameUnloadEvents = DumpRenderTreeSupportGtk::getPendingUnloadEventCount(frame);
850         if (pendingFrameUnloadEvents) {
851             char* frameName = getFrameNameSuitableForTestResult(view, frame);
852             printf("%s - has %u onunload handler(s)\n", frameName, pendingFrameUnloadEvents);
853             g_free(frameName);
854         }
855     }
856 }
857
858 static void webViewOnloadEvent(WebKitWebView* view, WebKitWebFrame* frame, void*)
859 {
860     if (!done && gTestRunner->dumpFrameLoadCallbacks()) {
861         char* frameName = getFrameNameSuitableForTestResult(view, frame);
862         printf("%s - didHandleOnloadEventsForFrame\n", frameName);
863         g_free(frameName);
864     }
865 }
866
867 static void addControllerToWindow(JSContextRef context, JSObjectRef windowObject, const char* controllerName, JSValueRef controller)
868 {
869     JSStringRef controllerNameStr = JSStringCreateWithUTF8CString(controllerName);
870     JSObjectSetProperty(context, windowObject, controllerNameStr, controller, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, 0);
871     JSStringRelease(controllerNameStr); 
872 }
873
874 static void webViewWindowObjectCleared(WebKitWebView* view, WebKitWebFrame* frame, JSGlobalContextRef context, JSObjectRef windowObject, gpointer data)
875 {
876     JSValueRef exception = 0;
877     ASSERT(gTestRunner);
878
879     gTestRunner->makeWindowObject(context, windowObject, &exception);
880     ASSERT(!exception);
881
882     gcController->makeWindowObject(context, windowObject, &exception);
883     ASSERT(!exception);
884
885     axController->makeWindowObject(context, windowObject, &exception);
886     ASSERT(!exception);
887
888     addControllerToWindow(context, windowObject, "eventSender", makeEventSender(context, !webkit_web_frame_get_parent(frame)));
889     addControllerToWindow(context, windowObject, "textInputController", makeTextInputController(context));
890     WebCoreTestSupport::injectInternalsObject(context);
891 }
892
893 static gboolean webViewConsoleMessage(WebKitWebView* view, const gchar* message, unsigned int line, const gchar* sourceId, gpointer data)
894 {
895     gchar* testMessage = 0;
896     const gchar* uriScheme;
897
898     // Tests expect only the filename part of local URIs
899     uriScheme = g_strstr_len(message, -1, "file://");
900     if (uriScheme) {
901         GString* tempString = g_string_sized_new(strlen(message));
902         gchar* filename = g_strrstr(uriScheme, G_DIR_SEPARATOR_S);
903
904         if (filename) {
905             // If the path is a lone slash, keep it to avoid empty output.
906             if (strlen(filename) > 1)
907                 filename += strlen(G_DIR_SEPARATOR_S);
908             tempString = g_string_append_len(tempString, message, (uriScheme - message));
909             tempString = g_string_append_len(tempString, filename, strlen(filename));
910             testMessage = g_string_free(tempString, FALSE);
911         }
912     }
913
914     fprintf(stdout, "CONSOLE MESSAGE: ");
915     if (line)
916         fprintf(stdout, "line %d: ", line);
917     fprintf(stdout, "%s\n", testMessage ? testMessage : message);
918     g_free(testMessage);
919
920     return TRUE;
921 }
922
923
924 static gboolean webViewScriptAlert(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gpointer data)
925 {
926     fprintf(stdout, "ALERT: %s\n", message);
927     return TRUE;
928 }
929
930 static gboolean webViewScriptPrompt(WebKitWebView* webView, WebKitWebFrame* frame, const gchar* message, const gchar* defaultValue, gchar** value, gpointer data)
931 {
932     fprintf(stdout, "PROMPT: %s, default text: %s\n", message, defaultValue);
933     *value = g_strdup(defaultValue);
934     return TRUE;
935 }
936
937 static gboolean webViewScriptConfirm(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gboolean* didConfirm, gpointer data)
938 {
939     fprintf(stdout, "CONFIRM: %s\n", message);
940     *didConfirm = TRUE;
941     return TRUE;
942 }
943
944 static void webViewTitleChanged(WebKitWebView* view, WebKitWebFrame* frame, const gchar* title, gpointer data)
945 {
946     if (gTestRunner->dumpFrameLoadCallbacks() && !done) {
947         GOwnPtr<char> frameName(getFrameNameSuitableForTestResult(view, frame));
948         printf("%s - didReceiveTitle: %s\n", frameName.get(), title ? title : "");
949     }
950
951     if (gTestRunner->dumpTitleChanges() && !done)
952         printf("TITLE CHANGED: '%s'\n", title ? title : "");
953 }
954
955 static bool webViewNavigationPolicyDecisionRequested(WebKitWebView* view, WebKitWebFrame* frame,
956                                                      WebKitNetworkRequest* request,
957                                                      WebKitWebNavigationAction* navAction,
958                                                      WebKitWebPolicyDecision* policyDecision)
959 {
960     // Use the default handler if we're not waiting for policy,
961     // i.e., TestRunner::waitForPolicyDelegate
962     if (!waitForPolicy)
963         return FALSE;
964
965     gchar* typeDescription;
966     WebKitWebNavigationReason reason;
967     g_object_get(G_OBJECT(navAction), "reason", &reason, NULL);
968
969     switch(reason) {
970         case WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED:
971             typeDescription = g_strdup("link clicked");
972             break;
973         case WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED:
974             typeDescription = g_strdup("form submitted");
975             break;
976         case WEBKIT_WEB_NAVIGATION_REASON_BACK_FORWARD:
977             typeDescription = g_strdup("back/forward");
978             break;
979         case WEBKIT_WEB_NAVIGATION_REASON_RELOAD:
980             typeDescription = g_strdup("reload");
981             break;
982         case WEBKIT_WEB_NAVIGATION_REASON_FORM_RESUBMITTED:
983             typeDescription = g_strdup("form resubmitted");
984             break;
985         case WEBKIT_WEB_NAVIGATION_REASON_OTHER:
986             typeDescription = g_strdup("other");
987             break;
988         default:
989             typeDescription = g_strdup("illegal value");
990     }
991
992     printf("Policy delegate: attempt to load %s with navigation type '%s'\n", webkit_network_request_get_uri(request), typeDescription);
993     g_free(typeDescription);
994
995     webkit_web_policy_decision_ignore(policyDecision);
996     gTestRunner->notifyDone();
997
998     return TRUE;
999 }
1000
1001 static void webViewStatusBarTextChanged(WebKitWebView* view, const gchar* message, gpointer data)
1002 {
1003     // Are we doing anything wrong? One test that does not call
1004     // dumpStatusCallbacks gets true here
1005     if (gTestRunner->dumpStatusCallbacks())
1006         printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", message);
1007 }
1008
1009 static gboolean webViewClose(WebKitWebView* view)
1010 {
1011     ASSERT(view);
1012
1013     webViewList = g_slist_remove(webViewList, view);
1014     g_object_unref(view);
1015
1016     return TRUE;
1017 }
1018
1019 static void databaseQuotaExceeded(WebKitWebView* view, WebKitWebFrame* frame, WebKitWebDatabase *database)
1020 {
1021     ASSERT(view);
1022     ASSERT(frame);
1023     ASSERT(database);
1024
1025     WebKitSecurityOrigin* origin = webkit_web_database_get_security_origin(database);
1026     if (gTestRunner->dumpDatabaseCallbacks()) {
1027         printf("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{%s, %s, %i} database:%s\n",
1028             webkit_security_origin_get_protocol(origin),
1029             webkit_security_origin_get_host(origin),
1030             webkit_security_origin_get_port(origin),
1031             webkit_web_database_get_name(database));
1032     }
1033     webkit_security_origin_set_web_database_quota(origin, 5 * 1024 * 1024);
1034 }
1035
1036 static bool
1037 geolocationPolicyDecisionRequested(WebKitWebView*, WebKitWebFrame*, WebKitGeolocationPolicyDecision* decision)
1038 {
1039     if (!gTestRunner->isGeolocationPermissionSet())
1040         return FALSE;
1041     if (gTestRunner->geolocationPermission())
1042         webkit_geolocation_policy_allow(decision);
1043     else
1044         webkit_geolocation_policy_deny(decision);
1045
1046     return TRUE;
1047 }
1048
1049
1050 static WebKitWebView* webViewCreate(WebKitWebView*, WebKitWebFrame*);
1051
1052 static gboolean webInspectorShowWindow(WebKitWebInspector*, gpointer data)
1053 {
1054     gtk_window_set_default_size(GTK_WINDOW(webInspectorWindow), 800, 600);
1055     gtk_widget_show_all(webInspectorWindow);
1056     return TRUE;
1057 }
1058
1059 static gboolean webInspectorCloseWindow(WebKitWebInspector*, gpointer data)
1060 {
1061     gtk_widget_destroy(webInspectorWindow);
1062     webInspectorWindow = 0;
1063     return TRUE;
1064 }
1065
1066 static WebKitWebView* webInspectorInspectWebView(WebKitWebInspector*, gpointer data)
1067 {
1068     webInspectorWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1069
1070     GtkWidget* webView = self_scrolling_webkit_web_view_new();
1071     gtk_container_add(GTK_CONTAINER(webInspectorWindow),
1072                       webView);
1073
1074     return WEBKIT_WEB_VIEW(webView);
1075 }
1076
1077 static void topLoadingFrameLoadFinished()
1078 {
1079     topLoadingFrame = 0;
1080     WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
1081     if (gTestRunner->waitToDump())
1082         return;
1083
1084     if (WorkQueue::shared()->count())
1085         g_timeout_add(0, processWork, 0);
1086     else
1087         dump();
1088 }
1089
1090 static void webFrameLoadStatusNotified(WebKitWebFrame* frame, gpointer user_data)
1091 {
1092     WebKitLoadStatus loadStatus = webkit_web_frame_get_load_status(frame);
1093
1094     if (gTestRunner->dumpFrameLoadCallbacks()) {
1095         GOwnPtr<char> frameName(getFrameNameSuitableForTestResult(webkit_web_frame_get_web_view(frame), frame));
1096
1097         switch (loadStatus) {
1098         case WEBKIT_LOAD_PROVISIONAL:
1099             if (!done)
1100                 printf("%s - didStartProvisionalLoadForFrame\n", frameName.get());
1101             break;
1102         case WEBKIT_LOAD_COMMITTED:
1103             if (!done)
1104                 printf("%s - didCommitLoadForFrame\n", frameName.get());
1105             break;
1106         case WEBKIT_LOAD_FINISHED:
1107             if (!done)
1108                 printf("%s - didFinishLoadForFrame\n", frameName.get());
1109             break;
1110         default:
1111             break;
1112         }
1113     }
1114
1115     if ((loadStatus == WEBKIT_LOAD_FINISHED || loadStatus == WEBKIT_LOAD_FAILED)
1116         && frame == topLoadingFrame)
1117         topLoadingFrameLoadFinished();
1118 }
1119
1120 static void frameCreatedCallback(WebKitWebView* webView, WebKitWebFrame* webFrame, gpointer user_data)
1121 {
1122     g_signal_connect(webFrame, "notify::load-status", G_CALLBACK(webFrameLoadStatusNotified), NULL);
1123     g_signal_connect(webFrame, "insecure-content-run", G_CALLBACK(didRunInsecureContent), NULL);
1124 }
1125
1126
1127 static CString pathFromSoupURI(SoupURI* uri)
1128 {
1129     if (!uri)
1130         return CString();
1131
1132     if (g_str_equal(uri->scheme, "http") || g_str_equal(uri->scheme, "ftp")) {
1133         GOwnPtr<char> uriString(soup_uri_to_string(uri, FALSE));
1134         return CString(uriString.get());
1135     }
1136
1137     GOwnPtr<gchar> parentPath(g_path_get_dirname(uri->path));
1138     GOwnPtr<gchar> pathDirname(g_path_get_basename(parentPath.get()));
1139     GOwnPtr<gchar> pathBasename(g_path_get_basename(uri->path));
1140     GOwnPtr<gchar> urlPath(g_strdup_printf("%s/%s", pathDirname.get(), pathBasename.get()));
1141     return CString(urlPath.get());
1142 }
1143
1144 static CString convertSoupMessageToURLPath(SoupMessage* soupMessage)
1145 {
1146     if (!soupMessage)
1147         return CString();
1148     if (SoupURI* requestURI = soup_message_get_uri(soupMessage))
1149         return pathFromSoupURI(requestURI);
1150     return CString();
1151 }
1152
1153 static CString convertNetworkRequestToURLPath(WebKitNetworkRequest* request)
1154 {
1155     return convertSoupMessageToURLPath(webkit_network_request_get_message(request));
1156 }
1157
1158 static CString convertWebResourceToURLPath(WebKitWebResource* webResource)
1159 {
1160     SoupURI* uri = soup_uri_new(webkit_web_resource_get_uri(webResource));
1161     CString urlPath(pathFromSoupURI(uri));
1162     soup_uri_free(uri);
1163     return urlPath;
1164 }
1165
1166 static CString urlSuitableForTestResult(const char* uriString)
1167 {
1168     if (!g_str_has_prefix(uriString, "file://"))
1169         return CString(uriString);
1170
1171     GOwnPtr<gchar> basename(g_path_get_basename(uriString));
1172     return CString(basename.get());
1173 }
1174
1175 static CString descriptionSuitableForTestResult(SoupURI* uri)
1176 {
1177     if (!uri)
1178         return CString("");
1179
1180     GOwnPtr<char> uriString(soup_uri_to_string(uri, false));
1181     return urlSuitableForTestResult(uriString.get());
1182 }
1183
1184 static CString descriptionSuitableForTestResult(WebKitWebView* webView, WebKitWebFrame* webFrame, WebKitWebResource* webResource)
1185 {
1186     SoupURI* uri = soup_uri_new(webkit_web_resource_get_uri(webResource));
1187     CString description;
1188     WebKitWebDataSource* dataSource = webkit_web_frame_get_data_source(webFrame);
1189
1190     if (webResource == webkit_web_data_source_get_main_resource(dataSource)
1191         && (!webkit_web_view_get_progress(webView) || g_str_equal(uri->scheme, "file")))
1192         description = CString("<unknown>");
1193     else
1194         description = convertWebResourceToURLPath(webResource);
1195
1196     if (uri)
1197         soup_uri_free(uri);
1198
1199     return description;
1200 }
1201
1202 static CString descriptionSuitableForTestResult(GError* error, WebKitWebResource* webResource)
1203 {
1204     const gchar* errorDomain = g_quark_to_string(error->domain);
1205     CString resourceURIString(urlSuitableForTestResult(webkit_web_resource_get_uri(webResource)));
1206
1207     if (g_str_equal(errorDomain, "webkit-network-error-quark") || g_str_equal(errorDomain, "soup_http_error_quark"))
1208         errorDomain = "NSURLErrorDomain";
1209
1210     if (g_str_equal(errorDomain, "WebKitPolicyError"))
1211         errorDomain = "WebKitErrorDomain";
1212
1213     // TODO: the other ports get the failingURL from the ResourceError
1214     GOwnPtr<char> errorString(g_strdup_printf("<NSError domain %s, code %d, failing URL \"%s\">",
1215                                               errorDomain, error->code, resourceURIString.data()));
1216     return CString(errorString.get());
1217 }
1218
1219 static CString descriptionSuitableForTestResult(WebKitNetworkRequest* request)
1220 {
1221     SoupMessage* soupMessage = webkit_network_request_get_message(request);
1222
1223     if (!soupMessage)
1224         return CString("");
1225
1226     SoupURI* requestURI = soup_message_get_uri(soupMessage);
1227     SoupURI* mainDocumentURI = soup_message_get_first_party(soupMessage);
1228     CString requestURIString(descriptionSuitableForTestResult(requestURI));
1229     CString mainDocumentURIString(descriptionSuitableForTestResult(mainDocumentURI));
1230     CString path(convertNetworkRequestToURLPath(request));
1231     GOwnPtr<char> description(g_strdup_printf("<NSURLRequest URL %s, main document URL %s, http method %s>",
1232                                               path.data(), mainDocumentURIString.data(),
1233                                               soupMessage ? soupMessage->method : "(none)"));
1234     return CString(description.get());
1235 }
1236
1237 static CString descriptionSuitableForTestResult(WebKitNetworkResponse* response)
1238 {
1239     if (!response)
1240         return CString("(null)");
1241
1242     int statusCode = 0;
1243     CString responseURIString(urlSuitableForTestResult(webkit_network_response_get_uri(response)));
1244     SoupMessage* soupMessage = webkit_network_response_get_message(response);
1245     CString path;
1246
1247     if (soupMessage) {
1248         statusCode = soupMessage->status_code;
1249         path = convertSoupMessageToURLPath(soupMessage);
1250     } else
1251         path = CString("");
1252
1253     GOwnPtr<char> description(g_strdup_printf("<NSURLResponse %s, http status code %d>", path.data(), statusCode));
1254     return CString(description.get());
1255 }
1256
1257 static void willSendRequestCallback(WebKitWebView* webView, WebKitWebFrame* webFrame, WebKitWebResource* resource, WebKitNetworkRequest* request, WebKitNetworkResponse* response)
1258 {
1259
1260
1261     if (!done && gTestRunner->willSendRequestReturnsNull()) {
1262         // As requested by the TestRunner, don't perform the request.
1263         webkit_network_request_set_uri(request, "about:blank");
1264         return;
1265     }
1266
1267     if (!done && gTestRunner->dumpResourceLoadCallbacks())
1268         printf("%s - willSendRequest %s redirectResponse %s\n",
1269                convertNetworkRequestToURLPath(request).data(),
1270                descriptionSuitableForTestResult(request).data(),
1271                descriptionSuitableForTestResult(response).data());
1272
1273     SoupMessage* soupMessage = webkit_network_request_get_message(request);
1274     SoupURI* uri = soup_uri_new(webkit_network_request_get_uri(request));
1275
1276     if (SOUP_URI_VALID_FOR_HTTP(uri) && g_strcmp0(uri->host, "127.0.0.1")
1277         && g_strcmp0(uri->host, "255.255.255.255")
1278         && g_ascii_strncasecmp(uri->host, "localhost", 9)) {
1279         printf("Blocked access to external URL %s\n", soup_uri_to_string(uri, FALSE));
1280         // Cancel load of blocked resource to avoid potential
1281         // network-related timeouts in tests.
1282         webkit_network_request_set_uri(request, "about:blank");
1283         soup_uri_free(uri);
1284         return;
1285     }
1286
1287     if (uri)
1288         soup_uri_free(uri);
1289
1290     if (soupMessage) {
1291         const set<string>& clearHeaders = gTestRunner->willSendRequestClearHeaders();
1292         for (set<string>::const_iterator header = clearHeaders.begin(); header != clearHeaders.end(); ++header)
1293             soup_message_headers_remove(soupMessage->request_headers, header->c_str());
1294     }
1295 }
1296
1297
1298 static void didReceiveResponse(WebKitWebView* webView, WebKitWebFrame*, WebKitWebResource* webResource, WebKitNetworkResponse* response)
1299 {
1300     if (!done && gTestRunner->dumpResourceLoadCallbacks()) {
1301         CString responseDescription(descriptionSuitableForTestResult(response));
1302         CString path(convertWebResourceToURLPath(webResource));
1303         printf("%s - didReceiveResponse %s\n", path.data(), responseDescription.data());
1304     }
1305
1306     // TODO: add "has MIME type" whenever dumpResourceResponseMIMETypes() is supported.
1307     // See https://bugs.webkit.org/show_bug.cgi?id=58222.
1308 }
1309
1310 static void didFinishLoading(WebKitWebView* webView, WebKitWebFrame* webFrame, WebKitWebResource* webResource)
1311 {
1312     if (!done && gTestRunner->dumpResourceLoadCallbacks())
1313         printf("%s - didFinishLoading\n", descriptionSuitableForTestResult(webView, webFrame, webResource).data());
1314 }
1315
1316 static void didFailLoadingWithError(WebKitWebView* webView, WebKitWebFrame* webFrame, WebKitWebResource* webResource, GError* webError)
1317 {
1318     if (!done && gTestRunner->dumpResourceLoadCallbacks()) {
1319         CString webErrorString(descriptionSuitableForTestResult(webError, webResource));
1320         printf("%s - didFailLoadingWithError: %s\n", descriptionSuitableForTestResult(webView, webFrame, webResource).data(),
1321                webErrorString.data());
1322     }
1323 }
1324
1325 static void didRunInsecureContent(WebKitWebFrame*, WebKitSecurityOrigin*, const char* url)
1326 {
1327     if (!done && gTestRunner->dumpFrameLoadCallbacks())
1328         printf("didRunInsecureContent\n");
1329 }
1330
1331 static gboolean webViewRunFileChooser(WebKitWebView*, WebKitFileChooserRequest*)
1332 {
1333     // We return TRUE to not propagate the event further so the
1334     // default file chooser dialog is not shown.
1335     return TRUE;
1336 }
1337
1338 static WebKitWebView* createWebView()
1339 {
1340     // It is important to declare DRT is running early so when creating
1341     // web view mock clients are used instead of proper ones.
1342     DumpRenderTreeSupportGtk::setDumpRenderTreeModeEnabled(true);
1343
1344     WebKitWebView* view = WEBKIT_WEB_VIEW(self_scrolling_webkit_web_view_new());
1345
1346     g_object_connect(G_OBJECT(view),
1347                      "signal::load-started", webViewLoadStarted, 0,
1348                      "signal::load-finished", webViewLoadFinished, 0,
1349                      "signal::load-error", webViewLoadError, 0,
1350                      "signal::window-object-cleared", webViewWindowObjectCleared, 0,
1351                      "signal::console-message", webViewConsoleMessage, 0,
1352                      "signal::script-alert", webViewScriptAlert, 0,
1353                      "signal::script-prompt", webViewScriptPrompt, 0,
1354                      "signal::script-confirm", webViewScriptConfirm, 0,
1355                      "signal::title-changed", webViewTitleChanged, 0,
1356                      "signal::navigation-policy-decision-requested", webViewNavigationPolicyDecisionRequested, 0,
1357                      "signal::status-bar-text-changed", webViewStatusBarTextChanged, 0,
1358                      "signal::create-web-view", webViewCreate, 0,
1359                      "signal::close-web-view", webViewClose, 0,
1360                      "signal::database-quota-exceeded", databaseQuotaExceeded, 0,
1361                      "signal::document-load-finished", webViewDocumentLoadFinished, 0,
1362                      "signal::geolocation-policy-decision-requested", geolocationPolicyDecisionRequested, 0,
1363                      "signal::onload-event", webViewOnloadEvent, 0,
1364                      "signal::drag-begin", dragBeginCallback, 0,
1365                      "signal::drag-end", dragEndCallback, 0,
1366                      "signal::drag-failed", dragFailedCallback, 0,
1367                      "signal::frame-created", frameCreatedCallback, 0,
1368                      "signal::resource-request-starting", willSendRequestCallback, 0,
1369                      "signal::resource-response-received", didReceiveResponse, 0,
1370                      "signal::resource-load-finished", didFinishLoading, 0,
1371                      "signal::resource-load-failed", didFailLoadingWithError, 0,
1372                      "signal::run-file-chooser", webViewRunFileChooser, 0,
1373                      NULL);
1374     connectEditingCallbacks(view);
1375
1376     WebKitWebInspector* inspector = webkit_web_view_get_inspector(view);
1377     g_object_connect(G_OBJECT(inspector),
1378                      "signal::inspect-web-view", webInspectorInspectWebView, 0,
1379                      "signal::show-window", webInspectorShowWindow, 0,
1380                      "signal::close-window", webInspectorCloseWindow, 0,
1381                      NULL);
1382
1383     if (webView) {
1384         WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
1385         webkit_web_view_set_settings(view, settings);
1386     }
1387
1388     // frame-created is not issued for main frame. That's why we must do this here
1389     WebKitWebFrame* frame = webkit_web_view_get_main_frame(view);
1390     g_signal_connect(frame, "notify::load-status", G_CALLBACK(webFrameLoadStatusNotified), NULL);
1391     g_signal_connect(frame, "insecure-content-run", G_CALLBACK(didRunInsecureContent), NULL);
1392
1393     return view;
1394 }
1395
1396 static WebKitWebView* webViewCreate(WebKitWebView* view, WebKitWebFrame* frame)
1397 {
1398     if (!gTestRunner->canOpenWindows())
1399         return 0;
1400
1401     // Make sure that waitUntilDone has been called.
1402     ASSERT(gTestRunner->waitToDump());
1403
1404     WebKitWebView* newWebView = createWebView();
1405     g_object_ref_sink(G_OBJECT(newWebView));
1406     webViewList = g_slist_prepend(webViewList, newWebView);
1407     return newWebView;
1408 }
1409
1410 static void logHandler(const gchar* domain, GLogLevelFlags level, const gchar* message, gpointer data)
1411 {
1412     if (level < G_LOG_LEVEL_DEBUG)
1413         fprintf(stderr, "%s\n", message);
1414 }
1415
1416 int main(int argc, char* argv[])
1417 {
1418     gtk_init(&argc, &argv);
1419
1420     // Some plugins might try to use the GLib logger for printing debug
1421     // messages. This will cause tests to fail because of unexpected output.
1422     // We squelch all debug messages sent to the logger.
1423     g_log_set_default_handler(logHandler, 0);
1424
1425     initializeGlobalsFromCommandLineOptions(argc, argv);
1426     initializeFonts();
1427
1428     window = gtk_window_new(GTK_WINDOW_POPUP);
1429 #ifdef GTK_API_VERSION_2
1430     container = gtk_hbox_new(TRUE, 0);
1431 #else
1432     container = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1433     gtk_box_set_homogeneous(GTK_BOX(container), TRUE);
1434 #endif
1435     gtk_container_add(GTK_CONTAINER(window), container);
1436     gtk_widget_show_all(window);
1437
1438     webView = createWebView();
1439     gtk_box_pack_start(GTK_BOX(container), GTK_WIDGET(webView), TRUE, TRUE, 0);
1440     gtk_widget_realize(GTK_WIDGET(webView));
1441     gtk_widget_show_all(container);
1442     mainFrame = webkit_web_view_get_main_frame(webView);
1443
1444     setDefaultsToConsistentStateValuesForTesting();
1445
1446     gcController = new GCController();
1447     axController = new AccessibilityController();
1448
1449     if (useLongRunningServerMode(argc, argv)) {
1450         printSeparators = true;
1451         runTestingServerLoop();
1452     } else {
1453         printSeparators = (optind < argc-1 || (dumpPixelsForCurrentTest && dumpTree));
1454         for (int i = optind; i != argc; ++i)
1455             runTest(argv[i]);
1456     }
1457
1458     delete gcController;
1459     gcController = 0;
1460
1461     delete axController;
1462     axController = 0;
1463
1464     gtk_widget_destroy(window);
1465
1466     return 0;
1467 }