// 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_host.h" #include "base/command_line.h" #include "base/message_loop.h" #include "base/run_loop.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/shell/shell.h" #include "content/shell/shell_browser_context.h" #include "content/shell/shell_content_browser_client.h" #include "content/shell/shell_messages.h" #include "content/shell/shell_switches.h" #include "webkit/support/webkit_support_gfx.h" namespace content { namespace { const int kTestTimeoutMilliseconds = 30 * 1000; } // namespace // WebKitTestResultPrinter ---------------------------------------------------- WebKitTestResultPrinter::WebKitTestResultPrinter() : state_(BEFORE_TEST) { } WebKitTestResultPrinter::~WebKitTestResultPrinter() { } void WebKitTestResultPrinter::PrintTextHeader() { DCHECK_EQ(state_, BEFORE_TEST); printf("Content-Type: text/plain\n"); state_ = IN_TEXT_BLOCK; } void WebKitTestResultPrinter::PrintTextBlock(const std::string& block) { DCHECK_EQ(state_, IN_TEXT_BLOCK); printf("%s", block.c_str()); } void WebKitTestResultPrinter::PrintTextFooter() { if (state_ != IN_TEXT_BLOCK) return; printf("#EOF\n"); fprintf(stderr, "#EOF\n"); state_ = IN_IMAGE_BLOCK; } void WebKitTestResultPrinter::PrintImageHeader( const std::string& actual_hash, const std::string& expected_hash) { if (state_ != IN_IMAGE_BLOCK) return; printf("\nActualHash: %s\n", actual_hash.c_str()); if (!expected_hash.empty()) printf("\nExpectedHash: %s\n", expected_hash.c_str()); } void WebKitTestResultPrinter::PrintImageBlock( const std::vector<unsigned char>& png_image) { if (state_ != IN_IMAGE_BLOCK) return; printf("Content-Type: image/png\n"); printf("Content-Length: %u\n", static_cast<unsigned>(png_image.size())); fwrite(&png_image[0], 1, png_image.size(), stdout); } void WebKitTestResultPrinter::PrintImageFooter() { if (state_ != IN_IMAGE_BLOCK) return; printf("#EOF\n"); state_ = AFTER_TEST; } void WebKitTestResultPrinter::AddMessage(const std::string& message) { if (state_ != IN_TEXT_BLOCK) return; printf("%s\n", message.c_str()); } void WebKitTestResultPrinter::AddErrorMessage(const std::string& message) { if (state_ != IN_TEXT_BLOCK) return; printf("%s\n", message.c_str()); fprintf(stderr, "%s\n", message.c_str()); PrintTextFooter(); PrintImageFooter(); MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); } // WebKitTestController ------------------------------------------------------- WebKitTestController* WebKitTestController::instance_ = NULL; // static WebKitTestController* WebKitTestController::Get() { DCHECK(!instance_ || instance_->CalledOnValidThread()); return instance_; } WebKitTestController::WebKitTestController() { CHECK(!instance_); instance_ = this; content::ShellBrowserContext* browser_context = static_cast<content::ShellContentBrowserClient*>( content::GetContentClient()->browser())->browser_context(); main_window_ = content::Shell::CreateNewWindow( browser_context, GURL("about:blank"), NULL, MSG_ROUTING_NONE, NULL); Observe(main_window_->web_contents()); ResetAfterLayoutTest(); } WebKitTestController::~WebKitTestController() { DCHECK(CalledOnValidThread()); CHECK(instance_ == this); if (main_window_) main_window_->Close(); instance_ = NULL; } bool WebKitTestController::PrepareForLayoutTest( const GURL& test_url, bool enable_pixel_dumping, const std::string& expected_pixel_hash) { DCHECK(CalledOnValidThread()); if (!main_window_) return false; enable_pixel_dumping_ = enable_pixel_dumping; expected_pixel_hash_ = expected_pixel_hash; printer_.reset(); printer_.PrintTextHeader(); main_window_->LoadURL(test_url); return true; } bool WebKitTestController::ResetAfterLayoutTest() { DCHECK(CalledOnValidThread()); printer_.PrintTextFooter(); printer_.PrintImageFooter(); pumping_messages_ = false; enable_pixel_dumping_ = false; expected_pixel_hash_.clear(); captured_dump_ = false; dump_as_text_ = false; dump_child_frames_ = false; is_printing_ = false; should_stay_on_page_after_handling_before_unload_ = false; wait_until_done_ = false; watchdog_.Cancel(); if (!main_window_) return false; if (main_window_->web_contents()->GetController().GetEntryCount() > 0) { // Reset the WebContents for the next test. // TODO(jochen): Reset it more thoroughly. main_window_->web_contents()->GetController().GoToIndex(0); } renderer_crashed_ = false; // Wait for the main_window_ to finish loading. pumping_messages_ = true; base::RunLoop run_loop; run_loop.Run(); pumping_messages_ = false; return !renderer_crashed_; } void WebKitTestController::RendererUnresponsive() { printer_.AddErrorMessage("#PROCESS UNRESPONSIVE - renderer"); } void WebKitTestController::NotifyDone() { if (!wait_until_done_) return; watchdog_.Cancel(); CaptureDump(); } void WebKitTestController::WaitUntilDone() { if (wait_until_done_) return; if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoTimeout)) { watchdog_.Reset(base::Bind(&WebKitTestController::TimeoutHandler, base::Unretained(this))); MessageLoop::current()->PostDelayedTask( FROM_HERE, watchdog_.callback(), base::TimeDelta::FromMilliseconds(kTestTimeoutMilliseconds)); } wait_until_done_ = true; } void WebKitTestController::NotImplemented( const std::string& object_name, const std::string& property_name) { printer_.AddErrorMessage( std::string("FAIL: NOT IMPLEMENTED: ") + object_name + "." + property_name); } bool WebKitTestController::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(WebKitTestController, message) IPC_MESSAGE_HANDLER(ShellViewHostMsg_DidFinishLoad, OnDidFinishLoad) IPC_MESSAGE_HANDLER(ShellViewHostMsg_TextDump, OnTextDump) IPC_MESSAGE_HANDLER(ShellViewHostMsg_ImageDump, OnImageDump) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } void WebKitTestController::PluginCrashed(const FilePath& plugin_path) { printer_.AddErrorMessage("#CRASHED - plugin"); } void WebKitTestController::RenderViewGone(base::TerminationStatus status) { renderer_crashed_ = true; printer_.AddErrorMessage("#CRASHED - renderer"); } void WebKitTestController::WebContentsDestroyed(WebContents* web_contents) { main_window_ = NULL; printer_.AddErrorMessage("FAIL: main window was destroyed"); } void WebKitTestController::CaptureDump() { if (captured_dump_ || !main_window_ || !printer_.in_text_block()) return; captured_dump_ = true; RenderViewHost* render_view_host = main_window_->web_contents()->GetRenderViewHost(); render_view_host->Send(new ShellViewMsg_CaptureTextDump( render_view_host->GetRoutingID(), dump_as_text_, is_printing_, dump_child_frames_)); if (!dump_as_text_ && enable_pixel_dumping_) { render_view_host->Send(new ShellViewMsg_CaptureImageDump( render_view_host->GetRoutingID(), expected_pixel_hash_)); } } void WebKitTestController::TimeoutHandler() { printer_.AddErrorMessage( "FAIL: Timed out waiting for notifyDone to be called"); } void WebKitTestController::OnDidFinishLoad() { if (pumping_messages_) { MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); return; } if (wait_until_done_) return; CaptureDump(); } void WebKitTestController::OnImageDump( const std::string& actual_pixel_hash, const SkBitmap& image) { SkAutoLockPixels image_lock(image); printer_.PrintImageHeader(actual_pixel_hash, expected_pixel_hash_); // Only encode and dump the png if the hashes don't match. Encoding the // image is really expensive. if (actual_pixel_hash != expected_pixel_hash_) { std::vector<unsigned char> png; // Only the expected PNGs for Mac have a valid alpha channel. #if defined(OS_MACOSX) bool discard_transparency = false; #else bool discard_transparency = true; #endif bool success = false; #if defined(OS_ANDROID) success = webkit_support::EncodeRGBAPNGWithChecksum( reinterpret_cast<const unsigned char*>(image.getPixels()), image.width(), image.height(), static_cast<int>(image.rowBytes()), discard_transparency, actual_pixel_hash, &png); #else success = webkit_support::EncodeBGRAPNGWithChecksum( reinterpret_cast<const unsigned char*>(image.getPixels()), image.width(), image.height(), static_cast<int>(image.rowBytes()), discard_transparency, actual_pixel_hash, &png); #endif if (success) printer_.PrintImageBlock(png); } printer_.PrintImageFooter(); MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); } void WebKitTestController::OnTextDump(const std::string& dump) { printer_.PrintTextBlock(dump); printer_.PrintTextFooter(); if (dump_as_text_ || !enable_pixel_dumping_) { printer_.PrintImageFooter(); MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); } } // WebKitTestRunnerHost ------------------------------------------------------- WebKitTestRunnerHost::WebKitTestRunnerHost( RenderViewHost* render_view_host) : RenderViewHostObserver(render_view_host) { } WebKitTestRunnerHost::~WebKitTestRunnerHost() { } bool WebKitTestRunnerHost::OnMessageReceived( const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(WebKitTestRunnerHost, message) IPC_MESSAGE_HANDLER(ShellViewHostMsg_NotifyDone, OnNotifyDone) IPC_MESSAGE_HANDLER(ShellViewHostMsg_DumpAsText, OnDumpAsText) IPC_MESSAGE_HANDLER(ShellViewHostMsg_DumpChildFramesAsText, OnDumpChildFramesAsText) IPC_MESSAGE_HANDLER(ShellViewHostMsg_SetPrinting, OnSetPrinting) IPC_MESSAGE_HANDLER( ShellViewHostMsg_SetShouldStayOnPageAfterHandlingBeforeUnload, OnSetShouldStayOnPageAfterHandlingBeforeUnload) IPC_MESSAGE_HANDLER(ShellViewHostMsg_WaitUntilDone, OnWaitUntilDone) IPC_MESSAGE_HANDLER(ShellViewHostMsg_NotImplemented, OnNotImplemented) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } void WebKitTestRunnerHost::OnNotifyDone() { WebKitTestController::Get()->NotifyDone(); } void WebKitTestRunnerHost::OnDumpAsText() { WebKitTestController::Get()->set_dump_as_text(true); } void WebKitTestRunnerHost::OnSetPrinting() { WebKitTestController::Get()->set_is_printing(true); } void WebKitTestRunnerHost::OnSetShouldStayOnPageAfterHandlingBeforeUnload( bool should_stay_on_page) { WebKitTestController* controller = WebKitTestController::Get(); controller->set_should_stay_on_page_after_handling_before_unload( should_stay_on_page); } void WebKitTestRunnerHost::OnDumpChildFramesAsText() { WebKitTestController::Get()->set_dump_child_frames(true); } void WebKitTestRunnerHost::OnWaitUntilDone() { WebKitTestController::Get()->WaitUntilDone(); } void WebKitTestRunnerHost::OnNotImplemented( const std::string& object_name, const std::string& property_name) { WebKitTestController::Get()->NotImplemented(object_name, property_name); } } // namespace content