// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This file contains the definition for LayoutTestController. #include #include "webkit/tools/test_shell/layout_test_controller.h" #include "base/basictypes.h" #include "base/file_path.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/path_service.h" #include "base/utf_string_conversions.h" #include "third_party/WebKit/WebKit/chromium/public/WebConsoleMessage.h" #include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/WebKit/chromium/public/WebKit.h" #include "third_party/WebKit/WebKit/chromium/public/WebScriptSource.h" #include "third_party/WebKit/WebKit/chromium/public/WebSecurityPolicy.h" #include "third_party/WebKit/WebKit/chromium/public/WebSize.h" #include "third_party/WebKit/WebKit/chromium/public/WebURL.h" #include "third_party/WebKit/WebKit/chromium/public/WebView.h" #include "webkit/glue/dom_operations.h" #include "webkit/glue/webkit_glue.h" #include "webkit/glue/webpreferences.h" #include "webkit/tools/test_shell/notification_presenter.h" #include "webkit/tools/test_shell/simple_database_system.h" #include "webkit/tools/test_shell/simple_resource_loader_bridge.h" #include "webkit/tools/test_shell/test_navigation_controller.h" #include "webkit/tools/test_shell/test_shell.h" #include "webkit/tools/test_shell/test_shell_devtools_agent.h" #include "webkit/tools/test_shell/test_webview_delegate.h" using std::string; using std::wstring; using WebKit::WebConsoleMessage; using WebKit::WebScriptSource; using WebKit::WebSecurityPolicy; using WebKit::WebString; TestShell* LayoutTestController::shell_ = NULL; // Most of these flags need to be cleared in Reset() so that they get turned // off between each test run. bool LayoutTestController::dump_as_text_ = false; bool LayoutTestController::dump_editing_callbacks_ = false; bool LayoutTestController::dump_frame_load_callbacks_ = false; bool LayoutTestController::dump_resource_load_callbacks_ = false; bool LayoutTestController::dump_back_forward_list_ = false; bool LayoutTestController::dump_child_frame_scroll_positions_ = false; bool LayoutTestController::dump_child_frames_as_text_ = false; bool LayoutTestController::dump_window_status_changes_ = false; bool LayoutTestController::dump_title_changes_ = false; bool LayoutTestController::dump_selection_rect_ = false; bool LayoutTestController::accepts_editing_ = true; bool LayoutTestController::wait_until_done_ = false; bool LayoutTestController::can_open_windows_ = false; bool LayoutTestController::close_remaining_windows_ = true; bool LayoutTestController::test_repaint_ = false; bool LayoutTestController::sweep_horizontally_ = false; bool LayoutTestController::should_add_file_to_pasteboard_ = false; bool LayoutTestController::stop_provisional_frame_loads_ = false; LayoutTestController::WorkQueue LayoutTestController::work_queue_; CppVariant LayoutTestController::globalFlag_; CppVariant LayoutTestController::webHistoryItemCount_; LayoutTestController::LayoutTestController(TestShell* shell) : ALLOW_THIS_IN_INITIALIZER_LIST(timeout_factory_(this)) { // Set static shell_ variable since we can't do it in an initializer list. // We also need to be careful not to assign shell_ to new windows which are // temporary. if (NULL == shell_) shell_ = shell; // Initialize the map that associates methods of this class with the names // they will use when called by JavaScript. The actual binding of those // names to their methods will be done by calling BindToJavaScript() (defined // by CppBoundClass, the parent to LayoutTestController). BindMethod("dumpAsText", &LayoutTestController::dumpAsText); BindMethod("dumpChildFrameScrollPositions", &LayoutTestController::dumpChildFrameScrollPositions); BindMethod("dumpChildFramesAsText", &LayoutTestController::dumpChildFramesAsText); BindMethod("dumpDatabaseCallbacks", &LayoutTestController::dumpDatabaseCallbacks); BindMethod("dumpEditingCallbacks", &LayoutTestController::dumpEditingCallbacks); BindMethod("dumpBackForwardList", &LayoutTestController::dumpBackForwardList); BindMethod("dumpFrameLoadCallbacks", &LayoutTestController::dumpFrameLoadCallbacks); BindMethod("dumpResourceLoadCallbacks", &LayoutTestController::dumpResourceLoadCallbacks); BindMethod("dumpStatusCallbacks", &LayoutTestController::dumpWindowStatusChanges); BindMethod("dumpTitleChanges", &LayoutTestController::dumpTitleChanges); BindMethod("setAcceptsEditing", &LayoutTestController::setAcceptsEditing); BindMethod("waitUntilDone", &LayoutTestController::waitUntilDone); BindMethod("notifyDone", &LayoutTestController::notifyDone); BindMethod("queueReload", &LayoutTestController::queueReload); BindMethod("queueLoadingScript", &LayoutTestController::queueLoadingScript); BindMethod("queueNonLoadingScript", &LayoutTestController::queueNonLoadingScript); BindMethod("queueLoad", &LayoutTestController::queueLoad); BindMethod("queueBackNavigation", &LayoutTestController::queueBackNavigation); BindMethod("queueForwardNavigation", &LayoutTestController::queueForwardNavigation); BindMethod("windowCount", &LayoutTestController::windowCount); BindMethod("setCanOpenWindows", &LayoutTestController::setCanOpenWindows); BindMethod("setCloseRemainingWindowsWhenComplete", &LayoutTestController::setCloseRemainingWindowsWhenComplete); BindMethod("objCIdentityIsEqual", &LayoutTestController::objCIdentityIsEqual); BindMethod("setAlwaysAcceptCookies", &LayoutTestController::setAlwaysAcceptCookies); BindMethod("showWebInspector", &LayoutTestController::showWebInspector); BindMethod("setWindowIsKey", &LayoutTestController::setWindowIsKey); BindMethod("setTabKeyCyclesThroughElements", &LayoutTestController::setTabKeyCyclesThroughElements); BindMethod("setUserStyleSheetLocation", &LayoutTestController::setUserStyleSheetLocation); BindMethod("setUserStyleSheetEnabled", &LayoutTestController::setUserStyleSheetEnabled); BindMethod("setAuthorAndUserStylesEnabled", &LayoutTestController::setAuthorAndUserStylesEnabled); BindMethod("pathToLocalResource", &LayoutTestController::pathToLocalResource); BindMethod("addFileToPasteboardOnDrag", &LayoutTestController::addFileToPasteboardOnDrag); BindMethod("execCommand", &LayoutTestController::execCommand); BindMethod("isCommandEnabled", &LayoutTestController::isCommandEnabled); BindMethod("setPopupBlockingEnabled", &LayoutTestController::setPopupBlockingEnabled); BindMethod("setStopProvisionalFrameLoads", &LayoutTestController::setStopProvisionalFrameLoads); BindMethod("setSmartInsertDeleteEnabled", &LayoutTestController::setSmartInsertDeleteEnabled); BindMethod("setSelectTrailingWhitespaceEnabled", &LayoutTestController::setSelectTrailingWhitespaceEnabled); BindMethod("pauseAnimationAtTimeOnElementWithId", &LayoutTestController::pauseAnimationAtTimeOnElementWithId); BindMethod("pauseTransitionAtTimeOnElementWithId", &LayoutTestController::pauseTransitionAtTimeOnElementWithId); BindMethod("elementDoesAutoCompleteForElementWithId", &LayoutTestController::elementDoesAutoCompleteForElementWithId); BindMethod("numberOfActiveAnimations", &LayoutTestController::numberOfActiveAnimations); BindMethod("disableImageLoading", &LayoutTestController::disableImageLoading); BindMethod("setIconDatabaseEnabled", &LayoutTestController::setIconDatabaseEnabled); BindMethod("setCustomPolicyDelegate", &LayoutTestController::setCustomPolicyDelegate); BindMethod("setScrollbarPolicy", &LayoutTestController::setScrollbarPolicy); BindMethod("waitForPolicyDelegate", &LayoutTestController::waitForPolicyDelegate); BindMethod("setWillSendRequestClearHeader", &LayoutTestController::setWillSendRequestClearHeader); BindMethod("setWillSendRequestReturnsNullOnRedirect", &LayoutTestController::setWillSendRequestReturnsNullOnRedirect); BindMethod("setWillSendRequestReturnsNull", &LayoutTestController::setWillSendRequestReturnsNull); BindMethod("addOriginAccessWhitelistEntry", &LayoutTestController::addOriginAccessWhitelistEntry); BindMethod("removeOriginAccessWhitelistEntry", &LayoutTestController::removeOriginAccessWhitelistEntry); BindMethod("clearAllDatabases", &LayoutTestController::clearAllDatabases); BindMethod("setDatabaseQuota", &LayoutTestController::setDatabaseQuota); BindMethod("setPOSIXLocale", &LayoutTestController::setPOSIXLocale); BindMethod("counterValueForElementById", &LayoutTestController::counterValueForElementById); BindMethod("addUserScript", &LayoutTestController::addUserScript); BindMethod("addUserStyleSheet", &LayoutTestController::addUserStyleSheet); BindMethod("pageNumberForElementById", &LayoutTestController::pageNumberForElementById); BindMethod("numberOfPages", &LayoutTestController::numberOfPages); BindMethod("dumpSelectionRect", &LayoutTestController::dumpSelectionRect); BindMethod("grantDesktopNotificationPermission", &LayoutTestController::grantDesktopNotificationPermission); // The following are stubs. BindMethod("dumpAsWebArchive", &LayoutTestController::dumpAsWebArchive); BindMethod("setMainFrameIsFirstResponder", &LayoutTestController::setMainFrameIsFirstResponder); BindMethod("display", &LayoutTestController::display); BindMethod("testRepaint", &LayoutTestController::testRepaint); BindMethod("repaintSweepHorizontally", &LayoutTestController::repaintSweepHorizontally); BindMethod("clearBackForwardList", &LayoutTestController::clearBackForwardList); BindMethod("keepWebHistory", &LayoutTestController::keepWebHistory); BindMethod("storeWebScriptObject", &LayoutTestController::storeWebScriptObject); BindMethod("accessStoredWebScriptObject", &LayoutTestController::accessStoredWebScriptObject); BindMethod("objCClassNameOf", &LayoutTestController::objCClassNameOf); BindMethod("addDisallowedURL", &LayoutTestController::addDisallowedURL); BindMethod("callShouldCloseOnWebView", &LayoutTestController::callShouldCloseOnWebView); BindMethod("setCallCloseOnWebViews", &LayoutTestController::setCallCloseOnWebViews); BindMethod("setPrivateBrowsingEnabled", &LayoutTestController::setPrivateBrowsingEnabled); BindMethod("setUseDashboardCompatibilityMode", &LayoutTestController::setUseDashboardCompatibilityMode); BindMethod("setJavaScriptCanAccessClipboard", &LayoutTestController::setJavaScriptCanAccessClipboard); BindMethod("setXSSAuditorEnabled", &LayoutTestController::setXSSAuditorEnabled); BindMethod("evaluateScriptInIsolatedWorld", &LayoutTestController::evaluateScriptInIsolatedWorld); BindMethod("overridePreference", &LayoutTestController::overridePreference); BindMethod("setAllowUniversalAccessFromFileURLs", &LayoutTestController::setAllowUniversalAccessFromFileURLs); BindMethod("setAllowFileAccessFromFileURLs", &LayoutTestController::setAllowFileAccessFromFileURLs); BindMethod("setTimelineProfilingEnabled", &LayoutTestController::setTimelineProfilingEnabled); BindMethod("evaluateInWebInspector", &LayoutTestController::evaluateInWebInspector); BindMethod("forceRedSelectionColors", &LayoutTestController::forceRedSelectionColors); // The fallback method is called when an unknown method is invoked. BindFallbackMethod(&LayoutTestController::fallbackMethod); // Shared properties. // globalFlag is used by a number of layout tests in // LayoutTests\http\tests\security\dataURL. BindProperty("globalFlag", &globalFlag_); // webHistoryItemCount is used by tests in LayoutTests\http\tests\history BindProperty("webHistoryItemCount", &webHistoryItemCount_); } LayoutTestController::WorkQueue::~WorkQueue() { Reset(); } void LayoutTestController::WorkQueue::ProcessWorkSoon() { if (shell_->delegate()->top_loading_frame()) return; if (!queue_.empty()) { // We delay processing queued work to avoid recursion problems. timer_.Start(base::TimeDelta(), this, &WorkQueue::ProcessWork); } else if (!wait_until_done_) { shell_->TestFinished(); } } void LayoutTestController::WorkQueue::ProcessWork() { // Quit doing work once a load is in progress. while (!queue_.empty()) { bool started_load = queue_.front()->Run(shell_); delete queue_.front(); queue_.pop(); if (started_load) return; } if (!wait_until_done_ && !shell_->delegate()->top_loading_frame()) shell_->TestFinished(); } void LayoutTestController::WorkQueue::Reset() { frozen_ = false; while (!queue_.empty()) { delete queue_.front(); queue_.pop(); } } void LayoutTestController::WorkQueue::AddWork(WorkItem* work) { if (frozen_) { delete work; return; } queue_.push(work); } void LayoutTestController::dumpAsText(const CppArgumentList& args, CppVariant* result) { dump_as_text_ = true; result->SetNull(); } void LayoutTestController::dumpDatabaseCallbacks( const CppArgumentList& args, CppVariant* result) { // Do nothing; we don't use this flag anywhere for now result->SetNull(); } void LayoutTestController::dumpEditingCallbacks( const CppArgumentList& args, CppVariant* result) { dump_editing_callbacks_ = true; result->SetNull(); } void LayoutTestController::dumpBackForwardList( const CppArgumentList& args, CppVariant* result) { dump_back_forward_list_ = true; result->SetNull(); } void LayoutTestController::dumpFrameLoadCallbacks( const CppArgumentList& args, CppVariant* result) { dump_frame_load_callbacks_ = true; result->SetNull(); } void LayoutTestController::dumpResourceLoadCallbacks( const CppArgumentList& args, CppVariant* result) { dump_resource_load_callbacks_ = true; result->SetNull(); } void LayoutTestController::dumpChildFrameScrollPositions( const CppArgumentList& args, CppVariant* result) { dump_child_frame_scroll_positions_ = true; result->SetNull(); } void LayoutTestController::dumpChildFramesAsText( const CppArgumentList& args, CppVariant* result) { dump_child_frames_as_text_ = true; result->SetNull(); } void LayoutTestController::dumpWindowStatusChanges( const CppArgumentList& args, CppVariant* result) { dump_window_status_changes_ = true; result->SetNull(); } void LayoutTestController::dumpTitleChanges( const CppArgumentList& args, CppVariant* result) { dump_title_changes_ = true; result->SetNull(); } void LayoutTestController::setAcceptsEditing( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) accepts_editing_ = args[0].value.boolValue; result->SetNull(); } void LayoutTestController::waitUntilDone( const CppArgumentList& args, CppVariant* result) { bool is_debugger_present = false; #if defined(OS_WIN) // TODO(ojan): Make cross-platform. is_debugger_present = ::IsDebuggerPresent(); #endif if (!is_debugger_present) { // TODO(ojan): Use base::OneShotTimer. For some reason, using OneShotTimer // seems to cause layout test failures on the try bots. MessageLoop::current()->PostDelayedTask(FROM_HERE, timeout_factory_.NewRunnableMethod( &LayoutTestController::notifyDoneTimedOut), shell_->GetLayoutTestTimeout()); } wait_until_done_ = true; result->SetNull(); } void LayoutTestController::notifyDone( const CppArgumentList& args, CppVariant* result) { // Test didn't timeout. Kill the timeout timer. timeout_factory_.RevokeAll(); completeNotifyDone(false); result->SetNull(); } void LayoutTestController::notifyDoneTimedOut() { completeNotifyDone(true); } void LayoutTestController::completeNotifyDone(bool is_timeout) { if (shell_->layout_test_mode() && wait_until_done_ && !shell_->delegate()->top_loading_frame() && work_queue_.empty()) { if (is_timeout) shell_->TestTimedOut(); else shell_->TestFinished(); } wait_until_done_ = false; } class WorkItemBackForward : public LayoutTestController::WorkItem { public: WorkItemBackForward(int distance) : distance_(distance) {} bool Run(TestShell* shell) { shell->GoBackOrForward(distance_); return true; // TODO(darin): Did it really start a navigation? } private: int distance_; }; void LayoutTestController::queueBackNavigation( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isNumber()) work_queue_.AddWork(new WorkItemBackForward(-args[0].ToInt32())); result->SetNull(); } void LayoutTestController::queueForwardNavigation( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isNumber()) work_queue_.AddWork(new WorkItemBackForward(args[0].ToInt32())); result->SetNull(); } class WorkItemReload : public LayoutTestController::WorkItem { public: bool Run(TestShell* shell) { shell->Reload(); return true; } }; void LayoutTestController::queueReload( const CppArgumentList& args, CppVariant* result) { work_queue_.AddWork(new WorkItemReload); result->SetNull(); } class WorkItemLoadingScript : public LayoutTestController::WorkItem { public: WorkItemLoadingScript(const string& script) : script_(script) {} bool Run(TestShell* shell) { shell->webView()->mainFrame()->executeScript( WebScriptSource(WebString::fromUTF8(script_))); return true; // TODO(darin): Did it really start a navigation? } private: string script_; }; class WorkItemNonLoadingScript : public LayoutTestController::WorkItem { public: WorkItemNonLoadingScript(const string& script) : script_(script) {} bool Run(TestShell* shell) { shell->webView()->mainFrame()->executeScript( WebScriptSource(WebString::fromUTF8(script_))); return false; } private: string script_; }; void LayoutTestController::queueLoadingScript( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isString()) work_queue_.AddWork(new WorkItemLoadingScript(args[0].ToString())); result->SetNull(); } void LayoutTestController::queueNonLoadingScript( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isString()) work_queue_.AddWork(new WorkItemNonLoadingScript(args[0].ToString())); result->SetNull(); } class WorkItemLoad : public LayoutTestController::WorkItem { public: WorkItemLoad(const GURL& url, const string& target) : url_(url), target_(target) {} bool Run(TestShell* shell) { shell->LoadURLForFrame(url_, UTF8ToWide(target_).c_str()); return true; // TODO(darin): Did it really start a navigation? } private: GURL url_; string target_; }; void LayoutTestController::queueLoad( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isString()) { GURL current_url = shell_->webView()->mainFrame()->url(); GURL full_url = current_url.Resolve(args[0].ToString()); string target = ""; if (args.size() > 1 && args[1].isString()) target = args[1].ToString(); work_queue_.AddWork(new WorkItemLoad(full_url, target)); } result->SetNull(); } void LayoutTestController::objCIdentityIsEqual( const CppArgumentList& args, CppVariant* result) { if (args.size() < 2) { // This is the best we can do to return an error. result->SetNull(); return; } result->Set(args[0].isEqual(args[1])); } void LayoutTestController::Reset() { if (shell_) { shell_->webView()->setZoomLevel(false, 0); shell_->webView()->setTabKeyCyclesThroughElements(true); #if defined(TOOLKIT_GTK) // (Constants copied because we can't depend on the header that defined // them from this file.) shell_->webView()->setSelectionColors( 0xff1e90ff, 0xff000000, 0xffc8c8c8, 0xff323232); #endif // defined(TOOLKIT_GTK) shell_->webView()->removeAllUserContent(); } dump_as_text_ = false; dump_editing_callbacks_ = false; dump_frame_load_callbacks_ = false; dump_resource_load_callbacks_ = false; dump_back_forward_list_ = false; dump_child_frame_scroll_positions_ = false; dump_child_frames_as_text_ = false; dump_window_status_changes_ = false; dump_selection_rect_ = false; dump_title_changes_ = false; accepts_editing_ = true; wait_until_done_ = false; can_open_windows_ = false; test_repaint_ = false; sweep_horizontally_ = false; should_add_file_to_pasteboard_ = false; stop_provisional_frame_loads_ = false; globalFlag_.Set(false); webHistoryItemCount_.Set(0); SimpleResourceLoaderBridge::SetAcceptAllCookies(false); WebSecurityPolicy::resetOriginAccessWhitelists(); // Reset the default quota for each origin to 5MB SimpleDatabaseSystem::GetInstance()->SetDatabaseQuota(5 * 1024 * 1024); setlocale(LC_ALL, ""); if (close_remaining_windows_) { // Iterate through the window list and close everything except the original // shell. We don't want to delete elements as we're iterating, so we copy // to a temp vector first. WindowList* windows = TestShell::windowList(); std::vector windows_to_delete; for (WindowList::iterator i = windows->begin(); i != windows->end(); ++i) { if (*i != shell_->mainWnd()) windows_to_delete.push_back(*i); } DCHECK(windows_to_delete.size() + 1 == windows->size()); for (size_t i = 0; i < windows_to_delete.size(); ++i) { TestShell::DestroyWindow(windows_to_delete[i]); } DCHECK(windows->size() == 1); } else { // Reset the value close_remaining_windows_ = true; } work_queue_.Reset(); } void LayoutTestController::LocationChangeDone() { webHistoryItemCount_.Set(shell_->navigation_controller()->GetEntryCount()); // no more new work after the first complete load. work_queue_.set_frozen(true); if (!wait_until_done_) work_queue_.ProcessWorkSoon(); } void LayoutTestController::PolicyDelegateDone() { if (!shell_->layout_test_mode()) return; DCHECK(wait_until_done_); shell_->TestFinished(); wait_until_done_ = false; } void LayoutTestController::setCanOpenWindows( const CppArgumentList& args, CppVariant* result) { can_open_windows_ = true; result->SetNull(); } void LayoutTestController::setTabKeyCyclesThroughElements( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) { shell_->webView()->setTabKeyCyclesThroughElements(args[0].ToBoolean()); } result->SetNull(); } void LayoutTestController::windowCount( const CppArgumentList& args, CppVariant* result) { int num_windows = static_cast(TestShell::windowList()->size()); result->Set(num_windows); } void LayoutTestController::setCloseRemainingWindowsWhenComplete( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) { close_remaining_windows_ = args[0].value.boolValue; } result->SetNull(); } void LayoutTestController::setAlwaysAcceptCookies( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0) { SimpleResourceLoaderBridge::SetAcceptAllCookies(CppVariantToBool(args[0])); } result->SetNull(); } void LayoutTestController::showWebInspector( const CppArgumentList& args, CppVariant* result) { shell_->ShowDevTools(); result->SetNull(); } void LayoutTestController::setWindowIsKey( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) { shell_->SetFocus(shell_->webViewHost(), args[0].value.boolValue); } result->SetNull(); } void LayoutTestController::setUserStyleSheetEnabled( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) { shell_->delegate()->SetUserStyleSheetEnabled(args[0].value.boolValue); } result->SetNull(); } void LayoutTestController::setUserStyleSheetLocation( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isString()) { GURL location(TestShell::RewriteLocalUrl(args[0].ToString())); shell_->delegate()->SetUserStyleSheetLocation(location); } result->SetNull(); } void LayoutTestController::setAuthorAndUserStylesEnabled( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) { shell_->delegate()->SetAuthorAndUserStylesEnabled(args[0].value.boolValue); } result->SetNull(); } void LayoutTestController::execCommand( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isString()) { std::string command = args[0].ToString(); std::string value(""); // Ignore the second parameter (which is userInterface) // since this command emulates a manual action. if (args.size() >= 3 && args[2].isString()) value = args[2].ToString(); // Note: webkit's version does not return the boolean, so neither do we. shell_->webView()->focusedFrame()->executeCommand( WebString::fromUTF8(command), WebString::fromUTF8(value)); } result->SetNull(); } void LayoutTestController::isCommandEnabled( const CppArgumentList& args, CppVariant* result) { if (args.size() <= 0 || !args[0].isString()) { result->SetNull(); return; } std::string command = args[0].ToString(); bool rv = shell_->webView()->focusedFrame()->isCommandEnabled( WebString::fromUTF8(command)); result->Set(rv); } void LayoutTestController::setPopupBlockingEnabled( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) { bool block_popups = args[0].ToBoolean(); WebPreferences* prefs = shell_->GetWebPreferences(); prefs->javascript_can_open_windows_automatically = !block_popups; prefs->Apply(shell_->webView()); } result->SetNull(); } void LayoutTestController::setUseDashboardCompatibilityMode( const CppArgumentList& args, CppVariant* result) { // We have no need to support Dashboard Compatibility Mode (mac-only) result->SetNull(); } void LayoutTestController::setScrollbarPolicy( const CppArgumentList& args, CppVariant* result) { // FIXME: implement. // Currently only has a non-null implementation on QT. result->SetNull(); } void LayoutTestController::setCustomPolicyDelegate( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) { bool enable = args[0].value.boolValue; bool permissive = false; if (args.size() > 1 && args[1].isBool()) permissive = args[1].value.boolValue; shell_->delegate()->SetCustomPolicyDelegate(enable, permissive); } result->SetNull(); } void LayoutTestController::waitForPolicyDelegate( const CppArgumentList& args, CppVariant* result) { shell_->delegate()->WaitForPolicyDelegate(); wait_until_done_ = true; result->SetNull(); } void LayoutTestController::setWillSendRequestClearHeader( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isString()) { std::string header = args[0].ToString(); if (!header.empty()) shell_->delegate()->set_clear_header(header); } result->SetNull(); } void LayoutTestController::setWillSendRequestReturnsNullOnRedirect( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) shell_->delegate()->set_block_redirects(args[0].value.boolValue); result->SetNull(); } void LayoutTestController::setWillSendRequestReturnsNull( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) shell_->delegate()->set_request_return_null(args[0].value.boolValue); result->SetNull(); } void LayoutTestController::pathToLocalResource( const CppArgumentList& args, CppVariant* result) { result->SetNull(); if (args.size() <= 0 || !args[0].isString()) return; std::string url = args[0].ToString(); if (StartsWithASCII(url, "/tmp/", true)) { // We want a temp file. FilePath path; PathService::Get(base::DIR_TEMP, &path); path = path.AppendASCII(url.substr(5)); result->Set(WideToUTF8(path.ToWStringHack())); return; } // Some layout tests use file://// which we resolve as a UNC path. Normalize // them to just file:///. while (StartsWithASCII(url, "file:////", false)) { url = url.substr(0, 8) + url.substr(9); } GURL location(TestShell::RewriteLocalUrl(url)); result->Set(location.spec()); } void LayoutTestController::addFileToPasteboardOnDrag( const CppArgumentList& args, CppVariant* result) { result->SetNull(); should_add_file_to_pasteboard_ = true; } void LayoutTestController::setStopProvisionalFrameLoads( const CppArgumentList& args, CppVariant* result) { result->SetNull(); stop_provisional_frame_loads_ = true; } void LayoutTestController::setSmartInsertDeleteEnabled( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) { shell_->delegate()->SetSmartInsertDeleteEnabled(args[0].value.boolValue); } result->SetNull(); } void LayoutTestController::setSelectTrailingWhitespaceEnabled( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) { shell_->delegate()->SetSelectTrailingWhitespaceEnabled( args[0].value.boolValue); } result->SetNull(); } void LayoutTestController::pauseAnimationAtTimeOnElementWithId( const CppArgumentList& args, CppVariant* result) { if (args.size() > 2 && args[0].isString() && args[1].isNumber() && args[2].isString()) { std::string animation_name = args[0].ToString(); double time = args[1].ToDouble(); std::string element_id = args[2].ToString(); result->Set( webkit_glue::PauseAnimationAtTimeOnElementWithId( shell_->webView(), animation_name, time, element_id)); } else { result->Set(false); } } void LayoutTestController::pauseTransitionAtTimeOnElementWithId( const CppArgumentList& args, CppVariant* result) { if (args.size() > 2 && args[0].isString() && args[1].isNumber() && args[2].isString()) { std::string property_name = args[0].ToString(); double time = args[1].ToDouble(); std::string element_id = args[2].ToString(); result->Set(webkit_glue::PauseTransitionAtTimeOnElementWithId( shell_->webView(), property_name, time, element_id)); } else { result->Set(false); } } void LayoutTestController::elementDoesAutoCompleteForElementWithId( const CppArgumentList& args, CppVariant* result) { if (args.size() != 1 || !args[0].isString()) { result->Set(false); return; } std::string element_id = args[0].ToString(); result->Set(webkit_glue::ElementDoesAutoCompleteForElementWithId( shell_->webView(), element_id)); } void LayoutTestController::numberOfActiveAnimations(const CppArgumentList& args, CppVariant* result) { result->Set(webkit_glue::NumberOfActiveAnimations(shell_->webView())); } void LayoutTestController::disableImageLoading(const CppArgumentList& args, CppVariant* result) { WebPreferences* prefs = shell_->GetWebPreferences(); prefs->loads_images_automatically = false; prefs->Apply(shell_->webView()); result->SetNull(); } void LayoutTestController::setIconDatabaseEnabled(const CppArgumentList& args, CppVariant* result) { // We don't use the WebKit icon database. result->SetNull(); } void LayoutTestController::callShouldCloseOnWebView( const CppArgumentList& args, CppVariant* result) { bool rv = shell_->webView()->dispatchBeforeUnloadEvent(); result->Set(rv); } void LayoutTestController::grantDesktopNotificationPermission( const CppArgumentList& args, CppVariant* result) { if (args.size() != 1 || !args[0].isString()) { result->Set(false); return; } std::string origin = args[0].ToString(); shell_->notification_presenter()->grantPermission(origin); result->Set(true); } // // Unimplemented stubs // void LayoutTestController::dumpAsWebArchive( const CppArgumentList& args, CppVariant* result) { result->SetNull(); } void LayoutTestController::setMainFrameIsFirstResponder( const CppArgumentList& args, CppVariant* result) { result->SetNull(); } void LayoutTestController::dumpSelectionRect( const CppArgumentList& args, CppVariant* result) { dump_selection_rect_ = true; result->SetNull(); } void LayoutTestController::display( const CppArgumentList& args, CppVariant* result) { WebViewHost* view_host = shell_->webViewHost(); const WebKit::WebSize& size = view_host->webview()->size(); gfx::Rect rect(size.width, size.height); view_host->UpdatePaintRect(rect); view_host->Paint(); view_host->DisplayRepaintMask(); result->SetNull(); } void LayoutTestController::testRepaint( const CppArgumentList& args, CppVariant* result) { test_repaint_ = true; result->SetNull(); } void LayoutTestController::repaintSweepHorizontally( const CppArgumentList& args, CppVariant* result) { sweep_horizontally_ = true; result->SetNull(); } void LayoutTestController::clearBackForwardList( const CppArgumentList& args, CppVariant* result) { result->SetNull(); } void LayoutTestController::keepWebHistory( const CppArgumentList& args, CppVariant* result) { result->SetNull(); } void LayoutTestController::storeWebScriptObject( const CppArgumentList& args, CppVariant* result) { result->SetNull(); } void LayoutTestController::accessStoredWebScriptObject( const CppArgumentList& args, CppVariant* result) { result->SetNull(); } void LayoutTestController::objCClassNameOf( const CppArgumentList& args, CppVariant* result) { result->SetNull(); } void LayoutTestController::addDisallowedURL( const CppArgumentList& args, CppVariant* result) { result->SetNull(); } void LayoutTestController::setCallCloseOnWebViews( const CppArgumentList& args, CppVariant* result) { result->SetNull(); } void LayoutTestController::setPrivateBrowsingEnabled( const CppArgumentList& args, CppVariant* result) { result->SetNull(); } void LayoutTestController::setJavaScriptCanAccessClipboard( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) { WebPreferences* prefs = shell_->GetWebPreferences(); prefs->javascript_can_access_clipboard = args[0].value.boolValue; prefs->Apply(shell_->webView()); } result->SetNull(); } void LayoutTestController::setXSSAuditorEnabled( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) { WebPreferences* prefs = shell_->GetWebPreferences(); prefs->xss_auditor_enabled = args[0].value.boolValue; prefs->Apply(shell_->webView()); } result->SetNull(); } void LayoutTestController::evaluateScriptInIsolatedWorld( const CppArgumentList& args, CppVariant* result) { if (args.size() >= 2 && args[0].isNumber() && args[1].isString()) { WebScriptSource source(WebString::fromUTF8(args[1].ToString())); // This relies on the iframe focusing itself when it loads. This is a bit // sketchy, but it seems to be what other tests do. shell_->webView()->focusedFrame()->executeScriptInIsolatedWorld( args[0].ToInt32(), &source, 1, 1); } result->SetNull(); } void LayoutTestController::setAllowUniversalAccessFromFileURLs( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) { WebPreferences* prefs = shell_->GetWebPreferences(); prefs->allow_universal_access_from_file_urls = args[0].value.boolValue; prefs->Apply(shell_->webView()); } result->SetNull(); } void LayoutTestController::setAllowFileAccessFromFileURLs( const CppArgumentList& args, CppVariant* result) { if (args.size() > 0 && args[0].isBool()) { WebPreferences* prefs = shell_->GetWebPreferences(); prefs->allow_file_access_from_file_urls = args[0].value.boolValue; prefs->Apply(shell_->webView()); } result->SetNull(); } // Need these conversions because the format of the value for booleans // may vary - for example, on mac "1" and "0" are used for boolean. bool LayoutTestController::CppVariantToBool(const CppVariant& value) { if (value.isBool()) return value.ToBoolean(); else if (value.isInt32()) return 0 != value.ToInt32(); else if (value.isString()) { std::string valueString = value.ToString(); if (valueString == "true" || valueString == "1") return true; if (valueString == "false" || valueString == "0") return false; } LogErrorToConsole("Invalid value. Expected boolean value."); return false; } int32 LayoutTestController::CppVariantToInt32(const CppVariant& value) { if (value.isInt32()) return value.ToInt32(); else if (value.isString()) { int number; if (StringToInt(value.ToString(), &number)) return number; } LogErrorToConsole("Invalid value for preference. Expected integer value."); return 0; } std::wstring LayoutTestController::CppVariantToWstring( const CppVariant& value) { if (value.isString()) return UTF8ToWide(value.ToString()); LogErrorToConsole("Invalid value for preference. Expected string value."); return std::wstring(); } void LayoutTestController::overridePreference( const CppArgumentList& args, CppVariant* result) { if (args.size() == 2 && args[0].isString()) { std::string key = args[0].ToString(); CppVariant value = args[1]; WebPreferences* preferences = shell_->GetWebPreferences(); if (key == "WebKitStandardFont") preferences->standard_font_family = CppVariantToWstring(value); else if (key == "WebKitFixedFont") preferences->fixed_font_family = CppVariantToWstring(value); else if (key == "WebKitSerifFont") preferences->serif_font_family = CppVariantToWstring(value); else if (key == "WebKitSansSerifFont") preferences->sans_serif_font_family = CppVariantToWstring(value); else if (key == "WebKitCursiveFont") preferences->cursive_font_family = CppVariantToWstring(value); else if (key == "WebKitFantasyFont") preferences->fantasy_font_family = CppVariantToWstring(value); else if (key == "WebKitDefaultFontSize") preferences->default_font_size = CppVariantToInt32(value); else if (key == "WebKitDefaultFixedFontSize") preferences->default_fixed_font_size = CppVariantToInt32(value); else if (key == "WebKitMinimumFontSize") preferences->minimum_font_size = CppVariantToInt32(value); else if (key == "WebKitMinimumLogicalFontSize") preferences->minimum_logical_font_size = CppVariantToInt32(value); else if (key == "WebKitDefaultTextEncodingName") preferences->default_encoding = value.ToString(); else if (key == "WebKitJavaScriptEnabled") preferences->javascript_enabled = CppVariantToBool(value); else if (key == "WebKitWebSecurityEnabled") preferences->web_security_enabled = CppVariantToBool(value); else if (key == "WebKitJavaScriptCanOpenWindowsAutomatically") preferences->javascript_can_open_windows_automatically = CppVariantToBool(value); else if (key == "WebKitDisplayImagesKey") preferences->loads_images_automatically = CppVariantToBool(value); else if (key == "WebKitPluginsEnabled") preferences->plugins_enabled = CppVariantToBool(value); else if (key == "WebKitDOMPasteAllowedPreferenceKey") preferences->dom_paste_enabled = CppVariantToBool(value); else if (key == "WebKitDeveloperExtrasEnabledPreferenceKey") preferences->developer_extras_enabled = CppVariantToBool(value); else if (key == "WebKitShrinksStandaloneImagesToFit") preferences->shrinks_standalone_images_to_fit = CppVariantToBool(value); else if (key == "WebKitTextAreasAreResizable") preferences->text_areas_are_resizable = CppVariantToBool(value); else if (key == "WebKitJavaEnabled") preferences->java_enabled = CppVariantToBool(value); else if (key == "WebKitUsesPageCachePreferenceKey") preferences->uses_page_cache = CppVariantToBool(value); else if (key == "WebKitJavaScriptCanAccessClipboard") preferences->javascript_can_access_clipboard = CppVariantToBool(value); else if (key == "WebKitXSSAuditorEnabled") preferences->xss_auditor_enabled = CppVariantToBool(value); else if (key == "WebKitLocalStorageEnabledPreferenceKey") preferences->local_storage_enabled = CppVariantToBool(value); else if (key == "WebKitOfflineWebApplicationCacheEnabled") preferences->application_cache_enabled = CppVariantToBool(value); else if (key == "WebKitTabToLinksPreferenceKey") preferences->tabs_to_links = CppVariantToBool(value); else if (key == "WebKitWebGLEnabled") preferences->experimental_webgl_enabled = CppVariantToBool(value); else { std::string message("Invalid name for preference: "); message.append(key); LogErrorToConsole(message); } preferences->Apply(shell_->webView()); } result->SetNull(); } void LayoutTestController::fallbackMethod( const CppArgumentList& args, CppVariant* result) { std::wstring message(L"JavaScript ERROR: unknown method called on LayoutTestController"); if (!shell_->layout_test_mode()) { logging::LogMessage("CONSOLE:", 0).stream() << message; } else { printf("CONSOLE MESSAGE: %S\n", message.c_str()); } result->SetNull(); } void LayoutTestController::addOriginAccessWhitelistEntry( const CppArgumentList& args, CppVariant* result) { result->SetNull(); if (args.size() != 4 || !args[0].isString() || !args[1].isString() || !args[2].isString() || !args[3].isBool()) return; WebKit::WebURL url(GURL(args[0].ToString())); if (!url.isValid()) return; WebSecurityPolicy::addOriginAccessWhitelistEntry(url, WebString::fromUTF8(args[1].ToString()), WebString::fromUTF8(args[2].ToString()), args[3].ToBoolean()); } void LayoutTestController::removeOriginAccessWhitelistEntry( const CppArgumentList& args, CppVariant* result) { result->SetNull(); if (args.size() != 4 || !args[0].isString() || !args[1].isString() || !args[2].isString() || !args[3].isBool()) return; WebKit::WebURL url(GURL(args[0].ToString())); if (!url.isValid()) return; WebSecurityPolicy::removeOriginAccessWhitelistEntry(url, WebString::fromUTF8(args[1].ToString()), WebString::fromUTF8(args[2].ToString()), args[3].ToBoolean()); } void LayoutTestController::clearAllDatabases( const CppArgumentList& args, CppVariant* result) { result->SetNull(); SimpleDatabaseSystem::GetInstance()->ClearAllDatabases(); } void LayoutTestController::setDatabaseQuota( const CppArgumentList& args, CppVariant* result) { result->SetNull(); if ((args.size() >= 1) && args[0].isInt32()) SimpleDatabaseSystem::GetInstance()->SetDatabaseQuota(args[0].ToInt32()); } void LayoutTestController::setPOSIXLocale(const CppArgumentList& args, CppVariant* result) { result->SetNull(); if (args.size() == 1 && args[0].isString()) { std::string new_locale = args[0].ToString(); setlocale(LC_ALL, new_locale.c_str()); } } void LayoutTestController::counterValueForElementById( const CppArgumentList& args, CppVariant* result) { result->SetNull(); if (args.size() < 1 || !args[0].isString()) return; std::wstring counterValue; if (!webkit_glue::CounterValueForElementById(shell_->webView()->mainFrame(), args[0].ToString(), &counterValue)) return; result->Set(WideToUTF8(counterValue)); } static bool ParsePageSizeParameters(const CppArgumentList& args, int arg_offset, float* page_width_in_pixels, float* page_height_in_pixels) { // WebKit is using the window width/height of DumpRenderTree as the // default value of the page size. // TODO(hamaji): Once chromium DumpRenderTree is implemented, // share these values with other ports. *page_width_in_pixels = 800; *page_height_in_pixels = 600; switch (args.size() - arg_offset) { case 2: if (!args[arg_offset].isNumber() || !args[1 + arg_offset].isNumber()) return false; *page_width_in_pixels = static_cast(args[arg_offset].ToInt32()); *page_height_in_pixels = static_cast(args[1 + arg_offset].ToInt32()); // fall through. case 0: break; default: return false; } return true; } void LayoutTestController::pageNumberForElementById( const CppArgumentList& args, CppVariant* result) { result->SetNull(); float page_width_in_pixels = 0; float page_height_in_pixels = 0; if (!ParsePageSizeParameters(args, 1, &page_width_in_pixels, &page_height_in_pixels)) return; if (!args[0].isString()) return; int page_number = webkit_glue::PageNumberForElementById( shell_->webView()->mainFrame(), args[0].ToString(), page_width_in_pixels, page_height_in_pixels); result->Set(page_number); } void LayoutTestController::numberOfPages( const CppArgumentList& args, CppVariant* result) { result->SetNull(); float page_width_in_pixels = 0; float page_height_in_pixels = 0; if (!ParsePageSizeParameters(args, 0, &page_width_in_pixels, &page_height_in_pixels)) return; int page_number = webkit_glue::NumberOfPages(shell_->webView()->mainFrame(), page_width_in_pixels, page_height_in_pixels); result->Set(page_number); } void LayoutTestController::LogErrorToConsole(const std::string& text) { shell_->delegate()->didAddMessageToConsole( WebConsoleMessage(WebConsoleMessage::LevelError, WebString::fromUTF8(text)), WebString(), 0); } void LayoutTestController::setTimelineProfilingEnabled( const CppArgumentList& args, CppVariant* result) { result->SetNull(); if (args.size() < 1 || !args[0].isBool()) return; shell_->dev_tools_agent()->setTimelineProfilingEnabled(args[0].ToBoolean()); } void LayoutTestController::evaluateInWebInspector(const CppArgumentList& args, CppVariant* result) { result->SetNull(); if (args.size() < 2 || !args[0].isInt32() || !args[1].isString()) return; shell_->dev_tools_agent()->evaluateInWebInspector(args[0].ToInt32(), args[1].ToString()); } void LayoutTestController::forceRedSelectionColors(const CppArgumentList& args, CppVariant* result) { result->SetNull(); shell_->webView()->setSelectionColors(0xffee0000, 0xff00ee00, 0xff000000, 0xffc0c0c0); } void LayoutTestController::addUserScript(const CppArgumentList& args, CppVariant* result) { result->SetNull(); if (args.size() < 2 || !args[0].isString() || !args[1].isBool()) return; shell_->webView()->addUserScript(WebString::fromUTF8(args[0].ToString()), args[1].ToBoolean()); } void LayoutTestController::addUserStyleSheet(const CppArgumentList& args, CppVariant* result) { result->SetNull(); if (args.size() < 1 || !args[0].isString()) return; shell_->webView()->addUserStyleSheet(WebString::fromUTF8(args[0].ToString())); }