// Copyright (c) 2011 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. #include "webkit/support/webkit_support.h" #include "base/at_exit.h" #include "base/base64.h" #include "base/command_line.h" #include "base/debug/debugger.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/i18n/icu_util.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/message_loop.h" #include "base/path_service.h" #include "base/process_util.h" #include "base/string_piece.h" #include "base/string_util.h" #include "base/stringprintf.h" #include "base/sys_string_conversions.h" #include "base/time.h" #include "base/utf_string_conversions.h" #include "grit/webkit_chromium_resources.h" #include "media/base/filter_collection.h" #include "media/base/message_loop_factory_impl.h" #include "net/base/escape.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFileSystemCallbacks.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginParams.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h" #include "ui/gfx/gl/gl_context.h" #include "ui/gfx/gl/gl_implementation.h" #include "webkit/appcache/web_application_cache_host_impl.h" #include "webkit/glue/media/video_renderer_impl.h" #include "webkit/glue/webkit_constants.h" #include "webkit/glue/webkit_glue.h" #include "webkit/glue/webkitclient_impl.h" #include "webkit/glue/webmediaplayer_impl.h" #include "webkit/plugins/npapi/plugin_list.h" #include "webkit/plugins/npapi/webplugin_impl.h" #include "webkit/plugins/npapi/webplugin_page_delegate.h" #include "webkit/plugins/npapi/webplugininfo.h" #include "webkit/support/platform_support.h" #include "webkit/support/simple_database_system.h" #include "webkit/support/test_webplugin_page_delegate.h" #include "webkit/support/test_webkit_client.h" #include "webkit/tools/test_shell/simple_file_system.h" #include "webkit/tools/test_shell/simple_resource_loader_bridge.h" using WebKit::WebCString; using WebKit::WebDevToolsAgentClient; using WebKit::WebFileSystem; using WebKit::WebFileSystemCallbacks; using WebKit::WebFrame; using WebKit::WebMediaPlayerClient; using WebKit::WebPlugin; using WebKit::WebPluginParams; using WebKit::WebString; using WebKit::WebURL; namespace { // All fatal log messages (e.g. DCHECK failures) imply unit test failures void UnitTestAssertHandler(const std::string& str) { FAIL() << str; } void InitLogging(bool enable_gp_fault_error_box) { logging::SetLogAssertHandler(UnitTestAssertHandler); #if defined(OS_WIN) if (!::IsDebuggerPresent()) { UINT new_flags = SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX; if (!enable_gp_fault_error_box) new_flags |= SEM_NOGPFAULTERRORBOX; // Preserve existing error mode, as discussed at // http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx UINT existing_flags = SetErrorMode(new_flags); SetErrorMode(existing_flags | new_flags); } #endif FilePath log_filename; PathService::Get(base::DIR_EXE, &log_filename); log_filename = log_filename.AppendASCII("DumpRenderTree.log"); logging::InitLogging( log_filename.value().c_str(), // Only log to a file. This prevents debugging output from disrupting // whether or not we pass. logging::LOG_ONLY_TO_FILE, // We might have multiple DumpRenderTree processes going at once. logging::LOCK_LOG_FILE, logging::DELETE_OLD_LOG_FILE, logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS); // We want process and thread IDs because we may have multiple processes. const bool kProcessId = true; const bool kThreadId = true; const bool kTimestamp = true; const bool kTickcount = true; logging::SetLogItems(kProcessId, kThreadId, !kTimestamp, kTickcount); } class TestEnvironment { public: explicit TestEnvironment(bool unit_test_mode) { if (!unit_test_mode) { at_exit_manager_.reset(new base::AtExitManager); InitLogging(false); } main_message_loop_.reset(new MessageLoopForUI); // TestWebKitClient must be instantiated after the MessageLoopForUI. webkit_client_.reset(new TestWebKitClient(unit_test_mode)); } ~TestEnvironment() { SimpleResourceLoaderBridge::Shutdown(); } TestWebKitClient* webkit_client() const { return webkit_client_.get(); } #if defined(OS_WIN) || defined(OS_MACOSX) void set_theme_engine(WebKit::WebThemeEngine* engine) { DCHECK(webkit_client_ != 0); webkit_client_->SetThemeEngine(engine); } WebKit::WebThemeEngine* theme_engine() const { return webkit_client_->themeEngine(); } #endif private: scoped_ptr at_exit_manager_; scoped_ptr main_message_loop_; scoped_ptr webkit_client_; }; class WebPluginImplWithPageDelegate : public webkit_support::TestWebPluginPageDelegate, public base::SupportsWeakPtr, public webkit::npapi::WebPluginImpl { public: WebPluginImplWithPageDelegate(WebFrame* frame, const WebPluginParams& params, const FilePath& path, const std::string& mime_type) : webkit_support::TestWebPluginPageDelegate(), webkit::npapi::WebPluginImpl( frame, params, path, mime_type, AsWeakPtr()) {} virtual ~WebPluginImplWithPageDelegate() {} private: DISALLOW_COPY_AND_ASSIGN(WebPluginImplWithPageDelegate); }; FilePath GetWebKitRootDirFilePath() { FilePath basePath; PathService::Get(base::DIR_SOURCE_ROOT, &basePath); if (file_util::PathExists(basePath.Append(FILE_PATH_LITERAL("chrome")))) { return basePath.Append(FILE_PATH_LITERAL("third_party/WebKit")); } else { // WebKit/Source/WebKit/chromium/ -> WebKit/ return basePath.Append(FILE_PATH_LITERAL("../../..")); } } class WebKitClientMessageLoopImpl : public WebDevToolsAgentClient::WebKitClientMessageLoop { public: WebKitClientMessageLoopImpl() : message_loop_(MessageLoop::current()) {} virtual ~WebKitClientMessageLoopImpl() { message_loop_ = NULL; } virtual void run() { bool old_state = message_loop_->NestableTasksAllowed(); message_loop_->SetNestableTasksAllowed(true); message_loop_->Run(); message_loop_->SetNestableTasksAllowed(old_state); } virtual void quitNow() { message_loop_->QuitNow(); } private: MessageLoop* message_loop_; }; // An wrapper object for giving TaskAdaptor ref-countability, // which NewRunnableMethod() requires. class TaskAdaptorHolder : public CancelableTask { public: explicit TaskAdaptorHolder(webkit_support::TaskAdaptor* adaptor) : adaptor_(adaptor) { } virtual void Run() { adaptor_->Run(); } virtual void Cancel() { adaptor_.reset(); } private: scoped_ptr adaptor_; }; } // namespace namespace webkit_support { static TestEnvironment* test_environment; static void SetUpTestEnvironmentImpl(bool unit_test_mode) { base::EnableInProcessStackDumping(); base::EnableTerminationOnHeapCorruption(); // Initialize the singleton CommandLine with fixed values. Some code refer to // CommandLine::ForCurrentProcess(). We don't use the actual command-line // arguments of DRT to avoid unexpected behavior change. // // webkit/glue/webmediaplayer_impl.cc checks --enable-openmax. // webkit/glue/plugin/plugin_list_posix.cc checks --debug-plugin-loading. // webkit/glue/plugin/plugin_list_win.cc checks --old-wmp. // If DRT needs these flags, specify them in the following kFixedArguments. const char* kFixedArguments[] = {"DumpRenderTree"}; CommandLine::Init(arraysize(kFixedArguments), kFixedArguments); webkit_support::BeforeInitialize(unit_test_mode); webkit_support::test_environment = new TestEnvironment(unit_test_mode); webkit_support::AfterInitialize(unit_test_mode); if (!unit_test_mode) { // Load ICU data tables. This has to run after TestEnvironment is created // because on Linux, we need base::AtExitManager. icu_util::Initialize(); } } void SetUpTestEnvironment() { SetUpTestEnvironmentImpl(false); } void SetUpTestEnvironmentForUnitTests() { SetUpTestEnvironmentImpl(true); } void TearDownTestEnvironment() { // Flush any remaining messages before we kill ourselves. // http://code.google.com/p/chromium/issues/detail?id=9500 MessageLoop::current()->RunAllPending(); BeforeShutdown(); WebKit::shutdown(); delete test_environment; test_environment = NULL; AfterShutdown(); logging::CloseLogFile(); } WebKit::WebKitClient* GetWebKitClient() { DCHECK(test_environment); return test_environment->webkit_client(); } WebPlugin* CreateWebPlugin(WebFrame* frame, const WebPluginParams& params) { const bool kAllowWildcard = true; webkit::npapi::WebPluginInfo info; std::string actual_mime_type; if (!webkit::npapi::PluginList::Singleton()->GetPluginInfo( params.url, params.mimeType.utf8(), kAllowWildcard, &info, &actual_mime_type) || !webkit::npapi::IsPluginEnabled(info)) { return NULL; } return new WebPluginImplWithPageDelegate( frame, params, info.path, actual_mime_type); } WebKit::WebMediaPlayer* CreateMediaPlayer(WebFrame* frame, WebMediaPlayerClient* client) { scoped_ptr message_loop_factory( new media::MessageLoopFactoryImpl()); scoped_ptr collection( new media::FilterCollection()); scoped_refptr video_renderer( new webkit_glue::VideoRendererImpl(false)); collection->AddVideoRenderer(video_renderer); scoped_ptr result( new webkit_glue::WebMediaPlayerImpl(client, collection.release(), message_loop_factory.release())); if (!result->Initialize(frame, false, video_renderer)) { return NULL; } return result.release(); } WebKit::WebApplicationCacheHost* CreateApplicationCacheHost( WebFrame*, WebKit::WebApplicationCacheHostClient* client) { return SimpleAppCacheSystem::CreateApplicationCacheHost(client); } WebKit::WebString GetWebKitRootDir() { FilePath path = GetWebKitRootDirFilePath(); std::string path_ascii = path.MaybeAsASCII(); CHECK(!path_ascii.empty()); return WebKit::WebString::fromUTF8(path_ascii.c_str()); } void SetUpGLBindings(GLBindingPreferences bindingPref) { switch(bindingPref) { case GL_BINDING_DEFAULT: gfx::GLContext::InitializeOneOff(); break; case GL_BINDING_SOFTWARE_RENDERER: gfx::InitializeGLBindings(gfx::kGLImplementationOSMesaGL); break; default: NOTREACHED(); } } void RegisterMockedURL(const WebKit::WebURL& url, const WebKit::WebURLResponse& response, const WebKit::WebString& file_path) { test_environment->webkit_client()->url_loader_factory()-> RegisterURL(url, response, file_path); } void UnregisterMockedURL(const WebKit::WebURL& url) { test_environment->webkit_client()->url_loader_factory()->UnregisterURL(url); } void UnregisterAllMockedURLs() { test_environment->webkit_client()->url_loader_factory()->UnregisterAllURLs(); } void ServeAsynchronousMockedRequests() { test_environment->webkit_client()->url_loader_factory()-> ServeAsynchronousRequests(); } // Wrapper for debug_util bool BeingDebugged() { return base::debug::BeingDebugged(); } // Wrappers for MessageLoop void RunMessageLoop() { MessageLoop::current()->Run(); } void QuitMessageLoop() { MessageLoop::current()->Quit(); } void RunAllPendingMessages() { MessageLoop::current()->RunAllPending(); } bool MessageLoopNestableTasksAllowed() { return MessageLoop::current()->NestableTasksAllowed(); } void MessageLoopSetNestableTasksAllowed(bool allowed) { MessageLoop::current()->SetNestableTasksAllowed(allowed); } void DispatchMessageLoop() { MessageLoop* current = MessageLoop::current(); bool old_state = current->NestableTasksAllowed(); current->SetNestableTasksAllowed(true); current->RunAllPending(); current->SetNestableTasksAllowed(old_state); } WebDevToolsAgentClient::WebKitClientMessageLoop* CreateDevToolsMessageLoop() { return new WebKitClientMessageLoopImpl(); } void PostDelayedTask(void (*func)(void*), void* context, int64 delay_ms) { MessageLoop::current()->PostDelayedTask( FROM_HERE, NewRunnableFunction(func, context), delay_ms); } void PostDelayedTask(TaskAdaptor* task, int64 delay_ms) { MessageLoop::current()->PostDelayedTask( FROM_HERE, new TaskAdaptorHolder(task), delay_ms); } // Wrappers for FilePath and file_util WebString GetAbsoluteWebStringFromUTF8Path(const std::string& utf8_path) { #if defined(OS_WIN) FilePath path(UTF8ToWide(utf8_path)); file_util::AbsolutePath(&path); return WebString(path.value()); #else FilePath path(base::SysWideToNativeMB(base::SysUTF8ToWide(utf8_path))); file_util::AbsolutePath(&path); return WideToUTF16(base::SysNativeMBToWide(path.value())); #endif } WebURL CreateURLForPathOrURL(const std::string& path_or_url_in_nativemb) { // NativeMB to UTF-8 std::wstring wide_path_or_url = base::SysNativeMBToWide(path_or_url_in_nativemb); std::string path_or_url_in_utf8 = WideToUTF8(wide_path_or_url); GURL url(path_or_url_in_utf8); if (url.is_valid() && url.has_scheme()) return WebURL(url); #if defined(OS_WIN) return net::FilePathToFileURL(FilePath(wide_path_or_url)); #else return net::FilePathToFileURL(FilePath(path_or_url_in_nativemb)); #endif } WebURL RewriteLayoutTestsURL(const std::string& utf8_url) { const char kPrefix[] = "file:///tmp/LayoutTests/"; const int kPrefixLen = arraysize(kPrefix) - 1; if (utf8_url.compare(0, kPrefixLen, kPrefix, kPrefixLen)) return WebURL(GURL(utf8_url)); FilePath replacePath = GetWebKitRootDirFilePath().Append(FILE_PATH_LITERAL("LayoutTests/")); CHECK(file_util::PathExists(replacePath)) << replacePath.value() << " (re-written from " << utf8_url << ") does not exit"; #if defined(OS_WIN) std::string utf8_path = WideToUTF8(replacePath.value()); #else std::string utf8_path = WideToUTF8(base::SysNativeMBToWide(replacePath.value())); #endif std::string newUrl = std::string("file://") + utf8_path + utf8_url.substr(kPrefixLen); return WebURL(GURL(newUrl)); } bool SetCurrentDirectoryForFileURL(const WebKit::WebURL& fileUrl) { FilePath local_path; return net::FileURLToFilePath(fileUrl, &local_path) && file_util::SetCurrentDirectory(local_path.DirName()); } WebURL LocalFileToDataURL(const WebURL& fileUrl) { FilePath local_path; if (!net::FileURLToFilePath(fileUrl, &local_path)) return WebURL(); std::string contents; if (!file_util::ReadFileToString(local_path, &contents)) return WebURL(); std::string contents_base64; if (!base::Base64Encode(contents, &contents_base64)) return WebURL(); const char kDataUrlPrefix[] = "data:text/css;charset=utf-8;base64,"; return WebURL(GURL(kDataUrlPrefix + contents_base64)); } int64 GetCurrentTimeInMillisecond() { return base::TimeTicks::Now().ToInternalValue() / base::Time::kMicrosecondsPerMillisecond; } std::string EscapePath(const std::string& path) { return ::EscapePath(path); } std::string MakeURLErrorDescription(const WebKit::WebURLError& error) { std::string domain = error.domain.utf8(); int code = error.reason; if (domain == net::kErrorDomain) { domain = "NSURLErrorDomain"; switch (error.reason) { case net::ERR_ABORTED: code = -999; // NSURLErrorCancelled break; case net::ERR_UNSAFE_PORT: // Our unsafe port checking happens at the network stack level, but we // make this translation here to match the behavior of stock WebKit. domain = "WebKitErrorDomain"; code = 103; break; case net::ERR_ADDRESS_INVALID: case net::ERR_ADDRESS_UNREACHABLE: case net::ERR_NETWORK_ACCESS_DENIED: code = -1004; // NSURLErrorCannotConnectToHost break; } } else DLOG(WARNING) << "Unknown error domain"; return base::StringPrintf("", domain.c_str(), code, error.unreachableURL.spec().data()); } WebKit::WebURLError CreateCancelledError(const WebKit::WebURLRequest& request) { WebKit::WebURLError error; error.domain = WebKit::WebString::fromUTF8(net::kErrorDomain); error.reason = net::ERR_ABORTED; error.unreachableURL = request.url(); return error; } // Bridge for SimpleDatabaseSystem void SetDatabaseQuota(int quota) { SimpleDatabaseSystem::GetInstance()->SetDatabaseQuota(quota); } void ClearAllDatabases() { SimpleDatabaseSystem::GetInstance()->ClearAllDatabases(); } // Bridge for SimpleResourceLoaderBridge void SetAcceptAllCookies(bool accept) { SimpleResourceLoaderBridge::SetAcceptAllCookies(accept); } // Theme engine #if defined(OS_WIN) || defined(OS_MACOSX) void SetThemeEngine(WebKit::WebThemeEngine* engine) { DCHECK(test_environment); test_environment->set_theme_engine(engine); } WebKit::WebThemeEngine* GetThemeEngine() { DCHECK(test_environment); return test_environment->theme_engine(); } #endif // DevTools WebURL GetDevToolsPathAsURL() { FilePath dirExe; if (!webkit_glue::GetExeDirectory(&dirExe)) { DCHECK(false); return WebURL(); } FilePath devToolsPath = dirExe.AppendASCII( "resources/inspector/devtools.html"); return net::FilePathToFileURL(devToolsPath); } // FileSystem void OpenFileSystem(WebFrame* frame, WebFileSystem::Type type, long long size, bool create, WebFileSystemCallbacks* callbacks) { SimpleFileSystem* fileSystem = static_cast( test_environment->webkit_client()->fileSystem()); fileSystem->OpenFileSystem(frame, type, size, create, callbacks); } // Timers double GetForegroundTabTimerInterval() { return webkit_glue::kForegroundTabTimerInterval; } } // namespace webkit_support