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 Igalia S.L.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
33 #include "DumpRenderTree.h"
35 #include "AccessibilityController.h"
36 #include "EventSender.h"
37 #include "GCController.h"
39 #include "LayoutTestController.h"
40 #include "PixelDumpSupport.h"
41 #include "WorkQueue.h"
42 #include "WorkQueueItem.h"
43 #include <JavaScriptCore/JavaScript.h>
49 #include <webkit/webkit.h>
50 #include <wtf/Assertions.h>
53 #include <fontconfig/fontconfig.h>
60 // This API is not yet public.
61 extern G_CONST_RETURN gchar* webkit_web_history_item_get_target(WebKitWebHistoryItem*);
62 extern gboolean webkit_web_history_item_is_target_item(WebKitWebHistoryItem*);
63 extern GList* webkit_web_history_item_get_children(WebKitWebHistoryItem*);
64 extern GSList* webkit_web_frame_get_children(WebKitWebFrame* frame);
65 extern gchar* webkit_web_frame_get_inner_text(WebKitWebFrame* frame);
66 extern gchar* webkit_web_frame_dump_render_tree(WebKitWebFrame* frame);
67 extern guint webkit_web_frame_get_pending_unload_event_count(WebKitWebFrame* frame);
68 extern void webkit_web_settings_add_extra_plugin_directory(WebKitWebView* view, const gchar* directory);
69 extern gchar* webkit_web_frame_get_response_mime_type(WebKitWebFrame* frame);
70 extern void webkit_web_frame_clear_main_frame_name(WebKitWebFrame* frame);
71 extern void webkit_web_view_set_group_name(WebKitWebView* view, const gchar* groupName);
72 extern void webkit_reset_origin_access_white_lists();
76 static bool printSeparators;
77 static int dumpPixels;
78 static int dumpTree = 1;
80 AccessibilityController* axController = 0;
81 RefPtr<LayoutTestController> gLayoutTestController;
82 static GCController* gcController = 0;
83 static WebKitWebView* webView;
84 static GtkWidget* window;
85 static GtkWidget* container;
86 static GtkWidget* webInspectorWindow;
87 WebKitWebFrame* mainFrame = 0;
88 WebKitWebFrame* topLoadingFrame = 0;
89 guint waitToDumpWatchdog = 0;
90 bool waitForPolicy = false;
92 // This is a list of opened webviews
93 GSList* webViewList = 0;
95 // current b/f item at the end of the previous test
96 static WebKitWebHistoryItem* prevTestBFItem = NULL;
98 const unsigned historyItemIndent = 8;
100 static void runTest(const string& testPathOrURL);
102 static bool shouldLogFrameLoadDelegates(const string& pathOrURL)
104 return pathOrURL.find("loading/") != string::npos;
107 static bool shouldOpenWebInspector(const string& pathOrURL)
109 return pathOrURL.find("inspector/") != string::npos;
112 static bool shouldEnableDeveloperExtras(const string& pathOrURL)
114 return shouldOpenWebInspector(pathOrURL) || pathOrURL.find("inspector-enabled/") != string::npos;
117 void dumpFrameScrollPosition(WebKitWebFrame* frame)
122 void displayWebView()
124 gtk_widget_queue_draw(GTK_WIDGET(webView));
127 static void appendString(gchar*& target, gchar* string)
129 gchar* oldString = target;
130 target = g_strconcat(target, string, NULL);
134 static void initializeGtkFontSettings(const char* testURL)
136 GtkSettings* settings = gtk_settings_get_default();
139 g_object_set(settings, "gtk-xft-antialias", 1, NULL);
140 g_object_set(settings, "gtk-xft-hinting", 1, NULL);
141 g_object_set(settings, "gtk-xft-hintstyle", "hintfull", NULL);
142 g_object_set(settings, "gtk-font-name", "Liberation Sans 16", NULL);
144 // One test needs subpixel anti-aliasing turned on, but generally we
145 // want all text in other tests to use to grayscale anti-aliasing.
146 if (testURL && strstr(testURL, "xsettings_antialias_settings.html"))
147 g_object_set(settings, "gtk-xft-rgba", "rgb", NULL);
149 g_object_set(settings, "gtk-xft-rgba", "none", NULL);
152 static void initializeFonts(const char* testURL = 0)
155 initializeGtkFontSettings(testURL);
159 // If a test resulted a font being added or removed via the @font-face rule, then
160 // we want to reset the FontConfig configuration to prevent it from affecting other tests.
161 static int numFonts = 0;
162 FcFontSet* appFontSet = FcConfigGetFonts(0, FcSetApplication);
163 if (appFontSet && numFonts && appFontSet->nfont == numFonts)
166 // Load our configuration file, which sets up proper aliases for family
167 // names like sans, serif and monospace.
168 FcConfig* config = FcConfigCreate();
169 GOwnPtr<gchar> fontConfigFilename(g_build_filename(FONTS_CONF_DIR, "fonts.conf", NULL));
170 if (!FcConfigParseAndLoad(config, reinterpret_cast<FcChar8*>(fontConfigFilename.get()), true))
171 g_error("Couldn't load font configuration file from: %s", fontConfigFilename.get());
173 static const char *const fontPaths[][2] = {
174 { "/usr/share/fonts/truetype/ttf-liberation/LiberationMono-BoldItalic.ttf",
175 "/usr/share/fonts/liberation/LiberationMono-BoldItalic.ttf", },
176 { "/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Bold.ttf",
177 "/usr/share/fonts/liberation/LiberationMono-Bold.ttf", },
178 { "/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Italic.ttf",
179 "/usr/share/fonts/liberation/LiberationMono-Italic.ttf", },
180 { "/usr/share/fonts/truetype/ttf-liberation/LiberationMono-Regular.ttf",
181 "/usr/share/fonts/liberation/LiberationMono-Regular.ttf", },
182 { "/usr/share/fonts/truetype/ttf-liberation/LiberationSans-BoldItalic.ttf",
183 "/usr/share/fonts/liberation/LiberationSans-BoldItalic.ttf", },
184 { "/usr/share/fonts/truetype/ttf-liberation/LiberationSans-Bold.ttf",
185 "/usr/share/fonts/liberation/LiberationSans-Bold.ttf", },
186 { "/usr/share/fonts/truetype/ttf-liberation/LiberationSans-Italic.ttf",
187 "/usr/share/fonts/liberation/LiberationSans-Italic.ttf", },
188 { "/usr/share/fonts/truetype/ttf-liberation/LiberationSans-Regular.ttf",
189 "/usr/share/fonts/liberation/LiberationSans-Regular.ttf", },
190 { "/usr/share/fonts/truetype/ttf-liberation/LiberationSerif-BoldItalic.ttf",
191 "/usr/share/fonts/liberation/LiberationSerif-BoldItalic.ttf", },
192 { "/usr/share/fonts/truetype/ttf-liberation/LiberationSerif-Bold.ttf",
193 "/usr/share/fonts/liberation/LiberationSerif-Bold.ttf", },
194 { "/usr/share/fonts/truetype/ttf-liberation/LiberationSerif-Italic.ttf",
195 "/usr/share/fonts/liberation/LiberationSerif-Italic.ttf", },
196 { "/usr/share/fonts/truetype/ttf-liberation/LiberationSerif-Regular.ttf",
197 "/usr/share/fonts/liberation/LiberationSerif-Regular.ttf", },
198 { "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf",
199 "/usr/share/fonts/dejavu/DejaVuSans.ttf", },
200 { "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerif.ttf",
201 "/usr/share/fonts/dejavu/DejaVuSerif.ttf", },
203 // MathML tests require the STIX fonts.
204 { "/usr/share/fonts/opentype/stix/STIXGeneral.otf",
205 "/usr/share/fonts/stix/STIXGeneral.otf" },
206 { "/usr/share/fonts/opentype/stix/STIXGeneralBolIta.otf",
207 "/usr/share/fonts/stix/STIXGeneralBolIta.otf" },
208 { "/usr/share/fonts/opentype/stix/STIXGeneralBol.otf",
209 "/usr/share/fonts/stix/STIXGeneralBol.otf" },
210 { "/usr/share/fonts/opentype/stix/STIXGeneralItalic.otf",
211 "/usr/share/fonts/stix/STIXGeneralItalic.otf" }
214 // TODO: Some tests use Lucida. We should load these as well, once it becomes
215 // clear how to install these fonts easily on Fedora.
216 for (size_t font = 0; font < G_N_ELEMENTS(fontPaths); font++) {
218 for (size_t path = 0; path < 2; path++) {
220 if (g_file_test(fontPaths[font][path], G_FILE_TEST_EXISTS)) {
222 if (!FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(fontPaths[font][path])))
223 g_error("Could not load font at %s!", fontPaths[font][path]);
230 g_error("Could not find font at %s. Either install this font or file a bug "
231 "at http://bugs.webkit.org if it is installed in another location.",
235 // Ahem is used by many layout tests.
236 GOwnPtr<gchar> ahemFontFilename(g_build_filename(FONTS_CONF_DIR, "AHEM____.TTF", NULL));
237 if (!FcConfigAppFontAddFile(config, reinterpret_cast<FcChar8*>(ahemFontFilename.get())))
238 g_error("Could not load font at %s!", ahemFontFilename.get());
240 if (!FcConfigSetCurrent(config))
241 g_error("Could not set the current font configuration!");
243 numFonts = FcConfigGetFonts(config, FcSetApplication)->nfont;
247 static gchar* dumpFramesAsText(WebKitWebFrame* frame)
251 // Add header for all but the main frame.
252 bool isMainFrame = (webkit_web_view_get_main_frame(webView) == frame);
254 gchar* innerText = webkit_web_frame_get_inner_text(frame);
256 result = g_strdup_printf("%s\n", innerText);
258 const gchar* frameName = webkit_web_frame_get_name(frame);
259 result = g_strdup_printf("\n--------\nFrame: '%s'\n--------\n%s\n", frameName, innerText);
263 if (gLayoutTestController->dumpChildFramesAsText()) {
264 GSList* children = webkit_web_frame_get_children(frame);
265 for (GSList* child = children; child; child = g_slist_next(child))
266 appendString(result, dumpFramesAsText(static_cast<WebKitWebFrame* >(child->data)));
267 g_slist_free(children);
273 static gint compareHistoryItems(gpointer* item1, gpointer* item2)
275 return g_ascii_strcasecmp(webkit_web_history_item_get_target(WEBKIT_WEB_HISTORY_ITEM(item1)),
276 webkit_web_history_item_get_target(WEBKIT_WEB_HISTORY_ITEM(item2)));
279 static void dumpHistoryItem(WebKitWebHistoryItem* item, int indent, bool current)
281 ASSERT(item != NULL);
288 for (int i = start; i < indent; i++)
291 // normalize file URLs.
292 const gchar* uri = webkit_web_history_item_get_uri(item);
293 gchar* uriScheme = g_uri_parse_scheme(uri);
294 if (g_strcmp0(uriScheme, "file") == 0) {
295 gchar* pos = g_strstr_len(uri, -1, "/LayoutTests/");
299 GString* result = g_string_sized_new(strlen(uri));
300 result = g_string_append(result, "(file test):");
301 result = g_string_append(result, pos + strlen("/LayoutTests/"));
302 printf("%s", result->str);
303 g_string_free(result, TRUE);
309 const gchar* target = webkit_web_history_item_get_target(item);
310 if (target && strlen(target) > 0)
311 printf(" (in frame \"%s\")", target);
312 if (webkit_web_history_item_is_target_item(item))
313 printf(" **nav target**");
315 GList* kids = webkit_web_history_item_get_children(item);
317 // must sort to eliminate arbitrary result ordering which defeats reproducible testing
318 kids = g_list_sort(kids, (GCompareFunc) compareHistoryItems);
319 for (unsigned i = 0; i < g_list_length(kids); i++)
320 dumpHistoryItem(WEBKIT_WEB_HISTORY_ITEM(g_list_nth_data(kids, i)), indent+4, FALSE);
322 g_object_unref(item);
325 static void dumpBackForwardListForWebView(WebKitWebView* view)
327 printf("\n============== Back Forward List ==============\n");
328 WebKitWebBackForwardList* bfList = webkit_web_view_get_back_forward_list(view);
330 // Print out all items in the list after prevTestBFItem, which was from the previous test
331 // Gather items from the end of the list, the print them out from oldest to newest
332 GList* itemsToPrint = NULL;
333 gint forwardListCount = webkit_web_back_forward_list_get_forward_length(bfList);
334 for (int i = forwardListCount; i > 0; i--) {
335 WebKitWebHistoryItem* item = webkit_web_back_forward_list_get_nth_item(bfList, i);
336 // something is wrong if the item from the last test is in the forward part of the b/f list
337 ASSERT(item != prevTestBFItem);
339 itemsToPrint = g_list_append(itemsToPrint, item);
342 WebKitWebHistoryItem* currentItem = webkit_web_back_forward_list_get_current_item(bfList);
344 g_object_ref(currentItem);
345 itemsToPrint = g_list_append(itemsToPrint, currentItem);
347 gint currentItemIndex = g_list_length(itemsToPrint) - 1;
348 gint backListCount = webkit_web_back_forward_list_get_back_length(bfList);
349 for (int i = -1; i >= -(backListCount); i--) {
350 WebKitWebHistoryItem* item = webkit_web_back_forward_list_get_nth_item(bfList, i);
351 if (item == prevTestBFItem)
354 itemsToPrint = g_list_append(itemsToPrint, item);
357 for (int i = g_list_length(itemsToPrint) - 1; i >= 0; i--) {
358 WebKitWebHistoryItem* item = WEBKIT_WEB_HISTORY_ITEM(g_list_nth_data(itemsToPrint, i));
359 dumpHistoryItem(item, historyItemIndent, i == currentItemIndex);
360 g_object_unref(item);
362 g_list_free(itemsToPrint);
363 printf("===============================================\n");
366 static void dumpBackForwardListForAllWebViews()
368 // Dump the back forward list of the main WebView first
369 dumpBackForwardListForWebView(webView);
371 // The view list is prepended. Reverse the list so we get the order right.
372 GSList* viewList = g_slist_reverse(webViewList);
373 for (unsigned i = 0; i < g_slist_length(viewList); ++i)
374 dumpBackForwardListForWebView(WEBKIT_WEB_VIEW(g_slist_nth_data(viewList, i)));
377 static void invalidateAnyPreviousWaitToDumpWatchdog()
379 if (waitToDumpWatchdog) {
380 g_source_remove(waitToDumpWatchdog);
381 waitToDumpWatchdog = 0;
384 waitForPolicy = false;
387 static void resetDefaultsToConsistentValues()
389 WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
390 g_object_set(G_OBJECT(settings),
391 "enable-private-browsing", FALSE,
392 "enable-developer-extras", FALSE,
393 "enable-spell-checking", TRUE,
394 "enable-html5-database", TRUE,
395 "enable-html5-local-storage", TRUE,
396 "enable-xss-auditor", FALSE,
397 "enable-spatial-navigation", FALSE,
398 "enable-frame-flattening", FALSE,
399 "javascript-can-access-clipboard", TRUE,
400 "javascript-can-open-windows-automatically", TRUE,
401 "enable-offline-web-application-cache", TRUE,
402 "enable-universal-access-from-file-uris", TRUE,
403 "enable-scripts", TRUE,
404 "enable-dom-paste", TRUE,
405 "default-font-family", "Times",
406 "monospace-font-family", "Courier",
407 "serif-font-family", "Times",
408 "sans-serif-font-family", "Helvetica",
409 "cursive-font-family", "cursive",
410 "fantasy-font-family", "fantasy",
411 "default-font-size", 16,
412 "default-monospace-font-size", 13,
413 "minimum-font-size", 1,
414 "enable-caret-browsing", FALSE,
415 "enable-page-cache", FALSE,
416 "auto-resize-window", TRUE,
417 "enable-java-applet", FALSE,
418 "enable-plugins", TRUE,
419 "enable-hyperlink-auditing", FALSE,
420 "editing-behavior", WEBKIT_EDITING_BEHAVIOR_MAC,
423 webkit_web_frame_clear_main_frame_name(mainFrame);
425 WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView);
426 g_object_set(G_OBJECT(inspector), "javascript-profiling-enabled", FALSE, NULL);
428 webkit_web_view_set_zoom_level(webView, 1.0);
430 webkit_reset_origin_access_white_lists();
432 WebKitWebBackForwardList* list = webkit_web_view_get_back_forward_list(webView);
433 webkit_web_back_forward_list_clear(list);
435 #ifdef HAVE_LIBSOUP_2_29_90
436 SoupSession* session = webkit_get_default_session();
437 SoupCookieJar* jar = reinterpret_cast<SoupCookieJar*>(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR));
439 // We only create the jar when the soup backend needs to do
440 // HTTP. Should we initialize it earlier, perhaps?
442 g_object_set(G_OBJECT(jar), SOUP_COOKIE_JAR_ACCEPT_POLICY, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY, NULL);
445 setlocale(LC_ALL, "");
448 static bool useLongRunningServerMode(int argc, char *argv[])
450 // This assumes you've already called getopt_long
451 return (argc == optind+1 && !strcmp(argv[optind], "-"));
454 static void runTestingServerLoop()
456 // When DumpRenderTree runs in server mode, we just wait around for file names
457 // to be passed to us and read each in turn, passing the results back to the client
458 char filenameBuffer[2048];
459 while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
460 char* newLineCharacter = strchr(filenameBuffer, '\n');
461 if (newLineCharacter)
462 *newLineCharacter = '\0';
464 if (!strlen(filenameBuffer))
467 runTest(filenameBuffer);
471 static void initializeGlobalsFromCommandLineOptions(int argc, char *argv[])
473 struct option options[] = {
474 {"notree", no_argument, &dumpTree, false},
475 {"pixel-tests", no_argument, &dumpPixels, true},
476 {"tree", no_argument, &dumpTree, true},
481 while ((option = getopt_long(argc, (char * const *)argv, "", options, NULL)) != -1) {
483 case '?': // unknown or ambiguous option
484 case ':': // missing argument
494 invalidateAnyPreviousWaitToDumpWatchdog();
496 bool dumpAsText = gLayoutTestController->dumpAsText();
499 gchar* responseMimeType = webkit_web_frame_get_response_mime_type(mainFrame);
501 dumpAsText = g_str_equal(responseMimeType, "text/plain");
502 g_free(responseMimeType);
504 // Test can request controller to be dumped as text even
505 // while test's response mime type is not text/plain.
506 // Overriding this behavior with dumpAsText being false is a bad idea.
508 gLayoutTestController->setDumpAsText(dumpAsText);
510 if (gLayoutTestController->dumpAsText())
511 result = dumpFramesAsText(mainFrame);
513 // Widget resizing is done asynchronously in GTK+. We pump the main
514 // loop here, to flush any pending resize requests. This prevents
515 // timing issues which affect the size of elements in the output.
516 // We only enable this workaround for tests that print the render tree
517 // because this seems to break some dumpAsText tests: see bug 39988
518 // After fixing that test, we should apply this approach to all dumps.
519 while (gtk_events_pending())
520 gtk_main_iteration();
522 result = webkit_web_frame_dump_render_tree(mainFrame);
526 const char* errorMessage;
527 if (gLayoutTestController->dumpAsText())
528 errorMessage = "[documentElement innerText]";
529 else if (gLayoutTestController->dumpDOMAsWebArchive())
530 errorMessage = "[[mainFrame DOMDocument] webArchive]";
531 else if (gLayoutTestController->dumpSourceAsWebArchive())
532 errorMessage = "[[mainFrame dataSource] webArchive]";
534 errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
535 printf("ERROR: nil result from %s", errorMessage);
537 printf("%s", result);
539 if (!gLayoutTestController->dumpAsText() && !gLayoutTestController->dumpDOMAsWebArchive() && !gLayoutTestController->dumpSourceAsWebArchive())
540 dumpFrameScrollPosition(mainFrame);
542 if (gLayoutTestController->dumpBackForwardList())
543 dumpBackForwardListForAllWebViews();
546 if (printSeparators) {
547 puts("#EOF"); // terminate the content block
548 fputs("#EOF\n", stderr);
555 && gLayoutTestController->generatePixelResults()
556 && !gLayoutTestController->dumpDOMAsWebArchive()
557 && !gLayoutTestController->dumpSourceAsWebArchive())
558 dumpWebViewAsPixelsAndCompareWithExpected(gLayoutTestController->expectedPixelHash());
560 // FIXME: call displayWebView here when we support --paint
566 static void setDefaultsToConsistentStateValuesForTesting()
568 gdk_screen_set_resolution(gdk_screen_get_default(), 72.0);
570 resetDefaultsToConsistentValues();
572 /* Disable the default auth dialog for testing */
573 SoupSession* session = webkit_get_default_session();
574 soup_session_remove_feature_by_type(session, WEBKIT_TYPE_SOUP_AUTH_DIALOG);
577 webkit_web_settings_add_extra_plugin_directory(webView, TEST_PLUGIN_DIR);
580 gchar* databaseDirectory = g_build_filename(g_get_user_data_dir(), "gtkwebkitdrt", "databases", NULL);
581 webkit_set_web_database_directory_path(databaseDirectory);
582 g_free(databaseDirectory);
585 static void sendPixelResultsEOF()
593 static void runTest(const string& testPathOrURL)
595 ASSERT(!testPathOrURL.empty());
597 // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows.
598 string testURL(testPathOrURL);
599 string expectedPixelHash;
600 size_t separatorPos = testURL.find("'");
601 if (separatorPos != string::npos) {
602 testURL = string(testPathOrURL, 0, separatorPos);
603 expectedPixelHash = string(testPathOrURL, separatorPos + 1);
606 // Convert the path into a full file URL if it does not look
607 // like an HTTP/S URL (doesn't start with http:// or https://).
608 if (testURL.find("http://") && testURL.find("https://")) {
609 GFile* testFile = g_file_new_for_path(testURL.c_str());
610 gchar* testURLCString = g_file_get_uri(testFile);
611 testURL = testURLCString;
612 g_free(testURLCString);
613 g_object_unref(testFile);
616 resetDefaultsToConsistentValues();
618 gLayoutTestController = LayoutTestController::create(testURL, expectedPixelHash);
622 gLayoutTestController->setIconDatabaseEnabled(false);
624 if (shouldLogFrameLoadDelegates(testURL))
625 gLayoutTestController->setDumpFrameLoadCallbacks(true);
627 if (shouldEnableDeveloperExtras(testURL)) {
628 gLayoutTestController->setDeveloperExtrasEnabled(true);
629 if (shouldOpenWebInspector(testURL))
630 gLayoutTestController->showWebInspector();
633 WorkQueue::shared()->clear();
634 WorkQueue::shared()->setFrozen(false);
636 bool isSVGW3CTest = (testURL.find("svg/W3C-SVG-1.1") != string::npos);
639 size.width = isSVGW3CTest ? 480 : LayoutTestController::maxViewWidth;
640 size.height = isSVGW3CTest ? 360 : LayoutTestController::maxViewHeight;
641 gtk_window_resize(GTK_WINDOW(window), size.width, size.height);
642 gtk_widget_size_allocate(container, &size);
645 g_object_unref(prevTestBFItem);
646 WebKitWebBackForwardList* bfList = webkit_web_view_get_back_forward_list(webView);
647 prevTestBFItem = webkit_web_back_forward_list_get_current_item(bfList);
649 g_object_ref(prevTestBFItem);
651 initializeFonts(testURL.c_str());
653 // Focus the web view before loading the test to avoid focusing problems
654 gtk_widget_grab_focus(GTK_WIDGET(webView));
655 webkit_web_view_open(webView, testURL.c_str());
659 // If developer extras enabled Web Inspector may have been open by the test.
660 if (shouldEnableDeveloperExtras(testURL)) {
661 gLayoutTestController->closeWebInspector();
662 gLayoutTestController->setDeveloperExtrasEnabled(false);
665 // Also check if we still have opened webViews and free them.
666 if (gLayoutTestController->closeRemainingWindowsWhenComplete() || webViewList) {
667 while (webViewList) {
668 g_object_unref(WEBKIT_WEB_VIEW(webViewList->data));
669 webViewList = g_slist_next(webViewList);
671 g_slist_free(webViewList);
675 // A blank load seems to be necessary to reset state after certain tests.
676 webkit_web_view_open(webView, "about:blank");
678 gLayoutTestController.clear();
680 // terminate the (possibly empty) pixels block after all the state reset
681 sendPixelResultsEOF();
684 void webViewLoadStarted(WebKitWebView* view, WebKitWebFrame* frame, void*)
686 // Make sure we only set this once per test. If it gets cleared, and then set again, we might
687 // end up doing two dumps for one test.
688 if (!topLoadingFrame && !done)
689 topLoadingFrame = frame;
692 static gboolean processWork(void* data)
694 // if we finish all the commands, we're ready to dump state
695 if (WorkQueue::shared()->processWork() && !gLayoutTestController->waitToDump())
701 static char* getFrameNameSuitableForTestResult(WebKitWebView* view, WebKitWebFrame* frame)
703 char* frameName = g_strdup(webkit_web_frame_get_name(frame));
705 if (frame == webkit_web_view_get_main_frame(view)) {
706 // This is a bit strange. Shouldn't web_frame_get_name return NULL?
707 if (frameName && (frameName[0] != '\0')) {
708 char* tmp = g_strdup_printf("main frame \"%s\"", frameName);
713 frameName = g_strdup("main frame");
715 } else if (!frameName || (frameName[0] == '\0')) {
717 frameName = g_strdup("frame (anonymous)");
719 char* tmp = g_strdup_printf("frame \"%s\"", frameName);
727 static void webViewLoadCommitted(WebKitWebView* view, WebKitWebFrame* frame, void*)
729 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
730 char* frameName = getFrameNameSuitableForTestResult(view, frame);
731 printf("%s - didCommitLoadForFrame\n", frameName);
737 static void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
739 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
740 char* frameName = getFrameNameSuitableForTestResult(view, frame);
741 printf("%s - didFinishLoadForFrame\n", frameName);
745 if (frame != topLoadingFrame)
749 WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
750 if (gLayoutTestController->waitToDump())
753 if (WorkQueue::shared()->count())
754 g_timeout_add(0, processWork, 0);
759 static void webViewDocumentLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
761 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
762 char* frameName = getFrameNameSuitableForTestResult(view, frame);
763 printf("%s - didFinishDocumentLoadForFrame\n", frameName);
766 guint pendingFrameUnloadEvents = webkit_web_frame_get_pending_unload_event_count(frame);
767 if (pendingFrameUnloadEvents) {
768 char* frameName = getFrameNameSuitableForTestResult(view, frame);
769 printf("%s - has %u onunload handler(s)\n", frameName, pendingFrameUnloadEvents);
775 static void webViewOnloadEvent(WebKitWebView* view, WebKitWebFrame* frame, void*)
777 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
778 char* frameName = getFrameNameSuitableForTestResult(view, frame);
779 printf("%s - didHandleOnloadEventsForFrame\n", frameName);
784 static void webViewWindowObjectCleared(WebKitWebView* view, WebKitWebFrame* frame, JSGlobalContextRef context, JSObjectRef windowObject, gpointer data)
786 JSValueRef exception = 0;
787 ASSERT(gLayoutTestController);
789 gLayoutTestController->makeWindowObject(context, windowObject, &exception);
792 gcController->makeWindowObject(context, windowObject, &exception);
795 axController->makeWindowObject(context, windowObject, &exception);
798 JSStringRef eventSenderStr = JSStringCreateWithUTF8CString("eventSender");
799 JSValueRef eventSender = makeEventSender(context, !webkit_web_frame_get_parent(frame));
800 JSObjectSetProperty(context, windowObject, eventSenderStr, eventSender, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, 0);
801 JSStringRelease(eventSenderStr);
804 static gboolean webViewConsoleMessage(WebKitWebView* view, const gchar* message, unsigned int line, const gchar* sourceId, gpointer data)
806 gchar* testMessage = 0;
807 const gchar* uriScheme;
809 // Tests expect only the filename part of local URIs
810 uriScheme = g_strstr_len(message, -1, "file://");
812 GString* tempString = g_string_sized_new(strlen(message));
813 gchar* filename = g_strrstr(uriScheme, G_DIR_SEPARATOR_S);
816 filename += strlen(G_DIR_SEPARATOR_S);
817 tempString = g_string_append_len(tempString, message, (uriScheme - message));
818 tempString = g_string_append_len(tempString, filename, strlen(filename));
819 testMessage = g_string_free(tempString, FALSE);
823 fprintf(stdout, "CONSOLE MESSAGE: line %d: %s\n", line, testMessage ? testMessage : message);
830 static gboolean webViewScriptAlert(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gpointer data)
832 fprintf(stdout, "ALERT: %s\n", message);
836 static gboolean webViewScriptPrompt(WebKitWebView* webView, WebKitWebFrame* frame, const gchar* message, const gchar* defaultValue, gchar** value, gpointer data)
838 fprintf(stdout, "PROMPT: %s, default text: %s\n", message, defaultValue);
839 *value = g_strdup(defaultValue);
843 static gboolean webViewScriptConfirm(WebKitWebView* view, WebKitWebFrame* frame, const gchar* message, gboolean* didConfirm, gpointer data)
845 fprintf(stdout, "CONFIRM: %s\n", message);
850 static void webViewTitleChanged(WebKitWebView* view, WebKitWebFrame* frame, const gchar* title, gpointer data)
852 if (gLayoutTestController->dumpTitleChanges() && !done)
853 printf("TITLE CHANGED: %s\n", title ? title : "");
856 static bool webViewNavigationPolicyDecisionRequested(WebKitWebView* view, WebKitWebFrame* frame,
857 WebKitNetworkRequest* request,
858 WebKitWebNavigationAction* navAction,
859 WebKitWebPolicyDecision* policyDecision)
861 // Use the default handler if we're not waiting for policy,
862 // i.e., LayoutTestController::waitForPolicyDelegate
866 gchar* typeDescription;
867 WebKitWebNavigationReason reason;
868 g_object_get(G_OBJECT(navAction), "reason", &reason, NULL);
871 case WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED:
872 typeDescription = g_strdup("link clicked");
874 case WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED:
875 typeDescription = g_strdup("form submitted");
877 case WEBKIT_WEB_NAVIGATION_REASON_BACK_FORWARD:
878 typeDescription = g_strdup("back/forward");
880 case WEBKIT_WEB_NAVIGATION_REASON_RELOAD:
881 typeDescription = g_strdup("reload");
883 case WEBKIT_WEB_NAVIGATION_REASON_FORM_RESUBMITTED:
884 typeDescription = g_strdup("form resubmitted");
886 case WEBKIT_WEB_NAVIGATION_REASON_OTHER:
887 typeDescription = g_strdup("other");
890 typeDescription = g_strdup("illegal value");
893 printf("Policy delegate: attempt to load %s with navigation type '%s'\n", webkit_network_request_get_uri(request), typeDescription);
894 g_free(typeDescription);
896 webkit_web_policy_decision_ignore(policyDecision);
897 gLayoutTestController->notifyDone();
902 static void webViewStatusBarTextChanged(WebKitWebView* view, const gchar* message, gpointer data)
904 // Are we doing anything wrong? One test that does not call
905 // dumpStatusCallbacks gets true here
906 if (gLayoutTestController->dumpStatusCallbacks()) {
907 if (message && strcmp(message, ""))
908 printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", message);
912 static gboolean webViewClose(WebKitWebView* view)
916 webViewList = g_slist_remove(webViewList, view);
917 g_object_unref(view);
922 static void databaseQuotaExceeded(WebKitWebView* view, WebKitWebFrame* frame, WebKitWebDatabase *database)
928 WebKitSecurityOrigin* origin = webkit_web_database_get_security_origin(database);
929 if (gLayoutTestController->dumpDatabaseCallbacks()) {
930 printf("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{%s, %s, %i} database:%s\n",
931 webkit_security_origin_get_protocol(origin),
932 webkit_security_origin_get_host(origin),
933 webkit_security_origin_get_port(origin),
934 webkit_web_database_get_name(database));
936 webkit_security_origin_set_web_database_quota(origin, 5 * 1024 * 1024);
940 geolocationPolicyDecisionRequested(WebKitWebView*, WebKitWebFrame*, WebKitGeolocationPolicyDecision* decision)
942 if (!gLayoutTestController->isGeolocationPermissionSet())
944 if (gLayoutTestController->geolocationPermission())
945 webkit_geolocation_policy_allow(decision);
947 webkit_geolocation_policy_deny(decision);
953 static WebKitWebView* webViewCreate(WebKitWebView*, WebKitWebFrame*);
955 static gboolean webInspectorShowWindow(WebKitWebInspector*, gpointer data)
957 gtk_window_set_default_size(GTK_WINDOW(webInspectorWindow), 800, 600);
958 gtk_widget_show_all(webInspectorWindow);
962 static gboolean webInspectorCloseWindow(WebKitWebInspector*, gpointer data)
964 gtk_widget_destroy(webInspectorWindow);
965 webInspectorWindow = 0;
969 static WebKitWebView* webInspectorInspectWebView(WebKitWebInspector*, gpointer data)
971 webInspectorWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
973 GtkWidget* webView = webkit_web_view_new();
974 gtk_container_add(GTK_CONTAINER(webInspectorWindow),
977 return WEBKIT_WEB_VIEW(webView);
980 static void webViewLoadStatusNotified(WebKitWebView* view, gpointer user_data)
982 WebKitLoadStatus loadStatus = webkit_web_view_get_load_status(view);
984 if (gLayoutTestController->dumpFrameLoadCallbacks()) {
985 if (loadStatus == WEBKIT_LOAD_PROVISIONAL) {
986 char* frameName = getFrameNameSuitableForTestResult(view, mainFrame);
987 printf("%s - didStartProvisionalLoadForFrame\n", frameName);
993 static WebKitWebView* createWebView()
995 WebKitWebView* view = WEBKIT_WEB_VIEW(webkit_web_view_new());
997 // From bug 11756: Use a frame group name for all WebViews created by
998 // DumpRenderTree to allow testing of cross-page frame lookup.
999 webkit_web_view_set_group_name(view, "org.webkit.gtk.DumpRenderTree");
1001 g_object_connect(G_OBJECT(view),
1002 "signal::load-started", webViewLoadStarted, 0,
1003 "signal::load-finished", webViewLoadFinished, 0,
1004 "signal::load-committed", webViewLoadCommitted, 0,
1005 "signal::window-object-cleared", webViewWindowObjectCleared, 0,
1006 "signal::console-message", webViewConsoleMessage, 0,
1007 "signal::script-alert", webViewScriptAlert, 0,
1008 "signal::script-prompt", webViewScriptPrompt, 0,
1009 "signal::script-confirm", webViewScriptConfirm, 0,
1010 "signal::title-changed", webViewTitleChanged, 0,
1011 "signal::navigation-policy-decision-requested", webViewNavigationPolicyDecisionRequested, 0,
1012 "signal::status-bar-text-changed", webViewStatusBarTextChanged, 0,
1013 "signal::create-web-view", webViewCreate, 0,
1014 "signal::close-web-view", webViewClose, 0,
1015 "signal::database-quota-exceeded", databaseQuotaExceeded, 0,
1016 "signal::document-load-finished", webViewDocumentLoadFinished, 0,
1017 "signal::geolocation-policy-decision-requested", geolocationPolicyDecisionRequested, 0,
1018 "signal::onload-event", webViewOnloadEvent, 0,
1019 "signal::drag-begin", dragBeginCallback, 0,
1020 "signal::drag-end", dragEndCallback, 0,
1021 "signal::drag-failed", dragFailedCallback, 0,
1025 g_signal_connect(view,
1026 "notify::load-status", G_CALLBACK(webViewLoadStatusNotified),
1029 WebKitWebInspector* inspector = webkit_web_view_get_inspector(view);
1030 g_object_connect(G_OBJECT(inspector),
1031 "signal::inspect-web-view", webInspectorInspectWebView, 0,
1032 "signal::show-window", webInspectorShowWindow, 0,
1033 "signal::close-window", webInspectorCloseWindow, 0,
1037 WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
1038 webkit_web_view_set_settings(view, settings);
1044 static WebKitWebView* webViewCreate(WebKitWebView* view, WebKitWebFrame* frame)
1046 if (!gLayoutTestController->canOpenWindows())
1049 // Make sure that waitUntilDone has been called.
1050 ASSERT(gLayoutTestController->waitToDump());
1052 WebKitWebView* newWebView = createWebView();
1053 g_object_ref_sink(G_OBJECT(newWebView));
1054 webViewList = g_slist_prepend(webViewList, newWebView);
1058 static void logHandler(const gchar* domain, GLogLevelFlags level, const gchar* message, gpointer data)
1060 if (level < G_LOG_LEVEL_DEBUG)
1061 fprintf(stderr, "%s\n", message);
1064 int main(int argc, char* argv[])
1066 g_thread_init(NULL);
1067 gtk_init(&argc, &argv);
1069 // Some plugins might try to use the GLib logger for printing debug
1070 // messages. This will cause tests to fail because of unexpected output.
1071 // We squelch all debug messages sent to the logger.
1072 g_log_set_default_handler(logHandler, 0);
1074 initializeGlobalsFromCommandLineOptions(argc, argv);
1077 window = gtk_window_new(GTK_WINDOW_POPUP);
1078 container = GTK_WIDGET(gtk_scrolled_window_new(NULL, NULL));
1079 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(container), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1080 gtk_container_add(GTK_CONTAINER(window), container);
1081 gtk_widget_show_all(window);
1083 webView = createWebView();
1084 gtk_container_add(GTK_CONTAINER(container), GTK_WIDGET(webView));
1085 gtk_widget_realize(GTK_WIDGET(webView));
1086 gtk_widget_show_all(container);
1087 gtk_widget_grab_focus(GTK_WIDGET(webView));
1088 mainFrame = webkit_web_view_get_main_frame(webView);
1090 setDefaultsToConsistentStateValuesForTesting();
1092 gcController = new GCController();
1093 axController = new AccessibilityController();
1095 if (useLongRunningServerMode(argc, argv)) {
1096 printSeparators = true;
1097 runTestingServerLoop();
1099 printSeparators = (optind < argc-1 || (dumpPixels && dumpTree));
1100 for (int i = optind; i != argc; ++i)
1104 delete gcController;
1107 delete axController;
1110 gtk_widget_destroy(window);