dabb09d2911c812fbe6b23a96883f625f3ecf847
[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
774     // A blank load seems to be necessary to reset state after certain tests.
775     webkit_web_view_open(webView, "about:blank");
776
777     gTestRunner.clear();
778
779     // terminate the (possibly empty) pixels block after all the state reset
780     sendPixelResultsEOF();
781 }
782
783 void webViewLoadStarted(WebKitWebView* view, WebKitWebFrame* frame, void*)
784 {
785     // Make sure we only set this once per test.  If it gets cleared, and then set again, we might
786     // end up doing two dumps for one test.
787     if (!topLoadingFrame && !done)
788         topLoadingFrame = frame;
789 }
790
791 static gboolean processWork(void* data)
792 {
793     // if we finish all the commands, we're ready to dump state
794     if (WorkQueue::shared()->processWork() && !gTestRunner->waitToDump())
795         dump();
796
797     return FALSE;
798 }
799
800 static char* getFrameNameSuitableForTestResult(WebKitWebView* view, WebKitWebFrame* frame)
801 {
802     char* frameName = g_strdup(webkit_web_frame_get_name(frame));
803
804     if (frame == webkit_web_view_get_main_frame(view)) {
805         // This is a bit strange. Shouldn't web_frame_get_name return NULL?
806         if (frameName && (frameName[0] != '\0')) {
807             char* tmp = g_strdup_printf("main frame \"%s\"", frameName);
808             g_free(frameName);
809             frameName = tmp;
810         } else {
811             g_free(frameName);
812             frameName = g_strdup("main frame");
813         }
814     } else if (!frameName || (frameName[0] == '\0')) {
815         g_free(frameName);
816         frameName = g_strdup("frame (anonymous)");
817     } else {
818         char* tmp = g_strdup_printf("frame \"%s\"", frameName);
819         g_free(frameName);
820         frameName = tmp;
821     }
822
823     return frameName;
824 }
825
826 static void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
827 {
828     // The deprecated "load-finished" signal is triggered by postProgressFinishedNotification(),
829     // so we can use it here in the DRT to provide the correct dump.
830     if (frame != topLoadingFrame)
831         return;
832     if (gTestRunner->dumpProgressFinishedCallback())
833         printf("postProgressFinishedNotification\n");
834 }
835
836 static gboolean webViewLoadError(WebKitWebView*, WebKitWebFrame*, gchar*, gpointer, gpointer)
837 {
838     return TRUE; // Return true here to disable the default error page.
839 }
840
841 static void webViewDocumentLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
842 {
843     if (!done && gTestRunner->dumpFrameLoadCallbacks()) {
844         char* frameName = getFrameNameSuitableForTestResult(view, frame);
845         printf("%s - didFinishDocumentLoadForFrame\n", frameName);
846         g_free(frameName);
847     } else if (!done) {
848         guint pendingFrameUnloadEvents = DumpRenderTreeSupportGtk::getPendingUnloadEventCount(frame);
849         if (pendingFrameUnloadEvents) {
850             char* frameName = getFrameNameSuitableForTestResult(view, frame);
851             printf("%s - has %u onunload handler(s)\n", frameName, pendingFrameUnloadEvents);
852             g_free(frameName);
853         }
854     }
855 }
856
857 static void webViewOnloadEvent(WebKitWebView* view, WebKitWebFrame* frame, void*)
858 {
859     if (!done && gTestRunner->dumpFrameLoadCallbacks()) {
860         char* frameName = getFrameNameSuitableForTestResult(view, frame);
861         printf("%s - didHandleOnloadEventsForFrame\n", frameName);
862         g_free(frameName);
863     }
864 }
865
866 static void addControllerToWindow(JSContextRef context, JSObjectRef windowObject, const char* controllerName, JSValueRef controller)
867 {
868     JSStringRef controllerNameStr = JSStringCreateWithUTF8CString(controllerName);
869     JSObjectSetProperty(context, windowObject, controllerNameStr, controller, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, 0);
870     JSStringRelease(controllerNameStr); 
871 }
872
873 static void webViewWindowObjectCleared(WebKitWebView* view, WebKitWebFrame* frame, JSGlobalContextRef context, JSObjectRef windowObject, gpointer data)
874 {
875     JSValueRef exception = 0;
876     ASSERT(gTestRunner);
877
878     gTestRunner->makeWindowObject(context, windowObject, &exception);
879     ASSERT(!exception);
880
881     gcController->makeWindowObject(context, windowObject, &exception);
882     ASSERT(!exception);
883
884     axController->makeWindowObject(context, windowObject, &exception);
885     ASSERT(!exception);
886
887     addControllerToWindow(context, windowObject, "eventSender", makeEventSender(context, !webkit_web_frame_get_parent(frame)));
888     addControllerToWindow(context, windowObject, "textInputController", makeTextInputController(context));
889     WebCoreTestSupport::injectInternalsObject(context);
890 }
891
892 static gboolean webViewConsoleMessage(WebKitWebView* view, const gchar* message, unsigned int line, const gchar* sourceId, gpointer data)
893 {
894     gchar* testMessage = 0;
895     const gchar* uriScheme;
896
897     // Tests expect only the filename part of local URIs
898     uriScheme = g_strstr_len(message, -1, "file://");
899     if (uriScheme) {
900         GString* tempString = g_string_sized_new(strlen(message));
901         gchar* filename = g_strrstr(uriScheme, G_DIR_SEPARATOR_S);
902
903         if (filename) {
904             // If the path is a lone slash, keep it to avoid empty output.
905             if (strlen(filename) > 1)
906                 filename += strlen(G_DIR_SEPARATOR_S);
907             tempString = g_string_append_len(tempString, message, (uriScheme - message));
908             tempString = g_string_append_len(tempString, filename, strlen(filename));
909             testMessage = g_string_free(tempString, FALSE);
910         }
911     }
912
913     fprintf(stdout, "CONSOLE MESSAGE: ");
914     if (line)
915         fprintf(stdout, "line %d: ", line);
916     fprintf(stdout, "%s\n", testMessage ? testMessage : message);
917     g_free(testMessage);
918
919     return TRUE;
920 }
921
922
923 static gboolean webViewScriptAlert(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gpointer data)
924 {
925     fprintf(stdout, "ALERT: %s\n", message);
926     return TRUE;
927 }
928
929 static gboolean webViewScriptPrompt(WebKitWebView* webView, WebKitWebFrame* frame, const gchar* message, const gchar* defaultValue, gchar** value, gpointer data)
930 {
931     fprintf(stdout, "PROMPT: %s, default text: %s\n", message, defaultValue);
932     *value = g_strdup(defaultValue);
933     return TRUE;
934 }
935
936 static gboolean webViewScriptConfirm(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gboolean* didConfirm, gpointer data)
937 {
938     fprintf(stdout, "CONFIRM: %s\n", message);
939     *didConfirm = TRUE;
940     return TRUE;
941 }
942
943 static void webViewTitleChanged(WebKitWebView* view, WebKitWebFrame* frame, const gchar* title, gpointer data)
944 {
945     if (gTestRunner->dumpTitleChanges() && !done)
946         printf("TITLE CHANGED: %s\n", title ? title : "");
947 }
948
949 static bool webViewNavigationPolicyDecisionRequested(WebKitWebView* view, WebKitWebFrame* frame,
950                                                      WebKitNetworkRequest* request,
951                                                      WebKitWebNavigationAction* navAction,
952                                                      WebKitWebPolicyDecision* policyDecision)
953 {
954     // Use the default handler if we're not waiting for policy,
955     // i.e., TestRunner::waitForPolicyDelegate
956     if (!waitForPolicy)
957         return FALSE;
958
959     gchar* typeDescription;
960     WebKitWebNavigationReason reason;
961     g_object_get(G_OBJECT(navAction), "reason", &reason, NULL);
962
963     switch(reason) {
964         case WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED:
965             typeDescription = g_strdup("link clicked");
966             break;
967         case WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED:
968             typeDescription = g_strdup("form submitted");
969             break;
970         case WEBKIT_WEB_NAVIGATION_REASON_BACK_FORWARD:
971             typeDescription = g_strdup("back/forward");
972             break;
973         case WEBKIT_WEB_NAVIGATION_REASON_RELOAD:
974             typeDescription = g_strdup("reload");
975             break;
976         case WEBKIT_WEB_NAVIGATION_REASON_FORM_RESUBMITTED:
977             typeDescription = g_strdup("form resubmitted");
978             break;
979         case WEBKIT_WEB_NAVIGATION_REASON_OTHER:
980             typeDescription = g_strdup("other");
981             break;
982         default:
983             typeDescription = g_strdup("illegal value");
984     }
985
986     printf("Policy delegate: attempt to load %s with navigation type '%s'\n", webkit_network_request_get_uri(request), typeDescription);
987     g_free(typeDescription);
988
989     webkit_web_policy_decision_ignore(policyDecision);
990     gTestRunner->notifyDone();
991
992     return TRUE;
993 }
994
995 static void webViewStatusBarTextChanged(WebKitWebView* view, const gchar* message, gpointer data)
996 {
997     // Are we doing anything wrong? One test that does not call
998     // dumpStatusCallbacks gets true here
999     if (gTestRunner->dumpStatusCallbacks())
1000         printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", message);
1001 }
1002
1003 static gboolean webViewClose(WebKitWebView* view)
1004 {
1005     ASSERT(view);
1006
1007     webViewList = g_slist_remove(webViewList, view);
1008     g_object_unref(view);
1009
1010     return TRUE;
1011 }
1012
1013 static void databaseQuotaExceeded(WebKitWebView* view, WebKitWebFrame* frame, WebKitWebDatabase *database)
1014 {
1015     ASSERT(view);
1016     ASSERT(frame);
1017     ASSERT(database);
1018
1019     WebKitSecurityOrigin* origin = webkit_web_database_get_security_origin(database);
1020     if (gTestRunner->dumpDatabaseCallbacks()) {
1021         printf("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{%s, %s, %i} database:%s\n",
1022             webkit_security_origin_get_protocol(origin),
1023             webkit_security_origin_get_host(origin),
1024             webkit_security_origin_get_port(origin),
1025             webkit_web_database_get_name(database));
1026     }
1027     webkit_security_origin_set_web_database_quota(origin, 5 * 1024 * 1024);
1028 }
1029
1030 static bool
1031 geolocationPolicyDecisionRequested(WebKitWebView*, WebKitWebFrame*, WebKitGeolocationPolicyDecision* decision)
1032 {
1033     if (!gTestRunner->isGeolocationPermissionSet())
1034         return FALSE;
1035     if (gTestRunner->geolocationPermission())
1036         webkit_geolocation_policy_allow(decision);
1037     else
1038         webkit_geolocation_policy_deny(decision);
1039
1040     return TRUE;
1041 }
1042
1043
1044 static WebKitWebView* webViewCreate(WebKitWebView*, WebKitWebFrame*);
1045
1046 static gboolean webInspectorShowWindow(WebKitWebInspector*, gpointer data)
1047 {
1048     gtk_window_set_default_size(GTK_WINDOW(webInspectorWindow), 800, 600);
1049     gtk_widget_show_all(webInspectorWindow);
1050     return TRUE;
1051 }
1052
1053 static gboolean webInspectorCloseWindow(WebKitWebInspector*, gpointer data)
1054 {
1055     gtk_widget_destroy(webInspectorWindow);
1056     webInspectorWindow = 0;
1057     return TRUE;
1058 }
1059
1060 static WebKitWebView* webInspectorInspectWebView(WebKitWebInspector*, gpointer data)
1061 {
1062     webInspectorWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1063
1064     GtkWidget* webView = self_scrolling_webkit_web_view_new();
1065     gtk_container_add(GTK_CONTAINER(webInspectorWindow),
1066                       webView);
1067
1068     return WEBKIT_WEB_VIEW(webView);
1069 }
1070
1071 static void topLoadingFrameLoadFinished()
1072 {
1073     topLoadingFrame = 0;
1074     WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
1075     if (gTestRunner->waitToDump())
1076         return;
1077
1078     if (WorkQueue::shared()->count())
1079         g_timeout_add(0, processWork, 0);
1080     else
1081         dump();
1082 }
1083
1084 static void webFrameLoadStatusNotified(WebKitWebFrame* frame, gpointer user_data)
1085 {
1086     WebKitLoadStatus loadStatus = webkit_web_frame_get_load_status(frame);
1087
1088     if (gTestRunner->dumpFrameLoadCallbacks()) {
1089         GOwnPtr<char> frameName(getFrameNameSuitableForTestResult(webkit_web_frame_get_web_view(frame), frame));
1090
1091         switch (loadStatus) {
1092         case WEBKIT_LOAD_PROVISIONAL:
1093             if (!done)
1094                 printf("%s - didStartProvisionalLoadForFrame\n", frameName.get());
1095             break;
1096         case WEBKIT_LOAD_COMMITTED:
1097             if (!done)
1098                 printf("%s - didCommitLoadForFrame\n", frameName.get());
1099             break;
1100         case WEBKIT_LOAD_FINISHED:
1101             if (!done)
1102                 printf("%s - didFinishLoadForFrame\n", frameName.get());
1103             break;
1104         default:
1105             break;
1106         }
1107     }
1108
1109     if ((loadStatus == WEBKIT_LOAD_FINISHED || loadStatus == WEBKIT_LOAD_FAILED)
1110         && frame == topLoadingFrame)
1111         topLoadingFrameLoadFinished();
1112 }
1113
1114 static void frameCreatedCallback(WebKitWebView* webView, WebKitWebFrame* webFrame, gpointer user_data)
1115 {
1116     g_signal_connect(webFrame, "notify::load-status", G_CALLBACK(webFrameLoadStatusNotified), NULL);
1117     g_signal_connect(webFrame, "insecure-content-run", G_CALLBACK(didRunInsecureContent), NULL);
1118 }
1119
1120
1121 static CString pathFromSoupURI(SoupURI* uri)
1122 {
1123     if (!uri)
1124         return CString();
1125
1126     if (g_str_equal(uri->scheme, "http") || g_str_equal(uri->scheme, "ftp")) {
1127         GOwnPtr<char> uriString(soup_uri_to_string(uri, FALSE));
1128         return CString(uriString.get());
1129     }
1130
1131     GOwnPtr<gchar> parentPath(g_path_get_dirname(uri->path));
1132     GOwnPtr<gchar> pathDirname(g_path_get_basename(parentPath.get()));
1133     GOwnPtr<gchar> pathBasename(g_path_get_basename(uri->path));
1134     GOwnPtr<gchar> urlPath(g_strdup_printf("%s/%s", pathDirname.get(), pathBasename.get()));
1135     return CString(urlPath.get());
1136 }
1137
1138 static CString convertSoupMessageToURLPath(SoupMessage* soupMessage)
1139 {
1140     if (!soupMessage)
1141         return CString();
1142     if (SoupURI* requestURI = soup_message_get_uri(soupMessage))
1143         return pathFromSoupURI(requestURI);
1144     return CString();
1145 }
1146
1147 static CString convertNetworkRequestToURLPath(WebKitNetworkRequest* request)
1148 {
1149     return convertSoupMessageToURLPath(webkit_network_request_get_message(request));
1150 }
1151
1152 static CString convertWebResourceToURLPath(WebKitWebResource* webResource)
1153 {
1154     SoupURI* uri = soup_uri_new(webkit_web_resource_get_uri(webResource));
1155     CString urlPath(pathFromSoupURI(uri));
1156     soup_uri_free(uri);
1157     return urlPath;
1158 }
1159
1160 static CString urlSuitableForTestResult(const char* uriString)
1161 {
1162     if (!g_str_has_prefix(uriString, "file://"))
1163         return CString(uriString);
1164
1165     GOwnPtr<gchar> basename(g_path_get_basename(uriString));
1166     return CString(basename.get());
1167 }
1168
1169 static CString descriptionSuitableForTestResult(SoupURI* uri)
1170 {
1171     if (!uri)
1172         return CString("");
1173
1174     GOwnPtr<char> uriString(soup_uri_to_string(uri, false));
1175     return urlSuitableForTestResult(uriString.get());
1176 }
1177
1178 static CString descriptionSuitableForTestResult(WebKitWebView* webView, WebKitWebFrame* webFrame, WebKitWebResource* webResource)
1179 {
1180     SoupURI* uri = soup_uri_new(webkit_web_resource_get_uri(webResource));
1181     CString description;
1182     WebKitWebDataSource* dataSource = webkit_web_frame_get_data_source(webFrame);
1183
1184     if (webResource == webkit_web_data_source_get_main_resource(dataSource)
1185         && (!webkit_web_view_get_progress(webView) || g_str_equal(uri->scheme, "file")))
1186         description = CString("<unknown>");
1187     else
1188         description = convertWebResourceToURLPath(webResource);
1189
1190     if (uri)
1191         soup_uri_free(uri);
1192
1193     return description;
1194 }
1195
1196 static CString descriptionSuitableForTestResult(GError* error, WebKitWebResource* webResource)
1197 {
1198     const gchar* errorDomain = g_quark_to_string(error->domain);
1199     CString resourceURIString(urlSuitableForTestResult(webkit_web_resource_get_uri(webResource)));
1200
1201     if (g_str_equal(errorDomain, "webkit-network-error-quark") || g_str_equal(errorDomain, "soup_http_error_quark"))
1202         errorDomain = "NSURLErrorDomain";
1203
1204     if (g_str_equal(errorDomain, "WebKitPolicyError"))
1205         errorDomain = "WebKitErrorDomain";
1206
1207     // TODO: the other ports get the failingURL from the ResourceError
1208     GOwnPtr<char> errorString(g_strdup_printf("<NSError domain %s, code %d, failing URL \"%s\">",
1209                                               errorDomain, error->code, resourceURIString.data()));
1210     return CString(errorString.get());
1211 }
1212
1213 static CString descriptionSuitableForTestResult(WebKitNetworkRequest* request)
1214 {
1215     SoupMessage* soupMessage = webkit_network_request_get_message(request);
1216
1217     if (!soupMessage)
1218         return CString("");
1219
1220     SoupURI* requestURI = soup_message_get_uri(soupMessage);
1221     SoupURI* mainDocumentURI = soup_message_get_first_party(soupMessage);
1222     CString requestURIString(descriptionSuitableForTestResult(requestURI));
1223     CString mainDocumentURIString(descriptionSuitableForTestResult(mainDocumentURI));
1224     CString path(convertNetworkRequestToURLPath(request));
1225     GOwnPtr<char> description(g_strdup_printf("<NSURLRequest URL %s, main document URL %s, http method %s>",
1226                                               path.data(), mainDocumentURIString.data(),
1227                                               soupMessage ? soupMessage->method : "(none)"));
1228     return CString(description.get());
1229 }
1230
1231 static CString descriptionSuitableForTestResult(WebKitNetworkResponse* response)
1232 {
1233     if (!response)
1234         return CString("(null)");
1235
1236     int statusCode = 0;
1237     CString responseURIString(urlSuitableForTestResult(webkit_network_response_get_uri(response)));
1238     SoupMessage* soupMessage = webkit_network_response_get_message(response);
1239     CString path;
1240
1241     if (soupMessage) {
1242         statusCode = soupMessage->status_code;
1243         path = convertSoupMessageToURLPath(soupMessage);
1244     } else
1245         path = CString("");
1246
1247     GOwnPtr<char> description(g_strdup_printf("<NSURLResponse %s, http status code %d>", path.data(), statusCode));
1248     return CString(description.get());
1249 }
1250
1251 static void willSendRequestCallback(WebKitWebView* webView, WebKitWebFrame* webFrame, WebKitWebResource* resource, WebKitNetworkRequest* request, WebKitNetworkResponse* response)
1252 {
1253
1254
1255     if (!done && gTestRunner->willSendRequestReturnsNull()) {
1256         // As requested by the TestRunner, don't perform the request.
1257         webkit_network_request_set_uri(request, "about:blank");
1258         return;
1259     }
1260
1261     if (!done && gTestRunner->dumpResourceLoadCallbacks())
1262         printf("%s - willSendRequest %s redirectResponse %s\n",
1263                convertNetworkRequestToURLPath(request).data(),
1264                descriptionSuitableForTestResult(request).data(),
1265                descriptionSuitableForTestResult(response).data());
1266
1267     SoupMessage* soupMessage = webkit_network_request_get_message(request);
1268     SoupURI* uri = soup_uri_new(webkit_network_request_get_uri(request));
1269
1270     if (SOUP_URI_VALID_FOR_HTTP(uri) && g_strcmp0(uri->host, "127.0.0.1")
1271         && g_strcmp0(uri->host, "255.255.255.255")
1272         && g_ascii_strncasecmp(uri->host, "localhost", 9)) {
1273         printf("Blocked access to external URL %s\n", soup_uri_to_string(uri, FALSE));
1274         // Cancel load of blocked resource to avoid potential
1275         // network-related timeouts in tests.
1276         webkit_network_request_set_uri(request, "about:blank");
1277         soup_uri_free(uri);
1278         return;
1279     }
1280
1281     if (uri)
1282         soup_uri_free(uri);
1283
1284     if (soupMessage) {
1285         const set<string>& clearHeaders = gTestRunner->willSendRequestClearHeaders();
1286         for (set<string>::const_iterator header = clearHeaders.begin(); header != clearHeaders.end(); ++header)
1287             soup_message_headers_remove(soupMessage->request_headers, header->c_str());
1288     }
1289 }
1290
1291
1292 static void didReceiveResponse(WebKitWebView* webView, WebKitWebFrame*, WebKitWebResource* webResource, WebKitNetworkResponse* response)
1293 {
1294     if (!done && gTestRunner->dumpResourceLoadCallbacks()) {
1295         CString responseDescription(descriptionSuitableForTestResult(response));
1296         CString path(convertWebResourceToURLPath(webResource));
1297         printf("%s - didReceiveResponse %s\n", path.data(), responseDescription.data());
1298     }
1299
1300     // TODO: add "has MIME type" whenever dumpResourceResponseMIMETypes() is supported.
1301     // See https://bugs.webkit.org/show_bug.cgi?id=58222.
1302 }
1303
1304 static void didFinishLoading(WebKitWebView* webView, WebKitWebFrame* webFrame, WebKitWebResource* webResource)
1305 {
1306     if (!done && gTestRunner->dumpResourceLoadCallbacks())
1307         printf("%s - didFinishLoading\n", descriptionSuitableForTestResult(webView, webFrame, webResource).data());
1308 }
1309
1310 static void didFailLoadingWithError(WebKitWebView* webView, WebKitWebFrame* webFrame, WebKitWebResource* webResource, GError* webError)
1311 {
1312     if (!done && gTestRunner->dumpResourceLoadCallbacks()) {
1313         CString webErrorString(descriptionSuitableForTestResult(webError, webResource));
1314         printf("%s - didFailLoadingWithError: %s\n", descriptionSuitableForTestResult(webView, webFrame, webResource).data(),
1315                webErrorString.data());
1316     }
1317 }
1318
1319 static void didRunInsecureContent(WebKitWebFrame*, WebKitSecurityOrigin*, const char* url)
1320 {
1321     if (!done && gTestRunner->dumpFrameLoadCallbacks())
1322         printf("didRunInsecureContent\n");
1323 }
1324
1325 static gboolean webViewRunFileChooser(WebKitWebView*, WebKitFileChooserRequest*)
1326 {
1327     // We return TRUE to not propagate the event further so the
1328     // default file chooser dialog is not shown.
1329     return TRUE;
1330 }
1331
1332 static WebKitWebView* createWebView()
1333 {
1334     // It is important to declare DRT is running early so when creating
1335     // web view mock clients are used instead of proper ones.
1336     DumpRenderTreeSupportGtk::setDumpRenderTreeModeEnabled(true);
1337
1338     WebKitWebView* view = WEBKIT_WEB_VIEW(self_scrolling_webkit_web_view_new());
1339
1340     g_object_connect(G_OBJECT(view),
1341                      "signal::load-started", webViewLoadStarted, 0,
1342                      "signal::load-finished", webViewLoadFinished, 0,
1343                      "signal::load-error", webViewLoadError, 0,
1344                      "signal::window-object-cleared", webViewWindowObjectCleared, 0,
1345                      "signal::console-message", webViewConsoleMessage, 0,
1346                      "signal::script-alert", webViewScriptAlert, 0,
1347                      "signal::script-prompt", webViewScriptPrompt, 0,
1348                      "signal::script-confirm", webViewScriptConfirm, 0,
1349                      "signal::title-changed", webViewTitleChanged, 0,
1350                      "signal::navigation-policy-decision-requested", webViewNavigationPolicyDecisionRequested, 0,
1351                      "signal::status-bar-text-changed", webViewStatusBarTextChanged, 0,
1352                      "signal::create-web-view", webViewCreate, 0,
1353                      "signal::close-web-view", webViewClose, 0,
1354                      "signal::database-quota-exceeded", databaseQuotaExceeded, 0,
1355                      "signal::document-load-finished", webViewDocumentLoadFinished, 0,
1356                      "signal::geolocation-policy-decision-requested", geolocationPolicyDecisionRequested, 0,
1357                      "signal::onload-event", webViewOnloadEvent, 0,
1358                      "signal::drag-begin", dragBeginCallback, 0,
1359                      "signal::drag-end", dragEndCallback, 0,
1360                      "signal::drag-failed", dragFailedCallback, 0,
1361                      "signal::frame-created", frameCreatedCallback, 0,
1362                      "signal::resource-request-starting", willSendRequestCallback, 0,
1363                      "signal::resource-response-received", didReceiveResponse, 0,
1364                      "signal::resource-load-finished", didFinishLoading, 0,
1365                      "signal::resource-load-failed", didFailLoadingWithError, 0,
1366                      "signal::run-file-chooser", webViewRunFileChooser, 0,
1367                      NULL);
1368     connectEditingCallbacks(view);
1369
1370     WebKitWebInspector* inspector = webkit_web_view_get_inspector(view);
1371     g_object_connect(G_OBJECT(inspector),
1372                      "signal::inspect-web-view", webInspectorInspectWebView, 0,
1373                      "signal::show-window", webInspectorShowWindow, 0,
1374                      "signal::close-window", webInspectorCloseWindow, 0,
1375                      NULL);
1376
1377     if (webView) {
1378         WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
1379         webkit_web_view_set_settings(view, settings);
1380     }
1381
1382     // frame-created is not issued for main frame. That's why we must do this here
1383     WebKitWebFrame* frame = webkit_web_view_get_main_frame(view);
1384     g_signal_connect(frame, "notify::load-status", G_CALLBACK(webFrameLoadStatusNotified), NULL);
1385     g_signal_connect(frame, "insecure-content-run", G_CALLBACK(didRunInsecureContent), NULL);
1386
1387     return view;
1388 }
1389
1390 static WebKitWebView* webViewCreate(WebKitWebView* view, WebKitWebFrame* frame)
1391 {
1392     if (!gTestRunner->canOpenWindows())
1393         return 0;
1394
1395     // Make sure that waitUntilDone has been called.
1396     ASSERT(gTestRunner->waitToDump());
1397
1398     WebKitWebView* newWebView = createWebView();
1399     g_object_ref_sink(G_OBJECT(newWebView));
1400     webViewList = g_slist_prepend(webViewList, newWebView);
1401     return newWebView;
1402 }
1403
1404 static void logHandler(const gchar* domain, GLogLevelFlags level, const gchar* message, gpointer data)
1405 {
1406     if (level < G_LOG_LEVEL_DEBUG)
1407         fprintf(stderr, "%s\n", message);
1408 }
1409
1410 int main(int argc, char* argv[])
1411 {
1412     gtk_init(&argc, &argv);
1413
1414     // Some plugins might try to use the GLib logger for printing debug
1415     // messages. This will cause tests to fail because of unexpected output.
1416     // We squelch all debug messages sent to the logger.
1417     g_log_set_default_handler(logHandler, 0);
1418
1419     initializeGlobalsFromCommandLineOptions(argc, argv);
1420     initializeFonts();
1421
1422     window = gtk_window_new(GTK_WINDOW_POPUP);
1423 #ifdef GTK_API_VERSION_2
1424     container = gtk_hbox_new(TRUE, 0);
1425 #else
1426     container = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1427     gtk_box_set_homogeneous(GTK_BOX(container), TRUE);
1428 #endif
1429     gtk_container_add(GTK_CONTAINER(window), container);
1430     gtk_widget_show_all(window);
1431
1432     webView = createWebView();
1433     gtk_box_pack_start(GTK_BOX(container), GTK_WIDGET(webView), TRUE, TRUE, 0);
1434     gtk_widget_realize(GTK_WIDGET(webView));
1435     gtk_widget_show_all(container);
1436     mainFrame = webkit_web_view_get_main_frame(webView);
1437
1438     setDefaultsToConsistentStateValuesForTesting();
1439
1440     gcController = new GCController();
1441     axController = new AccessibilityController();
1442
1443     if (useLongRunningServerMode(argc, argv)) {
1444         printSeparators = true;
1445         runTestingServerLoop();
1446     } else {
1447         printSeparators = (optind < argc-1 || (dumpPixelsForCurrentTest && dumpTree));
1448         for (int i = optind; i != argc; ++i)
1449             runTest(argv[i]);
1450     }
1451
1452     delete gcController;
1453     gcController = 0;
1454
1455     delete axController;
1456     axController = 0;
1457
1458     gtk_widget_destroy(window);
1459
1460     return 0;
1461 }