blob: 8ac70bc2d7dba33f199670d23c4f0292c98b2c75 [file] [log] [blame]
// 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 "app/gfx/gl/gl_context.h"
#include "app/gfx/gl/gl_implementation.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 "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<base::AtExitManager> at_exit_manager_;
scoped_ptr<MessageLoopForUI> main_message_loop_;
scoped_ptr<TestWebKitClient> webkit_client_;
};
class WebPluginImplWithPageDelegate
: public webkit_support::TestWebPluginPageDelegate,
public base::SupportsWeakPtr<WebPluginImplWithPageDelegate>,
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<webkit_support::TaskAdaptor> 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<media::MessageLoopFactory> message_loop_factory(
new media::MessageLoopFactoryImpl());
scoped_ptr<media::FilterCollection> collection(
new media::FilterCollection());
scoped_refptr<webkit_glue::VideoRendererImpl> video_renderer(
new webkit_glue::VideoRendererImpl(false));
collection->AddVideoRenderer(video_renderer);
scoped_ptr<webkit_glue::WebMediaPlayerImpl> 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("<NSError domain %s, code %d, failing URL \"%s\">",
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<SimpleFileSystem*>(
test_environment->webkit_client()->fileSystem());
fileSystem->OpenFileSystem(frame, type, size, create, callbacks);
}
// Timers
double GetForegroundTabTimerInterval() {
return webkit_glue::kForegroundTabTimerInterval;
}
} // namespace webkit_support