1/* 2 * Copyright (C) 2007 Apple Inc. All rights reserved. 3 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> 4 * Copyright (C) 2008 Nuanti Ltd. 5 * Copyright (C) 2009 Jan Michael Alonzo <jmalonzo@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 49extern "C" { 50bool webkit_web_frame_pause_animation(WebKitWebFrame* frame, const gchar* name, double time, const gchar* element); 51bool webkit_web_frame_pause_transition(WebKitWebFrame* frame, const gchar* name, double time, const gchar* element); 52bool webkit_web_frame_pause_svg_animation(WebKitWebFrame* frame, const gchar* name, double time, const gchar* element); 53unsigned int webkit_web_frame_number_of_active_animations(WebKitWebFrame* frame); 54void webkit_application_cache_set_maximum_size(unsigned long long size); 55unsigned int webkit_worker_thread_count(void); 56void webkit_white_list_access_from_origin(const gchar* sourceOrigin, const gchar* destinationProtocol, const gchar* destinationHost, bool allowDestinationSubdomains); 57gchar* webkit_web_frame_counter_value_for_element_by_id(WebKitWebFrame* frame, const gchar* id); 58int webkit_web_frame_page_number_for_element_by_id(WebKitWebFrame* frame, const gchar* id, float pageWidth, float pageHeight); 59void webkit_web_inspector_execute_script(WebKitWebInspector* inspector, long callId, const gchar* script); 60} 61 62static 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 79LayoutTestController::~LayoutTestController() 80{ 81 // FIXME: implement 82} 83 84void LayoutTestController::addDisallowedURL(JSStringRef url) 85{ 86 // FIXME: implement 87} 88 89void 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 106JSStringRef LayoutTestController::copyDecodedHostName(JSStringRef name) 107{ 108 // FIXME: implement 109 return 0; 110} 111 112JSStringRef LayoutTestController::copyEncodedHostName(JSStringRef name) 113{ 114 // FIXME: implement 115 return 0; 116} 117 118void LayoutTestController::dispatchPendingLoadRequests() 119{ 120 // FIXME: Implement for testing fix for 6727495 121} 122 123void LayoutTestController::display() 124{ 125 displayWebView(); 126} 127 128JSRetainPtr<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 139void LayoutTestController::keepWebHistory() 140{ 141 // FIXME: implement 142} 143 144int 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 152int LayoutTestController::numberOfPages(float, float) 153{ 154 // FIXME: implement 155 return -1; 156} 157 158size_t LayoutTestController::webHistoryItemCount() 159{ 160 // FIXME: implement 161 return 0; 162} 163 164unsigned LayoutTestController::workerThreadCount() const 165{ 166 return webkit_worker_thread_count(); 167} 168 169void LayoutTestController::notifyDone() 170{ 171 if (m_waitToDump && !topLoadingFrame && !WorkQueue::shared()->count()) 172 dump(); 173 m_waitToDump = false; 174 waitForPolicy = false; 175} 176 177JSStringRef 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 183void 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 205void 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 211void LayoutTestController::setAlwaysAcceptCookies(bool alwaysAcceptCookies) 212{ 213 // FIXME: Implement this (and restore the default value before running each test in DumpRenderTree.cpp). 214} 215 216void LayoutTestController::setCustomPolicyDelegate(bool setDelegate, bool permissive) 217{ 218 // FIXME: implement 219} 220 221void LayoutTestController::waitForPolicyDelegate() 222{ 223 waitForPolicy = true; 224 setWaitToDump(true); 225} 226 227void 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 238void LayoutTestController::setMainFrameIsFirstResponder(bool flag) 239{ 240 // FIXME: implement 241} 242 243void 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 250void 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 259void LayoutTestController::setUseDashboardCompatibilityMode(bool flag) 260{ 261 // FIXME: implement 262} 263 264static gchar* userStyleSheet = NULL; 265static gboolean userStyleSheetEnabled = TRUE; 266 267void 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 279void LayoutTestController::setUserStyleSheetLocation(JSStringRef path) 280{ 281 g_free(userStyleSheet); 282 userStyleSheet = JSStringCopyUTF8CString(path); 283 if (userStyleSheetEnabled) 284 setUserStyleSheetEnabled(true); 285} 286 287void LayoutTestController::setWindowIsKey(bool windowIsKey) 288{ 289 // FIXME: implement 290} 291 292void LayoutTestController::setSmartInsertDeleteEnabled(bool flag) 293{ 294 // FIXME: implement 295} 296 297static gboolean waitToDumpWatchdogFired(void*) 298{ 299 waitToDumpWatchdog = 0; 300 gLayoutTestController->waitToDumpWatchdogTimerFired(); 301 return FALSE; 302} 303 304void 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 313int LayoutTestController::windowCount() 314{ 315 // +1 -> including the main view 316 return g_slist_length(webViewList) + 1; 317} 318 319void 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 328void 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 337void LayoutTestController::setFrameSetFlatteningEnabled(bool flag) 338{ 339 // FIXME: implement 340} 341 342void 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 351void LayoutTestController::setAuthorAndUserStylesEnabled(bool flag) 352{ 353 // FIXME: implement 354} 355 356void 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 362void 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 368void 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 374void LayoutTestController::setIconDatabaseEnabled(bool flag) 375{ 376 // FIXME: implement 377} 378 379void 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 391void LayoutTestController::setSelectTrailingWhitespaceEnabled(bool flag) 392{ 393 // FIXME: implement 394} 395 396void 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 406bool LayoutTestController::elementDoesAutoCompleteForElementWithId(JSStringRef id) 407{ 408 // FIXME: implement 409 return false; 410} 411 412void LayoutTestController::execCommand(JSStringRef name, JSStringRef value) 413{ 414 // FIXME: implement 415} 416 417void LayoutTestController::setCacheModel(int) 418{ 419 // FIXME: implement 420} 421 422bool LayoutTestController::isCommandEnabled(JSStringRef /*name*/) 423{ 424 // FIXME: implement 425 return false; 426} 427 428void LayoutTestController::setPersistentUserStyleSheetLocation(JSStringRef jsURL) 429{ 430 // FIXME: implement 431} 432 433void LayoutTestController::clearPersistentUserStyleSheet() 434{ 435 // FIXME: implement 436} 437 438void LayoutTestController::clearAllDatabases() 439{ 440 webkit_remove_all_web_databases(); 441} 442 443void 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 449void LayoutTestController::setDomainRelaxationForbiddenForURLScheme(bool, JSStringRef) 450{ 451 // FIXME: implement 452} 453 454void LayoutTestController::setAppCacheMaximumSize(unsigned long long size) 455{ 456 webkit_application_cache_set_maximum_size(size); 457} 458 459bool 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 469bool 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 479bool 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 489unsigned LayoutTestController::numberOfActiveAnimations() const 490{ 491 return webkit_web_frame_number_of_active_animations(mainFrame); 492} 493 494void 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 540void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart) 541{ 542 printf("LayoutTestController::addUserScript not implemented.\n"); 543} 544 545void LayoutTestController::addUserStyleSheet(JSStringRef source) 546{ 547 printf("LayoutTestController::addUserStyleSheet not implemented.\n"); 548} 549 550void 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 560void 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 570void 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 580void LayoutTestController::evaluateScriptInIsolatedWorld(unsigned worldID, JSObjectRef globalObject, JSStringRef script) 581{ 582 // FIXME: Implement this. 583} 584 585void LayoutTestController::removeAllVisitedLinks() 586{ 587 // FIXME: Implement this. 588} 589