summaryrefslogtreecommitdiffstats
path: root/webkit/tools/test_shell/test_shell.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-27 00:20:51 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-27 00:20:51 +0000
commitf5b16fed647e941aa66933178da85db2860d639b (patch)
treef00e9856c04aad3b558a140955e7674add33f051 /webkit/tools/test_shell/test_shell.cc
parent920c091ac3ee15079194c82ae8a7a18215f3f23c (diff)
downloadchromium_src-f5b16fed647e941aa66933178da85db2860d639b.zip
chromium_src-f5b16fed647e941aa66933178da85db2860d639b.tar.gz
chromium_src-f5b16fed647e941aa66933178da85db2860d639b.tar.bz2
Add webkit to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/tools/test_shell/test_shell.cc')
-rw-r--r--webkit/tools/test_shell/test_shell.cc1128
1 files changed, 1128 insertions, 0 deletions
diff --git a/webkit/tools/test_shell/test_shell.cc b/webkit/tools/test_shell/test_shell.cc
new file mode 100644
index 0000000..407b95d
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell.cc
@@ -0,0 +1,1128 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <atlbase.h>
+#include <commdlg.h>
+#include <objbase.h>
+#include <shlwapi.h>
+#include <wininet.h>
+
+#include "webkit/tools/test_shell/test_shell.h"
+
+#include "base/command_line.h"
+#include "base/debug_on_start.h"
+#include "base/file_util.h"
+#include "base/gfx/bitmap_platform_device.h"
+#include "base/gfx/png_encoder.h"
+#include "base/gfx/size.h"
+#include "base/icu_util.h"
+#include "base/md5.h"
+#include "base/memory_debug.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/stats_table.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+#include "googleurl/src/url_util.h"
+#include "net/base/mime_util.h"
+#include "net/url_request/url_request_file_job.h"
+#include "net/url_request/url_request_filter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/glue/webdatasource.h"
+#include "webkit/glue/webframe.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/webkit_resources.h"
+#include "webkit/glue/webpreferences.h"
+#include "webkit/glue/weburlrequest.h"
+#include "webkit/glue/webview.h"
+#include "webkit/glue/webwidget.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_navigation_controller.h"
+
+#include "webkit_strings.h"
+
+#include "SkBitmap.h"
+
+using std::min;
+using std::max;
+
+#define MAX_LOADSTRING 100
+
+#define BUTTON_WIDTH 72
+#define URLBAR_HEIGHT 24
+
+// Global Variables:
+static TCHAR g_windowTitle[MAX_LOADSTRING]; // The title bar text
+static TCHAR g_windowClass[MAX_LOADSTRING]; // The main window class name
+
+// Forward declarations of functions included in this code module:
+static INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
+
+// Default timeout for page load when running non-interactive file
+// tests, in ms.
+const int kDefaultFileTestTimeoutMillisecs = 10 * 1000;
+
+// Content area size for newly created windows.
+const int kTestWindowWidth = 800;
+const int kTestWindowHeight = 600;
+
+// The W3C SVG layout tests use a different size than the other layout tests
+const int kSVGTestWindowWidth = 480;
+const int kSVGTestWindowHeight = 360;
+
+// Hide the window offscreen when it is non-interactive.
+// This would correspond with a minimized window position if x = y = -32000.
+// However we shift the x to 0 to pass test cross-frame-access-put.html
+// which expects screenX/screenLeft to be 0 (http://b/issue?id=1227945).
+// TODO(ericroman): x should be defined as 0 rather than -4. There is
+// probably a frameborder not being accounted for in the setting/getting.
+const int kTestWindowXLocation = -4;
+const int kTestWindowYLocation = -32000;
+
+// Initialize static member variable
+WindowList* TestShell::window_list_;
+HINSTANCE TestShell::instance_handle_;
+WebPreferences* TestShell::web_prefs_ = NULL;
+bool TestShell::interactive_ = true;
+int TestShell::file_test_timeout_ms_ = kDefaultFileTestTimeoutMillisecs;
+
+// URLRequestTestShellFileJob is used to serve the inspector
+class URLRequestTestShellFileJob : public URLRequestFileJob {
+ public:
+ virtual ~URLRequestTestShellFileJob() { }
+
+ static URLRequestJob* InspectorFactory(URLRequest* request,
+ const std::string& scheme) {
+ std::wstring path;
+ PathService::Get(base::DIR_EXE, &path);
+ file_util::AppendToPath(&path, L"Resources");
+ file_util::AppendToPath(&path, L"Inspector");
+ file_util::AppendToPath(&path, UTF8ToWide(request->url().path()));
+ return new URLRequestTestShellFileJob(request, path);
+ }
+
+ private:
+ URLRequestTestShellFileJob(URLRequest* request, const std::wstring& path)
+ : URLRequestFileJob(request) {
+ this->file_path_ = path; // set URLRequestFileJob::file_path_
+ }
+
+ DISALLOW_EVIL_CONSTRUCTORS(URLRequestTestShellFileJob);
+};
+
+TestShell::TestShell()
+ : m_mainWnd(NULL),
+ m_editWnd(NULL),
+ m_webViewHost(NULL),
+ m_popupHost(NULL),
+ m_focusedWidgetHost(NULL),
+ default_edit_wnd_proc_(0),
+ delegate_(new TestWebViewDelegate(this)),
+ test_is_preparing_(false),
+ test_is_pending_(false),
+ is_modal_(false),
+ dump_stats_table_on_exit_(false) {
+ layout_test_controller_.reset(new LayoutTestController(this));
+ event_sending_controller_.reset(new EventSendingController(this));
+ text_input_controller_.reset(new TextInputController(this));
+ navigation_controller_.reset(new TestNavigationController(this));
+
+ URLRequestFilter* filter = URLRequestFilter::GetInstance();
+ filter->AddHostnameHandler("test-shell-resource", "inspector",
+ &URLRequestTestShellFileJob::InspectorFactory);
+ url_util::AddStandardScheme("test-shell-resource");
+}
+
+TestShell::~TestShell() {
+
+ // Call GC twice to clean up garbage.
+ CallJSGC();
+ CallJSGC();
+
+ // When the window is destroyed, tell the Edit field to forget about us,
+ // otherwise we will crash.
+ win_util::SetWindowProc(m_editWnd, default_edit_wnd_proc_);
+ win_util::SetWindowUserData(m_editWnd, NULL);
+
+ StatsTable *table = StatsTable::current();
+ if (dump_stats_table_on_exit_) {
+ // Dump the stats table.
+ printf("<stats>\n");
+ if (table != NULL) {
+ int counter_max = table->GetMaxCounters();
+ for (int index=0; index < counter_max; index++) {
+ std::wstring name(table->GetRowName(index));
+ if (name.length() > 0) {
+ int value = table->GetRowValue(index);
+ printf("%s:\t%d\n", WideToUTF8(name).c_str(), value);
+ }
+ }
+ }
+ printf("</stats>\n");
+ }
+}
+
+// All fatal log messages (e.g. DCHECK failures) imply unit test failures
+static void UnitTestAssertHandler(const std::string& str) {
+ FAIL() << str;
+}
+
+// static
+void TestShell::InitLogging(bool suppress_error_dialogs) {
+ if (!IsDebuggerPresent() && suppress_error_dialogs) {
+ UINT new_flags = SEM_FAILCRITICALERRORS |
+ SEM_NOGPFAULTERRORBOX |
+ SEM_NOOPENFILEERRORBOX;
+ // Preserve existing error mode, as discussed at http://t/dmea
+ UINT existing_flags = SetErrorMode(new_flags);
+ SetErrorMode(existing_flags | new_flags);
+
+ logging::SetLogAssertHandler(UnitTestAssertHandler);
+ }
+
+ // We might have multiple test_shell processes going at once
+ std::wstring log_filename;
+ PathService::Get(base::DIR_EXE, &log_filename);
+ file_util::AppendToPath(&log_filename, L"test_shell.log");
+ logging::InitLogging(log_filename.c_str(),
+ logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG,
+ logging::LOCK_LOG_FILE,
+ logging::DELETE_OLD_LOG_FILE);
+
+ // we want process and thread IDs because we may have multiple processes
+ logging::SetLogItems(true, true, false, true);
+}
+
+// static
+void TestShell::CleanupLogging() {
+ logging::CloseLogFile();
+}
+
+// static
+void TestShell::InitializeTestShell(bool interactive) {
+ // Start COM stuff.
+ HRESULT res = OleInitialize(NULL);
+ DCHECK(SUCCEEDED(res));
+
+ window_list_ = new WindowList;
+ instance_handle_ = ::GetModuleHandle(NULL);
+ interactive_ = interactive;
+
+ web_prefs_ = new WebPreferences;
+
+ ResetWebPreferences();
+}
+
+// static
+void TestShell::ResetWebPreferences() {
+ DCHECK(web_prefs_);
+
+ // Match the settings used by Mac DumpRenderTree.
+ if (web_prefs_) {
+ *web_prefs_ = WebPreferences();
+ web_prefs_->standard_font_family = L"Times";
+ web_prefs_->fixed_font_family = L"Courier";
+ web_prefs_->serif_font_family = L"Times";
+ web_prefs_->sans_serif_font_family = L"Helvetica";
+ web_prefs_->cursive_font_family = L"Apple Chancery";
+ web_prefs_->fantasy_font_family = L"Papyrus";
+ web_prefs_->default_encoding = L"ISO-8859-1";
+ web_prefs_->default_font_size = 16;
+ web_prefs_->default_fixed_font_size = 13;
+ web_prefs_->minimum_font_size = 1;
+ web_prefs_->minimum_logical_font_size = 9;
+ web_prefs_->javascript_can_open_windows_automatically = true;
+ web_prefs_->dom_paste_enabled = true;
+ web_prefs_->developer_extras_enabled = interactive_;
+ web_prefs_->shrinks_standalone_images_to_fit = false;
+ web_prefs_->uses_universal_detector = false;
+ web_prefs_->text_areas_are_resizable = false;
+ web_prefs_->user_agent = webkit_glue::GetDefaultUserAgent();
+ web_prefs_->dashboard_compatibility_mode = false;
+ web_prefs_->java_enabled = true;
+ }
+}
+
+// static
+void TestShell::ShutdownTestShell() {
+ delete window_list_;
+ SimpleResourceLoaderBridge::Shutdown();
+ delete TestShell::web_prefs_;
+ OleUninitialize();
+}
+
+bool TestShell::Initialize(const std::wstring& startingURL) {
+ // Perform application initialization:
+ m_mainWnd = CreateWindow(g_windowClass, g_windowTitle,
+ WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
+ CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
+ NULL, NULL, instance_handle_, NULL);
+ win_util::SetWindowUserData(m_mainWnd, this);
+
+ HWND hwnd;
+ int x = 0;
+
+ hwnd = CreateWindow(L"BUTTON", L"Back",
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
+ x, 0, BUTTON_WIDTH, URLBAR_HEIGHT,
+ m_mainWnd, (HMENU) IDC_NAV_BACK, instance_handle_, 0);
+ x += BUTTON_WIDTH;
+
+ hwnd = CreateWindow(L"BUTTON", L"Forward",
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
+ x, 0, BUTTON_WIDTH, URLBAR_HEIGHT,
+ m_mainWnd, (HMENU) IDC_NAV_FORWARD, instance_handle_, 0);
+ x += BUTTON_WIDTH;
+
+ hwnd = CreateWindow(L"BUTTON", L"Reload",
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
+ x, 0, BUTTON_WIDTH, URLBAR_HEIGHT,
+ m_mainWnd, (HMENU) IDC_NAV_RELOAD, instance_handle_, 0);
+ x += BUTTON_WIDTH;
+
+ hwnd = CreateWindow(L"BUTTON", L"Stop",
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
+ x, 0, BUTTON_WIDTH, URLBAR_HEIGHT,
+ m_mainWnd, (HMENU) IDC_NAV_STOP, instance_handle_, 0);
+ x += BUTTON_WIDTH;
+
+ // this control is positioned by ResizeSubViews
+ m_editWnd = CreateWindow(L"EDIT", 0,
+ WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT |
+ ES_AUTOVSCROLL | ES_AUTOHSCROLL,
+ x, 0, 0, 0, m_mainWnd, 0, instance_handle_, 0);
+
+ default_edit_wnd_proc_ =
+ win_util::SetWindowProc(m_editWnd, TestShell::EditWndProc);
+ win_util::SetWindowUserData(m_editWnd, this);
+
+ // create webview
+ m_webViewHost.reset(
+ WebViewHost::Create(m_mainWnd, delegate_.get(), *TestShell::web_prefs_));
+ webView()->SetUseEditorDelegate(true);
+ delegate_->RegisterDragDrop();
+
+ // Load our initial content.
+ if (!startingURL.empty())
+ LoadURL(startingURL.c_str());
+
+ ShowWindow(webViewWnd(), SW_SHOW);
+
+ bool bIsSVGTest = startingURL.find(L"W3C-SVG-1.1") != std::wstring::npos;
+
+ if (bIsSVGTest) {
+ SizeTo(kSVGTestWindowWidth, kSVGTestWindowHeight);
+ } else {
+ SizeToDefault();
+ }
+
+ return true;
+}
+
+void TestShell::TestFinished() {
+ if (!test_is_pending_)
+ return; // reached when running under test_shell_tests
+
+ UINT_PTR timer_id = reinterpret_cast<UINT_PTR>(this);
+ KillTimer(mainWnd(), timer_id);
+
+ test_is_pending_ = false;
+ MessageLoop::current()->Quit();
+}
+
+// Thread main to run for the thread which just tests for timeout.
+unsigned int __stdcall WatchDogThread(void *arg)
+{
+ // If we're debugging a layout test, don't timeout.
+ if (::IsDebuggerPresent())
+ return 0;
+
+ TestShell* shell = static_cast<TestShell*>(arg);
+ DWORD timeout = static_cast<DWORD>(shell->GetFileTestTimeout() * 2.5);
+ DWORD rv = WaitForSingleObject(shell->finished_event(), timeout);
+ if (rv == WAIT_TIMEOUT) {
+ // Print a warning to be caught by the layout-test script.
+ // Note: the layout test driver may or may not recognize
+ // this as a timeout.
+ puts("#TEST_TIMED_OUT\n");
+ puts("#EOF\n");
+ fflush(stdout);
+ TerminateProcess(GetCurrentProcess(), 0);
+ }
+ // Finished normally.
+ return 0;
+}
+
+void TestShell::WaitTestFinished() {
+ DCHECK(!test_is_pending_) << "cannot be used recursively";
+
+ test_is_pending_ = true;
+
+ // Create a watchdog thread which just sets a timer and
+ // kills the process if it times out. This catches really
+ // bad hangs where the shell isn't coming back to the
+ // message loop. If the watchdog is what catches a
+ // timeout, it can't do anything except terminate the test
+ // shell, which is unfortunate.
+ finished_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
+ DCHECK(finished_event_ != NULL);
+
+ HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(
+ NULL,
+ 0,
+ &WatchDogThread,
+ this,
+ 0,
+ 0));
+ DCHECK(thread_handle != NULL);
+
+ // TestFinished() will post a quit message to break this loop when the page
+ // finishes loading.
+ while (test_is_pending_)
+ MessageLoop::current()->Run();
+
+ // Tell the watchdog that we are finished.
+ SetEvent(finished_event_);
+
+ // Wait to join the watchdog thread. (up to 1s, then quit)
+ WaitForSingleObject(thread_handle, 1000);
+}
+
+void TestShell::Show(WebView* webview, WindowOpenDisposition disposition) {
+ delegate_->Show(webview, disposition);
+}
+
+void TestShell::SetFocus(WebWidgetHost* host, bool enable) {
+ if (interactive_) {
+ if (enable) {
+ ::SetFocus(host->window_handle());
+ } else {
+ if (GetFocus() == host->window_handle())
+ ::SetFocus(NULL);
+ }
+ } else {
+ if (enable) {
+ if (m_focusedWidgetHost != host) {
+ if (m_focusedWidgetHost)
+ m_focusedWidgetHost->webwidget()->SetFocus(false);
+ host->webwidget()->SetFocus(enable);
+ m_focusedWidgetHost = host;
+ }
+ } else {
+ if (m_focusedWidgetHost == host) {
+ host->webwidget()->SetFocus(enable);
+ m_focusedWidgetHost = NULL;
+ }
+ }
+ }
+}
+
+void TestShell::BindJSObjectsToWindow(WebFrame* frame) {
+ // Only bind the test classes if we're running tests.
+ if (!interactive_) {
+ layout_test_controller_->BindToJavascript(frame,
+ L"layoutTestController");
+ event_sending_controller_->BindToJavascript(frame,
+ L"eventSender");
+ text_input_controller_->BindToJavascript(frame,
+ L"textInputController");
+ }
+}
+
+
+void TestShell::CallJSGC() {
+ WebFrame* frame = webView()->GetMainFrame();
+ frame->CallJSGC();
+}
+
+
+/*static*/
+bool TestShell::CreateNewWindow(const std::wstring& startingURL,
+ TestShell** result)
+{
+ TestShell* shell = new TestShell();
+ bool rv = shell->Initialize(startingURL);
+ if (rv) {
+ if (result)
+ *result = shell;
+ TestShell::windowList()->push_back(shell->m_mainWnd);
+ }
+ return rv;
+}
+
+WebView* TestShell::CreateWebView(WebView* webview) {
+ // If we're running layout tests, only open a new window if the test has
+ // called layoutTestController.setCanOpenWindows()
+ if (!interactive_ && !layout_test_controller_->CanOpenWindows())
+ return NULL;
+
+ TestShell* new_win;
+ if (!CreateNewWindow(std::wstring(), &new_win))
+ return NULL;
+
+ return new_win->webView();
+}
+
+WebWidget* TestShell::CreatePopupWidget(WebView* webview) {
+ DCHECK(!m_popupHost);
+ m_popupHost = WebWidgetHost::Create(NULL, delegate_.get());
+ ShowWindow(popupWnd(), SW_SHOW);
+
+ return m_popupHost->webwidget();
+}
+
+void TestShell::ClosePopup() {
+ PostMessage(popupWnd(), WM_CLOSE, 0, 0);
+ m_popupHost = NULL;
+}
+
+void TestShell::SizeToDefault() {
+ SizeTo(kTestWindowWidth, kTestWindowHeight);
+}
+
+void TestShell::SizeTo(int width, int height) {
+ RECT rc, rw;
+ GetClientRect(m_mainWnd, &rc);
+ GetWindowRect(m_mainWnd, &rw);
+
+ int client_width = rc.right - rc.left;
+ int window_width = rw.right - rw.left;
+ window_width = (window_width - client_width) + width;
+
+ int client_height = rc.bottom - rc.top;
+ int window_height = rw.bottom - rw.top;
+ window_height = (window_height - client_height) + height;
+
+ // add space for the url bar:
+ window_height += URLBAR_HEIGHT;
+
+ SetWindowPos(m_mainWnd, NULL, 0, 0, window_width, window_height,
+ SWP_NOMOVE | SWP_NOZORDER);
+}
+
+void TestShell::ResizeSubViews() {
+ RECT rc;
+ GetClientRect(m_mainWnd, &rc);
+
+ int x = BUTTON_WIDTH * 4;
+ MoveWindow(m_editWnd, x, 0, rc.right - x, URLBAR_HEIGHT, TRUE);
+
+ MoveWindow(webViewWnd(), 0, URLBAR_HEIGHT, rc.right,
+ rc.bottom - URLBAR_HEIGHT, TRUE);
+}
+
+/* static */ std::string TestShell::DumpImage(
+ WebFrame* web_frame,
+ const std::wstring& file_name) {
+ gfx::BitmapPlatformDevice device(web_frame->CaptureImage(true));
+ const SkBitmap& src_bmp = device.accessBitmap(false);
+
+ // Encode image.
+ std::vector<unsigned char> png;
+ SkAutoLockPixels src_bmp_lock(src_bmp);
+ PNGEncoder::Encode(
+ reinterpret_cast<const unsigned char*>(src_bmp.getPixels()),
+ PNGEncoder::FORMAT_BGRA, src_bmp.width(), src_bmp.height(),
+ static_cast<int>(src_bmp.rowBytes()), true, &png);
+
+ // Write to disk.
+ FILE* file = NULL;
+ if (_wfopen_s(&file, file_name.c_str(), L"wb") == 0) {
+ fwrite(&png[0], 1, png.size(), file);
+ fclose(file);
+ }
+
+ // Compute MD5 sum.
+ MD5Context ctx;
+ MD5Init(&ctx);
+ MD5Update(&ctx, src_bmp.getPixels(), src_bmp.getSize());
+
+ MD5Digest digest;
+ MD5Final(&digest, &ctx);
+ return MD5DigestToBase16(digest);
+}
+
+/* static */ void TestShell::DumpBackForwardList(std::wstring* result) {
+ result->clear();
+ for (WindowList::iterator iter = TestShell::windowList()->begin();
+ iter != TestShell::windowList()->end(); iter++) {
+ HWND hwnd = *iter;
+ TestShell* shell =
+ static_cast<TestShell*>(win_util::GetWindowUserData(hwnd));
+ webkit_glue::DumpBackForwardList(shell->webView(), NULL, result);
+ }
+}
+
+/* static */ bool TestShell::RunFileTest(const char *filename,
+ const TestParams& params) {
+ // Load the test file into the first available window.
+ if (TestShell::windowList()->empty()) {
+ LOG(ERROR) << "No windows open.";
+ return false;
+ }
+
+ HWND hwnd = *(TestShell::windowList()->begin());
+ TestShell* shell =
+ static_cast<TestShell*>(win_util::GetWindowUserData(hwnd));
+ shell->ResetTestController();
+
+ // ResetTestController may have closed the window we were holding on to.
+ // Grab the first window again.
+ hwnd = *(TestShell::windowList()->begin());
+ shell = static_cast<TestShell*>(win_util::GetWindowUserData(hwnd));
+ DCHECK(shell);
+
+ // Clear focus between tests.
+ shell->m_focusedWidgetHost = NULL;
+
+ // Make sure the previous load is stopped.
+ shell->webView()->StopLoading();
+ shell->navigation_controller()->Reset();
+
+ // Clean up state between test runs.
+ webkit_glue::ResetBeforeTestRun(shell->webView());
+ ResetWebPreferences();
+ shell->webView()->SetPreferences(*web_prefs_);
+
+ SetWindowPos(shell->m_mainWnd, NULL,
+ kTestWindowXLocation, kTestWindowYLocation, 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER);
+ shell->ResizeSubViews();
+
+ if (strstr(filename, "loading/") || strstr(filename, "loading\\"))
+ shell->layout_test_controller()->SetShouldDumpFrameLoadCallbacks(true);
+
+ shell->test_is_preparing_ = true;
+
+ std::wstring wstr = UTF8ToWide(filename);
+ shell->LoadURL(wstr.c_str());
+
+ shell->test_is_preparing_ = false;
+ shell->WaitTestFinished();
+
+ // Echo the url in the output so we know we're not getting out of sync.
+ printf("#URL:%s\n", filename);
+
+ // Dump the requested representation.
+ WebFrame* webFrame = shell->webView()->GetMainFrame();
+ if (webFrame) {
+ bool should_dump_as_text =
+ shell->layout_test_controller_->ShouldDumpAsText();
+ bool dumped_anything = false;
+ if (params.dump_tree) {
+ dumped_anything = true;
+ // Text output: the test page can request different types of output
+ // which we handle here.
+ if (!should_dump_as_text) {
+ // Plain text pages should be dumped as text
+ std::wstring mime_type = webFrame->GetDataSource()->GetResponseMimeType();
+ should_dump_as_text = (mime_type == L"text/plain");
+ }
+ if (should_dump_as_text) {
+ bool recursive = shell->layout_test_controller_->
+ ShouldDumpChildFramesAsText();
+ std::string data_utf8 = WideToUTF8(
+ webkit_glue::DumpFramesAsText(webFrame, recursive));
+ fwrite(data_utf8.c_str(), 1, data_utf8.size(), stdout);
+ } else {
+ printf("%s", WideToUTF8(
+ webkit_glue::DumpRenderer(webFrame)).c_str());
+
+ bool recursive = shell->layout_test_controller_->
+ ShouldDumpChildFrameScrollPositions();
+ printf("%s", WideToUTF8(
+ webkit_glue::DumpFrameScrollPosition(webFrame, recursive)).
+ c_str());
+ }
+
+ if (shell->layout_test_controller_->ShouldDumpBackForwardList()) {
+ std::wstring bfDump;
+ DumpBackForwardList(&bfDump);
+ printf("%s", WideToUTF8(bfDump).c_str());
+ }
+ }
+
+ if (params.dump_pixels && !should_dump_as_text) {
+ // Image output: we write the image data to the file given on the
+ // command line (for the dump pixels argument), and the MD5 sum to
+ // stdout.
+ dumped_anything = true;
+ std::string md5sum = DumpImage(webFrame, params.pixel_file_name);
+ printf("#MD5:%s\n", md5sum.c_str());
+ }
+ if (dumped_anything)
+ printf("#EOF\n");
+ fflush(stdout);
+ }
+
+ return true;
+}
+
+
+/* static */
+ATOM TestShell::RegisterWindowClass()
+{
+ LoadString(instance_handle_, IDS_APP_TITLE, g_windowTitle, MAX_LOADSTRING);
+ LoadString(instance_handle_, IDC_TESTSHELL, g_windowClass, MAX_LOADSTRING);
+
+ WNDCLASSEX wcex = {
+ /* cbSize = */ sizeof(WNDCLASSEX),
+ /* style = */ CS_HREDRAW | CS_VREDRAW,
+ /* lpfnWndProc = */ TestShell::WndProc,
+ /* cbClsExtra = */ 0,
+ /* cbWndExtra = */ 0,
+ /* hInstance = */ instance_handle_,
+ /* hIcon = */ LoadIcon(instance_handle_, MAKEINTRESOURCE(IDI_TESTSHELL)),
+ /* hCursor = */ LoadCursor(NULL, IDC_ARROW),
+ /* hbrBackground = */ 0,
+ /* lpszMenuName = */ MAKEINTRESOURCE(IDC_TESTSHELL),
+ /* lpszClassName = */ g_windowClass,
+ /* hIconSm = */ LoadIcon(instance_handle_, MAKEINTRESOURCE(IDI_SMALL)),
+ };
+ return RegisterClassEx(&wcex);
+}
+
+LRESULT CALLBACK TestShell::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ TestShell* shell = static_cast<TestShell*>(win_util::GetWindowUserData(hwnd));
+
+ switch (message) {
+ case WM_COMMAND:
+ {
+ int wmId = LOWORD(wParam);
+ int wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case IDM_ABOUT:
+ DialogBox(shell->instance_handle_, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd,
+ About);
+ break;
+ case IDM_EXIT:
+ DestroyWindow(hwnd);
+ break;
+ case IDC_NAV_BACK:
+ shell->GoBackOrForward(-1);
+ break;
+ case IDC_NAV_FORWARD:
+ shell->GoBackOrForward(1);
+ break;
+ case IDC_NAV_RELOAD:
+ case IDC_NAV_STOP:
+ {
+ if (wmId == IDC_NAV_RELOAD) {
+ shell->Reload();
+ } else {
+ shell->webView()->StopLoading();
+ }
+ }
+ break;
+ case IDM_DUMP_BODY_TEXT:
+ shell->DumpDocumentText();
+ break;
+ case IDM_DUMP_RENDER_TREE:
+ shell->DumpRenderTree();
+ break;
+ case IDM_SHOW_WEB_INSPECTOR:
+ shell->webView()->InspectElement(0, 0);
+ break;
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ {
+ // Dump all in use memory just before shutdown if in use memory
+ // debugging has been enabled.
+ base::MemoryDebug::DumpAllMemoryInUse();
+
+ WindowList::iterator entry =
+ std::find(TestShell::windowList()->begin(),
+ TestShell::windowList()->end(), hwnd);
+ if (entry != TestShell::windowList()->end())
+ TestShell::windowList()->erase(entry);
+
+ if (TestShell::windowList()->empty() || shell->is_modal())
+ MessageLoop::current()->Quit();
+ delete shell;
+ }
+ return 0;
+
+ case WM_SIZE:
+ if (shell->webView())
+ shell->ResizeSubViews();
+ return 0;
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+
+#define MAX_URL_LENGTH 1024
+
+LRESULT CALLBACK TestShell::EditWndProc(HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam)
+{
+ TestShell* shell =
+ static_cast<TestShell*>(win_util::GetWindowUserData(hwnd));
+
+ switch (message) {
+ case WM_CHAR:
+ if (wParam == 13) { // Enter Key
+ wchar_t strPtr[MAX_URL_LENGTH];
+ *((LPWORD)strPtr) = MAX_URL_LENGTH;
+ LRESULT strLen = SendMessage(hwnd, EM_GETLINE, 0, (LPARAM)strPtr);
+ if (strLen > 0)
+ shell->LoadURL(strPtr);
+
+ return 0;
+ }
+ }
+
+ return (LRESULT) CallWindowProc(shell->default_edit_wnd_proc_, hwnd,
+ message, wParam, lParam);
+}
+
+
+// Message handler for about box.
+INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(lParam);
+ switch (message) {
+ case WM_INITDIALOG:
+ return (INT_PTR)TRUE;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
+ EndDialog(hDlg, LOWORD(wParam));
+ return (INT_PTR)TRUE;
+ }
+ break;
+ }
+ return (INT_PTR)FALSE;
+}
+
+void TestShell::LoadURL(const wchar_t* url)
+{
+ LoadURLForFrame(url, NULL);
+}
+
+void TestShell::LoadURLForFrame(const wchar_t* url,
+ const wchar_t* frame_name) {
+ if (!url)
+ return;
+
+ bool bIsSVGTest = wcsstr(url, L"W3C-SVG-1.1") > 0;
+
+ if (bIsSVGTest) {
+ SizeTo(kSVGTestWindowWidth, kSVGTestWindowHeight);
+ } else {
+ SizeToDefault();
+ }
+
+ std::wstring urlString(url);
+ if (!urlString.empty() && (PathFileExists(url) || PathIsUNC(url))) {
+ TCHAR fileURL[INTERNET_MAX_URL_LENGTH];
+ DWORD fileURLLength = sizeof(fileURL)/sizeof(fileURL[0]);
+ if (SUCCEEDED(UrlCreateFromPath(url, fileURL, &fileURLLength, 0)))
+ urlString.assign(fileURL);
+ }
+
+ std::wstring frame_string;
+ if (frame_name)
+ frame_string = frame_name;
+
+ navigation_controller_->LoadEntry(new TestNavigationEntry(
+ -1, GURL(urlString), std::wstring(), PageTransition::LINK,
+ frame_string));
+}
+
+bool TestShell::Navigate(const NavigationEntry& entry, bool reload) {
+ const TestNavigationEntry& test_entry =
+ *static_cast<const TestNavigationEntry*>(&entry);
+
+ WebRequestCachePolicy cache_policy;
+ if (reload) {
+ cache_policy = WebRequestReloadIgnoringCacheData;
+ } else if (entry.GetPageID() != -1) {
+ cache_policy = WebRequestReturnCacheDataElseLoad;
+ } else {
+ cache_policy = WebRequestUseProtocolCachePolicy;
+ }
+
+ scoped_ptr<WebRequest> request(WebRequest::Create(entry.GetURL()));
+ request->SetCachePolicy(cache_policy);
+ // If we are reloading, then WebKit will use the state of the current page.
+ // Otherwise, we give it the state to navigate to.
+ if (!reload)
+ request->SetHistoryState(entry.GetContentState());
+
+ request->SetExtraData(
+ new TestShellExtraRequestData(entry.GetPageID(),
+ entry.GetTransition()));
+
+ // Get the right target frame for the entry.
+ WebFrame* frame = webView()->GetMainFrame();
+ if (!test_entry.GetTargetFrame().empty())
+ frame = webView()->GetFrameWithName(test_entry.GetTargetFrame());
+ // TODO(mpcomplete): should we clear the target frame, or should
+ // back/forward navigations maintain the target frame?
+
+ frame->LoadRequest(request.get());
+ SetFocus(webViewHost(), true);
+
+ return true;
+}
+
+void TestShell::GoBackOrForward(int offset) {
+ navigation_controller_->GoToOffset(offset);
+}
+
+bool TestShell::PromptForSaveFile(const wchar_t* prompt_title,
+ std::wstring* result)
+{
+ wchar_t path_buf[MAX_PATH] = L"data.txt";
+
+ OPENFILENAME info = {0};
+ info.lStructSize = sizeof(info);
+ info.hwndOwner = m_mainWnd;
+ info.hInstance = instance_handle_;
+ info.lpstrFilter = L"*.txt";
+ info.lpstrFile = path_buf;
+ info.nMaxFile = arraysize(path_buf);
+ info.lpstrTitle = prompt_title;
+ if (!GetSaveFileName(&info))
+ return false;
+
+ result->assign(info.lpstrFile);
+ return true;
+}
+
+static void WriteTextToFile(const std::wstring& data,
+ const std::wstring& file_path)
+{
+ FILE* fp;
+ errno_t err = _wfopen_s(&fp, file_path.c_str(), L"wt");
+ if (err)
+ return;
+ std::string data_utf8 = WideToUTF8(data);
+ fwrite(data_utf8.c_str(), 1, data_utf8.size(), fp);
+ fclose(fp);
+}
+
+std::wstring TestShell::GetDocumentText()
+{
+ return webkit_glue::DumpDocumentText(webView()->GetMainFrame());
+}
+
+void TestShell::DumpDocumentText()
+{
+ std::wstring file_path;
+ if (!PromptForSaveFile(L"Dump document text", &file_path))
+ return;
+
+ WriteTextToFile(webkit_glue::DumpDocumentText(webView()->GetMainFrame()),
+ file_path);
+}
+
+void TestShell::DumpRenderTree()
+{
+ std::wstring file_path;
+ if (!PromptForSaveFile(L"Dump render tree", &file_path))
+ return;
+
+ WriteTextToFile(webkit_glue::DumpRenderer(webView()->GetMainFrame()),
+ file_path);
+}
+
+void TestShell::Reload() {
+ navigation_controller_->Reload();
+}
+
+/* static */
+std::string TestShell::RewriteLocalUrl(const std::string& url) {
+ // Convert file:///tmp/LayoutTests urls to the actual location on disk.
+ const char kPrefix[] = "file:///tmp/LayoutTests/";
+ const int kPrefixLen = arraysize(kPrefix) - 1;
+
+ std::string new_url(url);
+ if (url.compare(0, kPrefixLen, kPrefix, kPrefixLen) == 0) {
+ std::wstring replace_url;
+ PathService::Get(base::DIR_EXE, &replace_url);
+ file_util::UpOneDirectory(&replace_url);
+ file_util::UpOneDirectory(&replace_url);
+ file_util::AppendToPath(&replace_url, L"webkit");
+ file_util::AppendToPath(&replace_url, L"data");
+ file_util::AppendToPath(&replace_url, L"layout_tests");
+ file_util::AppendToPath(&replace_url, L"LayoutTests");
+ replace_url.push_back(file_util::kPathSeparator);
+ new_url = std::string("file:///") +
+ WideToUTF8(replace_url).append(url.substr(kPrefixLen));
+ }
+ return new_url;
+}
+
+//-----------------------------------------------------------------------------
+
+namespace webkit_glue {
+
+bool HistoryContains(const char16* url, int url_len,
+ const char* document_host, int document_host_len,
+ bool is_dns_prefetch_enabled) {
+ return false;
+}
+
+void DnsPrefetchUrl(const char16* url, int url_length) {}
+
+void PrecacheUrl(const char16* url, int url_length) {}
+
+void AppendToLog(const char* file, int line, const char* msg) {
+ logging::LogMessage(file, line).stream() << msg;
+}
+
+bool GetMimeTypeFromExtension(std::wstring &ext, std::string *mime_type) {
+ return mime_util::GetMimeTypeFromExtension(ext, mime_type);
+}
+
+bool GetMimeTypeFromFile(const std::wstring &file_path,
+ std::string *mime_type) {
+ return mime_util::GetMimeTypeFromFile(file_path, mime_type);
+}
+
+bool GetPreferredExtensionForMimeType(const std::string& mime_type,
+ std::wstring* ext) {
+ return mime_util::GetPreferredExtensionForMimeType(mime_type, ext);
+}
+
+IMLangFontLink2* GetLangFontLink() {
+ return webkit_glue::GetLangFontLinkHelper();
+}
+
+std::wstring GetLocalizedString(int message_id) {
+ const ATLSTRINGRESOURCEIMAGE* image =
+ AtlGetStringResourceImage(_AtlBaseModule.GetModuleInstance(),
+ message_id);
+ if (!image) {
+ NOTREACHED();
+ return L"No string for this identifier!";
+ }
+ return std::wstring(image->achString, image->nLength);
+}
+
+std::string GetDataResource(int resource_id) {
+ if (resource_id == IDR_BROKENIMAGE) {
+ // Use webkit's broken image icon (16x16)
+ static std::string broken_image_data;
+ if (broken_image_data.empty()) {
+ std::wstring path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ file_util::AppendToPath(&path, L"webkit");
+ file_util::AppendToPath(&path, L"tools");
+ file_util::AppendToPath(&path, L"test_shell");
+ file_util::AppendToPath(&path, L"resources");
+ file_util::AppendToPath(&path, L"missingImage.gif");
+ bool success = file_util::ReadFileToString(path, &broken_image_data);
+ if (!success) {
+ LOG(FATAL) << "Failed reading: " << path;
+ }
+ }
+ return broken_image_data;
+ } else if (resource_id == IDR_FEED_PREVIEW) {
+ // It is necessary to return a feed preview template that contains
+ // a {{URL}} substring where the feed URL should go; see the code
+ // that computes feed previews in feed_preview.cc:MakeFeedPreview.
+ // This fixes issue #932714.
+ return std::string("Feed preview for {{URL}}");
+ } else {
+ return std::string();
+ }
+}
+
+HCURSOR LoadCursor(int cursor_id) {
+ return NULL;
+}
+
+bool GetApplicationDirectory(std::wstring *path) {
+ return PathService::Get(base::DIR_EXE, path);
+}
+
+GURL GetInspectorURL() {
+ return GURL("test-shell-resource://inspector/inspector.html");
+}
+
+std::string GetUIResourceProtocol() {
+ return "test-shell-resource";
+}
+
+bool GetExeDirectory(std::wstring *path) {
+ return PathService::Get(base::DIR_EXE, path);
+}
+
+bool SpellCheckWord(const wchar_t* word, int word_len,
+ int* misspelling_start, int* misspelling_len) {
+ // Report all words being correctly spelled.
+ *misspelling_start = 0;
+ *misspelling_len = 0;
+ return true;
+}
+
+bool GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins) {
+ return NPAPI::PluginList::Singleton()->GetPlugins(refresh, plugins);
+}
+
+bool webkit_glue::IsPluginRunningInRendererProcess() {
+ return true;
+}
+
+bool EnsureFontLoaded(HFONT font) {
+ return true;
+}
+
+MONITORINFOEX GetMonitorInfoForWindow(HWND window) {
+ return webkit_glue::GetMonitorInfoForWindowHelper(window);
+}
+
+bool DownloadUrl(const std::string& url, HWND caller_window) {
+ return false;
+}
+
+bool GetPluginFinderURL(std::string* plugin_finder_url) {
+ return false;
+}
+
+bool IsDefaultPluginEnabled() {
+ return false;
+}
+
+std::wstring GetWebKitLocale() {
+ return L"en-US";
+}
+
+} // namespace webkit_glue