Use unsigned char for bitfield instead of unsigned.
[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->dumpTitleChanges() && !done)
947         printf("TITLE CHANGED: '%s'\n", title ? title : "");
948 }
949
950 static bool webViewNavigationPolicyDecisionRequested(WebKitWebView* view, WebKitWebFrame* frame,
951                                                      WebKitNetworkRequest* request,
952                                                      WebKitWebNavigationAction* navAction,
953                                                      WebKitWebPolicyDecision* policyDecision)
954 {
955     // Use the default handler if we're not waiting for policy,
956     // i.e., TestRunner::waitForPolicyDelegate
957     if (!waitForPolicy)
958         return FALSE;
959
960     gchar* typeDescription;
961     WebKitWebNavigationReason reason;
962     g_object_get(G_OBJECT(navAction), "reason", &reason, NULL);
963
964     switch(reason) {
965         case WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED:
966             typeDescription = g_strdup("link clicked");
967             break;
968         case WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED:
969             typeDescription = g_strdup("form submitted");
970             break;
971         case WEBKIT_WEB_NAVIGATION_REASON_BACK_FORWARD:
972             typeDescription = g_strdup("back/forward");
973             break;
974         case WEBKIT_WEB_NAVIGATION_REASON_RELOAD:
975             typeDescription = g_strdup("reload");
976             break;
977         case WEBKIT_WEB_NAVIGATION_REASON_FORM_RESUBMITTED:
978             typeDescription = g_strdup("form resubmitted");
979             break;
980         case WEBKIT_WEB_NAVIGATION_REASON_OTHER:
981             typeDescription = g_strdup("other");
982             break;
983         default:
984             typeDescription = g_strdup("illegal value");
985     }
986
987     printf("Policy delegate: attempt to load %s with navigation type '%s'\n", webkit_network_request_get_uri(request), typeDescription);
988     g_free(typeDescription);
989
990     webkit_web_policy_decision_ignore(policyDecision);
991     gTestRunner->notifyDone();
992
993     return TRUE;
994 }
995
996 static void webViewStatusBarTextChanged(WebKitWebView* view, const gchar* message, gpointer data)
997 {
998     // Are we doing anything wrong? One test that does not call
999     // dumpStatusCallbacks gets true here
1000     if (gTestRunner->dumpStatusCallbacks())
1001         printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", message);
1002 }
1003
1004 static gboolean webViewClose(WebKitWebView* view)
1005 {
1006     ASSERT(view);
1007
1008     webViewList = g_slist_remove(webViewList, view);
1009     g_object_unref(view);
1010
1011     return TRUE;
1012 }
1013
1014 static void databaseQuotaExceeded(WebKitWebView* view, WebKitWebFrame* frame, WebKitWebDatabase *database)
1015 {
1016     ASSERT(view);
1017     ASSERT(frame);
1018     ASSERT(database);
1019
1020     WebKitSecurityOrigin* origin = webkit_web_database_get_security_origin(database);
1021     if (gTestRunner->dumpDatabaseCallbacks()) {
1022         printf("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{%s, %s, %i} database:%s\n",
1023             webkit_security_origin_get_protocol(origin),
1024             webkit_security_origin_get_host(origin),
1025             webkit_security_origin_get_port(origin),
1026             webkit_web_database_get_name(database));
1027     }
1028     webkit_security_origin_set_web_database_quota(origin, 5 * 1024 * 1024);
1029 }
1030
1031 static bool
1032 geolocationPolicyDecisionRequested(WebKitWebView*, WebKitWebFrame*, WebKitGeolocationPolicyDecision* decision)
1033 {
1034     if (!gTestRunner->isGeolocationPermissionSet())
1035         return FALSE;
1036     if (gTestRunner->geolocationPermission())
1037         webkit_geolocation_policy_allow(decision);
1038     else
1039         webkit_geolocation_policy_deny(decision);
1040
1041     return TRUE;
1042 }
1043
1044
1045 static WebKitWebView* webViewCreate(WebKitWebView*, WebKitWebFrame*);
1046
1047 static gboolean webInspectorShowWindow(WebKitWebInspector*, gpointer data)
1048 {
1049     gtk_window_set_default_size(GTK_WINDOW(webInspectorWindow), 800, 600);
1050     gtk_widget_show_all(webInspectorWindow);
1051     return TRUE;
1052 }
1053
1054 static gboolean webInspectorCloseWindow(WebKitWebInspector*, gpointer data)
1055 {
1056     gtk_widget_destroy(webInspectorWindow);
1057     webInspectorWindow = 0;
1058     return TRUE;
1059 }
1060
1061 static WebKitWebView* webInspectorInspectWebView(WebKitWebInspector*, gpointer data)
1062 {
1063     webInspectorWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1064
1065     GtkWidget* webView = self_scrolling_webkit_web_view_new();
1066     gtk_container_add(GTK_CONTAINER(webInspectorWindow),
1067                       webView);
1068
1069     return WEBKIT_WEB_VIEW(webView);
1070 }
1071
1072 static void topLoadingFrameLoadFinished()
1073 {
1074     topLoadingFrame = 0;
1075     WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
1076     if (gTestRunner->waitToDump())
1077         return;
1078
1079     if (WorkQueue::shared()->count())
1080         g_timeout_add(0, processWork, 0);
1081     else
1082         dump();
1083 }
1084
1085 static void webFrameLoadStatusNotified(WebKitWebFrame* frame, gpointer user_data)
1086 {
1087     WebKitLoadStatus loadStatus = webkit_web_frame_get_load_status(frame);
1088
1089     if (gTestRunner->dumpFrameLoadCallbacks()) {
1090         GOwnPtr<char> frameName(getFrameNameSuitableForTestResult(webkit_web_frame_get_web_view(frame), frame));
1091
1092         switch (loadStatus) {
1093         case WEBKIT_LOAD_PROVISIONAL:
1094             if (!done)
1095                 printf("%s - didStartProvisionalLoadForFrame\n", frameName.get());
1096             break;
1097         case WEBKIT_LOAD_COMMITTED:
1098             if (!done)
1099                 printf("%s - didCommitLoadForFrame\n", frameName.get());
1100             break;
1101         case WEBKIT_LOAD_FINISHED:
1102             if (!done)
1103                 printf("%s - didFinishLoadForFrame\n", frameName.get());
1104             break;
1105         default:
1106             break;
1107         }
1108     }
1109
1110     if ((loadStatus == WEBKIT_LOAD_FINISHED || loadStatus == WEBKIT_LOAD_FAILED)
1111         && frame == topLoadingFrame)
1112         topLoadingFrameLoadFinished();
1113 }
1114
1115 static void frameCreatedCallback(WebKitWebView* webView, WebKitWebFrame* webFrame, gpointer user_data)
1116 {
1117     g_signal_connect(webFrame, "notify::load-status", G_CALLBACK(webFrameLoadStatusNotified), NULL);
1118     g_signal_connect(webFrame, "insecure-content-run", G_CALLBACK(didRunInsecureContent), NULL);
1119 }
1120
1121
1122 static CString pathFromSoupURI(SoupURI* uri)
1123 {
1124     if (!uri)
1125         return CString();
1126
1127     if (g_str_equal(uri->scheme, "http") || g_str_equal(uri->scheme, "ftp")) {
1128         GOwnPtr<char> uriString(soup_uri_to_string(uri, FALSE));
1129         return CString(uriString.get());
1130     }
1131
1132     GOwnPtr<gchar> parentPath(g_path_get_dirname(uri->path));
1133     GOwnPtr<gchar> pathDirname(g_path_get_basename(parentPath.get()));
1134     GOwnPtr<gchar> pathBasename(g_path_get_basename(uri->path));
1135     GOwnPtr<gchar> urlPath(g_strdup_printf("%s/%s", pathDirname.get(), pathBasename.get()));
1136     return CString(urlPath.get());
1137 }
1138
1139 static CString convertSoupMessageToURLPath(SoupMessage* soupMessage)
1140 {
1141     if (!soupMessage)
1142         return CString();
1143     if (SoupURI* requestURI = soup_message_get_uri(soupMessage))
1144         return pathFromSoupURI(requestURI);
1145     return CString();
1146 }
1147
1148 static CString convertNetworkRequestToURLPath(WebKitNetworkRequest* request)
1149 {
1150     return convertSoupMessageToURLPath(webkit_network_request_get_message(request));
1151 }
1152
1153 static CString convertWebResourceToURLPath(WebKitWebResource* webResource)
1154 {
1155     SoupURI* uri = soup_uri_new(webkit_web_resource_get_uri(webResource));
1156     CString urlPath(pathFromSoupURI(uri));
1157     soup_uri_free(uri);
1158     return urlPath;
1159 }
1160
1161 static CString urlSuitableForTestResult(const char* uriString)
1162 {
1163     if (!g_str_has_prefix(uriString, "file://"))
1164         return CString(uriString);
1165
1166     GOwnPtr<gchar> basename(g_path_get_basename(uriString));
1167     return CString(basename.get());
1168 }
1169
1170 static CString descriptionSuitableForTestResult(SoupURI* uri)
1171 {
1172     if (!uri)
1173         return CString("");
1174
1175     GOwnPtr<char> uriString(soup_uri_to_string(uri, false));
1176     return urlSuitableForTestResult(uriString.get());
1177 }
1178
1179 static CString descriptionSuitableForTestResult(WebKitWebView* webView, WebKitWebFrame* webFrame, WebKitWebResource* webResource)
1180 {
1181     SoupURI* uri = soup_uri_new(webkit_web_resource_get_uri(webResource));
1182     CString description;
1183     WebKitWebDataSource* dataSource = webkit_web_frame_get_data_source(webFrame);
1184
1185     if (webResource == webkit_web_data_source_get_main_resource(dataSource)
1186         && (!webkit_web_view_get_progress(webView) || g_str_equal(uri->scheme, "file")))
1187         description = CString("<unknown>");
1188     else
1189         description = convertWebResourceToURLPath(webResource);
1190
1191     if (uri)
1192         soup_uri_free(uri);
1193
1194     return description;
1195 }
1196
1197 static CString descriptionSuitableForTestResult(GError* error, WebKitWebResource* webResource)
1198 {
1199     const gchar* errorDomain = g_quark_to_string(error->domain);
1200     CString resourceURIString(urlSuitableForTestResult(webkit_web_resource_get_uri(webResource)));
1201
1202     if (g_str_equal(errorDomain, "webkit-network-error-quark") || g_str_equal(errorDomain, "soup_http_error_quark"))
1203         errorDomain = "NSURLErrorDomain";
1204
1205     if (g_str_equal(errorDomain, "WebKitPolicyError"))
1206         errorDomain = "WebKitErrorDomain";
1207
1208     // TODO: the other ports get the failingURL from the ResourceError
1209     GOwnPtr<char> errorString(g_strdup_printf("<NSError domain %s, code %d, failing URL \"%s\">",
1210                                               errorDomain, error->code, resourceURIString.data()));
1211     return CString(errorString.get());
1212 }
1213
1214 static CString descriptionSuitableForTestResult(WebKitNetworkRequest* request)
1215 {
1216     SoupMessage* soupMessage = webkit_network_request_get_message(request);
1217
1218     if (!soupMessage)
1219         return CString("");
1220
1221     SoupURI* requestURI = soup_message_get_uri(soupMessage);
1222     SoupURI* mainDocumentURI = soup_message_get_first_party(soupMessage);
1223     CString requestURIString(descriptionSuitableForTestResult(requestURI));
1224     CString mainDocumentURIString(descriptionSuitableForTestResult(mainDocumentURI));
1225     CString path(convertNetworkRequestToURLPath(request));
1226     GOwnPtr<char> description(g_strdup_printf("<NSURLRequest URL %s, main document URL %s, http method %s>",
1227                                               path.data(), mainDocumentURIString.data(),
1228                                               soupMessage ? soupMessage->method : "(none)"));
1229     return CString(description.get());
1230 }
1231
1232 static CString descriptionSuitableForTestResult(WebKitNetworkResponse* response)
1233 {
1234     if (!response)
1235         return CString("(null)");
1236
1237     int statusCode = 0;
1238     CString responseURIString(urlSuitableForTestResult(webkit_network_response_get_uri(response)));
1239     SoupMessage* soupMessage = webkit_network_response_get_message(response);
1240     CString path;
1241
1242     if (soupMessage) {
1243         statusCode = soupMessage->status_code;
1244         path = convertSoupMessageToURLPath(soupMessage);
1245     } else
1246         path = CString("");
1247
1248     GOwnPtr<char> description(g_strdup_printf("<NSURLResponse %s, http status code %d>", path.data(), statusCode));
1249     return CString(description.get());
1250 }
1251
1252 static void willSendRequestCallback(WebKitWebView* webView, WebKitWebFrame* webFrame, WebKitWebResource* resource, WebKitNetworkRequest* request, WebKitNetworkResponse* response)
1253 {
1254
1255
1256     if (!done && gTestRunner->willSendRequestReturnsNull()) {
1257         // As requested by the TestRunner, don't perform the request.
1258         webkit_network_request_set_uri(request, "about:blank");
1259         return;
1260     }
1261
1262     if (!done && gTestRunner->dumpResourceLoadCallbacks())
1263         printf("%s - willSendRequest %s redirectResponse %s\n",
1264                convertNetworkRequestToURLPath(request).data(),
1265                descriptionSuitableForTestResult(request).data(),
1266                descriptionSuitableForTestResult(response).data());
1267
1268     SoupMessage* soupMessage = webkit_network_request_get_message(request);
1269     SoupURI* uri = soup_uri_new(webkit_network_request_get_uri(request));
1270
1271     if (SOUP_URI_VALID_FOR_HTTP(uri) && g_strcmp0(uri->host, "127.0.0.1")
1272         && g_strcmp0(uri->host, "255.255.255.255")
1273         && g_ascii_strncasecmp(uri->host, "localhost", 9)) {
1274         printf("Blocked access to external URL %s\n", soup_uri_to_string(uri, FALSE));
1275         // Cancel load of blocked resource to avoid potential
1276         // network-related timeouts in tests.
1277         webkit_network_request_set_uri(request, "about:blank");
1278         soup_uri_free(uri);
1279         return;
1280     }
1281
1282     if (uri)
1283         soup_uri_free(uri);
1284
1285     if (soupMessage) {
1286         const set<string>& clearHeaders = gTestRunner->willSendRequestClearHeaders();
1287         for (set<string>::const_iterator header = clearHeaders.begin(); header != clearHeaders.end(); ++header)
1288             soup_message_headers_remove(soupMessage->request_headers, header->c_str());
1289     }
1290 }
1291
1292
1293 static void didReceiveResponse(WebKitWebView* webView, WebKitWebFrame*, WebKitWebResource* webResource, WebKitNetworkResponse* response)
1294 {
1295     if (!done && gTestRunner->dumpResourceLoadCallbacks()) {
1296         CString responseDescription(descriptionSuitableForTestResult(response));
1297         CString path(convertWebResourceToURLPath(webResource));
1298         printf("%s - didReceiveResponse %s\n", path.data(), responseDescription.data());
1299     }
1300
1301     // TODO: add "has MIME type" whenever dumpResourceResponseMIMETypes() is supported.
1302     // See https://bugs.webkit.org/show_bug.cgi?id=58222.
1303 }
1304
1305 static void didFinishLoading(WebKitWebView* webView, WebKitWebFrame* webFrame, WebKitWebResource* webResource)
1306 {
1307     if (!done && gTestRunner->dumpResourceLoadCallbacks())
1308         printf("%s - didFinishLoading\n", descriptionSuitableForTestResult(webView, webFrame, webResource).data());
1309 }
1310
1311 static void didFailLoadingWithError(WebKitWebView* webView, WebKitWebFrame* webFrame, WebKitWebResource* webResource, GError* webError)
1312 {
1313     if (!done && gTestRunner->dumpResourceLoadCallbacks()) {
1314         CString webErrorString(descriptionSuitableForTestResult(webError, webResource));
1315         printf("%s - didFailLoadingWithError: %s\n", descriptionSuitableForTestResult(webView, webFrame, webResource).data(),
1316                webErrorString.data());
1317     }
1318 }
1319
1320 static void didRunInsecureContent(WebKitWebFrame*, WebKitSecurityOrigin*, const char* url)
1321 {
1322     if (!done && gTestRunner->dumpFrameLoadCallbacks())
1323         printf("didRunInsecureContent\n");
1324 }
1325
1326 static gboolean webViewRunFileChooser(WebKitWebView*, WebKitFileChooserRequest*)
1327 {
1328     // We return TRUE to not propagate the event further so the
1329     // default file chooser dialog is not shown.
1330     return TRUE;
1331 }
1332
1333 static WebKitWebView* createWebView()
1334 {
1335     // It is important to declare DRT is running early so when creating
1336     // web view mock clients are used instead of proper ones.
1337     DumpRenderTreeSupportGtk::setDumpRenderTreeModeEnabled(true);
1338
1339     WebKitWebView* view = WEBKIT_WEB_VIEW(self_scrolling_webkit_web_view_new());
1340
1341     g_object_connect(G_OBJECT(view),
1342                      "signal::load-started", webViewLoadStarted, 0,
1343                      "signal::load-finished", webViewLoadFinished, 0,
1344                      "signal::load-error", webViewLoadError, 0,
1345                      "signal::window-object-cleared", webViewWindowObjectCleared, 0,
1346                      "signal::console-message", webViewConsoleMessage, 0,
1347                      "signal::script-alert", webViewScriptAlert, 0,
1348                      "signal::script-prompt", webViewScriptPrompt, 0,
1349                      "signal::script-confirm", webViewScriptConfirm, 0,
1350                      "signal::title-changed", webViewTitleChanged, 0,
1351                      "signal::navigation-policy-decision-requested", webViewNavigationPolicyDecisionRequested, 0,
1352                      "signal::status-bar-text-changed", webViewStatusBarTextChanged, 0,
1353                      "signal::create-web-view", webViewCreate, 0,
1354                      "signal::close-web-view", webViewClose, 0,
1355                      "signal::database-quota-exceeded", databaseQuotaExceeded, 0,
1356                      "signal::document-load-finished", webViewDocumentLoadFinished, 0,
1357                      "signal::geolocation-policy-decision-requested", geolocationPolicyDecisionRequested, 0,
1358                      "signal::onload-event", webViewOnloadEvent, 0,
1359                      "signal::drag-begin", dragBeginCallback, 0,
1360                      "signal::drag-end", dragEndCallback, 0,
1361                      "signal::drag-failed", dragFailedCallback, 0,
1362                      "signal::frame-created", frameCreatedCallback, 0,
1363                      "signal::resource-request-starting", willSendRequestCallback, 0,
1364                      "signal::resource-response-received", didReceiveResponse, 0,
1365                      "signal::resource-load-finished", didFinishLoading, 0,
1366                      "signal::resource-load-failed", didFailLoadingWithError, 0,
1367                      "signal::run-file-chooser", webViewRunFileChooser, 0,
1368                      NULL);
1369     connectEditingCallbacks(view);
1370
1371     WebKitWebInspector* inspector = webkit_web_view_get_inspector(view);
1372     g_object_connect(G_OBJECT(inspector),
1373                      "signal::inspect-web-view", webInspectorInspectWebView, 0,
1374                      "signal::show-window", webInspectorShowWindow, 0,
1375                      "signal::close-window", webInspectorCloseWindow, 0,
1376                      NULL);
1377
1378     if (webView) {
1379         WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
1380         webkit_web_view_set_settings(view, settings);
1381     }
1382
1383     // frame-created is not issued for main frame. That's why we must do this here
1384     WebKitWebFrame* frame = webkit_web_view_get_main_frame(view);
1385     g_signal_connect(frame, "notify::load-status", G_CALLBACK(webFrameLoadStatusNotified), NULL);
1386     g_signal_connect(frame, "insecure-content-run", G_CALLBACK(didRunInsecureContent), NULL);
1387
1388     return view;
1389 }
1390
1391 static WebKitWebView* webViewCreate(WebKitWebView* view, WebKitWebFrame* frame)
1392 {
1393     if (!gTestRunner->canOpenWindows())
1394         return 0;
1395
1396     // Make sure that waitUntilDone has been called.
1397     ASSERT(gTestRunner->waitToDump());
1398
1399     WebKitWebView* newWebView = createWebView();
1400     g_object_ref_sink(G_OBJECT(newWebView));
1401     webViewList = g_slist_prepend(webViewList, newWebView);
1402     return newWebView;
1403 }
1404
1405 static void logHandler(const gchar* domain, GLogLevelFlags level, const gchar* message, gpointer data)
1406 {
1407     if (level < G_LOG_LEVEL_DEBUG)
1408         fprintf(stderr, "%s\n", message);
1409 }
1410
1411 int main(int argc, char* argv[])
1412 {
1413     gtk_init(&argc, &argv);
1414
1415     // Some plugins might try to use the GLib logger for printing debug
1416     // messages. This will cause tests to fail because of unexpected output.
1417     // We squelch all debug messages sent to the logger.
1418     g_log_set_default_handler(logHandler, 0);
1419
1420     initializeGlobalsFromCommandLineOptions(argc, argv);
1421     initializeFonts();
1422
1423     window = gtk_window_new(GTK_WINDOW_POPUP);
1424 #ifdef GTK_API_VERSION_2
1425     container = gtk_hbox_new(TRUE, 0);
1426 #else
1427     container = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1428     gtk_box_set_homogeneous(GTK_BOX(container), TRUE);
1429 #endif
1430     gtk_container_add(GTK_CONTAINER(window), container);
1431     gtk_widget_show_all(window);
1432
1433     webView = createWebView();
1434     gtk_box_pack_start(GTK_BOX(container), GTK_WIDGET(webView), TRUE, TRUE, 0);
1435     gtk_widget_realize(GTK_WIDGET(webView));
1436     gtk_widget_show_all(container);
1437     mainFrame = webkit_web_view_get_main_frame(webView);
1438
1439     setDefaultsToConsistentStateValuesForTesting();
1440
1441     gcController = new GCController();
1442     axController = new AccessibilityController();
1443
1444     if (useLongRunningServerMode(argc, argv)) {
1445         printSeparators = true;
1446         runTestingServerLoop();
1447     } else {
1448         printSeparators = (optind < argc-1 || (dumpPixelsForCurrentTest && dumpTree));
1449         for (int i = optind; i != argc; ++i)
1450             runTest(argv[i]);
1451     }
1452
1453     delete gcController;
1454     gcController = 0;
1455
1456     delete axController;
1457     axController = 0;
1458
1459     gtk_widget_destroy(window);
1460
1461     return 0;
1462 }