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