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 "GOwnPtr.h"
40#include "LayoutTestController.h"
41#include "PixelDumpSupport.h"
42#include "PlainTextController.h"
43#include "TextInputController.h"
44#include "WebCoreSupport/DumpRenderTreeSupportGtk.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
56#if PLATFORM(X11)
57#include <fontconfig/fontconfig.h>
58#endif
59
60
61using namespace std;
62
63extern "C" {
64// This API is not yet public.
65extern G_CONST_RETURN gchar* webkit_web_history_item_get_target(WebKitWebHistoryItem*);
66extern gboolean webkit_web_history_item_is_target_item(WebKitWebHistoryItem*);
67extern GList* webkit_web_history_item_get_children(WebKitWebHistoryItem*);
68extern void webkit_web_settings_add_extra_plugin_directory(WebKitWebView* view, const gchar* directory);
69extern gchar* webkit_web_frame_get_response_mime_type(WebKitWebFrame* frame);
70}
71
72volatile bool done;
73static bool printSeparators;
74static int dumpPixels;
75static int dumpTree = 1;
76
77AccessibilityController* axController = 0;
78RefPtr<LayoutTestController> gLayoutTestController;
79static GCController* gcController = 0;
80static WebKitWebView* webView;
81static GtkWidget* window;
82static GtkWidget* container;
83static GtkWidget* webInspectorWindow;
84WebKitWebFrame* mainFrame = 0;
85WebKitWebFrame* topLoadingFrame = 0;
86guint waitToDumpWatchdog = 0;
87bool waitForPolicy = false;
88
89// This is a list of opened webviews
90GSList* webViewList = 0;
91
92// current b/f item at the end of the previous test
93static WebKitWebHistoryItem* prevTestBFItem = NULL;
94
95const unsigned historyItemIndent = 8;
96
97static void runTest(const string& testPathOrURL);
98
99static bool shouldLogFrameLoadDelegates(const string& pathOrURL)
100{
101    return pathOrURL.find("loading/") != string::npos;
102}
103
104static bool shouldOpenWebInspector(const string& pathOrURL)
105{
106    return pathOrURL.find("inspector/") != string::npos;
107}
108
109static bool shouldDumpAsText(const string& pathOrURL)
110{
111    return pathOrURL.find("dumpAsText/") != string::npos;
112}
113
114static bool shouldEnableDeveloperExtras(const string& pathOrURL)
115{
116    return true;
117}
118
119void dumpFrameScrollPosition(WebKitWebFrame* frame)
120{
121
122}
123
124void displayWebView()
125{
126    gtk_widget_queue_draw(GTK_WIDGET(webView));
127}
128
129static void appendString(gchar*& target, gchar* string)
130{
131    gchar* oldString = target;
132    target = g_strconcat(target, string, NULL);
133    g_free(oldString);
134}
135
136static void initializeGtkFontSettings(const char* testURL)
137{
138    GtkSettings* settings = gtk_settings_get_default();
139    if (!settings)
140        return;
141    g_object_set(settings,
142                 "gtk-xft-dpi", 98304, // This is 96 * 1024 or 96 DPI according to the GTK+ docs.
143                 "gtk-xft-antialias", 1,
144                 "gtk-xft-hinting", 0,
145                 "gtk-font-name", "Liberation Sans 12",
146                 NULL);
147    gdk_screen_set_resolution(gdk_screen_get_default(), 96.0);
148
149    // One test needs subpixel anti-aliasing turned on, but generally we
150    // want all text in other tests to use to grayscale anti-aliasing.
151    if (testURL && strstr(testURL, "xsettings_antialias_settings.html"))
152        g_object_set(settings, "gtk-xft-rgba", "rgb", NULL);
153    else
154        g_object_set(settings, "gtk-xft-rgba", "none", NULL);
155}
156
157static void initializeFonts(const char* testURL = 0)
158{
159#if PLATFORM(X11)
160    initializeGtkFontSettings(testURL);
161
162    FcInit();
163
164    // If a test resulted a font being added or removed via the @font-face rule, then
165    // we want to reset the FontConfig configuration to prevent it from affecting other tests.
166    static int numFonts = 0;
167    FcFontSet* appFontSet = FcConfigGetFonts(0, FcSetApplication);
168    if (appFontSet && numFonts && appFontSet->nfont == numFonts)
169        return;
170
171    // Load our configuration file, which sets up proper aliases for family
172    // names like sans, serif and monospace.
173    FcConfig* config = FcConfigCreate();
174    GOwnPtr<gchar> fontConfigFilename(g_build_filename(FONTS_CONF_DIR, "fonts.conf", NULL));
175    if (!FcConfigParseAndLoad(config, reinterpret_cast<FcChar8*>(fontConfigFilename.get()), true))
176        g_error("Couldn't load font configuration file from: %s", fontConfigFilename.get());
177
178    static const char *const fontPaths[][2] = {
179        { "/usr/share/fonts/truetype/ttf-liberation/LiberationMono-BoldItalic.ttf",
180          "/usr/share/fonts/liberation/LiberationMono-BoldItalic.ttf", },
181        { "/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Bold.ttf",
182          "/usr/share/fonts/liberation/LiberationMono-Bold.ttf", },
183        { "/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Italic.ttf",
184          "/usr/share/fonts/liberation/LiberationMono-Italic.ttf", },
185        { "/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Regular.ttf",
186          "/usr/share/fonts/liberation/LiberationMono-Regular.ttf", },
187        { "/usr/share/fonts/truetype/ttf-liberation/LiberationSans-BoldItalic.ttf",
188          "/usr/share/fonts/liberation/LiberationSans-BoldItalic.ttf", },
189        { "/usr/share/fonts/truetype/ttf-liberation/LiberationSans-Bold.ttf",
190          "/usr/share/fonts/liberation/LiberationSans-Bold.ttf", },
191        { "/usr/share/fonts/truetype/ttf-liberation/LiberationSans-Italic.ttf",
192          "/usr/share/fonts/liberation/LiberationSans-Italic.ttf", },
193        { "/usr/share/fonts/truetype/ttf-liberation/LiberationSans-Regular.ttf",
194          "/usr/share/fonts/liberation/LiberationSans-Regular.ttf", },
195        { "/usr/share/fonts/truetype/ttf-liberation/LiberationSerif-BoldItalic.ttf",
196          "/usr/share/fonts/liberation/LiberationSerif-BoldItalic.ttf", },
197        { "/usr/share/fonts/truetype/ttf-liberation/LiberationSerif-Bold.ttf",
198          "/usr/share/fonts/liberation/LiberationSerif-Bold.ttf", },
199        { "/usr/share/fonts/truetype/ttf-liberation/LiberationSerif-Italic.ttf",
200          "/usr/share/fonts/liberation/LiberationSerif-Italic.ttf", },
201        { "/usr/share/fonts/truetype/ttf-liberation/LiberationSerif-Regular.ttf",
202          "/usr/share/fonts/liberation/LiberationSerif-Regular.ttf", },
203        { "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf",
204          "/usr/share/fonts/dejavu/DejaVuSans.ttf", },
205        { "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerif.ttf",
206          "/usr/share/fonts/dejavu/DejaVuSerif.ttf", },
207
208        // MathML tests require the STIX fonts.
209        { "/usr/share/fonts/opentype/stix/STIXGeneral.otf",
210          "/usr/share/fonts/stix/STIXGeneral.otf" },
211        { "/usr/share/fonts/opentype/stix/STIXGeneralBolIta.otf",
212          "/usr/share/fonts/stix/STIXGeneralBolIta.otf" },
213        { "/usr/share/fonts/opentype/stix/STIXGeneralBol.otf",
214          "/usr/share/fonts/stix/STIXGeneralBol.otf" },
215        { "/usr/share/fonts/opentype/stix/STIXGeneralItalic.otf",
216          "/usr/share/fonts/stix/STIXGeneralItalic.otf" }
217    };
218
219    // TODO: Some tests use Lucida. We should load these as well, once it becomes
220    // clear how to install these fonts easily on Fedora.
221    for (size_t font = 0; font < G_N_ELEMENTS(fontPaths); font++) {
222        bool found = false;
223        for (size_t path = 0; path < 2; path++) {
224
225            if (g_file_test(fontPaths[font][path], G_FILE_TEST_EXISTS)) {
226                found = true;
227                if (!FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(fontPaths[font][path])))
228                    g_error("Could not load font at %s!", fontPaths[font][path]);
229                else
230                    break;
231            }
232        }
233
234        if (!found)
235            g_error("Could not find font at %s. Either install this font or file a bug "
236                    "at http://bugs.webkit.org if it is installed in another location.",
237                    fontPaths[font][0]);
238    }
239
240    // Ahem is used by many layout tests.
241    GOwnPtr<gchar> ahemFontFilename(g_build_filename(FONTS_CONF_DIR, "AHEM____.TTF", NULL));
242    if (!FcConfigAppFontAddFile(config, reinterpret_cast<FcChar8*>(ahemFontFilename.get())))
243        g_error("Could not load font at %s!", ahemFontFilename.get());
244
245    for (int i = 1; i <= 9; i++) {
246        GOwnPtr<gchar> fontFilename(g_strdup_printf("WebKitWeightWatcher%i00.ttf", i));
247        GOwnPtr<gchar> fontPath(g_build_filename(FONTS_CONF_DIR, "..", "..", "fonts", fontFilename.get(), NULL));
248        if (!FcConfigAppFontAddFile(config, reinterpret_cast<FcChar8*>(fontPath.get())))
249            g_error("Could not load font at %s!", fontPath.get());
250    }
251
252    // A font with no valid Fontconfig encoding to test https://bugs.webkit.org/show_bug.cgi?id=47452
253    GOwnPtr<gchar> fontWithNoValidEncodingFilename(g_build_filename(FONTS_CONF_DIR, "FontWithNoValidEncoding.fon", NULL));
254    if (!FcConfigAppFontAddFile(config, reinterpret_cast<FcChar8*>(fontWithNoValidEncodingFilename.get())))
255        g_error("Could not load font at %s!", fontWithNoValidEncodingFilename.get());
256
257    if (!FcConfigSetCurrent(config))
258        g_error("Could not set the current font configuration!");
259
260    numFonts = FcConfigGetFonts(config, FcSetApplication)->nfont;
261#endif
262}
263
264static gchar* dumpFramesAsText(WebKitWebFrame* frame)
265{
266    gchar* result = 0;
267
268    // Add header for all but the main frame.
269    bool isMainFrame = (webkit_web_view_get_main_frame(webView) == frame);
270
271    CString innerText = DumpRenderTreeSupportGtk::getInnerText(frame);
272    if (isMainFrame)
273        result = g_strdup_printf("%s\n", innerText.data());
274    else {
275        const gchar* frameName = webkit_web_frame_get_name(frame);
276        result = g_strdup_printf("\n--------\nFrame: '%s'\n--------\n%s\n", frameName, innerText.data());
277    }
278
279    if (gLayoutTestController->dumpChildFramesAsText()) {
280        GSList* children = DumpRenderTreeSupportGtk::getFrameChildren(frame);
281        for (GSList* child = children; child; child = g_slist_next(child))
282            appendString(result, dumpFramesAsText(static_cast<WebKitWebFrame* >(child->data)));
283        g_slist_free(children);
284    }
285
286    return result;
287}
288
289static gint compareHistoryItems(gpointer* item1, gpointer* item2)
290{
291    return g_ascii_strcasecmp(webkit_web_history_item_get_target(WEBKIT_WEB_HISTORY_ITEM(item1)),
292                              webkit_web_history_item_get_target(WEBKIT_WEB_HISTORY_ITEM(item2)));
293}
294
295static void dumpHistoryItem(WebKitWebHistoryItem* item, int indent, bool current)
296{
297    ASSERT(item != NULL);
298    int start = 0;
299    g_object_ref(item);
300    if (current) {
301        printf("curr->");
302        start = 6;
303    }
304    for (int i = start; i < indent; i++)
305        putchar(' ');
306
307    // normalize file URLs.
308    const gchar* uri = webkit_web_history_item_get_uri(item);
309    gchar* uriScheme = g_uri_parse_scheme(uri);
310    if (g_strcmp0(uriScheme, "file") == 0) {
311        gchar* pos = g_strstr_len(uri, -1, "/LayoutTests/");
312        if (!pos)
313            return;
314
315        GString* result = g_string_sized_new(strlen(uri));
316        result = g_string_append(result, "(file test):");
317        result = g_string_append(result, pos + strlen("/LayoutTests/"));
318        printf("%s", result->str);
319        g_string_free(result, TRUE);
320    } else
321        printf("%s", uri);
322
323    g_free(uriScheme);
324
325    const gchar* target = webkit_web_history_item_get_target(item);
326    if (target && strlen(target) > 0)
327        printf(" (in frame \"%s\")", target);
328    if (webkit_web_history_item_is_target_item(item))
329        printf("  **nav target**");
330    putchar('\n');
331    GList* kids = webkit_web_history_item_get_children(item);
332    if (kids) {
333        // must sort to eliminate arbitrary result ordering which defeats reproducible testing
334        kids = g_list_sort(kids, (GCompareFunc) compareHistoryItems);
335        for (unsigned i = 0; i < g_list_length(kids); i++)
336            dumpHistoryItem(WEBKIT_WEB_HISTORY_ITEM(g_list_nth_data(kids, i)), indent+4, FALSE);
337    }
338    g_object_unref(item);
339}
340
341static void dumpBackForwardListForWebView(WebKitWebView* view)
342{
343    printf("\n============== Back Forward List ==============\n");
344    WebKitWebBackForwardList* bfList = webkit_web_view_get_back_forward_list(view);
345
346    // Print out all items in the list after prevTestBFItem, which was from the previous test
347    // Gather items from the end of the list, the print them out from oldest to newest
348    GList* itemsToPrint = NULL;
349    gint forwardListCount = webkit_web_back_forward_list_get_forward_length(bfList);
350    for (int i = forwardListCount; i > 0; i--) {
351        WebKitWebHistoryItem* item = webkit_web_back_forward_list_get_nth_item(bfList, i);
352        // something is wrong if the item from the last test is in the forward part of the b/f list
353        ASSERT(item != prevTestBFItem);
354        g_object_ref(item);
355        itemsToPrint = g_list_append(itemsToPrint, item);
356    }
357
358    WebKitWebHistoryItem* currentItem = webkit_web_back_forward_list_get_current_item(bfList);
359
360    g_object_ref(currentItem);
361    itemsToPrint = g_list_append(itemsToPrint, currentItem);
362
363    gint currentItemIndex = g_list_length(itemsToPrint) - 1;
364    gint backListCount = webkit_web_back_forward_list_get_back_length(bfList);
365    for (int i = -1; i >= -(backListCount); i--) {
366        WebKitWebHistoryItem* item = webkit_web_back_forward_list_get_nth_item(bfList, i);
367        if (item == prevTestBFItem)
368            break;
369        g_object_ref(item);
370        itemsToPrint = g_list_append(itemsToPrint, item);
371    }
372
373    for (int i = g_list_length(itemsToPrint) - 1; i >= 0; i--) {
374        WebKitWebHistoryItem* item = WEBKIT_WEB_HISTORY_ITEM(g_list_nth_data(itemsToPrint, i));
375        dumpHistoryItem(item, historyItemIndent, i == currentItemIndex);
376        g_object_unref(item);
377    }
378    g_list_free(itemsToPrint);
379    printf("===============================================\n");
380}
381
382static void dumpBackForwardListForAllWebViews()
383{
384    // Dump the back forward list of the main WebView first
385    dumpBackForwardListForWebView(webView);
386
387    // The view list is prepended. Reverse the list so we get the order right.
388    GSList* viewList = g_slist_reverse(webViewList);
389    for (unsigned i = 0; i < g_slist_length(viewList); ++i)
390        dumpBackForwardListForWebView(WEBKIT_WEB_VIEW(g_slist_nth_data(viewList, i)));
391}
392
393static void invalidateAnyPreviousWaitToDumpWatchdog()
394{
395    if (waitToDumpWatchdog) {
396        g_source_remove(waitToDumpWatchdog);
397        waitToDumpWatchdog = 0;
398    }
399
400    waitForPolicy = false;
401}
402
403static void resetDefaultsToConsistentValues()
404{
405    WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
406    g_object_set(G_OBJECT(settings),
407                 "enable-private-browsing", FALSE,
408                 "enable-developer-extras", FALSE,
409                 "enable-spell-checking", TRUE,
410                 "enable-html5-database", TRUE,
411                 "enable-html5-local-storage", TRUE,
412                 "enable-xss-auditor", FALSE,
413                 "enable-spatial-navigation", FALSE,
414                 "enable-frame-flattening", FALSE,
415                 "javascript-can-access-clipboard", TRUE,
416                 "javascript-can-open-windows-automatically", TRUE,
417                 "enable-offline-web-application-cache", TRUE,
418                 "enable-universal-access-from-file-uris", TRUE,
419                 "enable-scripts", TRUE,
420                 "enable-dom-paste", TRUE,
421                 "default-font-family", "Times",
422                 "monospace-font-family", "Courier",
423                 "serif-font-family", "Times",
424                 "sans-serif-font-family", "Helvetica",
425                 "cursive-font-family", "cursive",
426                 "fantasy-font-family", "fantasy",
427                 "default-font-size", 12,
428                 "default-monospace-font-size", 10,
429                 "minimum-font-size", 0,
430                 "enable-caret-browsing", FALSE,
431                 "enable-page-cache", FALSE,
432                 "auto-resize-window", TRUE,
433                 "enable-java-applet", FALSE,
434                 "enable-plugins", TRUE,
435                 "enable-hyperlink-auditing", FALSE,
436                 "editing-behavior", WEBKIT_EDITING_BEHAVIOR_UNIX,
437                 "enable-fullscreen", TRUE,
438                 NULL);
439    webkit_web_view_set_settings(webView, settings);
440    webkit_set_cache_model(WEBKIT_CACHE_MODEL_DOCUMENT_BROWSER);
441
442    DumpRenderTreeSupportGtk::clearMainFrameName(mainFrame);
443
444    WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView);
445    g_object_set(G_OBJECT(inspector), "javascript-profiling-enabled", FALSE, NULL);
446
447    webkit_web_view_set_zoom_level(webView, 1.0);
448    DumpRenderTreeSupportGtk::setMinimumTimerInterval(webView, DumpRenderTreeSupportGtk::defaultMinimumTimerInterval());
449
450    DumpRenderTreeSupportGtk::resetOriginAccessWhiteLists();
451
452    WebKitWebBackForwardList* list = webkit_web_view_get_back_forward_list(webView);
453    webkit_web_back_forward_list_clear(list);
454
455    SoupSession* session = webkit_get_default_session();
456    SoupCookieJar* jar = reinterpret_cast<SoupCookieJar*>(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR));
457
458    // We only create the jar when the soup backend needs to do
459    // HTTP. Should we initialize it earlier, perhaps?
460    if (jar)
461        g_object_set(G_OBJECT(jar), SOUP_COOKIE_JAR_ACCEPT_POLICY, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY, NULL);
462
463    setlocale(LC_ALL, "");
464
465    DumpRenderTreeSupportGtk::setLinksIncludedInFocusChain(true);
466    webkit_icon_database_set_path(webkit_get_icon_database(), 0);
467    DumpRenderTreeSupportGtk::setSelectTrailingWhitespaceEnabled(false);
468
469    if (axController)
470        axController->resetToConsistentState();
471
472    DumpRenderTreeSupportGtk::clearOpener(mainFrame);
473}
474
475static bool useLongRunningServerMode(int argc, char *argv[])
476{
477    // This assumes you've already called getopt_long
478    return (argc == optind+1 && !strcmp(argv[optind], "-"));
479}
480
481static void runTestingServerLoop()
482{
483    // When DumpRenderTree runs in server mode, we just wait around for file names
484    // to be passed to us and read each in turn, passing the results back to the client
485    char filenameBuffer[2048];
486    while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
487        char* newLineCharacter = strchr(filenameBuffer, '\n');
488        if (newLineCharacter)
489            *newLineCharacter = '\0';
490
491        if (!strlen(filenameBuffer))
492            continue;
493
494        runTest(filenameBuffer);
495    }
496}
497
498static void initializeGlobalsFromCommandLineOptions(int argc, char *argv[])
499{
500    struct option options[] = {
501        {"notree", no_argument, &dumpTree, false},
502        {"pixel-tests", no_argument, &dumpPixels, true},
503        {"tree", no_argument, &dumpTree, true},
504        {NULL, 0, NULL, 0}
505    };
506
507    int option;
508    while ((option = getopt_long(argc, (char * const *)argv, "", options, NULL)) != -1) {
509        switch (option) {
510        case '?': // unknown or ambiguous option
511        case ':': // missing argument
512            exit(1);
513            break;
514        }
515    }
516}
517
518
519void dump()
520{
521    invalidateAnyPreviousWaitToDumpWatchdog();
522
523    if (dumpTree) {
524        char* result = 0;
525        gchar* responseMimeType = webkit_web_frame_get_response_mime_type(mainFrame);
526
527        if (g_str_equal(responseMimeType, "text/plain")) {
528            gLayoutTestController->setDumpAsText(true);
529            gLayoutTestController->setGeneratePixelResults(false);
530        }
531        g_free(responseMimeType);
532
533        if (gLayoutTestController->dumpAsText())
534            result = dumpFramesAsText(mainFrame);
535        else {
536            // Widget resizing is done asynchronously in GTK+. We pump the main
537            // loop here, to flush any pending resize requests. This prevents
538            // timing issues which affect the size of elements in the output.
539            // We only enable this workaround for tests that print the render tree
540            // because this seems to break some dumpAsText tests: see bug 39988
541            // After fixing that test, we should apply this approach to all dumps.
542            while (gtk_events_pending())
543                gtk_main_iteration();
544
545            result = g_strdup(DumpRenderTreeSupportGtk::dumpRenderTree(mainFrame).data());
546        }
547
548        if (!result) {
549            const char* errorMessage;
550            if (gLayoutTestController->dumpAsText())
551                errorMessage = "[documentElement innerText]";
552            else if (gLayoutTestController->dumpDOMAsWebArchive())
553                errorMessage = "[[mainFrame DOMDocument] webArchive]";
554            else if (gLayoutTestController->dumpSourceAsWebArchive())
555                errorMessage = "[[mainFrame dataSource] webArchive]";
556            else
557                errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
558            printf("ERROR: nil result from %s", errorMessage);
559        } else {
560            printf("%s", result);
561            g_free(result);
562            if (!gLayoutTestController->dumpAsText() && !gLayoutTestController->dumpDOMAsWebArchive() && !gLayoutTestController->dumpSourceAsWebArchive())
563                dumpFrameScrollPosition(mainFrame);
564
565            if (gLayoutTestController->dumpBackForwardList())
566                dumpBackForwardListForAllWebViews();
567        }
568
569        if (printSeparators) {
570            puts("#EOF"); // terminate the content block
571            fputs("#EOF\n", stderr);
572            fflush(stdout);
573            fflush(stderr);
574        }
575    }
576
577    if (dumpPixels
578     && gLayoutTestController->generatePixelResults()
579     && !gLayoutTestController->dumpDOMAsWebArchive()
580     && !gLayoutTestController->dumpSourceAsWebArchive())
581        dumpWebViewAsPixelsAndCompareWithExpected(gLayoutTestController->expectedPixelHash());
582
583    // FIXME: call displayWebView here when we support --paint
584
585    done = true;
586    gtk_main_quit();
587}
588
589static void setDefaultsToConsistentStateValuesForTesting()
590{
591    resetDefaultsToConsistentValues();
592
593    /* Disable the default auth dialog for testing */
594    SoupSession* session = webkit_get_default_session();
595    soup_session_remove_feature_by_type(session, WEBKIT_TYPE_SOUP_AUTH_DIALOG);
596
597#if PLATFORM(X11)
598    webkit_web_settings_add_extra_plugin_directory(webView, TEST_PLUGIN_DIR);
599#endif
600
601    gchar* databaseDirectory = g_build_filename(g_get_user_data_dir(), "gtkwebkitdrt", "databases", NULL);
602    webkit_set_web_database_directory_path(databaseDirectory);
603    g_free(databaseDirectory);
604
605#if defined(GTK_API_VERSION_2)
606    gtk_rc_parse_string("style \"nix_scrollbar_spacing\"                    "
607                        "{                                                  "
608                        "    GtkScrolledWindow::scrollbar-spacing = 0       "
609                        "}                                                  "
610                        "class \"GtkWidget\" style \"nix_scrollbar_spacing\"");
611
612#else
613    GtkCssProvider* cssProvider = gtk_css_provider_new();
614    gtk_css_provider_load_from_data(cssProvider,
615                                    " * {                                       "
616                                    "   -GtkScrolledWindow-scrollbar-spacing: 0;"
617                                    "}                                          ",
618                                    -1, 0);
619    gtk_style_context_add_provider_for_screen(gdk_display_get_default_screen(gdk_display_get_default()),
620                                              GTK_STYLE_PROVIDER(cssProvider),
621                                              GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
622    g_object_unref(cssProvider);
623#endif
624}
625
626static void sendPixelResultsEOF()
627{
628    puts("#EOF");
629
630    fflush(stdout);
631    fflush(stderr);
632}
633
634static void runTest(const string& testPathOrURL)
635{
636    ASSERT(!testPathOrURL.empty());
637
638    // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows.
639    string testURL(testPathOrURL);
640    string expectedPixelHash;
641    size_t separatorPos = testURL.find("'");
642    if (separatorPos != string::npos) {
643        testURL = string(testPathOrURL, 0, separatorPos);
644        expectedPixelHash = string(testPathOrURL, separatorPos + 1);
645    }
646
647    // Convert the path into a full file URL if it does not look
648    // like an HTTP/S URL (doesn't start with http:// or https://).
649    if (testURL.find("http://") && testURL.find("https://")) {
650        GFile* testFile = g_file_new_for_path(testURL.c_str());
651        gchar* testURLCString = g_file_get_uri(testFile);
652        testURL = testURLCString;
653        g_free(testURLCString);
654        g_object_unref(testFile);
655    }
656
657    resetDefaultsToConsistentValues();
658
659    gLayoutTestController = LayoutTestController::create(testURL, expectedPixelHash);
660    topLoadingFrame = 0;
661    done = false;
662
663    gLayoutTestController->setIconDatabaseEnabled(false);
664
665    if (shouldLogFrameLoadDelegates(testURL))
666        gLayoutTestController->setDumpFrameLoadCallbacks(true);
667
668    if (shouldEnableDeveloperExtras(testURL)) {
669        gLayoutTestController->setDeveloperExtrasEnabled(true);
670        if (shouldOpenWebInspector(testURL))
671            gLayoutTestController->showWebInspector();
672        if (shouldDumpAsText(testURL)) {
673            gLayoutTestController->setDumpAsText(true);
674            gLayoutTestController->setGeneratePixelResults(false);
675        }
676    }
677
678    WorkQueue::shared()->clear();
679    WorkQueue::shared()->setFrozen(false);
680
681    bool isSVGW3CTest = (testURL.find("svg/W3C-SVG-1.1") != string::npos);
682    GtkAllocation size;
683    size.x = size.y = 0;
684    size.width = isSVGW3CTest ? 480 : LayoutTestController::maxViewWidth;
685    size.height = isSVGW3CTest ? 360 : LayoutTestController::maxViewHeight;
686    gtk_window_resize(GTK_WINDOW(window), size.width, size.height);
687    gtk_widget_size_allocate(container, &size);
688
689    if (prevTestBFItem)
690        g_object_unref(prevTestBFItem);
691    WebKitWebBackForwardList* bfList = webkit_web_view_get_back_forward_list(webView);
692    prevTestBFItem = webkit_web_back_forward_list_get_current_item(bfList);
693    if (prevTestBFItem)
694        g_object_ref(prevTestBFItem);
695
696    initializeFonts(testURL.c_str());
697
698    // Focus the web view before loading the test to avoid focusing problems
699    gtk_widget_grab_focus(GTK_WIDGET(webView));
700    webkit_web_view_open(webView, testURL.c_str());
701
702    gtk_main();
703
704    // If developer extras enabled Web Inspector may have been open by the test.
705    if (shouldEnableDeveloperExtras(testURL)) {
706        gLayoutTestController->closeWebInspector();
707        gLayoutTestController->setDeveloperExtrasEnabled(false);
708    }
709
710    // Also check if we still have opened webViews and free them.
711    if (gLayoutTestController->closeRemainingWindowsWhenComplete() || webViewList) {
712        while (webViewList) {
713            g_object_unref(WEBKIT_WEB_VIEW(webViewList->data));
714            webViewList = g_slist_next(webViewList);
715        }
716        g_slist_free(webViewList);
717        webViewList = 0;
718    }
719
720    // A blank load seems to be necessary to reset state after certain tests.
721    webkit_web_view_open(webView, "about:blank");
722
723    gLayoutTestController.clear();
724
725    // terminate the (possibly empty) pixels block after all the state reset
726    sendPixelResultsEOF();
727}
728
729void webViewLoadStarted(WebKitWebView* view, WebKitWebFrame* frame, void*)
730{
731    // Make sure we only set this once per test.  If it gets cleared, and then set again, we might
732    // end up doing two dumps for one test.
733    if (!topLoadingFrame && !done)
734        topLoadingFrame = frame;
735}
736
737static gboolean processWork(void* data)
738{
739    // if we finish all the commands, we're ready to dump state
740    if (WorkQueue::shared()->processWork() && !gLayoutTestController->waitToDump())
741        dump();
742
743    return FALSE;
744}
745
746static char* getFrameNameSuitableForTestResult(WebKitWebView* view, WebKitWebFrame* frame)
747{
748    char* frameName = g_strdup(webkit_web_frame_get_name(frame));
749
750    if (frame == webkit_web_view_get_main_frame(view)) {
751        // This is a bit strange. Shouldn't web_frame_get_name return NULL?
752        if (frameName && (frameName[0] != '\0')) {
753            char* tmp = g_strdup_printf("main frame \"%s\"", frameName);
754            g_free(frameName);
755            frameName = tmp;
756        } else {
757            g_free(frameName);
758            frameName = g_strdup("main frame");
759        }
760    } else if (!frameName || (frameName[0] == '\0')) {
761        g_free(frameName);
762        frameName = g_strdup("frame (anonymous)");
763    } else {
764        char* tmp = g_strdup_printf("frame \"%s\"", frameName);
765        g_free(frameName);
766        frameName = tmp;
767    }
768
769    return frameName;
770}
771
772static void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
773{
774    if (frame != topLoadingFrame)
775        return;
776
777    topLoadingFrame = 0;
778    WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
779    if (gLayoutTestController->waitToDump())
780        return;
781
782    if (WorkQueue::shared()->count())
783        g_timeout_add(0, processWork, 0);
784    else
785        dump();
786}
787
788static gboolean webViewLoadError(WebKitWebView*, WebKitWebFrame*, gchar*, gpointer, gpointer)
789{
790    return TRUE; // Return true here to disable the default error page.
791}
792
793static void webViewDocumentLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
794{
795    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
796        char* frameName = getFrameNameSuitableForTestResult(view, frame);
797        printf("%s - didFinishDocumentLoadForFrame\n", frameName);
798        g_free(frameName);
799    } else if (!done) {
800        guint pendingFrameUnloadEvents = DumpRenderTreeSupportGtk::getPendingUnloadEventCount(frame);
801        if (pendingFrameUnloadEvents) {
802            char* frameName = getFrameNameSuitableForTestResult(view, frame);
803            printf("%s - has %u onunload handler(s)\n", frameName, pendingFrameUnloadEvents);
804            g_free(frameName);
805        }
806    }
807}
808
809static void webViewOnloadEvent(WebKitWebView* view, WebKitWebFrame* frame, void*)
810{
811    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
812        char* frameName = getFrameNameSuitableForTestResult(view, frame);
813        printf("%s - didHandleOnloadEventsForFrame\n", frameName);
814        g_free(frameName);
815    }
816}
817
818static void addControllerToWindow(JSContextRef context, JSObjectRef windowObject, const char* controllerName, JSValueRef controller)
819{
820    JSStringRef controllerNameStr = JSStringCreateWithUTF8CString(controllerName);
821    JSObjectSetProperty(context, windowObject, controllerNameStr, controller, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, 0);
822    JSStringRelease(controllerNameStr);
823}
824
825static void webViewWindowObjectCleared(WebKitWebView* view, WebKitWebFrame* frame, JSGlobalContextRef context, JSObjectRef windowObject, gpointer data)
826{
827    JSValueRef exception = 0;
828    ASSERT(gLayoutTestController);
829
830    gLayoutTestController->makeWindowObject(context, windowObject, &exception);
831    ASSERT(!exception);
832
833    gcController->makeWindowObject(context, windowObject, &exception);
834    ASSERT(!exception);
835
836    axController->makeWindowObject(context, windowObject, &exception);
837    ASSERT(!exception);
838
839    addControllerToWindow(context, windowObject, "eventSender", makeEventSender(context, !webkit_web_frame_get_parent(frame)));
840    addControllerToWindow(context, windowObject, "plainText", makePlainTextController(context));
841    addControllerToWindow(context, windowObject, "textInputController", makeTextInputController(context));
842}
843
844static gboolean webViewConsoleMessage(WebKitWebView* view, const gchar* message, unsigned int line, const gchar* sourceId, gpointer data)
845{
846    gchar* testMessage = 0;
847    const gchar* uriScheme;
848
849    // Tests expect only the filename part of local URIs
850    uriScheme = g_strstr_len(message, -1, "file://");
851    if (uriScheme) {
852        GString* tempString = g_string_sized_new(strlen(message));
853        gchar* filename = g_strrstr(uriScheme, G_DIR_SEPARATOR_S);
854
855        if (filename) {
856            filename += strlen(G_DIR_SEPARATOR_S);
857            tempString = g_string_append_len(tempString, message, (uriScheme - message));
858            tempString = g_string_append_len(tempString, filename, strlen(filename));
859            testMessage = g_string_free(tempString, FALSE);
860        }
861    }
862
863    fprintf(stdout, "CONSOLE MESSAGE: line %d: %s\n", line, testMessage ? testMessage : message);
864    g_free(testMessage);
865
866    return TRUE;
867}
868
869
870static gboolean webViewScriptAlert(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gpointer data)
871{
872    fprintf(stdout, "ALERT: %s\n", message);
873    return TRUE;
874}
875
876static gboolean webViewScriptPrompt(WebKitWebView* webView, WebKitWebFrame* frame, const gchar* message, const gchar* defaultValue, gchar** value, gpointer data)
877{
878    fprintf(stdout, "PROMPT: %s, default text: %s\n", message, defaultValue);
879    *value = g_strdup(defaultValue);
880    return TRUE;
881}
882
883static gboolean webViewScriptConfirm(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gboolean* didConfirm, gpointer data)
884{
885    fprintf(stdout, "CONFIRM: %s\n", message);
886    *didConfirm = TRUE;
887    return TRUE;
888}
889
890static void webViewTitleChanged(WebKitWebView* view, WebKitWebFrame* frame, const gchar* title, gpointer data)
891{
892    if (gLayoutTestController->dumpTitleChanges() && !done)
893        printf("TITLE CHANGED: %s\n", title ? title : "");
894}
895
896static bool webViewNavigationPolicyDecisionRequested(WebKitWebView* view, WebKitWebFrame* frame,
897                                                     WebKitNetworkRequest* request,
898                                                     WebKitWebNavigationAction* navAction,
899                                                     WebKitWebPolicyDecision* policyDecision)
900{
901    // Use the default handler if we're not waiting for policy,
902    // i.e., LayoutTestController::waitForPolicyDelegate
903    if (!waitForPolicy)
904        return FALSE;
905
906    gchar* typeDescription;
907    WebKitWebNavigationReason reason;
908    g_object_get(G_OBJECT(navAction), "reason", &reason, NULL);
909
910    switch(reason) {
911        case WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED:
912            typeDescription = g_strdup("link clicked");
913            break;
914        case WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED:
915            typeDescription = g_strdup("form submitted");
916            break;
917        case WEBKIT_WEB_NAVIGATION_REASON_BACK_FORWARD:
918            typeDescription = g_strdup("back/forward");
919            break;
920        case WEBKIT_WEB_NAVIGATION_REASON_RELOAD:
921            typeDescription = g_strdup("reload");
922            break;
923        case WEBKIT_WEB_NAVIGATION_REASON_FORM_RESUBMITTED:
924            typeDescription = g_strdup("form resubmitted");
925            break;
926        case WEBKIT_WEB_NAVIGATION_REASON_OTHER:
927            typeDescription = g_strdup("other");
928            break;
929        default:
930            typeDescription = g_strdup("illegal value");
931    }
932
933    printf("Policy delegate: attempt to load %s with navigation type '%s'\n", webkit_network_request_get_uri(request), typeDescription);
934    g_free(typeDescription);
935
936    webkit_web_policy_decision_ignore(policyDecision);
937    gLayoutTestController->notifyDone();
938
939    return TRUE;
940}
941
942static void webViewStatusBarTextChanged(WebKitWebView* view, const gchar* message, gpointer data)
943{
944    // Are we doing anything wrong? One test that does not call
945    // dumpStatusCallbacks gets true here
946    if (gLayoutTestController->dumpStatusCallbacks())
947        printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", message);
948}
949
950static gboolean webViewClose(WebKitWebView* view)
951{
952    ASSERT(view);
953
954    webViewList = g_slist_remove(webViewList, view);
955    g_object_unref(view);
956
957    return TRUE;
958}
959
960static void databaseQuotaExceeded(WebKitWebView* view, WebKitWebFrame* frame, WebKitWebDatabase *database)
961{
962    ASSERT(view);
963    ASSERT(frame);
964    ASSERT(database);
965
966    WebKitSecurityOrigin* origin = webkit_web_database_get_security_origin(database);
967    if (gLayoutTestController->dumpDatabaseCallbacks()) {
968        printf("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{%s, %s, %i} database:%s\n",
969            webkit_security_origin_get_protocol(origin),
970            webkit_security_origin_get_host(origin),
971            webkit_security_origin_get_port(origin),
972            webkit_web_database_get_name(database));
973    }
974    webkit_security_origin_set_web_database_quota(origin, 5 * 1024 * 1024);
975}
976
977static bool
978geolocationPolicyDecisionRequested(WebKitWebView*, WebKitWebFrame*, WebKitGeolocationPolicyDecision* decision)
979{
980    if (!gLayoutTestController->isGeolocationPermissionSet())
981        return FALSE;
982    if (gLayoutTestController->geolocationPermission())
983        webkit_geolocation_policy_allow(decision);
984    else
985        webkit_geolocation_policy_deny(decision);
986
987    return TRUE;
988}
989
990
991static WebKitWebView* webViewCreate(WebKitWebView*, WebKitWebFrame*);
992
993static gboolean webInspectorShowWindow(WebKitWebInspector*, gpointer data)
994{
995    gtk_window_set_default_size(GTK_WINDOW(webInspectorWindow), 800, 600);
996    gtk_widget_show_all(webInspectorWindow);
997    return TRUE;
998}
999
1000static gboolean webInspectorCloseWindow(WebKitWebInspector*, gpointer data)
1001{
1002    gtk_widget_destroy(webInspectorWindow);
1003    webInspectorWindow = 0;
1004    return TRUE;
1005}
1006
1007static WebKitWebView* webInspectorInspectWebView(WebKitWebInspector*, gpointer data)
1008{
1009    webInspectorWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1010
1011    GtkWidget* webView = webkit_web_view_new();
1012    gtk_container_add(GTK_CONTAINER(webInspectorWindow),
1013                      webView);
1014
1015    return WEBKIT_WEB_VIEW(webView);
1016}
1017
1018static void webFrameLoadStatusNotified(WebKitWebFrame* frame, gpointer user_data)
1019{
1020    WebKitLoadStatus loadStatus = webkit_web_frame_get_load_status(frame);
1021
1022    if (gLayoutTestController->dumpFrameLoadCallbacks()) {
1023        GOwnPtr<char> frameName(getFrameNameSuitableForTestResult(webkit_web_frame_get_web_view(frame), frame));
1024
1025        switch (loadStatus) {
1026        case WEBKIT_LOAD_PROVISIONAL:
1027            if (!done)
1028                printf("%s - didStartProvisionalLoadForFrame\n", frameName.get());
1029            break;
1030        case WEBKIT_LOAD_COMMITTED:
1031            if (!done)
1032                printf("%s - didCommitLoadForFrame\n", frameName.get());
1033            break;
1034        case WEBKIT_LOAD_FINISHED:
1035            if (frame != topLoadingFrame || !done)
1036                printf("%s - didFinishLoadForFrame\n", frameName.get());
1037            break;
1038        default:
1039            break;
1040        }
1041    }
1042}
1043
1044static void frameCreatedCallback(WebKitWebView* webView, WebKitWebFrame* webFrame, gpointer user_data)
1045{
1046    g_signal_connect(webFrame, "notify::load-status", G_CALLBACK(webFrameLoadStatusNotified), NULL);
1047}
1048
1049static void willSendRequestCallback(WebKitWebView* webView, WebKitWebFrame*, WebKitWebResource*, WebKitNetworkRequest* request, WebKitNetworkResponse*)
1050{
1051    if (!done && gLayoutTestController->willSendRequestReturnsNull())
1052        return;
1053
1054    SoupMessage* soupMessage = webkit_network_request_get_message(request);
1055    SoupURI* uri = soup_uri_new(webkit_network_request_get_uri(request));
1056
1057    if (SOUP_URI_VALID_FOR_HTTP(uri) && g_strcmp0(uri->host, "127.0.0.1")
1058        && g_strcmp0(uri->host, "255.255.255.255")
1059        && g_ascii_strncasecmp(uri->host, "localhost", 9)) {
1060        printf("Blocked access to external URL %s\n", soup_uri_to_string(uri, FALSE));
1061        soup_uri_free(uri);
1062        return;
1063    }
1064    if (uri)
1065        soup_uri_free(uri);
1066
1067    if (soupMessage) {
1068        const set<string>& clearHeaders = gLayoutTestController->willSendRequestClearHeaders();
1069        for (set<string>::const_iterator header = clearHeaders.begin(); header != clearHeaders.end(); ++header)
1070            soup_message_headers_remove(soupMessage->request_headers, header->c_str());
1071    }
1072}
1073
1074static WebKitWebView* createWebView()
1075{
1076    WebKitWebView* view = WEBKIT_WEB_VIEW(webkit_web_view_new());
1077
1078    DumpRenderTreeSupportGtk::setDumpRenderTreeModeEnabled(true);
1079
1080    g_object_connect(G_OBJECT(view),
1081                     "signal::load-started", webViewLoadStarted, 0,
1082                     "signal::load-finished", webViewLoadFinished, 0,
1083                     "signal::load-error", webViewLoadError, 0,
1084                     "signal::window-object-cleared", webViewWindowObjectCleared, 0,
1085                     "signal::console-message", webViewConsoleMessage, 0,
1086                     "signal::script-alert", webViewScriptAlert, 0,
1087                     "signal::script-prompt", webViewScriptPrompt, 0,
1088                     "signal::script-confirm", webViewScriptConfirm, 0,
1089                     "signal::title-changed", webViewTitleChanged, 0,
1090                     "signal::navigation-policy-decision-requested", webViewNavigationPolicyDecisionRequested, 0,
1091                     "signal::status-bar-text-changed", webViewStatusBarTextChanged, 0,
1092                     "signal::create-web-view", webViewCreate, 0,
1093                     "signal::close-web-view", webViewClose, 0,
1094                     "signal::database-quota-exceeded", databaseQuotaExceeded, 0,
1095                     "signal::document-load-finished", webViewDocumentLoadFinished, 0,
1096                     "signal::geolocation-policy-decision-requested", geolocationPolicyDecisionRequested, 0,
1097                     "signal::onload-event", webViewOnloadEvent, 0,
1098                     "signal::drag-begin", dragBeginCallback, 0,
1099                     "signal::drag-end", dragEndCallback, 0,
1100                     "signal::drag-failed", dragFailedCallback, 0,
1101                     "signal::frame-created", frameCreatedCallback, 0,
1102                     "signal::resource-request-starting", willSendRequestCallback, 0,
1103
1104                     NULL);
1105    connectEditingCallbacks(view);
1106
1107    WebKitWebInspector* inspector = webkit_web_view_get_inspector(view);
1108    g_object_connect(G_OBJECT(inspector),
1109                     "signal::inspect-web-view", webInspectorInspectWebView, 0,
1110                     "signal::show-window", webInspectorShowWindow, 0,
1111                     "signal::close-window", webInspectorCloseWindow, 0,
1112                     NULL);
1113
1114    if (webView) {
1115        WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
1116        webkit_web_view_set_settings(view, settings);
1117    }
1118
1119    // frame-created is not issued for main frame. That's why we must do this here
1120    WebKitWebFrame* frame = webkit_web_view_get_main_frame(view);
1121    g_signal_connect(frame, "notify::load-status", G_CALLBACK(webFrameLoadStatusNotified), NULL);
1122
1123    return view;
1124}
1125
1126static WebKitWebView* webViewCreate(WebKitWebView* view, WebKitWebFrame* frame)
1127{
1128    if (!gLayoutTestController->canOpenWindows())
1129        return 0;
1130
1131    // Make sure that waitUntilDone has been called.
1132    ASSERT(gLayoutTestController->waitToDump());
1133
1134    WebKitWebView* newWebView = createWebView();
1135    g_object_ref_sink(G_OBJECT(newWebView));
1136    webViewList = g_slist_prepend(webViewList, newWebView);
1137    return newWebView;
1138}
1139
1140static void logHandler(const gchar* domain, GLogLevelFlags level, const gchar* message, gpointer data)
1141{
1142    if (level < G_LOG_LEVEL_DEBUG)
1143        fprintf(stderr, "%s\n", message);
1144}
1145
1146int main(int argc, char* argv[])
1147{
1148    g_thread_init(NULL);
1149    gtk_init(&argc, &argv);
1150
1151    // Some plugins might try to use the GLib logger for printing debug
1152    // messages. This will cause tests to fail because of unexpected output.
1153    // We squelch all debug messages sent to the logger.
1154    g_log_set_default_handler(logHandler, 0);
1155
1156    initializeGlobalsFromCommandLineOptions(argc, argv);
1157    initializeFonts();
1158
1159    window = gtk_window_new(GTK_WINDOW_POPUP);
1160    container = GTK_WIDGET(gtk_scrolled_window_new(NULL, NULL));
1161    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(container), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1162    gtk_container_add(GTK_CONTAINER(window), container);
1163    gtk_widget_show_all(window);
1164
1165    webView = createWebView();
1166    gtk_container_add(GTK_CONTAINER(container), GTK_WIDGET(webView));
1167    gtk_widget_realize(GTK_WIDGET(webView));
1168    gtk_widget_show_all(container);
1169    gtk_widget_grab_focus(GTK_WIDGET(webView));
1170    mainFrame = webkit_web_view_get_main_frame(webView);
1171
1172    setDefaultsToConsistentStateValuesForTesting();
1173
1174    gcController = new GCController();
1175    axController = new AccessibilityController();
1176
1177    if (useLongRunningServerMode(argc, argv)) {
1178        printSeparators = true;
1179        runTestingServerLoop();
1180    } else {
1181        printSeparators = (optind < argc-1 || (dumpPixels && dumpTree));
1182        for (int i = optind; i != argc; ++i)
1183            runTest(argv[i]);
1184    }
1185
1186    delete gcController;
1187    gcController = 0;
1188
1189    delete axController;
1190    axController = 0;
1191
1192    gtk_widget_destroy(window);
1193
1194    return 0;
1195}
1196