blob: bcdd380aef5929f406a9c1ecc506fa3b5d791119 [file] [log] [blame]
// Copyright (c) 2012 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 "content/shell/webkit_test_runner.h"
#include <cmath>
#include "base/base64.h"
#include "base/debug/debugger.h"
#include "base/md5.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/stringprintf.h"
#include "base/sys_string_conversions.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "content/public/renderer/render_view.h"
#include "content/public/test/layouttest_support.h"
#include "content/shell/shell_messages.h"
#include "content/shell/shell_render_process_observer.h"
#include "content/shell/webkit_test_helpers.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/WebKit/Source/Platform/chromium/public/Platform.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebCString.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebString.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebURL.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebURLError.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebURLResponse.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebContextMenuData.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDevToolsAgent.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDeviceOrientation.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "third_party/WebKit/Tools/DumpRenderTree/chromium/TestRunner/public/WebTask.h"
#include "third_party/WebKit/Tools/DumpRenderTree/chromium/TestRunner/public/WebTestInterfaces.h"
#include "third_party/WebKit/Tools/DumpRenderTree/chromium/TestRunner/public/WebTestProxy.h"
#include "third_party/WebKit/Tools/DumpRenderTree/chromium/TestRunner/public/WebTestRunner.h"
#include "webkit/base/file_path_string_conversions.h"
#include "webkit/glue/webkit_glue.h"
#include "webkit/glue/webpreferences.h"
using WebKit::Platform;
using WebKit::WebContextMenuData;
using WebKit::WebDevToolsAgent;
using WebKit::WebDeviceOrientation;
using WebKit::WebElement;
using WebKit::WebFrame;
using WebKit::WebGamepads;
using WebKit::WebRect;
using WebKit::WebSize;
using WebKit::WebString;
using WebKit::WebURL;
using WebKit::WebURLError;
using WebKit::WebVector;
using WebKit::WebView;
using WebTestRunner::WebPreferences;
using WebTestRunner::WebTask;
using WebTestRunner::WebTestInterfaces;
namespace content {
namespace {
void InvokeTaskHelper(void* context) {
WebTask* task = reinterpret_cast<WebTask*>(context);
task->run();
delete task;
}
#if !defined(OS_MACOSX)
void MakeBitmapOpaque(SkBitmap* bitmap) {
SkAutoLockPixels lock(*bitmap);
DCHECK(bitmap->config() == SkBitmap::kARGB_8888_Config);
for (int y = 0; y < bitmap->height(); ++y) {
uint32_t* row = bitmap->getAddr32(0, y);
for (int x = 0; x < bitmap->width(); ++x)
row[x] |= 0xFF000000; // Set alpha bits to 1.
}
}
#endif
void CopyCanvasToBitmap(SkCanvas* canvas, SkBitmap* snapshot) {
SkDevice* device = skia::GetTopDevice(*canvas);
const SkBitmap& bitmap = device->accessBitmap(false);
bitmap.copyTo(snapshot, SkBitmap::kARGB_8888_Config);
#if !defined(OS_MACOSX)
// Only the expected PNGs for Mac have a valid alpha channel.
MakeBitmapOpaque(snapshot);
#endif
}
} // namespace
// static
int WebKitTestRunner::window_count_ = 0;
WebKitTestRunner::WebKitTestRunner(RenderView* render_view)
: RenderViewObserver(render_view),
is_main_window_(false) {
Reset();
++window_count_;
}
WebKitTestRunner::~WebKitTestRunner() {
--window_count_;
}
// WebTestDelegate -----------------------------------------------------------
void WebKitTestRunner::clearEditCommand() {
render_view()->ClearEditCommands();
}
void WebKitTestRunner::setEditCommand(const std::string& name,
const std::string& value) {
render_view()->SetEditCommandForNextKeyEvent(name, value);
}
void WebKitTestRunner::setGamepadData(const WebGamepads& gamepads) {
SetMockGamepads(gamepads);
}
void WebKitTestRunner::printMessage(const std::string& message) {
Send(new ShellViewHostMsg_PrintMessage(routing_id(), message));
}
void WebKitTestRunner::postTask(WebTask* task) {
Platform::current()->callOnMainThread(InvokeTaskHelper, task);
}
void WebKitTestRunner::postDelayedTask(WebTask* task, long long ms) {
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&WebTask::run, base::Owned(task)),
base::TimeDelta::FromMilliseconds(ms));
}
WebString WebKitTestRunner::registerIsolatedFileSystem(
const WebKit::WebVector<WebKit::WebString>& absolute_filenames) {
std::vector<base::FilePath> files;
for (size_t i = 0; i < absolute_filenames.size(); ++i)
files.push_back(webkit_base::WebStringToFilePath(absolute_filenames[i]));
std::string filesystem_id;
Send(new ShellViewHostMsg_RegisterIsolatedFileSystem(
routing_id(), files, &filesystem_id));
return WebString::fromUTF8(filesystem_id);
}
long long WebKitTestRunner::getCurrentTimeInMillisecond() {
return base::TimeTicks::Now().ToInternalValue() /
base::Time::kMicrosecondsPerMillisecond;
}
WebString WebKitTestRunner::getAbsoluteWebStringFromUTF8Path(
const std::string& utf8_path) {
#if defined(OS_WIN)
base::FilePath path(UTF8ToWide(utf8_path));
#else
base::FilePath path(base::SysWideToNativeMB(base::SysUTF8ToWide(utf8_path)));
#endif
if (!path.IsAbsolute()) {
GURL base_url =
net::FilePathToFileURL(current_working_directory_.Append(
FILE_PATH_LITERAL("foo")));
net::FileURLToFilePath(base_url.Resolve(utf8_path), &path);
}
return webkit_base::FilePathToWebString(path);
}
WebURL WebKitTestRunner::localFileToDataURL(const WebURL& file_url) {
base::FilePath local_path;
if (!net::FileURLToFilePath(file_url, &local_path))
return WebURL();
std::string contents;
Send(new ShellViewHostMsg_ReadFileToString(
routing_id(), local_path, &contents));
std::string contents_base64;
if (!base::Base64Encode(contents, &contents_base64))
return WebURL();
const char data_url_prefix[] = "data:text/css:charset=utf-8;base64,";
return WebURL(GURL(data_url_prefix + contents_base64));
}
WebURL WebKitTestRunner::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));
base::FilePath replace_path =
ShellRenderProcessObserver::GetInstance()->webkit_source_dir().Append(
FILE_PATH_LITERAL("LayoutTests/"));
#if defined(OS_WIN)
std::string utf8_path = WideToUTF8(replace_path.value());
#else
std::string utf8_path =
WideToUTF8(base::SysNativeMBToWide(replace_path.value()));
#endif
std::string new_url =
std::string("file://") + utf8_path + utf8_url.substr(kPrefixLen);
return WebURL(GURL(new_url));
}
WebPreferences* WebKitTestRunner::preferences() {
return &prefs_;
}
void WebKitTestRunner::applyPreferences() {
webkit_glue::WebPreferences prefs = render_view()->GetWebkitPreferences();
ExportLayoutTestSpecificPreferences(prefs_, &prefs);
render_view()->SetWebkitPreferences(prefs);
Send(new ShellViewHostMsg_OverridePreferences(routing_id(), prefs));
}
std::string WebKitTestRunner::makeURLErrorDescription(
const 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());
}
void WebKitTestRunner::setClientWindowRect(const WebRect& rect) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "setClientWindowRect"));
}
void WebKitTestRunner::showDevTools() {
Send(new ShellViewHostMsg_ShowDevTools(routing_id()));
}
void WebKitTestRunner::closeDevTools() {
Send(new ShellViewHostMsg_CloseDevTools(routing_id()));
}
void WebKitTestRunner::evaluateInWebInspector(long call_id,
const std::string& script) {
WebDevToolsAgent* agent = render_view()->GetWebView()->devToolsAgent();
if (agent)
agent->evaluateInWebInspector(call_id, WebString::fromUTF8(script));
}
void WebKitTestRunner::clearAllDatabases() {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "clearAllDatabases"));
}
void WebKitTestRunner::setDatabaseQuota(int quota) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "setDatabaseQuota"));
}
void WebKitTestRunner::setDeviceScaleFactor(float factor) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "setDeviceScaleFactor"));
}
void WebKitTestRunner::setFocus(bool focus) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "setFocus"));
}
void WebKitTestRunner::setAcceptAllCookies(bool accept) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "setAcceptAllCookies"));
}
std::string WebKitTestRunner::pathToLocalResource(const std::string& resource) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "pathToLocalResource"));
return std::string();
}
void WebKitTestRunner::setLocale(const std::string& locale) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "setLocale"));
}
void WebKitTestRunner::setDeviceOrientation(WebDeviceOrientation& orientation) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "setDeviceOrientation"));
}
void WebKitTestRunner::didAcquirePointerLock() {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "didAcquirePointerLock"));
}
void WebKitTestRunner::didNotAcquirePointerLock() {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "didNotAcquirePointerLock"));
}
void WebKitTestRunner::didLosePointerLock() {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "didLosePointerLock"));
}
void WebKitTestRunner::setPointerLockWillRespondAsynchronously() {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(),
"WebKitTestRunner",
"setPointerLockWillRespondAsynchronously"));
}
void WebKitTestRunner::setPointerLockWillFailSynchronously() {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "setPointerLockWillFailSynchronously"));
}
int WebKitTestRunner::numberOfPendingGeolocationPermissionRequests() {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(),
"WebKitTestRunner",
"numberOfPendingGeolocationPermissionRequests"));
return 0;
}
void WebKitTestRunner::setGeolocationPermission(bool allowed) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "setGeolocationPermission"));
}
void WebKitTestRunner::setMockGeolocationPosition(double latitude,
double longitude,
double precision) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "setMockGeolocationPosition"));
}
void WebKitTestRunner::setMockGeolocationPositionUnavailableError(
const std::string& message) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(),
"WebKitTestRunner",
"setMockGeolocationPositionUnavailableError"));
}
void WebKitTestRunner::addMockSpeechInputResult(const std::string& result,
double confidence,
const std::string& language) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "addMockSpeechInputResult"));
}
void WebKitTestRunner::setMockSpeechInputDumpRect(bool dump_rect) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "setMockSpeechInputDumpRect"));
}
void WebKitTestRunner::addMockSpeechRecognitionResult(
const std::string& transcript,
double confidence) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "addMockSpeechRecognitionResult"));
}
void WebKitTestRunner::setMockSpeechRecognitionError(
const std::string& error,
const std::string& message) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "setMockSpeechRecognitionError"));
}
bool WebKitTestRunner::wasMockSpeechRecognitionAborted() {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "wasMockSpeechRecognitionAborted"));
return false;
}
void WebKitTestRunner::testFinished() {
if (!is_main_window_)
return;
WebTestInterfaces* interfaces =
ShellRenderProcessObserver::GetInstance()->test_interfaces();
interfaces->setTestIsRunning(false);
CaptureDump();
}
void WebKitTestRunner::testTimedOut() {
if (!is_main_window_)
return;
WebTestInterfaces* interfaces =
ShellRenderProcessObserver::GetInstance()->test_interfaces();
interfaces->setTestIsRunning(false);
Send(new ShellViewHostMsg_TestFinished(routing_id(), true));
}
bool WebKitTestRunner::isBeingDebugged() {
return base::debug::BeingDebugged();
}
int WebKitTestRunner::layoutTestTimeout() {
return layout_test_timeout_;
}
void WebKitTestRunner::closeRemainingWindows() {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "closeRemainingWindows"));
}
int WebKitTestRunner::navigationEntryCount() {
return 0;
}
int WebKitTestRunner::windowCount() {
return window_count_;
}
void WebKitTestRunner::goToOffset(int offset) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "goToOffset"));
}
void WebKitTestRunner::reload() {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "reload"));
}
void WebKitTestRunner::loadURLForFrame(const WebURL& url,
const std::string& frame_name) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "loadURLForFrame"));
}
bool WebKitTestRunner::allowExternalPages() {
return allow_external_pages_;
}
void WebKitTestRunner::captureHistoryForWindow(
size_t windowIndex,
WebVector<WebKit::WebHistoryItem>* history,
size_t* currentEntryIndex) {
Send(new ShellViewHostMsg_NotImplemented(
routing_id(), "WebKitTestRunner", "captureHistoryForWindow"));
}
// RenderViewObserver --------------------------------------------------------
void WebKitTestRunner::DidClearWindowObject(WebFrame* frame) {
ShellRenderProcessObserver::GetInstance()->BindTestRunnersToWindow(frame);
}
bool WebKitTestRunner::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(WebKitTestRunner, message)
IPC_MESSAGE_HANDLER(ShellViewMsg_SetTestConfiguration,
OnSetTestConfiguration)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
// Public methods - -----------------------------------------------------------
void WebKitTestRunner::Reset() {
prefs_.reset();
webkit_glue::WebPreferences prefs = render_view()->GetWebkitPreferences();
ExportLayoutTestSpecificPreferences(prefs_, &prefs);
render_view()->SetWebkitPreferences(prefs);
enable_pixel_dumping_ = true;
layout_test_timeout_ = 30 * 1000;
allow_external_pages_ = false;
expected_pixel_hash_ = std::string();
}
// Private methods -----------------------------------------------------------
void WebKitTestRunner::CaptureDump() {
WebTestInterfaces* interfaces =
ShellRenderProcessObserver::GetInstance()->test_interfaces();
Send(
new ShellViewHostMsg_TextDump(routing_id(), proxy()->captureTree(false)));
if (interfaces->testRunner()->shouldGeneratePixelResults()) {
SkBitmap snapshot;
CopyCanvasToBitmap(proxy()->capturePixels(), &snapshot);
SkAutoLockPixels snapshot_lock(snapshot);
base::MD5Digest digest;
#if defined(OS_ANDROID)
// On Android, pixel layout is RGBA, however, other Chrome platforms use
// BGRA.
const uint8_t* raw_pixels =
reinterpret_cast<const uint8_t*>(snapshot.getPixels());
size_t snapshot_size = snapshot.getSize();
scoped_array<uint8_t> reordered_pixels(new uint8_t[snapshot_size]);
for (size_t i = 0; i < snapshot_size; i += 4) {
reordered_pixels[i] = raw_pixels[i + 2];
reordered_pixels[i + 1] = raw_pixels[i + 1];
reordered_pixels[i + 2] = raw_pixels[i];
reordered_pixels[i + 3] = raw_pixels[i + 3];
}
base::MD5Sum(reordered_pixels.get(), snapshot_size, &digest);
#else
base::MD5Sum(snapshot.getPixels(), snapshot.getSize(), &digest);
#endif
std::string actual_pixel_hash = base::MD5DigestToBase16(digest);
if (actual_pixel_hash == expected_pixel_hash_) {
SkBitmap empty_image;
Send(new ShellViewHostMsg_ImageDump(
routing_id(), actual_pixel_hash, empty_image));
} else {
Send(new ShellViewHostMsg_ImageDump(
routing_id(), actual_pixel_hash, snapshot));
}
}
Send(new ShellViewHostMsg_TestFinished(routing_id(), false));
}
void WebKitTestRunner::OnSetTestConfiguration(
const base::FilePath& current_working_directory,
bool enable_pixel_dumping,
int layout_test_timeout,
bool allow_external_pages,
const std::string& expected_pixel_hash) {
current_working_directory_ = current_working_directory;
enable_pixel_dumping_ = enable_pixel_dumping;
layout_test_timeout_ = layout_test_timeout;
allow_external_pages_ = allow_external_pages;
expected_pixel_hash_ = expected_pixel_hash;
is_main_window_ = true;
WebTestInterfaces* interfaces =
ShellRenderProcessObserver::GetInstance()->test_interfaces();
interfaces->setTestIsRunning(true);
interfaces->configureForTestWithURL(
net::FilePathToFileURL(current_working_directory), enable_pixel_dumping);
}
} // namespace content