--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2008 Alp Toker <alp@nuanti.com>
+ * Copyright (C) 2009 Jan Alonzo <jmalonzo@gmail.com>
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DumpRenderTree.h"
+
+#include "AccessibilityController.h"
+#include "EventSender.h"
+#include "GCController.h"
+#include "LayoutTestController.h"
+#include "PixelDumpSupport.h"
+#include "WorkQueue.h"
+#include "WorkQueueItem.h"
+#include <JavaScriptCore/JavaScript.h>
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+#include <getopt.h>
+#include <gtk/gtk.h>
+#include <webkit/webkit.h>
+#include <wtf/Assertions.h>
+
+#if PLATFORM(X11)
+#include <fontconfig/fontconfig.h>
+#endif
+
+
+using namespace std;
+
+extern "C" {
+// This API is not yet public.
+extern G_CONST_RETURN gchar* webkit_web_history_item_get_target(WebKitWebHistoryItem*);
+extern gboolean webkit_web_history_item_is_target_item(WebKitWebHistoryItem*);
+extern GList* webkit_web_history_item_get_children(WebKitWebHistoryItem*);
+extern GSList* webkit_web_frame_get_children(WebKitWebFrame* frame);
+extern gchar* webkit_web_frame_get_inner_text(WebKitWebFrame* frame);
+extern gchar* webkit_web_frame_dump_render_tree(WebKitWebFrame* frame);
+extern guint webkit_web_frame_get_pending_unload_event_count(WebKitWebFrame* frame);
+extern void webkit_web_settings_add_extra_plugin_directory(WebKitWebView* view, const gchar* directory);
+extern gchar* webkit_web_frame_get_response_mime_type(WebKitWebFrame* frame);
+extern void webkit_web_frame_clear_main_frame_name(WebKitWebFrame* frame);
+extern void webkit_web_view_set_group_name(WebKitWebView* view, const gchar* groupName);
+extern void webkit_reset_origin_access_white_lists();
+}
+
+volatile bool done;
+static bool printSeparators;
+static int dumpPixels;
+static int dumpTree = 1;
+
+AccessibilityController* axController = 0;
+RefPtr<LayoutTestController> gLayoutTestController;
+static GCController* gcController = 0;
+static WebKitWebView* webView;
+static GtkWidget* window;
+static GtkWidget* container;
+static GtkWidget* webInspectorWindow;
+WebKitWebFrame* mainFrame = 0;
+WebKitWebFrame* topLoadingFrame = 0;
+guint waitToDumpWatchdog = 0;
+bool waitForPolicy = false;
+
+// This is a list of opened webviews
+GSList* webViewList = 0;
+
+// current b/f item at the end of the previous test
+static WebKitWebHistoryItem* prevTestBFItem = NULL;
+
+const unsigned historyItemIndent = 8;
+
+static bool shouldLogFrameLoadDelegates(const string& pathOrURL)
+{
+ return pathOrURL.find("loading/") != string::npos;
+}
+
+static bool shouldOpenWebInspector(const string& pathOrURL)
+{
+ return pathOrURL.find("inspector/") != string::npos;
+}
+
+static bool shouldEnableDeveloperExtras(const string& pathOrURL)
+{
+ return shouldOpenWebInspector(pathOrURL) || pathOrURL.find("inspector-enabled/") != string::npos;
+}
+
+void dumpFrameScrollPosition(WebKitWebFrame* frame)
+{
+
+}
+
+void displayWebView()
+{
+ gtk_widget_queue_draw(GTK_WIDGET(webView));
+}
+
+static void appendString(gchar*& target, gchar* string)
+{
+ gchar* oldString = target;
+ target = g_strconcat(target, string, NULL);
+ g_free(oldString);
+}
+
+#if PLATFORM(X11)
+static void initializeFonts()
+{
+ static int numFonts = -1;
+
+ // Some tests may add or remove fonts via the @font-face rule.
+ // If that happens, font config should be re-created to suppress any unwanted change.
+ FcFontSet* appFontSet = FcConfigGetFonts(0, FcSetApplication);
+ if (appFontSet && numFonts >= 0 && appFontSet->nfont == numFonts)
+ return;
+
+ const char* fontDirEnv = g_getenv("WEBKIT_TESTFONTS");
+ if (!fontDirEnv)
+ g_error("WEBKIT_TESTFONTS environment variable is not set, but it should point to the directory "
+ "containing the fonts you can clone from git://gitorious.org/qtwebkit/testfonts.git\n");
+
+ GFile* fontDir = g_file_new_for_path(fontDirEnv);
+ if (!fontDir || !g_file_query_exists(fontDir, NULL))
+ g_error("WEBKIT_TESTFONTS environment variable is not set correctly - it should point to the directory "
+ "containing the fonts you can clone from git://gitorious.org/qtwebkit/testfonts.git\n");
+
+ FcConfig *config = FcConfigCreate();
+ if (!FcConfigParseAndLoad (config, (FcChar8*) FONTS_CONF_FILE, true))
+ g_error("Couldn't load font configuration file");
+ if (!FcConfigAppFontAddDir (config, (FcChar8*) g_file_get_path(fontDir)))
+ g_error("Couldn't add font dir!");
+ FcConfigSetCurrent(config);
+
+ g_object_unref(fontDir);
+
+ appFontSet = FcConfigGetFonts(config, FcSetApplication);
+ numFonts = appFontSet->nfont;
+}
+#endif
+
+static gchar* dumpFramesAsText(WebKitWebFrame* frame)
+{
+ gchar* result = 0;
+
+ // Add header for all but the main frame.
+ bool isMainFrame = (webkit_web_view_get_main_frame(webView) == frame);
+
+ gchar* innerText = webkit_web_frame_get_inner_text(frame);
+ if (isMainFrame)
+ result = g_strdup_printf("%s\n", innerText);
+ else {
+ const gchar* frameName = webkit_web_frame_get_name(frame);
+ result = g_strdup_printf("\n--------\nFrame: '%s'\n--------\n%s\n", frameName, innerText);
+ }
+ g_free(innerText);
+
+ if (gLayoutTestController->dumpChildFramesAsText()) {
+ GSList* children = webkit_web_frame_get_children(frame);
+ for (GSList* child = children; child; child = g_slist_next(child))
+ appendString(result, dumpFramesAsText(static_cast<WebKitWebFrame* >(child->data)));
+ g_slist_free(children);
+ }
+
+ return result;
+}
+
+static gint compareHistoryItems(gpointer* item1, gpointer* item2)
+{
+ return g_ascii_strcasecmp(webkit_web_history_item_get_target(WEBKIT_WEB_HISTORY_ITEM(item1)),
+ webkit_web_history_item_get_target(WEBKIT_WEB_HISTORY_ITEM(item2)));
+}
+
+static void dumpHistoryItem(WebKitWebHistoryItem* item, int indent, bool current)
+{
+ ASSERT(item != NULL);
+ int start = 0;
+ g_object_ref(item);
+ if (current) {
+ printf("curr->");
+ start = 6;
+ }
+ for (int i = start; i < indent; i++)
+ putchar(' ');
+
+ // normalize file URLs.
+ const gchar* uri = webkit_web_history_item_get_uri(item);
+ gchar* uriScheme = g_uri_parse_scheme(uri);
+ if (g_strcmp0(uriScheme, "file") == 0) {
+ gchar* pos = g_strstr_len(uri, -1, "/LayoutTests/");
+ if (!pos)
+ return;
+
+ GString* result = g_string_sized_new(strlen(uri));
+ result = g_string_append(result, "(file test):");
+ result = g_string_append(result, pos + strlen("/LayoutTests/"));
+ printf("%s", result->str);
+ g_string_free(result, TRUE);
+ } else
+ printf("%s", uri);
+
+ g_free(uriScheme);
+
+ const gchar* target = webkit_web_history_item_get_target(item);
+ if (target && strlen(target) > 0)
+ printf(" (in frame \"%s\")", target);
+ if (webkit_web_history_item_is_target_item(item))
+ printf(" **nav target**");
+ putchar('\n');
+ GList* kids = webkit_web_history_item_get_children(item);
+ if (kids) {
+ // must sort to eliminate arbitrary result ordering which defeats reproducible testing
+ kids = g_list_sort(kids, (GCompareFunc) compareHistoryItems);
+ for (unsigned i = 0; i < g_list_length(kids); i++)
+ dumpHistoryItem(WEBKIT_WEB_HISTORY_ITEM(g_list_nth_data(kids, i)), indent+4, FALSE);
+ }
+ g_object_unref(item);
+}
+
+static void dumpBackForwardListForWebView(WebKitWebView* view)
+{
+ printf("\n============== Back Forward List ==============\n");
+ WebKitWebBackForwardList* bfList = webkit_web_view_get_back_forward_list(view);
+
+ // Print out all items in the list after prevTestBFItem, which was from the previous test
+ // Gather items from the end of the list, the print them out from oldest to newest
+ GList* itemsToPrint = NULL;
+ gint forwardListCount = webkit_web_back_forward_list_get_forward_length(bfList);
+ for (int i = forwardListCount; i > 0; i--) {
+ WebKitWebHistoryItem* item = webkit_web_back_forward_list_get_nth_item(bfList, i);
+ // something is wrong if the item from the last test is in the forward part of the b/f list
+ ASSERT(item != prevTestBFItem);
+ g_object_ref(item);
+ itemsToPrint = g_list_append(itemsToPrint, item);
+ }
+
+ WebKitWebHistoryItem* currentItem = webkit_web_back_forward_list_get_current_item(bfList);
+
+ g_object_ref(currentItem);
+ itemsToPrint = g_list_append(itemsToPrint, currentItem);
+
+ gint currentItemIndex = g_list_length(itemsToPrint) - 1;
+ gint backListCount = webkit_web_back_forward_list_get_back_length(bfList);
+ for (int i = -1; i >= -(backListCount); i--) {
+ WebKitWebHistoryItem* item = webkit_web_back_forward_list_get_nth_item(bfList, i);
+ if (item == prevTestBFItem)
+ break;
+ g_object_ref(item);
+ itemsToPrint = g_list_append(itemsToPrint, item);
+ }
+
+ for (int i = g_list_length(itemsToPrint) - 1; i >= 0; i--) {
+ WebKitWebHistoryItem* item = WEBKIT_WEB_HISTORY_ITEM(g_list_nth_data(itemsToPrint, i));
+ dumpHistoryItem(item, historyItemIndent, i == currentItemIndex);
+ g_object_unref(item);
+ }
+ g_list_free(itemsToPrint);
+ printf("===============================================\n");
+}
+
+static void dumpBackForwardListForAllWebViews()
+{
+ // Dump the back forward list of the main WebView first
+ dumpBackForwardListForWebView(webView);
+
+ // The view list is prepended. Reverse the list so we get the order right.
+ GSList* viewList = g_slist_reverse(webViewList);
+ for (unsigned i = 0; i < g_slist_length(viewList); ++i)
+ dumpBackForwardListForWebView(WEBKIT_WEB_VIEW(g_slist_nth_data(viewList, i)));
+}
+
+static void invalidateAnyPreviousWaitToDumpWatchdog()
+{
+ if (waitToDumpWatchdog) {
+ g_source_remove(waitToDumpWatchdog);
+ waitToDumpWatchdog = 0;
+ }
+
+ waitForPolicy = false;
+}
+
+static void resetDefaultsToConsistentValues()
+{
+ WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
+ g_object_set(G_OBJECT(settings),
+ "enable-private-browsing", FALSE,
+ "enable-developer-extras", FALSE,
+ "enable-spell-checking", TRUE,
+ "enable-html5-database", TRUE,
+ "enable-html5-local-storage", TRUE,
+ "enable-xss-auditor", FALSE,
+ "enable-spatial-navigation", FALSE,
+ "javascript-can-access-clipboard", TRUE,
+ "javascript-can-open-windows-automatically", TRUE,
+ "enable-offline-web-application-cache", TRUE,
+ "enable-universal-access-from-file-uris", TRUE,
+ "enable-scripts", TRUE,
+ "enable-dom-paste", TRUE,
+ "default-font-family", "Times",
+ "monospace-font-family", "Courier",
+ "serif-font-family", "Times",
+ "sans-serif-font-family", "Helvetica",
+ "default-font-size", 16,
+ "default-monospace-font-size", 13,
+ "minimum-font-size", 1,
+ "enable-caret-browsing", FALSE,
+ "enable-page-cache", FALSE,
+ "auto-resize-window", TRUE,
+ "enable-java-applet", FALSE,
+ "enable-plugins", TRUE,
+ "editing-behavior", WEBKIT_EDITING_BEHAVIOR_MAC,
+ NULL);
+
+ webkit_web_frame_clear_main_frame_name(mainFrame);
+
+ WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView);
+ g_object_set(G_OBJECT(inspector), "javascript-profiling-enabled", FALSE, NULL);
+
+ webkit_web_view_set_zoom_level(webView, 1.0);
+
+ webkit_reset_origin_access_white_lists();
+
+#ifdef HAVE_LIBSOUP_2_29_90
+ SoupSession* session = webkit_get_default_session();
+ SoupCookieJar* jar = reinterpret_cast<SoupCookieJar*>(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR));
+
+ // We only create the jar when the soup backend needs to do
+ // HTTP. Should we initialize it earlier, perhaps?
+ if (jar)
+ g_object_set(G_OBJECT(jar), SOUP_COOKIE_JAR_ACCEPT_POLICY, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY, NULL);
+#endif
+
+ setlocale(LC_ALL, "");
+}
+
+void dump()
+{
+ invalidateAnyPreviousWaitToDumpWatchdog();
+
+ bool dumpAsText = gLayoutTestController->dumpAsText();
+ if (dumpTree) {
+ char* result = 0;
+ gchar* responseMimeType = webkit_web_frame_get_response_mime_type(mainFrame);
+
+ dumpAsText = g_str_equal(responseMimeType, "text/plain");
+ g_free(responseMimeType);
+
+ // Test can request controller to be dumped as text even
+ // while test's response mime type is not text/plain.
+ // Overriding this behavior with dumpAsText being false is a bad idea.
+ if (dumpAsText)
+ gLayoutTestController->setDumpAsText(dumpAsText);
+
+ if (gLayoutTestController->dumpAsText())
+ result = dumpFramesAsText(mainFrame);
+ else {
+ // Widget resizing is done asynchronously in GTK+. We pump the main
+ // loop here, to flush any pending resize requests. This prevents
+ // timing issues which affect the size of elements in the output.
+ // We only enable this workaround for tests that print the render tree
+ // because this seems to break some dumpAsText tests: see bug 39988
+ // After fixing that test, we should apply this approach to all dumps.
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ result = webkit_web_frame_dump_render_tree(mainFrame);
+ }
+
+ if (!result) {
+ const char* errorMessage;
+ if (gLayoutTestController->dumpAsText())
+ errorMessage = "[documentElement innerText]";
+ else if (gLayoutTestController->dumpDOMAsWebArchive())
+ errorMessage = "[[mainFrame DOMDocument] webArchive]";
+ else if (gLayoutTestController->dumpSourceAsWebArchive())
+ errorMessage = "[[mainFrame dataSource] webArchive]";
+ else
+ errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
+ printf("ERROR: nil result from %s", errorMessage);
+ } else {
+ printf("%s", result);
+ g_free(result);
+ if (!gLayoutTestController->dumpAsText() && !gLayoutTestController->dumpDOMAsWebArchive() && !gLayoutTestController->dumpSourceAsWebArchive())
+ dumpFrameScrollPosition(mainFrame);
+
+ if (gLayoutTestController->dumpBackForwardList())
+ dumpBackForwardListForAllWebViews();
+ }
+
+ if (printSeparators) {
+ puts("#EOF"); // terminate the content block
+ fputs("#EOF\n", stderr);
+ fflush(stdout);
+ fflush(stderr);
+ }
+ }
+
+ if (dumpPixels
+ && gLayoutTestController->generatePixelResults()
+ && !gLayoutTestController->dumpDOMAsWebArchive()
+ && !gLayoutTestController->dumpSourceAsWebArchive())
+ dumpWebViewAsPixelsAndCompareWithExpected(gLayoutTestController->expectedPixelHash());
+
+ // FIXME: call displayWebView here when we support --paint
+
+ done = true;
+ gtk_main_quit();
+}
+
+static void setDefaultsToConsistentStateValuesForTesting()
+{
+ gdk_screen_set_resolution(gdk_screen_get_default(), 72.0);
+
+ resetDefaultsToConsistentValues();
+
+ /* Disable the default auth dialog for testing */
+ SoupSession* session = webkit_get_default_session();
+ soup_session_remove_feature_by_type(session, WEBKIT_TYPE_SOUP_AUTH_DIALOG);
+
+#if PLATFORM(X11)
+ webkit_web_settings_add_extra_plugin_directory(webView, TEST_PLUGIN_DIR);
+#endif
+
+ gchar* databaseDirectory = g_build_filename(g_get_user_data_dir(), "gtkwebkitdrt", "databases", NULL);
+ webkit_set_web_database_directory_path(databaseDirectory);
+ g_free(databaseDirectory);
+}
+
+static void sendPixelResultsEOF()
+{
+ puts("#EOF");
+
+ fflush(stdout);
+ fflush(stderr);
+}
+
+static void runTest(const string& testPathOrURL)
+{
+ ASSERT(!testPathOrURL.empty());
+
+ // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows.
+ string testURL(testPathOrURL);
+ string expectedPixelHash;
+ size_t separatorPos = testURL.find("'");
+ if (separatorPos != string::npos) {
+ testURL = string(testPathOrURL, 0, separatorPos);
+ expectedPixelHash = string(testPathOrURL, separatorPos + 1);
+ }
+
+ // Convert the path into a full file URL if it does not look
+ // like an HTTP/S URL (doesn't start with http:// or https://).
+ if (testURL.find("http://") && testURL.find("https://")) {
+ GFile* testFile = g_file_new_for_path(testURL.c_str());
+ gchar* testURLCString = g_file_get_uri(testFile);
+ testURL = testURLCString;
+ g_free(testURLCString);
+ g_object_unref(testFile);
+ }
+
+ resetDefaultsToConsistentValues();
+
+ gLayoutTestController = LayoutTestController::create(testURL, expectedPixelHash);
+ topLoadingFrame = 0;
+ done = false;
+
+ gLayoutTestController->setIconDatabaseEnabled(false);
+
+ if (shouldLogFrameLoadDelegates(testURL))
+ gLayoutTestController->setDumpFrameLoadCallbacks(true);
+
+ if (shouldEnableDeveloperExtras(testURL)) {
+ gLayoutTestController->setDeveloperExtrasEnabled(true);
+ if (shouldOpenWebInspector(testURL))
+ gLayoutTestController->showWebInspector();
+ }
+
+ WorkQueue::shared()->clear();
+ WorkQueue::shared()->setFrozen(false);
+
+ bool isSVGW3CTest = (testURL.find("svg/W3C-SVG-1.1") != string::npos);
+ GtkAllocation size;
+ size.x = size.y = 0;
+ size.width = isSVGW3CTest ? 480 : LayoutTestController::maxViewWidth;
+ size.height = isSVGW3CTest ? 360 : LayoutTestController::maxViewHeight;
+ gtk_window_resize(GTK_WINDOW(window), size.width, size.height);
+ gtk_widget_size_allocate(container, &size);
+
+ if (prevTestBFItem)
+ g_object_unref(prevTestBFItem);
+ WebKitWebBackForwardList* bfList = webkit_web_view_get_back_forward_list(webView);
+ prevTestBFItem = webkit_web_back_forward_list_get_current_item(bfList);
+ if (prevTestBFItem)
+ g_object_ref(prevTestBFItem);
+
+#if PLATFORM(X11)
+ initializeFonts();
+#endif
+
+ // Focus the web view before loading the test to avoid focusing problems
+ gtk_widget_grab_focus(GTK_WIDGET(webView));
+ webkit_web_view_open(webView, testURL.c_str());
+
+ gtk_main();
+
+ // If developer extras enabled Web Inspector may have been open by the test.
+ if (shouldEnableDeveloperExtras(testURL)) {
+ gLayoutTestController->closeWebInspector();
+ gLayoutTestController->setDeveloperExtrasEnabled(false);
+ }
+
+ // Also check if we still have opened webViews and free them.
+ if (gLayoutTestController->closeRemainingWindowsWhenComplete() || webViewList) {
+ while (webViewList) {
+ g_object_unref(WEBKIT_WEB_VIEW(webViewList->data));
+ webViewList = g_slist_next(webViewList);
+ }
+ g_slist_free(webViewList);
+ webViewList = 0;
+ }
+
+ // A blank load seems to be necessary to reset state after certain tests.
+ webkit_web_view_open(webView, "about:blank");
+
+ gLayoutTestController.clear();
+
+ // terminate the (possibly empty) pixels block after all the state reset
+ sendPixelResultsEOF();
+}
+
+void webViewLoadStarted(WebKitWebView* view, WebKitWebFrame* frame, void*)
+{
+ // Make sure we only set this once per test. If it gets cleared, and then set again, we might
+ // end up doing two dumps for one test.
+ if (!topLoadingFrame && !done)
+ topLoadingFrame = frame;
+}
+
+static gboolean processWork(void* data)
+{
+ // if we finish all the commands, we're ready to dump state
+ if (WorkQueue::shared()->processWork() && !gLayoutTestController->waitToDump())
+ dump();
+
+ return FALSE;
+}
+
+static char* getFrameNameSuitableForTestResult(WebKitWebView* view, WebKitWebFrame* frame)
+{
+ char* frameName = g_strdup(webkit_web_frame_get_name(frame));
+
+ if (frame == webkit_web_view_get_main_frame(view)) {
+ // This is a bit strange. Shouldn't web_frame_get_name return NULL?
+ if (frameName && (frameName[0] != '\0')) {
+ char* tmp = g_strdup_printf("main frame \"%s\"", frameName);
+ g_free(frameName);
+ frameName = tmp;
+ } else {
+ g_free(frameName);
+ frameName = g_strdup("main frame");
+ }
+ } else if (!frameName || (frameName[0] == '\0')) {
+ g_free(frameName);
+ frameName = g_strdup("frame (anonymous)");
+ } else {
+ char* tmp = g_strdup_printf("frame \"%s\"", frameName);
+ g_free(frameName);
+ frameName = tmp;
+ }
+
+ return frameName;
+}
+
+static void webViewLoadCommitted(WebKitWebView* view, WebKitWebFrame* frame, void*)
+{
+ if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
+ char* frameName = getFrameNameSuitableForTestResult(view, frame);
+ printf("%s - didCommitLoadForFrame\n", frameName);
+ g_free(frameName);
+ }
+}
+
+
+static void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
+{
+ if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
+ char* frameName = getFrameNameSuitableForTestResult(view, frame);
+ printf("%s - didFinishLoadForFrame\n", frameName);
+ g_free(frameName);
+ }
+
+ if (frame != topLoadingFrame)
+ return;
+
+ topLoadingFrame = 0;
+ WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
+ if (gLayoutTestController->waitToDump())
+ return;
+
+ if (WorkQueue::shared()->count())
+ g_timeout_add(0, processWork, 0);
+ else
+ dump();
+}
+
+static void webViewDocumentLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
+{
+ if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
+ char* frameName = getFrameNameSuitableForTestResult(view, frame);
+ printf("%s - didFinishDocumentLoadForFrame\n", frameName);
+ g_free(frameName);
+ } else if (!done) {
+ guint pendingFrameUnloadEvents = webkit_web_frame_get_pending_unload_event_count(frame);
+ if (pendingFrameUnloadEvents) {
+ char* frameName = getFrameNameSuitableForTestResult(view, frame);
+ printf("%s - has %u onunload handler(s)\n", frameName, pendingFrameUnloadEvents);
+ g_free(frameName);
+ }
+ }
+}
+
+static void webViewOnloadEvent(WebKitWebView* view, WebKitWebFrame* frame, void*)
+{
+ if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
+ char* frameName = getFrameNameSuitableForTestResult(view, frame);
+ printf("%s - didHandleOnloadEventsForFrame\n", frameName);
+ g_free(frameName);
+ }
+}
+
+static void webViewWindowObjectCleared(WebKitWebView* view, WebKitWebFrame* frame, JSGlobalContextRef context, JSObjectRef windowObject, gpointer data)
+{
+ JSValueRef exception = 0;
+ ASSERT(gLayoutTestController);
+
+ gLayoutTestController->makeWindowObject(context, windowObject, &exception);
+ ASSERT(!exception);
+
+ gcController->makeWindowObject(context, windowObject, &exception);
+ ASSERT(!exception);
+
+ axController->makeWindowObject(context, windowObject, &exception);
+ ASSERT(!exception);
+
+ JSStringRef eventSenderStr = JSStringCreateWithUTF8CString("eventSender");
+ JSValueRef eventSender = makeEventSender(context, !webkit_web_frame_get_parent(frame));
+ JSObjectSetProperty(context, windowObject, eventSenderStr, eventSender, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, 0);
+ JSStringRelease(eventSenderStr);
+}
+
+static gboolean webViewConsoleMessage(WebKitWebView* view, const gchar* message, unsigned int line, const gchar* sourceId, gpointer data)
+{
+ gchar* testMessage = 0;
+ const gchar* uriScheme;
+
+ // Tests expect only the filename part of local URIs
+ uriScheme = g_strstr_len(message, -1, "file://");
+ if (uriScheme) {
+ GString* tempString = g_string_sized_new(strlen(message));
+ gchar* filename = g_strrstr(uriScheme, G_DIR_SEPARATOR_S);
+
+ if (filename) {
+ filename += strlen(G_DIR_SEPARATOR_S);
+ tempString = g_string_append_len(tempString, message, (uriScheme - message));
+ tempString = g_string_append_len(tempString, filename, strlen(filename));
+ testMessage = g_string_free(tempString, FALSE);
+ }
+ }
+
+ fprintf(stdout, "CONSOLE MESSAGE: line %d: %s\n", line, testMessage ? testMessage : message);
+ g_free(testMessage);
+
+ return TRUE;
+}
+
+
+static gboolean webViewScriptAlert(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gpointer data)
+{
+ fprintf(stdout, "ALERT: %s\n", message);
+ return TRUE;
+}
+
+static gboolean webViewScriptPrompt(WebKitWebView* webView, WebKitWebFrame* frame, const gchar* message, const gchar* defaultValue, gchar** value, gpointer data)
+{
+ fprintf(stdout, "PROMPT: %s, default text: %s\n", message, defaultValue);
+ *value = g_strdup(defaultValue);
+ return TRUE;
+}
+
+static gboolean webViewScriptConfirm(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gboolean* didConfirm, gpointer data)
+{
+ fprintf(stdout, "CONFIRM: %s\n", message);
+ *didConfirm = TRUE;
+ return TRUE;
+}
+
+static void webViewTitleChanged(WebKitWebView* view, WebKitWebFrame* frame, const gchar* title, gpointer data)
+{
+ if (gLayoutTestController->dumpTitleChanges() && !done)
+ printf("TITLE CHANGED: %s\n", title ? title : "");
+}
+
+static bool webViewNavigationPolicyDecisionRequested(WebKitWebView* view, WebKitWebFrame* frame,
+ WebKitNetworkRequest* request,
+ WebKitWebNavigationAction* navAction,
+ WebKitWebPolicyDecision* policyDecision)
+{
+ // Use the default handler if we're not waiting for policy,
+ // i.e., LayoutTestController::waitForPolicyDelegate
+ if (!waitForPolicy)
+ return FALSE;
+
+ gchar* typeDescription;
+ WebKitWebNavigationReason reason;
+ g_object_get(G_OBJECT(navAction), "reason", &reason, NULL);
+
+ switch(reason) {
+ case WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED:
+ typeDescription = g_strdup("link clicked");
+ break;
+ case WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED:
+ typeDescription = g_strdup("form submitted");
+ break;
+ case WEBKIT_WEB_NAVIGATION_REASON_BACK_FORWARD:
+ typeDescription = g_strdup("back/forward");
+ break;
+ case WEBKIT_WEB_NAVIGATION_REASON_RELOAD:
+ typeDescription = g_strdup("reload");
+ break;
+ case WEBKIT_WEB_NAVIGATION_REASON_FORM_RESUBMITTED:
+ typeDescription = g_strdup("form resubmitted");
+ break;
+ case WEBKIT_WEB_NAVIGATION_REASON_OTHER:
+ typeDescription = g_strdup("other");
+ break;
+ default:
+ typeDescription = g_strdup("illegal value");
+ }
+
+ printf("Policy delegate: attempt to load %s with navigation type '%s'\n", webkit_network_request_get_uri(request), typeDescription);
+ g_free(typeDescription);
+
+ webkit_web_policy_decision_ignore(policyDecision);
+ gLayoutTestController->notifyDone();
+
+ return TRUE;
+}
+
+static void webViewStatusBarTextChanged(WebKitWebView* view, const gchar* message, gpointer data)
+{
+ // Are we doing anything wrong? One test that does not call
+ // dumpStatusCallbacks gets true here
+ if (gLayoutTestController->dumpStatusCallbacks()) {
+ if (message && strcmp(message, ""))
+ printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", message);
+ }
+}
+
+static gboolean webViewClose(WebKitWebView* view)
+{
+ ASSERT(view);
+
+ webViewList = g_slist_remove(webViewList, view);
+ g_object_unref(view);
+
+ return TRUE;
+}
+
+static void databaseQuotaExceeded(WebKitWebView* view, WebKitWebFrame* frame, WebKitWebDatabase *database)
+{
+ ASSERT(view);
+ ASSERT(frame);
+ ASSERT(database);
+
+ WebKitSecurityOrigin* origin = webkit_web_database_get_security_origin(database);
+ if (gLayoutTestController->dumpDatabaseCallbacks()) {
+ printf("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{%s, %s, %i} database:%s\n",
+ webkit_security_origin_get_protocol(origin),
+ webkit_security_origin_get_host(origin),
+ webkit_security_origin_get_port(origin),
+ webkit_web_database_get_name(database));
+ }
+ webkit_security_origin_set_web_database_quota(origin, 5 * 1024 * 1024);
+}
+
+static bool
+geolocationPolicyDecisionRequested(WebKitWebView*, WebKitWebFrame*, WebKitGeolocationPolicyDecision* decision)
+{
+ if (!gLayoutTestController->isGeolocationPermissionSet())
+ return FALSE;
+ if (gLayoutTestController->geolocationPermission())
+ webkit_geolocation_policy_allow(decision);
+ else
+ webkit_geolocation_policy_deny(decision);
+
+ return TRUE;
+}
+
+
+static WebKitWebView* webViewCreate(WebKitWebView*, WebKitWebFrame*);
+
+static gboolean webInspectorShowWindow(WebKitWebInspector*, gpointer data)
+{
+ gtk_window_set_default_size(GTK_WINDOW(webInspectorWindow), 800, 600);
+ gtk_widget_show_all(webInspectorWindow);
+ return TRUE;
+}
+
+static gboolean webInspectorCloseWindow(WebKitWebInspector*, gpointer data)
+{
+ gtk_widget_destroy(webInspectorWindow);
+ webInspectorWindow = 0;
+ return TRUE;
+}
+
+static WebKitWebView* webInspectorInspectWebView(WebKitWebInspector*, gpointer data)
+{
+ webInspectorWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ GtkWidget* webView = webkit_web_view_new();
+ gtk_container_add(GTK_CONTAINER(webInspectorWindow),
+ webView);
+
+ return WEBKIT_WEB_VIEW(webView);
+}
+
+static void webViewLoadStatusNotified(WebKitWebView* view, gpointer user_data)
+{
+ WebKitLoadStatus loadStatus = webkit_web_view_get_load_status(view);
+
+ if (gLayoutTestController->dumpFrameLoadCallbacks()) {
+ if (loadStatus == WEBKIT_LOAD_PROVISIONAL) {
+ char* frameName = getFrameNameSuitableForTestResult(view, mainFrame);
+ printf("%s - didStartProvisionalLoadForFrame\n", frameName);
+ g_free(frameName);
+ }
+ }
+}
+
+static WebKitWebView* createWebView()
+{
+ WebKitWebView* view = WEBKIT_WEB_VIEW(webkit_web_view_new());
+
+ // From bug 11756: Use a frame group name for all WebViews created by
+ // DumpRenderTree to allow testing of cross-page frame lookup.
+ webkit_web_view_set_group_name(view, "org.webkit.gtk.DumpRenderTree");
+
+ g_object_connect(G_OBJECT(view),
+ "signal::load-started", webViewLoadStarted, 0,
+ "signal::load-finished", webViewLoadFinished, 0,
+ "signal::load-committed", webViewLoadCommitted, 0,
+ "signal::window-object-cleared", webViewWindowObjectCleared, 0,
+ "signal::console-message", webViewConsoleMessage, 0,
+ "signal::script-alert", webViewScriptAlert, 0,
+ "signal::script-prompt", webViewScriptPrompt, 0,
+ "signal::script-confirm", webViewScriptConfirm, 0,
+ "signal::title-changed", webViewTitleChanged, 0,
+ "signal::navigation-policy-decision-requested", webViewNavigationPolicyDecisionRequested, 0,
+ "signal::status-bar-text-changed", webViewStatusBarTextChanged, 0,
+ "signal::create-web-view", webViewCreate, 0,
+ "signal::close-web-view", webViewClose, 0,
+ "signal::database-quota-exceeded", databaseQuotaExceeded, 0,
+ "signal::document-load-finished", webViewDocumentLoadFinished, 0,
+ "signal::geolocation-policy-decision-requested", geolocationPolicyDecisionRequested, 0,
+ "signal::onload-event", webViewOnloadEvent, 0,
+ NULL);
+
+ g_signal_connect(view,
+ "notify::load-status", G_CALLBACK(webViewLoadStatusNotified),
+ NULL);
+
+ WebKitWebInspector* inspector = webkit_web_view_get_inspector(view);
+ g_object_connect(G_OBJECT(inspector),
+ "signal::inspect-web-view", webInspectorInspectWebView, 0,
+ "signal::show-window", webInspectorShowWindow, 0,
+ "signal::close-window", webInspectorCloseWindow, 0,
+ NULL);
+
+ if (webView) {
+ WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
+ webkit_web_view_set_settings(view, settings);
+ }
+
+ return view;
+}
+
+static WebKitWebView* webViewCreate(WebKitWebView* view, WebKitWebFrame* frame)
+{
+ if (!gLayoutTestController->canOpenWindows())
+ return 0;
+
+ // Make sure that waitUntilDone has been called.
+ ASSERT(gLayoutTestController->waitToDump());
+
+ WebKitWebView* newWebView = createWebView();
+ g_object_ref_sink(G_OBJECT(newWebView));
+ webViewList = g_slist_prepend(webViewList, newWebView);
+ return newWebView;
+}
+
+static void logHandler(const gchar* domain, GLogLevelFlags level, const gchar* message, gpointer data)
+{
+ if (level < G_LOG_LEVEL_DEBUG)
+ fprintf(stderr, "%s\n", message);
+}
+
+int main(int argc, char* argv[])
+{
+ g_thread_init(NULL);
+ gtk_init(&argc, &argv);
+
+ // Some plugins might try to use the GLib logger for printing debug
+ // messages. This will cause tests to fail because of unexpected output.
+ // We squelch all debug messages sent to the logger.
+ g_log_set_default_handler(logHandler, 0);
+
+#if PLATFORM(X11)
+ FcInit();
+ initializeFonts();
+#endif
+
+ struct option options[] = {
+ {"notree", no_argument, &dumpTree, false},
+ {"pixel-tests", no_argument, &dumpPixels, true},
+ {"tree", no_argument, &dumpTree, true},
+ {NULL, 0, NULL, 0}
+ };
+
+ int option;
+ while ((option = getopt_long(argc, (char* const*)argv, "", options, NULL)) != -1)
+ switch (option) {
+ case '?': // unknown or ambiguous option
+ case ':': // missing argument
+ exit(1);
+ break;
+ }
+
+ window = gtk_window_new(GTK_WINDOW_POPUP);
+ container = GTK_WIDGET(gtk_scrolled_window_new(NULL, NULL));
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(container), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(window), container);
+ gtk_widget_show_all(window);
+
+ webView = createWebView();
+ gtk_container_add(GTK_CONTAINER(container), GTK_WIDGET(webView));
+ gtk_widget_realize(GTK_WIDGET(webView));
+ gtk_widget_show_all(container);
+ gtk_widget_grab_focus(GTK_WIDGET(webView));
+ mainFrame = webkit_web_view_get_main_frame(webView);
+
+ setDefaultsToConsistentStateValuesForTesting();
+
+ gcController = new GCController();
+ axController = new AccessibilityController();
+
+ if (argc == optind+1 && strcmp(argv[optind], "-") == 0) {
+ char filenameBuffer[2048];
+ printSeparators = true;
+ while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
+ char* newLineCharacter = strchr(filenameBuffer, '\n');
+ if (newLineCharacter)
+ *newLineCharacter = '\0';
+
+ if (strlen(filenameBuffer) == 0)
+ continue;
+
+ runTest(filenameBuffer);
+ }
+ } else {
+ printSeparators = (optind < argc-1 || (dumpPixels && dumpTree));
+ for (int i = optind; i != argc; ++i)
+ runTest(argv[i]);
+ }
+
+ delete gcController;
+ gcController = 0;
+
+ delete axController;
+ axController = 0;
+
+ gtk_widget_destroy(window);
+
+ return 0;
+}