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