1 /* 2 * Copyright (C) 2007 Apple Inc. All rights reserved. 3 * Copyright (C) 2007 Eric Seidel <eric (at) webkit.org> 4 * Copyright (C) 2008 Nuanti Ltd. 5 * Copyright (C) 2009 Jan Michael Alonzo <jmalonzo (at) gmail.com> 6 * Copyright (C) 2009 Collabora Ltd. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 18 * its contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include "config.h" 34 #include "LayoutTestController.h" 35 36 #include "DumpRenderTree.h" 37 #include "WorkQueue.h" 38 #include "WorkQueueItem.h" 39 #include <JavaScriptCore/JSRetainPtr.h> 40 #include <JavaScriptCore/JSStringRef.h> 41 42 #include <iostream> 43 #include <sstream> 44 #include <stdio.h> 45 #include <glib.h> 46 #include <libsoup/soup.h> 47 #include <webkit/webkit.h> 48 49 extern "C" { 50 bool webkit_web_frame_pause_animation(WebKitWebFrame* frame, const gchar* name, double time, const gchar* element); 51 bool webkit_web_frame_pause_transition(WebKitWebFrame* frame, const gchar* name, double time, const gchar* element); 52 bool webkit_web_frame_pause_svg_animation(WebKitWebFrame* frame, const gchar* name, double time, const gchar* element); 53 unsigned int webkit_web_frame_number_of_active_animations(WebKitWebFrame* frame); 54 void webkit_application_cache_set_maximum_size(unsigned long long size); 55 unsigned int webkit_worker_thread_count(void); 56 void webkit_white_list_access_from_origin(const gchar* sourceOrigin, const gchar* destinationProtocol, const gchar* destinationHost, bool allowDestinationSubdomains); 57 gchar* webkit_web_frame_counter_value_for_element_by_id(WebKitWebFrame* frame, const gchar* id); 58 int webkit_web_frame_page_number_for_element_by_id(WebKitWebFrame* frame, const gchar* id, float pageWidth, float pageHeight); 59 void webkit_web_inspector_execute_script(WebKitWebInspector* inspector, long callId, const gchar* script); 60 } 61 62 static gchar* copyWebSettingKey(gchar* preferenceKey) 63 { 64 static GHashTable* keyTable; 65 66 if (!keyTable) { 67 // If you add a pref here, make sure you reset the value in 68 // DumpRenderTree::resetWebViewToConsistentStateBeforeTesting. 69 keyTable = g_hash_table_new(g_str_hash, g_str_equal); 70 g_hash_table_insert(keyTable, g_strdup("WebKitJavaScriptEnabled"), g_strdup("enable-scripts")); 71 g_hash_table_insert(keyTable, g_strdup("WebKitDefaultFontSize"), g_strdup("default-font-size")); 72 g_hash_table_insert(keyTable, g_strdup("WebKitEnableCaretBrowsing"), g_strdup("enable-caret-browsing")); 73 g_hash_table_insert(keyTable, g_strdup("WebKitUsesPageCachePreferenceKey"), g_strdup("enable-page-cache")); 74 } 75 76 return g_strdup(static_cast<gchar*>(g_hash_table_lookup(keyTable, preferenceKey))); 77 } 78 79 LayoutTestController::~LayoutTestController() 80 { 81 // FIXME: implement 82 } 83 84 void LayoutTestController::addDisallowedURL(JSStringRef url) 85 { 86 // FIXME: implement 87 } 88 89 void LayoutTestController::clearBackForwardList() 90 { 91 WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); 92 WebKitWebBackForwardList* list = webkit_web_view_get_back_forward_list(webView); 93 WebKitWebHistoryItem* item = webkit_web_back_forward_list_get_current_item(list); 94 g_object_ref(item); 95 96 // We clear the history by setting the back/forward list's capacity to 0 97 // then restoring it back and adding back the current item. 98 gint limit = webkit_web_back_forward_list_get_limit(list); 99 webkit_web_back_forward_list_set_limit(list, 0); 100 webkit_web_back_forward_list_set_limit(list, limit); 101 webkit_web_back_forward_list_add_item(list, item); 102 webkit_web_back_forward_list_go_to_item(list, item); 103 g_object_unref(item); 104 } 105 106 JSStringRef LayoutTestController::copyDecodedHostName(JSStringRef name) 107 { 108 // FIXME: implement 109 return 0; 110 } 111 112 JSStringRef LayoutTestController::copyEncodedHostName(JSStringRef name) 113 { 114 // FIXME: implement 115 return 0; 116 } 117 118 void LayoutTestController::dispatchPendingLoadRequests() 119 { 120 // FIXME: Implement for testing fix for 6727495 121 } 122 123 void LayoutTestController::display() 124 { 125 displayWebView(); 126 } 127 128 JSRetainPtr<JSStringRef> LayoutTestController::counterValueForElementById(JSStringRef id) 129 { 130 gchar* idGChar = JSStringCopyUTF8CString(id); 131 gchar* counterValueGChar = webkit_web_frame_counter_value_for_element_by_id(mainFrame, idGChar); 132 g_free(idGChar); 133 if (!counterValueGChar) 134 return 0; 135 JSRetainPtr<JSStringRef> counterValue(Adopt, JSStringCreateWithUTF8CString(counterValueGChar)); 136 return counterValue; 137 } 138 139 void LayoutTestController::keepWebHistory() 140 { 141 // FIXME: implement 142 } 143 144 int LayoutTestController::pageNumberForElementById(JSStringRef id, float pageWidth, float pageHeight) 145 { 146 gchar* idGChar = JSStringCopyUTF8CString(id); 147 int pageNumber = webkit_web_frame_page_number_for_element_by_id(mainFrame, idGChar, pageWidth, pageHeight); 148 g_free(idGChar); 149 return pageNumber; 150 } 151 152 int LayoutTestController::numberOfPages(float, float) 153 { 154 // FIXME: implement 155 return -1; 156 } 157 158 size_t LayoutTestController::webHistoryItemCount() 159 { 160 // FIXME: implement 161 return 0; 162 } 163 164 unsigned LayoutTestController::workerThreadCount() const 165 { 166 return webkit_worker_thread_count(); 167 } 168 169 void LayoutTestController::notifyDone() 170 { 171 if (m_waitToDump && !topLoadingFrame && !WorkQueue::shared()->count()) 172 dump(); 173 m_waitToDump = false; 174 waitForPolicy = false; 175 } 176 177 JSStringRef LayoutTestController::pathToLocalResource(JSContextRef context, JSStringRef url) 178 { 179 // Function introduced in r28690. This may need special-casing on Windows. 180 return JSStringRetain(url); // Do nothing on Unix. 181 } 182 183 void LayoutTestController::queueLoad(JSStringRef url, JSStringRef target) 184 { 185 gchar* relativeURL = JSStringCopyUTF8CString(url); 186 SoupURI* baseURI = soup_uri_new(webkit_web_frame_get_uri(mainFrame)); 187 188 SoupURI* absoluteURI = soup_uri_new_with_base(baseURI, relativeURL); 189 soup_uri_free(baseURI); 190 g_free(relativeURL); 191 192 gchar* absoluteCString; 193 if (absoluteURI) { 194 absoluteCString = soup_uri_to_string(absoluteURI, FALSE); 195 soup_uri_free(absoluteURI); 196 } else 197 absoluteCString = JSStringCopyUTF8CString(url); 198 199 JSRetainPtr<JSStringRef> absoluteURL(Adopt, JSStringCreateWithUTF8CString(absoluteCString)); 200 g_free(absoluteCString); 201 202 WorkQueue::shared()->queue(new LoadItem(absoluteURL.get(), target)); 203 } 204 205 void LayoutTestController::setAcceptsEditing(bool acceptsEditing) 206 { 207 WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); 208 webkit_web_view_set_editable(webView, acceptsEditing); 209 } 210 211 void LayoutTestController::setAlwaysAcceptCookies(bool alwaysAcceptCookies) 212 { 213 // FIXME: Implement this (and restore the default value before running each test in DumpRenderTree.cpp). 214 } 215 216 void LayoutTestController::setCustomPolicyDelegate(bool setDelegate, bool permissive) 217 { 218 // FIXME: implement 219 } 220 221 void LayoutTestController::waitForPolicyDelegate() 222 { 223 waitForPolicy = true; 224 setWaitToDump(true); 225 } 226 227 void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef protocol, JSStringRef host, bool includeSubdomains) 228 { 229 gchar* sourceOriginGChar = JSStringCopyUTF8CString(sourceOrigin); 230 gchar* protocolGChar = JSStringCopyUTF8CString(protocol); 231 gchar* hostGChar = JSStringCopyUTF8CString(host); 232 webkit_white_list_access_from_origin(sourceOriginGChar, protocolGChar, hostGChar, includeSubdomains); 233 g_free(sourceOriginGChar); 234 g_free(protocolGChar); 235 g_free(hostGChar); 236 } 237 238 void LayoutTestController::setMainFrameIsFirstResponder(bool flag) 239 { 240 // FIXME: implement 241 } 242 243 void LayoutTestController::setTabKeyCyclesThroughElements(bool cycles) 244 { 245 WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); 246 WebKitWebSettings* settings = webkit_web_view_get_settings(webView); 247 g_object_set(G_OBJECT(settings), "tab-key-cycles-through-elements", cycles, NULL); 248 } 249 250 void LayoutTestController::setTimelineProfilingEnabled(bool flag) 251 { 252 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 253 ASSERT(view); 254 255 WebKitWebInspector* inspector = webkit_web_view_get_inspector(view); 256 g_object_set(G_OBJECT(inspector), "timeline-profiling-enabled", flag, NULL); 257 } 258 259 void LayoutTestController::setUseDashboardCompatibilityMode(bool flag) 260 { 261 // FIXME: implement 262 } 263 264 static gchar* userStyleSheet = NULL; 265 static gboolean userStyleSheetEnabled = TRUE; 266 267 void LayoutTestController::setUserStyleSheetEnabled(bool flag) 268 { 269 userStyleSheetEnabled = flag; 270 271 WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); 272 WebKitWebSettings* settings = webkit_web_view_get_settings(webView); 273 if (flag && userStyleSheet) 274 g_object_set(G_OBJECT(settings), "user-stylesheet-uri", userStyleSheet, NULL); 275 else 276 g_object_set(G_OBJECT(settings), "user-stylesheet-uri", "", NULL); 277 } 278 279 void LayoutTestController::setUserStyleSheetLocation(JSStringRef path) 280 { 281 g_free(userStyleSheet); 282 userStyleSheet = JSStringCopyUTF8CString(path); 283 if (userStyleSheetEnabled) 284 setUserStyleSheetEnabled(true); 285 } 286 287 void LayoutTestController::setWindowIsKey(bool windowIsKey) 288 { 289 // FIXME: implement 290 } 291 292 void LayoutTestController::setSmartInsertDeleteEnabled(bool flag) 293 { 294 // FIXME: implement 295 } 296 297 static gboolean waitToDumpWatchdogFired(void*) 298 { 299 waitToDumpWatchdog = 0; 300 gLayoutTestController->waitToDumpWatchdogTimerFired(); 301 return FALSE; 302 } 303 304 void LayoutTestController::setWaitToDump(bool waitUntilDone) 305 { 306 static const int timeoutSeconds = 15; 307 308 m_waitToDump = waitUntilDone; 309 if (m_waitToDump && !waitToDumpWatchdog) 310 waitToDumpWatchdog = g_timeout_add_seconds(timeoutSeconds, waitToDumpWatchdogFired, 0); 311 } 312 313 int LayoutTestController::windowCount() 314 { 315 // +1 -> including the main view 316 return g_slist_length(webViewList) + 1; 317 } 318 319 void LayoutTestController::setPrivateBrowsingEnabled(bool flag) 320 { 321 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 322 ASSERT(view); 323 324 WebKitWebSettings* settings = webkit_web_view_get_settings(view); 325 g_object_set(G_OBJECT(settings), "enable-private-browsing", flag, NULL); 326 } 327 328 void LayoutTestController::setXSSAuditorEnabled(bool flag) 329 { 330 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 331 ASSERT(view); 332 333 WebKitWebSettings* settings = webkit_web_view_get_settings(view); 334 g_object_set(G_OBJECT(settings), "enable-xss-auditor", flag, NULL); 335 } 336 337 void LayoutTestController::setFrameSetFlatteningEnabled(bool flag) 338 { 339 // FIXME: implement 340 } 341 342 void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool flag) 343 { 344 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 345 ASSERT(view); 346 347 WebKitWebSettings* settings = webkit_web_view_get_settings(view); 348 g_object_set(G_OBJECT(settings), "enable-universal-access-from-file-uris", flag, NULL); 349 } 350 351 void LayoutTestController::setAuthorAndUserStylesEnabled(bool flag) 352 { 353 // FIXME: implement 354 } 355 356 void LayoutTestController::disableImageLoading() 357 { 358 // FIXME: Implement for testing fix for https://bugs.webkit.org/show_bug.cgi?id=27896 359 // Also need to make sure image loading is re-enabled for each new test. 360 } 361 362 void LayoutTestController::setMockGeolocationPosition(double latitude, double longitude, double accuracy) 363 { 364 // FIXME: Implement for Geolocation layout tests. 365 // See https://bugs.webkit.org/show_bug.cgi?id=28264. 366 } 367 368 void LayoutTestController::setMockGeolocationError(int code, JSStringRef message) 369 { 370 // FIXME: Implement for Geolocation layout tests. 371 // See https://bugs.webkit.org/show_bug.cgi?id=28264. 372 } 373 374 void LayoutTestController::setIconDatabaseEnabled(bool flag) 375 { 376 // FIXME: implement 377 } 378 379 void LayoutTestController::setJavaScriptProfilingEnabled(bool flag) 380 { 381 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 382 ASSERT(view); 383 384 WebKitWebSettings* settings = webkit_web_view_get_settings(view); 385 g_object_set(G_OBJECT(settings), "enable-developer-extras", flag, NULL); 386 387 WebKitWebInspector* inspector = webkit_web_view_get_inspector(view); 388 g_object_set(G_OBJECT(inspector), "javascript-profiling-enabled", flag, NULL); 389 } 390 391 void LayoutTestController::setSelectTrailingWhitespaceEnabled(bool flag) 392 { 393 // FIXME: implement 394 } 395 396 void LayoutTestController::setPopupBlockingEnabled(bool flag) 397 { 398 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 399 ASSERT(view); 400 401 WebKitWebSettings* settings = webkit_web_view_get_settings(view); 402 g_object_set(G_OBJECT(settings), "javascript-can-open-windows-automatically", !flag, NULL); 403 404 } 405 406 bool LayoutTestController::elementDoesAutoCompleteForElementWithId(JSStringRef id) 407 { 408 // FIXME: implement 409 return false; 410 } 411 412 void LayoutTestController::execCommand(JSStringRef name, JSStringRef value) 413 { 414 // FIXME: implement 415 } 416 417 void LayoutTestController::setCacheModel(int) 418 { 419 // FIXME: implement 420 } 421 422 bool LayoutTestController::isCommandEnabled(JSStringRef /*name*/) 423 { 424 // FIXME: implement 425 return false; 426 } 427 428 void LayoutTestController::setPersistentUserStyleSheetLocation(JSStringRef jsURL) 429 { 430 // FIXME: implement 431 } 432 433 void LayoutTestController::clearPersistentUserStyleSheet() 434 { 435 // FIXME: implement 436 } 437 438 void LayoutTestController::clearAllDatabases() 439 { 440 webkit_remove_all_web_databases(); 441 } 442 443 void LayoutTestController::setDatabaseQuota(unsigned long long quota) 444 { 445 WebKitSecurityOrigin* origin = webkit_web_frame_get_security_origin(mainFrame); 446 webkit_security_origin_set_web_database_quota(origin, quota); 447 } 448 449 void LayoutTestController::setDomainRelaxationForbiddenForURLScheme(bool, JSStringRef) 450 { 451 // FIXME: implement 452 } 453 454 void LayoutTestController::setAppCacheMaximumSize(unsigned long long size) 455 { 456 webkit_application_cache_set_maximum_size(size); 457 } 458 459 bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(JSStringRef animationName, double time, JSStringRef elementId) 460 { 461 gchar* name = JSStringCopyUTF8CString(animationName); 462 gchar* element = JSStringCopyUTF8CString(elementId); 463 bool returnValue = webkit_web_frame_pause_animation(mainFrame, name, time, element); 464 g_free(name); 465 g_free(element); 466 return returnValue; 467 } 468 469 bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(JSStringRef propertyName, double time, JSStringRef elementId) 470 { 471 gchar* name = JSStringCopyUTF8CString(propertyName); 472 gchar* element = JSStringCopyUTF8CString(elementId); 473 bool returnValue = webkit_web_frame_pause_transition(mainFrame, name, time, element); 474 g_free(name); 475 g_free(element); 476 return returnValue; 477 } 478 479 bool LayoutTestController::sampleSVGAnimationForElementAtTime(JSStringRef animationId, double time, JSStringRef elementId) 480 { 481 gchar* name = JSStringCopyUTF8CString(animationId); 482 gchar* element = JSStringCopyUTF8CString(elementId); 483 bool returnValue = webkit_web_frame_pause_svg_animation(mainFrame, name, time, element); 484 g_free(name); 485 g_free(element); 486 return returnValue; 487 } 488 489 unsigned LayoutTestController::numberOfActiveAnimations() const 490 { 491 return webkit_web_frame_number_of_active_animations(mainFrame); 492 } 493 494 void LayoutTestController::overridePreference(JSStringRef key, JSStringRef value) 495 { 496 gchar* name = JSStringCopyUTF8CString(key); 497 gchar* strValue = JSStringCopyUTF8CString(value); 498 499 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 500 ASSERT(view); 501 502 WebKitWebSettings* settings = webkit_web_view_get_settings(view); 503 gchar* webSettingKey = copyWebSettingKey(name); 504 505 if (webSettingKey) { 506 GValue stringValue = { 0, { { 0 } } }; 507 g_value_init(&stringValue, G_TYPE_STRING); 508 g_value_set_string(&stringValue, const_cast<gchar*>(strValue)); 509 510 WebKitWebSettingsClass* klass = WEBKIT_WEB_SETTINGS_GET_CLASS(settings); 511 GParamSpec* pspec = g_object_class_find_property(G_OBJECT_CLASS(klass), webSettingKey); 512 GValue propValue = { 0, { { 0 } } }; 513 g_value_init(&propValue, pspec->value_type); 514 515 if (g_value_type_transformable(G_TYPE_STRING, pspec->value_type)) { 516 g_value_transform(const_cast<GValue*>(&stringValue), &propValue); 517 g_object_set_property(G_OBJECT(settings), webSettingKey, const_cast<GValue*>(&propValue)); 518 } else if (G_VALUE_HOLDS_BOOLEAN(&propValue)) { 519 char* lowered = g_utf8_strdown(strValue, -1); 520 g_object_set(G_OBJECT(settings), webSettingKey, 521 g_str_equal(lowered, "true") 522 || g_str_equal(strValue, "1"), 523 NULL); 524 g_free(lowered); 525 } else if (G_VALUE_HOLDS_INT(&propValue)) { 526 std::string str(strValue); 527 std::stringstream ss(str); 528 int val = 0; 529 if (!(ss >> val).fail()) 530 g_object_set(G_OBJECT(settings), webSettingKey, val, NULL); 531 } else 532 printf("LayoutTestController::overridePreference failed to override preference '%s'.\n", name); 533 } 534 535 g_free(webSettingKey); 536 g_free(name); 537 g_free(strValue); 538 } 539 540 void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart) 541 { 542 printf("LayoutTestController::addUserScript not implemented.\n"); 543 } 544 545 void LayoutTestController::addUserStyleSheet(JSStringRef source) 546 { 547 printf("LayoutTestController::addUserStyleSheet not implemented.\n"); 548 } 549 550 void LayoutTestController::showWebInspector() 551 { 552 WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); 553 WebKitWebSettings* webSettings = webkit_web_view_get_settings(webView); 554 WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView); 555 556 g_object_set(webSettings, "enable-developer-extras", TRUE, NULL); 557 webkit_web_inspector_show(inspector); 558 } 559 560 void LayoutTestController::closeWebInspector() 561 { 562 WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); 563 WebKitWebSettings* webSettings = webkit_web_view_get_settings(webView); 564 WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView); 565 566 webkit_web_inspector_close(inspector); 567 g_object_set(webSettings, "enable-developer-extras", FALSE, NULL); 568 } 569 570 void LayoutTestController::evaluateInWebInspector(long callId, JSStringRef script) 571 { 572 WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); 573 WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView); 574 char* scriptString = JSStringCopyUTF8CString(script); 575 576 webkit_web_inspector_execute_script(inspector, callId, scriptString); 577 g_free(scriptString); 578 } 579 580 void LayoutTestController::evaluateScriptInIsolatedWorld(unsigned worldID, JSObjectRef globalObject, JSStringRef script) 581 { 582 // FIXME: Implement this. 583 } 584 585 void LayoutTestController::removeAllVisitedLinks() 586 { 587 // FIXME: Implement this. 588 } 589