summaryrefslogtreecommitdiffstats
path: root/webkit/tools/test_shell
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
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')
-rw-r--r--webkit/tools/test_shell/SConscript227
-rw-r--r--webkit/tools/test_shell/drag_delegate.cc65
-rw-r--r--webkit/tools/test_shell/drag_delegate.h60
-rw-r--r--webkit/tools/test_shell/drop_delegate.cc76
-rw-r--r--webkit/tools/test_shell/drop_delegate.h66
-rw-r--r--webkit/tools/test_shell/event_sending_controller.cc511
-rw-r--r--webkit/tools/test_shell/event_sending_controller.h108
-rw-r--r--webkit/tools/test_shell/foreground_helper.h111
-rw-r--r--webkit/tools/test_shell/image_decoder_unittest.cc211
-rw-r--r--webkit/tools/test_shell/image_decoder_unittest.h103
-rw-r--r--webkit/tools/test_shell/keyboard_unittest.cc293
-rw-r--r--webkit/tools/test_shell/layout_test_controller.cc595
-rw-r--r--webkit/tools/test_shell/layout_test_controller.h306
-rw-r--r--webkit/tools/test_shell/layout_test_controller_unittest.cc114
-rw-r--r--webkit/tools/test_shell/node_leak_test.cc113
-rw-r--r--webkit/tools/test_shell/plugin_tests.cc147
-rw-r--r--webkit/tools/test_shell/resource.h38
-rw-r--r--webkit/tools/test_shell/resources/AHEM____.TTFbin0 -> 12480 bytes
-rw-r--r--webkit/tools/test_shell/resources/README.txt24
-rw-r--r--webkit/tools/test_shell/resources/missingImage.gifbin0 -> 362 bytes
-rw-r--r--webkit/tools/test_shell/resources/small.icobin0 -> 23558 bytes
-rw-r--r--webkit/tools/test_shell/resources/test_shell.icobin0 -> 23558 bytes
-rw-r--r--webkit/tools/test_shell/resources/test_shell.rc133
-rw-r--r--webkit/tools/test_shell/run_all_tests.cc85
-rw-r--r--webkit/tools/test_shell/simple_resource_loader_bridge.cc584
-rw-r--r--webkit/tools/test_shell/simple_resource_loader_bridge.h55
-rw-r--r--webkit/tools/test_shell/temp/navigation_controller_base.cc301
-rw-r--r--webkit/tools/test_shell/temp/navigation_controller_base.h220
-rw-r--r--webkit/tools/test_shell/temp/navigation_entry.h154
-rw-r--r--webkit/tools/test_shell/temp/page_transition_types.h173
-rw-r--r--webkit/tools/test_shell/test_navigation_controller.cc107
-rw-r--r--webkit/tools/test_shell/test_navigation_controller.h118
-rw-r--r--webkit/tools/test_shell/test_shell.cc1128
-rw-r--r--webkit/tools/test_shell/test_shell.h273
-rw-r--r--webkit/tools/test_shell/test_shell.vcproj546
-rw-r--r--webkit/tools/test_shell/test_shell.vsprops22
-rw-r--r--webkit/tools/test_shell/test_shell_main.cc360
-rw-r--r--webkit/tools/test_shell/test_shell_request_context.cc69
-rw-r--r--webkit/tools/test_shell/test_shell_request_context.h53
-rw-r--r--webkit/tools/test_shell/test_shell_switches.cc76
-rw-r--r--webkit/tools/test_shell/test_shell_switches.h55
-rw-r--r--webkit/tools/test_shell/test_shell_test.cc63
-rw-r--r--webkit/tools/test_shell/test_shell_test.h63
-rw-r--r--webkit/tools/test_shell/test_shell_tests.vcproj393
-rw-r--r--webkit/tools/test_shell/test_shell_tests.vsprops17
-rw-r--r--webkit/tools/test_shell/test_webview_delegate.cc943
-rw-r--r--webkit/tools/test_shell/test_webview_delegate.h303
-rw-r--r--webkit/tools/test_shell/text_input_controller.cc249
-rw-r--r--webkit/tools/test_shell/text_input_controller.h74
-rw-r--r--webkit/tools/test_shell/text_input_controller_unittest.cc82
-rw-r--r--webkit/tools/test_shell/webview_host.cc72
-rw-r--r--webkit/tools/test_shell/webview_host.h61
-rw-r--r--webkit/tools/test_shell/webwidget_host.cc354
-rw-r--r--webkit/tools/test_shell/webwidget_host.h111
54 files changed, 10465 insertions, 0 deletions
diff --git a/webkit/tools/test_shell/SConscript b/webkit/tools/test_shell/SConscript
new file mode 100644
index 0000000..3509a0c
--- /dev/null
+++ b/webkit/tools/test_shell/SConscript
@@ -0,0 +1,227 @@
+# 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.
+
+Import('env', 'env_res')
+
+env = env.Clone()
+env_res = env_res.Clone()
+
+env_res.Append(
+ CPPPATH = [
+ '.',
+ '#/..',
+ '$NET_DIR',
+ ],
+ RCFLAGS = [
+ ['/l', '0x409'],
+ ],
+)
+
+input_files = [
+ 'drag_delegate.cc',
+ 'drop_delegate.cc',
+ 'event_sending_controller.cc',
+ 'layout_test_controller.cc',
+ 'simple_resource_loader_bridge.cc',
+ 'test_navigation_controller.cc',
+ 'test_shell.cc',
+ 'test_shell_switches.cc',
+ 'test_shell_request_context.cc',
+ 'test_webview_delegate.cc',
+ 'text_input_controller.cc',
+ 'webview_host.cc',
+ 'webwidget_host.cc',
+ 'temp/navigation_controller_base.cc',
+]
+
+env.Append(
+ CPPPATH = [
+ '$BREAKPAD_DIR/src',
+ '$WEBKIT_DIR/glue',
+ '$GTEST_DIR/include',
+ ],
+ CCFLAGS = [
+ '/TP',
+ '/WX',
+ '/Wp64',
+ '/wd4503',
+ '/wd4819',
+ ],
+
+ LIBS = [
+ 'comctl32.lib',
+ 'shlwapi.lib',
+ 'rpcrt4.lib',
+ 'winmm.lib',
+ 'wininet.lib',
+ 'version.lib',
+ 'msimg32.lib',
+ 'ws2_32.lib',
+ 'usp10.lib',
+ 'psapi.lib',
+ 'kernel32.lib',
+ 'user32.lib',
+ 'gdi32.lib',
+ 'winspool.lib',
+ 'comdlg32.lib',
+ 'advapi32.lib',
+ 'shell32.lib',
+ 'ole32.lib',
+ 'oleaut32.lib',
+ 'uuid.lib',
+ 'odbc32.lib',
+ 'odbccp32.lib',
+
+ 'delayimp.lib',
+ ],
+
+ LINKFLAGS = [
+ '/DELAYLOAD:"ws2_32.dll"',
+ '/DELAYLOAD:"dwmapi.dll"',
+ '/DELAYLOAD:"uxtheme.dll"',
+ '/FIXED:No',
+ '/SUBSYSTEM:CONSOLE',
+ '/MACHINE:X86',
+ '/safeseh',
+ '/dynamicbase',
+ '/ignore:4199',
+ '/nxcompat',
+ ],
+)
+
+lib = env.Library('test_shell', input_files)
+
+
+
+
+resources = [
+ env_res.RES('resources/test_shell.rc'),
+ '$NET_DIR/net_resources.res',
+ '$WEBKIT_DIR/build/localized_strings/webkit_strings_en-US.res',
+]
+
+components = [
+ lib,
+
+ '$BASE_DIR/base.lib',
+ '$BASE_DIR/gfx/base_gfx.lib',
+ '$BREAKPAD_DIR/breakpad_handler.lib',
+ '$BREAKPAD_DIR/breakpad_sender.lib',
+ '$GOOGLEURL_DIR/googleurl.lib',
+ '$NET_DIR/net.lib',
+ '$SKIA_DIR/skia.lib',
+ '$TESTING_DIR/gtest.lib',
+ '$BZIP2_DIR/bzip2.lib',
+ '$ICU38_DIR/icuuc.lib',
+ '$LIBJPEG_DIR/libjpeg.lib',
+ '$LIBPNG_DIR/libpng.lib',
+ '$LIBXML_DIR/libxml.lib',
+ '$LIBXSLT_DIR/libxslt.lib',
+ '$MODP_B64_DIR/modp_b64.lib',
+ '$ZLIB_DIR/zlib.lib',
+ '$V8_DIR/v8.lib',
+ '$V8_DIR/snapshot-empty.obj',
+ '$WEBKIT_DIR/JavaScriptCore_pcre.lib',
+ '$WEBKIT_DIR/Port.lib',
+ '$WEBKIT_DIR/activex_shim/activex_shim.lib',
+ '$WEBKIT_DIR/build/JavaScriptCore/WTF.lib',
+ '$WEBKIT_DIR/build/V8Bindings/V8Bindings.lib',
+ '$WEBKIT_DIR/build/WebCore/WebCore.lib',
+ '$WEBKIT_DIR/default_plugin/default_plugin.lib',
+ '$WEBKIT_DIR/glue/Glue.lib',
+]
+
+test_shell = env.Program(['test_shell.exe',
+ 'test_shell.ilk',
+ 'test_shell.pdb'],
+ components + resources +
+ ['test_shell_main.cc'])
+i = env.Install('$TARGET_ROOT', test_shell)
+env.Alias('webkit', i)
+
+# This call can NOT use $WEBKIT_DIR because we need to copy
+# directly from the source location.
+i = env.Install('$TARGET_ROOT', '#/../webkit/tools/test_shell/resources/fonts')
+env.Alias('webkit', i)
+
+env.Depends(test_shell, '$V8_DIR/vc80.pdb')
+
+
+test_files = [
+ 'drag_delegate.cc',
+ 'drop_delegate.cc',
+ 'event_sending_controller.cc',
+ 'image_decoder_unittest.cc',
+ 'keyboard_unittest.cc',
+ 'layout_test_controller.cc',
+ 'layout_test_controller_unittest.cc',
+ 'node_leak_test.cc',
+ 'plugin_tests.cc',
+ 'run_all_tests.cc',
+ 'simple_resource_loader_bridge.cc',
+ 'temp/navigation_controller_base.cc',
+ 'test_navigation_controller.cc',
+ 'test_shell.cc',
+ 'test_shell_request_context.cc',
+ 'test_shell_switches.cc',
+ 'test_shell_test.cc',
+ 'test_webview_delegate.cc',
+ 'text_input_controller.cc',
+ 'text_input_controller_unittest.cc',
+ 'webview_host.cc',
+ 'webwidget_host.cc',
+ '$WEBKIT_DIR/glue/autocomplete_input_listener_unittest.cc',
+ '$WEBKIT_DIR/glue/bookmarklet_unittest.cc',
+ '$WEBKIT_DIR/glue/context_menu_unittest.cc',
+ '$WEBKIT_DIR/glue/cpp_bound_class_unittest.cc',
+ '$WEBKIT_DIR/glue/cpp_variant_unittest.cc',
+ '$WEBKIT_DIR/glue/dom_operations_unittest.cc',
+ '$WEBKIT_DIR/glue/dom_serializer_unittest.cc',
+ '$WEBKIT_DIR/glue/glue_serialize_unittest.cc',
+ '$WEBKIT_DIR/glue/iframe_redirect_unittest.cc',
+ '$WEBKIT_DIR/glue/mimetype_unittest.cc',
+ '$WEBKIT_DIR/glue/multipart_response_delegate_unittest.cc',
+ '$WEBKIT_DIR/glue/password_autocomplete_listener_unittest.cc',
+ '$WEBKIT_DIR/glue/regular_expression_unittest.cc',
+ '$WEBKIT_DIR/glue/resource_fetcher_unittest.cc',
+ # Commented out until a regression is fixed and this file is restored.
+ #'$WEBKIT_DIR/glue/stringimpl_unittest.cc',
+ '$WEBKIT_DIR/glue/webplugin_impl_unittest.cc',
+ '$WEBKIT_DIR/port/platform/GKURL_unittest.cpp',
+ '$WEBKIT_DIR/port/platform/image-decoders/bmp/BMPImageDecoder_unittest.cpp',
+ '$WEBKIT_DIR/port/platform/image-decoders/ico/ICOImageDecoder_unittest.cpp',
+ '$WEBKIT_DIR/port/platform/image-decoders/xbm/XBMImageDecoder_unittest.cpp',
+]
+
+test_shell_tests = env.Program(['test_shell_tests',
+ 'test_shell_tests.ilk',
+ 'test_shell_tests.pdb'],
+ components + resources + test_files)
+i = env.Install('$TARGET_ROOT', test_shell_tests)
+env.Alias('webkit', i)
diff --git a/webkit/tools/test_shell/drag_delegate.cc b/webkit/tools/test_shell/drag_delegate.cc
new file mode 100644
index 0000000..f0d01a8
--- /dev/null
+++ b/webkit/tools/test_shell/drag_delegate.cc
@@ -0,0 +1,65 @@
+// 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 "webkit/tools/test_shell/drag_delegate.h"
+
+#include <atltypes.h>
+
+#include "webkit/glue/webview.h"
+
+namespace {
+
+void GetCursorPositions(HWND hwnd, CPoint* client, CPoint* screen) {
+ // GetCursorPos will fail if the input desktop isn't the current desktop.
+ // See http://b/1173534. (0,0) is wrong, but better than uninitialized.
+ if (!GetCursorPos(screen))
+ screen->SetPoint(0, 0);
+
+ *client = *screen;
+ ScreenToClient(hwnd, client);
+}
+
+} // anonymous namespace
+
+void TestDragDelegate::OnDragSourceCancel() {
+ OnDragSourceDrop();
+}
+
+void TestDragDelegate::OnDragSourceDrop() {
+ CPoint client;
+ CPoint screen;
+ GetCursorPositions(source_hwnd_, &client, &screen);
+ webview_->DragSourceEndedAt(client.x, client.y, screen.x, screen.y);
+}
+void TestDragDelegate::OnDragSourceMove() {
+ CPoint client;
+ CPoint screen;
+ GetCursorPositions(source_hwnd_, &client, &screen);
+ webview_->DragSourceMovedTo(client.x, client.y, screen.x, screen.y);
+}
diff --git a/webkit/tools/test_shell/drag_delegate.h b/webkit/tools/test_shell/drag_delegate.h
new file mode 100644
index 0000000..2872cdf
--- /dev/null
+++ b/webkit/tools/test_shell/drag_delegate.h
@@ -0,0 +1,60 @@
+// 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.
+//
+// A class that implements BaseDragSource for the test shell webview delegate.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_DRAG_DELEGATE_H__
+#define WEBKIT_TOOLS_TEST_SHELL_DRAG_DELEGATE_H__
+
+#include "base/base_drag_source.h"
+
+class WebView;
+
+class TestDragDelegate : public BaseDragSource {
+ public:
+ TestDragDelegate(HWND source_hwnd, WebView* webview)
+ : BaseDragSource(),
+ source_hwnd_(source_hwnd),
+ webview_(webview) { }
+
+ protected:
+ // BaseDragSource
+ virtual void OnDragSourceCancel();
+ virtual void OnDragSourceDrop();
+ virtual void OnDragSourceMove();
+
+ private:
+ WebView* webview_;
+
+ // A HWND for the source we are associated with, used for translating
+ // mouse coordinates from screen to client coordinates.
+ HWND source_hwnd_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_DRAG_DELEGATE_H__
diff --git a/webkit/tools/test_shell/drop_delegate.cc b/webkit/tools/test_shell/drop_delegate.cc
new file mode 100644
index 0000000..0463190
--- /dev/null
+++ b/webkit/tools/test_shell/drop_delegate.cc
@@ -0,0 +1,76 @@
+// 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 "webkit/tools/test_shell/drop_delegate.h"
+
+#include "webkit/glue/webdropdata.h"
+#include "webkit/glue/webview.h"
+
+// BaseDropTarget methods ----------------------------------------------------
+DWORD TestDropDelegate::OnDragEnter(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ WebDropData drop_data;
+ WebDropData::PopulateWebDropData(data_object, &drop_data);
+
+ POINT client_pt = cursor_position;
+ ScreenToClient(GetHWND(), &client_pt);
+ bool valid = webview_->DragTargetDragEnter(drop_data, client_pt.x,
+ client_pt.y, cursor_position.x, cursor_position.y);
+ return valid ? DROPEFFECT_COPY : DROPEFFECT_NONE;
+}
+
+DWORD TestDropDelegate::OnDragOver(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ POINT client_pt = cursor_position;
+ ScreenToClient(GetHWND(), &client_pt);
+ bool valid = webview_->DragTargetDragOver(client_pt.x,
+ client_pt.y, cursor_position.x, cursor_position.y);
+ return valid ? DROPEFFECT_COPY : DROPEFFECT_NONE;
+}
+
+void TestDropDelegate::OnDragLeave(IDataObject* data_object) {
+ webview_->DragTargetDragLeave();
+}
+
+DWORD TestDropDelegate::OnDrop(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ POINT client_pt = cursor_position;
+ ScreenToClient(GetHWND(), &client_pt);
+ webview_->DragTargetDrop(client_pt.x, client_pt.y,
+ cursor_position.x, cursor_position.y);
+
+ // webkit win port always returns DROPEFFECT_NONE
+ return DROPEFFECT_NONE;
+}
diff --git a/webkit/tools/test_shell/drop_delegate.h b/webkit/tools/test_shell/drop_delegate.h
new file mode 100644
index 0000000..e2fa493
--- /dev/null
+++ b/webkit/tools/test_shell/drop_delegate.h
@@ -0,0 +1,66 @@
+// 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.
+//
+// A class that implements BaseDropTarget for the test shell webview delegate.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_DROP_DELEGATE_H__
+#define WEBKIT_TOOLS_TEST_SHELL_DROP_DELEGATE_H__
+
+#include "base/base_drop_target.h"
+
+class WebView;
+
+class TestDropDelegate : public BaseDropTarget {
+ public:
+ TestDropDelegate(HWND source_hwnd, WebView* webview)
+ : BaseDropTarget(source_hwnd),
+ webview_(webview) { }
+
+ protected:
+ // BaseDropTarget methods
+ virtual DWORD OnDragEnter(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+ virtual DWORD OnDragOver(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+ virtual void OnDragLeave(IDataObject* data_object);
+ virtual DWORD OnDrop(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+
+
+ private:
+ WebView* webview_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_DROP_DELEGATE_H__
diff --git a/webkit/tools/test_shell/event_sending_controller.cc b/webkit/tools/test_shell/event_sending_controller.cc
new file mode 100644
index 0000000..dee5eaf
--- /dev/null
+++ b/webkit/tools/test_shell/event_sending_controller.cc
@@ -0,0 +1,511 @@
+// 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.
+
+// This file contains the definition for EventSendingController.
+//
+// Some notes about drag and drop handling:
+// Windows drag and drop goes through a system call to DoDragDrop. At that
+// point, program control is given to Windows which then periodically makes
+// callbacks into the webview. This won't work for layout tests, so instead,
+// we queue up all the mouse move and mouse up events. When the test tries to
+// start a drag (by calling EvenSendingController::DoDragDrop), we take the
+// events in the queue and replay them.
+// The behavior of queueing events and replaying them can be disabled by a
+// layout test by setting eventSender.dragMode to false.
+
+#include "webkit/tools/test_shell/event_sending_controller.h"
+
+#include <objidl.h>
+#include <queue>
+
+#include "base/ref_counted.h"
+#include "base/string_util.h"
+#include "webkit/glue/webview.h"
+#include "webkit/tools/test_shell/test_shell.h"
+
+// TODO(mpcomplete): layout before each event?
+// TODO(mpcomplete): do we need modifiers for mouse events?
+
+TestShell* EventSendingController::shell_ = NULL;
+gfx::Point EventSendingController::last_mouse_pos_;
+WebMouseEvent::Button EventSendingController::pressed_button_ =
+ WebMouseEvent::BUTTON_NONE;
+
+namespace {
+
+static scoped_refptr<IDataObject> drag_data_object;
+static bool replaying_saved_events = false;
+static std::queue<WebMouseEvent> mouse_event_queue;
+
+// Time and place of the last mouse up event.
+static double last_click_time_sec = 0;
+static gfx::Point last_click_pos;
+static int click_count = 0;
+
+// maximum distance (in space and time) for a mouse click
+// to register as a double or triple click
+static const double kMultiClickTimeSec = 1;
+static const int kMultiClickRadiusPixels = 5;
+
+inline bool outside_multiclick_radius(const gfx::Point &a, const gfx::Point &b) {
+ return ((a.x() - b.x()) * (a.x() - b.x()) + (a.y() - b.y()) * (a.y() - b.y())) >
+ kMultiClickRadiusPixels * kMultiClickRadiusPixels;
+}
+
+// Used to offset the time the event hander things an event happened. This is
+// done so tests can run without a delay, but bypass checks that are time
+// dependent (e.g., dragging has a timeout vs selection).
+static uint32 time_offset_ms = 0;
+
+double GetCurrentEventTimeSec() {
+ return (GetTickCount() + time_offset_ms) / 1000.0;
+}
+
+void AdvanceEventTime(int32 delta_ms) {
+ time_offset_ms += delta_ms;
+}
+
+void InitMouseEvent(WebInputEvent::Type t, WebMouseEvent::Button b,
+ const gfx::Point& pos, WebMouseEvent* e) {
+ e->type = t;
+ e->button = b;
+ e->modifiers = 0;
+ e->x = pos.x();
+ e->y = pos.y();
+ e->global_x = pos.x();
+ e->global_y = pos.y();
+ e->timestamp_sec = GetCurrentEventTimeSec();
+ e->layout_test_click_count = click_count;
+}
+
+void ApplyKeyModifiers(const CppVariant* arg, WebKeyboardEvent* event) {
+ std::vector<std::wstring> args = arg->ToStringVector();
+ for (std::vector<std::wstring>::iterator i = args.begin();
+ i != args.end(); ++i) {
+ const wchar_t* arg_string = (*i).c_str();
+ if (!wcscmp(arg_string, L"ctrlKey")) {
+ event->modifiers |= WebInputEvent::CTRL_KEY;
+ } else if (!wcscmp(arg_string, L"shiftKey")) {
+ event->modifiers |= WebInputEvent::SHIFT_KEY;
+ } else if (!wcscmp(arg_string, L"altKey")) {
+ event->modifiers |= WebInputEvent::ALT_KEY;
+ event->system_key = true;
+ } else if (!wcscmp(arg_string, L"metaKey")) {
+ event->modifiers |= WebInputEvent::META_KEY;
+ }
+ }
+}
+
+} // anonymous namespace
+
+EventSendingController::EventSendingController(TestShell* shell) {
+ // Set static shell_ variable since we can't do it in an initializer list.
+ // We also need to be careful not to assign shell_ to new windows which are
+ // temporary.
+ if (NULL == shell_)
+ shell_ = shell;
+
+ // Initialize the map that associates methods of this class with the names
+ // they will use when called by JavaScript. The actual binding of those
+ // names to their methods will be done by calling BindToJavaScript() (defined
+ // by CppBoundClass, the parent to EventSendingController).
+ BindMethod("mouseDown", &EventSendingController::mouseDown);
+ BindMethod("mouseUp", &EventSendingController::mouseUp);
+ BindMethod("contextClick", &EventSendingController::contextClick);
+ BindMethod("mouseMoveTo", &EventSendingController::mouseMoveTo);
+ BindMethod("leapForward", &EventSendingController::leapForward);
+ BindMethod("keyDown", &EventSendingController::keyDown);
+ BindMethod("enableDOMUIEventLogging", &EventSendingController::enableDOMUIEventLogging);
+ BindMethod("fireKeyboardEventsToElement", &EventSendingController::fireKeyboardEventsToElement);
+ BindMethod("clearKillRing", &EventSendingController::clearKillRing);
+ BindMethod("textZoomIn", &EventSendingController::textZoomIn);
+ BindMethod("textZoomOut", &EventSendingController::textZoomOut);
+
+ // When set to true (the default value), we batch mouse move and mouse up
+ // events so we can simulate drag & drop.
+ BindProperty("dragMode", &dragMode);
+}
+
+void EventSendingController::Reset() {
+ // The test should have finished a drag and the mouse button state.
+ DCHECK(!drag_data_object);
+ drag_data_object = NULL;
+ pressed_button_ = WebMouseEvent::BUTTON_NONE;
+ dragMode.Set(true);
+ last_click_time_sec = 0;
+ click_count = 0;
+}
+
+/* static */ WebView* EventSendingController::webview() {
+ return shell_->webView();
+}
+
+/* static */ void EventSendingController::DoDragDrop(IDataObject* data_obj) {
+ drag_data_object = data_obj;
+
+ DWORD effect = 0;
+ POINTL screen_ptl = {0, 0};
+ TestWebViewDelegate* delegate = shell_->delegate();
+ delegate->drop_delegate()->DragEnter(drag_data_object, MK_LBUTTON,
+ screen_ptl, &effect);
+
+ // Finish processing events.
+ ReplaySavedEvents();
+}
+
+//
+// Implemented javascript methods.
+//
+
+ void EventSendingController::mouseDown(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ webview()->Layout();
+
+ if ((GetCurrentEventTimeSec() - last_click_time_sec >= kMultiClickTimeSec) ||
+ outside_multiclick_radius(last_mouse_pos_, last_click_pos)) {
+ click_count = 1;
+ } else {
+ ++click_count;
+ }
+
+ WebMouseEvent event;
+ pressed_button_ = WebMouseEvent::BUTTON_LEFT;
+ InitMouseEvent(WebInputEvent::MOUSE_DOWN, WebMouseEvent::BUTTON_LEFT,
+ last_mouse_pos_, &event);
+ webview()->HandleInputEvent(&event);
+
+}
+
+ void EventSendingController::mouseUp(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ webview()->Layout();
+
+ WebMouseEvent event;
+ InitMouseEvent(WebInputEvent::MOUSE_UP, WebMouseEvent::BUTTON_LEFT,
+ last_mouse_pos_, &event);
+ if (drag_mode() && !replaying_saved_events) {
+ mouse_event_queue.push(event);
+ ReplaySavedEvents();
+ } else {
+ DoMouseUp(event);
+ }
+
+ last_click_time_sec = event.timestamp_sec;
+ last_click_pos = gfx::Point(event.x, event.y);
+}
+
+/* static */ void EventSendingController::DoMouseUp(const WebMouseEvent& e) {
+ webview()->HandleInputEvent(&e);
+ pressed_button_ = WebMouseEvent::BUTTON_NONE;
+
+ // If we're in a drag operation, complete it.
+ if (drag_data_object) {
+ TestWebViewDelegate* delegate = shell_->delegate();
+ // Get screen mouse position.
+ POINT screen_pt = { static_cast<LONG>(e.x),
+ static_cast<LONG>(e.y) };
+ ClientToScreen(shell_->webViewWnd(), &screen_pt);
+ POINTL screen_ptl = { screen_pt.x, screen_pt.y };
+
+ DWORD effect = 0;
+ delegate->drop_delegate()->DragOver(0, screen_ptl, &effect);
+ HRESULT hr = delegate->drag_delegate()->QueryContinueDrag(0, 0);
+ if (hr == DRAGDROP_S_DROP && effect != DROPEFFECT_NONE) {
+ DWORD effect = 0;
+ delegate->drop_delegate()->Drop(drag_data_object.get(), 0, screen_ptl,
+ &effect);
+ } else {
+ delegate->drop_delegate()->DragLeave();
+ }
+ drag_data_object = NULL;
+ }
+}
+
+ void EventSendingController::mouseMoveTo(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ if (args.size() >= 2 && args[0].isNumber() && args[1].isNumber()) {
+ webview()->Layout();
+
+ WebMouseEvent event;
+ last_mouse_pos_.SetPoint(args[0].ToInt32(), args[1].ToInt32());
+ InitMouseEvent(WebInputEvent::MOUSE_MOVE, pressed_button_,
+ last_mouse_pos_, &event);
+
+ if (drag_mode() && pressed_button_ != WebMouseEvent::BUTTON_NONE &&
+ !replaying_saved_events) {
+ mouse_event_queue.push(event);
+ } else {
+ DoMouseMove(event);
+ }
+ }
+}
+
+/* static */ void EventSendingController::DoMouseMove(const WebMouseEvent& e) {
+ webview()->HandleInputEvent(&e);
+
+ if (pressed_button_ != WebMouseEvent::BUTTON_NONE && drag_data_object) {
+ TestWebViewDelegate* delegate = shell_->delegate();
+ // Get screen mouse position.
+ POINT screen_pt = { static_cast<LONG>(e.x),
+ static_cast<LONG>(e.y) };
+ ClientToScreen(shell_->webViewWnd(), &screen_pt);
+ POINTL screen_ptl = { screen_pt.x, screen_pt.y };
+
+ HRESULT hr = delegate->drag_delegate()->QueryContinueDrag(0, MK_LBUTTON);
+ DWORD effect = 0;
+ delegate->drop_delegate()->DragOver(MK_LBUTTON, screen_ptl, &effect);
+
+ delegate->drag_delegate()->GiveFeedback(effect);
+ }
+}
+
+ void EventSendingController::keyDown(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ static const int kPercentVirtualKeyCode = 0x25;
+ static const int kAmpersandVirtualKeyCode = 0x26;
+
+ static const int kLeftParenthesesVirtualKeyCode = 0x28;
+ static const int kRightParenthesesVirtualKeyCode = 0x29;
+
+ static const int kLeftCurlyBracketVirtualKeyCode = 0x7B;
+ static const int kRightCurlyBracketVirtualKeyCode = 0x7D;
+
+ bool generate_char = false;
+
+ if (args.size() >= 1 && args[0].isString()) {
+ // TODO(mpcomplete): I'm not exactly sure how we should convert the string
+ // to a key event. This seems to work in the cases I tested.
+ // TODO(mpcomplete): Should we also generate a KEY_UP?
+ std::wstring code_str = UTF8ToWide(args[0].ToString());
+
+ // Convert \n -> VK_RETURN. Some layout tests use \n to mean "Enter", when
+ // Windows uses \r for "Enter".
+ wchar_t code;
+ bool needs_shift_key_modifier = false;
+ if (L"\n" == code_str) {
+ generate_char = true;
+ code = VK_RETURN;
+ } else if (L"rightArrow" == code_str) {
+ code = VK_RIGHT;
+ } else if (L"downArrow" == code_str) {
+ code = VK_DOWN;
+ } else if (L"leftArrow" == code_str) {
+ code = VK_LEFT;
+ } else if (L"upArrow" == code_str) {
+ code = VK_UP;
+ } else if (L"delete" == code_str) {
+ code = VK_BACK;
+ } else {
+ DCHECK(code_str.length() == 1);
+ code = code_str[0];
+ needs_shift_key_modifier = NeedsShiftModifer(code);
+ generate_char = true;
+ }
+
+ // NOTE(jnd):For one keydown event, we need to generate
+ // keyDown/keyUp pair, refer EventSender.cpp in
+ // WebKit/WebKitTools/DumpRenderTree/win. We may also need
+ // to generate a keyChar event in certain cases.
+ WebKeyboardEvent event_down, event_up;
+ event_down.type = WebInputEvent::KEY_DOWN;
+ event_down.modifiers = 0;
+ event_down.key_code = code;
+ event_down.key_data = code;
+
+ if (args.size() >= 2 && args[1].isObject())
+ ApplyKeyModifiers(&(args[1]), &event_down);
+
+ if (needs_shift_key_modifier)
+ event_down.modifiers |= WebInputEvent::SHIFT_KEY;
+
+ event_up = event_down;
+ event_up.type = WebInputEvent::KEY_UP;
+ // EventSendingController.m forces a layout here, with at least one
+ // test (fast\forms\focus-control-to-page.html) relying on this.
+ webview()->Layout();
+
+ webview()->HandleInputEvent(&event_down);
+
+ if (generate_char) {
+ WebKeyboardEvent event_char = event_down;
+ if (event_down.modifiers & WebInputEvent::SHIFT_KEY) {
+ // Special case for the following characters when the shift key is
+ // pressed in conjunction with these characters.
+ // Windows generates a WM_KEYDOWN message with the ASCII code of
+ // the character followed by a WM_CHAR for the corresponding
+ // virtual key code.
+ // We check for these keys to catch regressions in keyEvent handling
+ // in webkit.
+ switch(code) {
+ case '5':
+ event_char.key_code = kPercentVirtualKeyCode;
+ event_char.key_data = kPercentVirtualKeyCode;
+ break;
+ case '7':
+ event_char.key_code = kAmpersandVirtualKeyCode;
+ event_char.key_data = kAmpersandVirtualKeyCode;
+ break;
+ case '9':
+ event_char.key_code = kLeftParenthesesVirtualKeyCode;
+ event_char.key_data = kLeftParenthesesVirtualKeyCode;
+ break;
+ case '0':
+ event_char.key_code = kRightParenthesesVirtualKeyCode;
+ event_char.key_data = kRightParenthesesVirtualKeyCode;
+ break;
+ // '[{' for US
+ case VK_OEM_4:
+ event_char.key_code = kLeftCurlyBracketVirtualKeyCode;
+ event_char.key_data = kLeftCurlyBracketVirtualKeyCode;
+ break;
+ // ']}' for US
+ case VK_OEM_6:
+ event_char.key_code = kRightCurlyBracketVirtualKeyCode;
+ event_char.key_data = kRightCurlyBracketVirtualKeyCode;
+ break;
+ default:
+ break;
+ }
+ }
+ event_char.type = WebInputEvent::CHAR;
+ webview()->HandleInputEvent(&event_char);
+ }
+
+ webview()->HandleInputEvent(&event_up);
+ }
+}
+
+ bool EventSendingController::NeedsShiftModifer(wchar_t key_code) {
+ // If code is an uppercase letter, assign a SHIFT key to
+ // event_down.modifier, this logic comes from
+ // WebKit/WebKitTools/DumpRenderTree/Win/EventSender.cpp
+ if ((LOBYTE(key_code)) >= 'A' && (LOBYTE(key_code)) <= 'Z')
+ return true;
+ return false;
+ }
+
+ void EventSendingController::leapForward(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ // TODO(mpcomplete): DumpRenderTree defers this under certain conditions.
+
+ if (args.size() >=1 && args[0].isNumber()) {
+ AdvanceEventTime(args[0].ToInt32());
+ }
+}
+
+// Apple's port of webkit zooms by a factor of 1.2 (see
+// WebKit/WebView/WebView.mm)
+ void EventSendingController::textZoomIn(
+ const CppArgumentList& args, CppVariant* result) {
+ webview()->MakeTextLarger();
+ result->SetNull();
+}
+
+ void EventSendingController::textZoomOut(
+ const CppArgumentList& args, CppVariant* result) {
+ webview()->MakeTextSmaller();
+ result->SetNull();
+}
+
+ void EventSendingController::ReplaySavedEvents() {
+ replaying_saved_events = true;
+ while (!mouse_event_queue.empty()) {
+ WebMouseEvent event = mouse_event_queue.front();
+ mouse_event_queue.pop();
+
+ switch (event.type) {
+ case WebInputEvent::MOUSE_UP:
+ DoMouseUp(event);
+ break;
+ case WebInputEvent::MOUSE_MOVE:
+ DoMouseMove(event);
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ replaying_saved_events = false;
+}
+
+ void EventSendingController::contextClick(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ webview()->Layout();
+
+ if (GetCurrentEventTimeSec() - last_click_time_sec >= 1) {
+ click_count = 1;
+ } else {
+ ++click_count;
+ }
+
+ // Generate right mouse down and up.
+
+ WebMouseEvent event;
+ pressed_button_ = WebMouseEvent::BUTTON_RIGHT;
+ InitMouseEvent(WebInputEvent::MOUSE_DOWN, WebMouseEvent::BUTTON_RIGHT,
+ last_mouse_pos_, &event);
+ webview()->HandleInputEvent(&event);
+
+ InitMouseEvent(WebInputEvent::MOUSE_UP, WebMouseEvent::BUTTON_RIGHT,
+ last_mouse_pos_, &event);
+ webview()->HandleInputEvent(&event);
+
+ pressed_button_ = WebMouseEvent::BUTTON_NONE;
+}
+
+//
+// Unimplemented stubs
+//
+
+ void EventSendingController::enableDOMUIEventLogging(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+ void EventSendingController::fireKeyboardEventsToElement(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+ void EventSendingController::clearKillRing(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
diff --git a/webkit/tools/test_shell/event_sending_controller.h b/webkit/tools/test_shell/event_sending_controller.h
new file mode 100644
index 0000000..ddcff48
--- /dev/null
+++ b/webkit/tools/test_shell/event_sending_controller.h
@@ -0,0 +1,108 @@
+// 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.
+
+
+/*
+ EventSendingController class:
+ Bound to a JavaScript window.eventSender object using
+ CppBoundClass::BindToJavascript(), this allows layout tests that are run in
+ the test_shell to fire DOM events.
+
+ The OSX reference file is in
+ WebKit/WebKitTools/DumpRenderTree/EventSendingController.m
+*/
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_EVENT_SENDING_CONTROLLER_H__
+#define WEBKIT_TOOLS_TEST_SHELL_EVENT_SENDING_CONTROLLER_H__
+
+#include "base/gfx/point.h"
+#include "webkit/glue/cpp_bound_class.h"
+#include "webkit/glue/webinputevent.h"
+
+struct IDataObject;
+struct IDropSource;
+class TestShell;
+class WebView;
+
+class EventSendingController : public CppBoundClass {
+ public:
+ // Builds the property and method lists needed to bind this class to a JS
+ // object.
+ EventSendingController(TestShell* shell);
+
+ // Resets some static variable state.
+ void Reset();
+
+ // Simulate Windows' drag&drop system call.
+ static void DoDragDrop(IDataObject* drag_data);
+
+ // JS callback methods.
+ void mouseDown(const CppArgumentList& args, CppVariant* result);
+ void mouseUp(const CppArgumentList& args, CppVariant* result);
+ void mouseMoveTo(const CppArgumentList& args, CppVariant* result);
+ void leapForward(const CppArgumentList& args, CppVariant* result);
+ void keyDown(const CppArgumentList& args, CppVariant* result);
+ void textZoomIn(const CppArgumentList& args, CppVariant* result);
+ void textZoomOut(const CppArgumentList& args, CppVariant* result);
+
+ // Unimplemented stubs
+ void contextClick(const CppArgumentList& args, CppVariant* result);
+ void enableDOMUIEventLogging(const CppArgumentList& args, CppVariant* result);
+ void fireKeyboardEventsToElement(const CppArgumentList& args, CppVariant* result);
+ void clearKillRing(const CppArgumentList& args, CppVariant* result);
+ CppVariant dragMode;
+
+ private:
+ // Returns the test shell's webview.
+ static WebView* webview();
+
+ // Returns true if dragMode is true.
+ bool drag_mode() { return dragMode.isBool() && dragMode.ToBoolean(); }
+
+ // Sometimes we queue up mouse move and mouse up events for drag drop
+ // handling purposes. These methods dispatch the event.
+ static void DoMouseMove(const WebMouseEvent& e);
+ static void DoMouseUp(const WebMouseEvent& e);
+ static void ReplaySavedEvents();
+
+ // Returns true if the key_code passed in needs a shift key modifier to
+ // be passed into the generated event.
+ bool NeedsShiftModifer(wchar_t key_code);
+
+ // Non-owning pointer. The LayoutTestController is owned by the host.
+ static TestShell* shell_;
+
+ // Location of last mouseMoveTo event.
+ static gfx::Point last_mouse_pos_;
+
+ // Currently pressed mouse button (Left/Right/Middle or None)
+ static WebMouseEvent::Button pressed_button_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_EVENT_SENDING_CONTROLLER_H__
diff --git a/webkit/tools/test_shell/foreground_helper.h b/webkit/tools/test_shell/foreground_helper.h
new file mode 100644
index 0000000..359428e
--- /dev/null
+++ b/webkit/tools/test_shell/foreground_helper.h
@@ -0,0 +1,111 @@
+// 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 <atlbase.h>
+#include <atlwin.h>
+
+#include "base/logging.h"
+
+// Helper class for moving a window to the foreground.
+// Windows XP and later will not allow a window which is in the background to
+// move to the foreground, unless requested by the current window in the
+// foreground. For automated testing, we really want some of our windows
+// to be capable of moving to the foreground.
+//
+// This is probably leveraging a windows bug.
+class ForegroundHelper : public CWindowImpl<ForegroundHelper> {
+ public:
+BEGIN_MSG_MAP(ForegroundHelper)
+ MESSAGE_HANDLER(WM_HOTKEY, OnHotKey)
+END_MSG_MAP()
+
+ // Brings a window into the foreground.
+ // Can be called from any window, even if the caller is not the
+ // foreground window.
+ static HRESULT SetForeground(HWND window) {
+ DCHECK(::IsWindow(window));
+ ForegroundHelper foreground_helper;
+ CHECK(foreground_helper.ForegroundHotKey(window) == S_OK);
+ return S_OK;
+ }
+
+ private:
+ HRESULT ForegroundHotKey(HWND window) {
+ // This implementation registers a hot key (F22) and then
+ // triggers the hot key. When receiving the hot key, we'll
+ // be in the foreground and allowed to move the target window
+ // into the foreground too.
+
+ if(NULL == Create(NULL, NULL, NULL, WS_POPUP))
+ return AtlHresultFromLastError();
+
+ static const int hotkey_id = 0x0000baba;
+
+ // Store the target window into our USERDATA for use in our
+ // HotKey handler.
+ SetWindowLongPtr(GWLP_USERDATA, reinterpret_cast<ULONG_PTR>(window));
+ RegisterHotKey(m_hWnd, hotkey_id, 0, VK_F22);
+
+ // If the calling thread is not yet a UI thread, call PeekMessage
+ // to ensure creation of its message queue.
+ MSG msg = {0};
+ PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
+
+ // Send the Hotkey.
+ INPUT hotkey = {0};
+ hotkey.type = INPUT_KEYBOARD;
+ hotkey.ki.wVk = VK_F22;
+ if (1 != SendInput(1, &hotkey, sizeof(hotkey)))
+ return E_FAIL;
+
+ // Loop until we get the key.
+ // TODO: It may be possible to get stuck here if the
+ // message gets lost?
+ while(GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+
+ if(WM_HOTKEY == msg.message)
+ break;
+ }
+
+ UnregisterHotKey(m_hWnd, hotkey_id);
+ DestroyWindow();
+
+ return S_OK;
+ }
+
+ // Handle the registered Hotkey being pressed.
+ LRESULT OnHotKey(UINT /*uMsg*/, WPARAM /*wParam*/,
+ LPARAM /*lParam*/, BOOL& bHandled) {
+ HWND window = reinterpret_cast<HWND>(GetWindowLongPtr(GWLP_USERDATA));
+ SetForegroundWindow(window);
+ return 1;
+ }
+};
diff --git a/webkit/tools/test_shell/image_decoder_unittest.cc b/webkit/tools/test_shell/image_decoder_unittest.cc
new file mode 100644
index 0000000..9e640fd
--- /dev/null
+++ b/webkit/tools/test_shell/image_decoder_unittest.cc
@@ -0,0 +1,211 @@
+// 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 "config.h"
+
+#include <windows.h>
+
+#include "base/file_util.h"
+#include "base/md5.h"
+#include "base/path_service.h"
+#include "base/scoped_handle.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "webkit/tools/test_shell/image_decoder_unittest.h"
+
+void ReadFileToVector(const std::wstring& path, Vector<char>* contents) {
+ std::string contents_str;
+ file_util::ReadFileToString(path, &contents_str);
+ contents->resize(contents_str.size());
+ memcpy(&contents->first(), contents_str.data(), contents_str.size());
+}
+
+std::wstring GetMD5SumPath(const std::wstring& path) {
+ static const std::wstring kDecodedDataExtension(L".md5sum");
+ return path + kDecodedDataExtension;
+}
+
+#ifdef CALCULATE_MD5_SUMS
+void SaveMD5Sum(const std::wstring& path, WebCore::RGBA32Buffer* buffer) {
+ // Create the file to write.
+ ScopedHandle handle(CreateFile(path.c_str(), GENERIC_WRITE, 0,
+ NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL));
+ ASSERT_TRUE(handle.IsValid());
+
+ // Calculate MD5 sum.
+ MD5Digest digest;
+ SkAutoLockPixels bmp_lock(buffer->bitmap());
+ MD5Sum(buffer->bitmap().getPixels(),
+ buffer->rect().width() * buffer->rect().height() * sizeof(unsigned),
+ &digest);
+
+ // Write sum to disk.
+ DWORD bytes_written;
+ ASSERT_TRUE(WriteFile(handle, &digest, sizeof digest, &bytes_written,
+ NULL));
+ ASSERT_EQ(sizeof digest, bytes_written);
+}
+#else
+void VerifyImage(WebCore::ImageDecoder* decoder,
+ const std::wstring& path,
+ const std::wstring& md5_sum_path) {
+ // Make sure decoding can complete successfully.
+ EXPECT_TRUE(decoder->isSizeAvailable()) << path;
+ WebCore::RGBA32Buffer* image_buffer = decoder->frameBufferAtIndex(0);
+ ASSERT_NE(static_cast<WebCore::RGBA32Buffer*>(NULL), image_buffer) << path;
+ EXPECT_EQ(WebCore::RGBA32Buffer::FrameComplete, image_buffer->status()) <<
+ path;
+ EXPECT_FALSE(decoder->failed()) << path;
+
+ // Calculate MD5 sum.
+ MD5Digest actual_digest;
+ SkAutoLockPixels bmp_lock(image_buffer->bitmap());
+ MD5Sum(image_buffer->bitmap().getPixels(), image_buffer->rect().width() *
+ image_buffer->rect().height() * sizeof(unsigned), &actual_digest);
+
+ // Read the MD5 sum off disk.
+ std::string file_bytes;
+ file_util::ReadFileToString(md5_sum_path, &file_bytes);
+ MD5Digest expected_digest;
+ ASSERT_EQ(sizeof expected_digest, file_bytes.size()) << path;
+ memcpy(&expected_digest, file_bytes.data(), sizeof expected_digest);
+
+ // Verify that the sums are the same.
+ EXPECT_EQ(0, memcmp(&expected_digest, &actual_digest, sizeof(MD5Digest))) <<
+ path;
+}
+#endif
+
+void ImageDecoderTest::SetUp() {
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir_));
+ file_util::AppendToPath(&data_dir_, L"webkit");
+ file_util::AppendToPath(&data_dir_, L"data");
+ file_util::AppendToPath(&data_dir_, format_ + L"_decoder");
+ ASSERT_TRUE(file_util::PathExists(data_dir_));
+}
+
+std::vector<std::wstring> ImageDecoderTest::GetImageFiles() const {
+ std::wstring find_string(data_dir_);
+ file_util::AppendToPath(&find_string, L"*." + format_);
+ WIN32_FIND_DATA find_data;
+ ScopedFindFileHandle handle(FindFirstFile(find_string.c_str(),
+ &find_data));
+ EXPECT_TRUE(handle.IsValid());
+
+ std::vector<std::wstring> image_files;
+ do {
+ std::wstring image_path = data_dir_;
+ file_util::AppendToPath(&image_path, find_data.cFileName);
+ image_files.push_back(image_path);
+ } while (FindNextFile(handle, &find_data));
+
+ return image_files;
+}
+
+bool ImageDecoderTest::ShouldImageFail(const std::wstring& path) const {
+ static const std::wstring kBadSuffix(L".bad.");
+ return (path.length() > (kBadSuffix.length() + format_.length()) &&
+ !path.compare(path.length() - format_.length() - kBadSuffix.length(),
+ kBadSuffix.length(), kBadSuffix));
+}
+
+void ImageDecoderTest::TestDecoding() const {
+ const std::vector<std::wstring> image_files(GetImageFiles());
+ for (std::vector<std::wstring>::const_iterator i(image_files.begin());
+ i != image_files.end(); ++i) {
+ Vector<char> image_contents;
+ ReadFileToVector(*i, &image_contents);
+
+ scoped_ptr<WebCore::ImageDecoder> decoder(CreateDecoder());
+ RefPtr<WebCore::SharedBuffer> shared_contents(new WebCore::SharedBuffer);
+ shared_contents->append(image_contents.data(),
+ static_cast<int>(image_contents.size()));
+ decoder->setData(shared_contents.get(), true);
+
+ if (ShouldImageFail(*i)) {
+ // We should always get a non-NULL frame buffer, but when the decoder
+ // tries to produce it, it should fail, and the frame buffer shouldn't
+ // complete.
+ WebCore::RGBA32Buffer* const image_buffer =
+ decoder->frameBufferAtIndex(0);
+ ASSERT_NE(static_cast<WebCore::RGBA32Buffer*>(NULL), image_buffer) <<
+ (*i);
+ EXPECT_NE(image_buffer->status(), WebCore::RGBA32Buffer::FrameComplete) <<
+ (*i);
+ EXPECT_TRUE(decoder->failed()) << (*i);
+ continue;
+ }
+
+#ifdef CALCULATE_MD5_SUMS
+ SaveMD5Sum(GetMD5SumPath(*i), decoder->frameBufferAtIndex(0));
+#else
+ VerifyImage(decoder.get(), *i, GetMD5SumPath(*i));
+#endif
+ }
+}
+
+#ifndef CALCULATE_MD5_SUMS
+void ImageDecoderTest::TestChunkedDecoding() const {
+ // Init random number generator with current day, so a failing case will fail
+ // consistently over the course of a whole day.
+ const Time today = Time::Now().LocalMidnight();
+ srand(static_cast<unsigned int>(today.ToInternalValue()));
+
+ const std::vector<std::wstring> image_files(GetImageFiles());
+ for (std::vector<std::wstring>::const_iterator i(image_files.begin());
+ i != image_files.end(); ++i) {
+ if (ShouldImageFail(*i))
+ continue;
+
+ // Read the file and split it at an arbitrary point.
+ Vector<char> image_contents;
+ ReadFileToVector(*i, &image_contents);
+ const int partial_size = static_cast<int>(
+ (static_cast<double>(rand()) / RAND_MAX) * image_contents.size());
+ RefPtr<WebCore::SharedBuffer> partial_contents(new WebCore::SharedBuffer);
+ partial_contents->append(image_contents.data(), partial_size);
+
+ // Make sure the image decoder doesn't fail when we ask for the frame buffer
+ // for this partial image.
+ scoped_ptr<WebCore::ImageDecoder> decoder(CreateDecoder());
+ decoder->setData(partial_contents.get(), false);
+ EXPECT_NE(static_cast<WebCore::RGBA32Buffer*>(NULL),
+ decoder->frameBufferAtIndex(0)) << (*i);
+ EXPECT_FALSE(decoder->failed()) << (*i);
+
+ // Make sure passing the complete image results in successful decoding.
+ partial_contents->append(
+ &image_contents.data()[partial_size],
+ static_cast<int>(image_contents.size() - partial_size));
+ decoder->setData(partial_contents.get(), true);
+ VerifyImage(decoder.get(), *i, GetMD5SumPath(*i));
+ }
+}
+#endif
diff --git a/webkit/tools/test_shell/image_decoder_unittest.h b/webkit/tools/test_shell/image_decoder_unittest.h
new file mode 100644
index 0000000..21a0f71
--- /dev/null
+++ b/webkit/tools/test_shell/image_decoder_unittest.h
@@ -0,0 +1,103 @@
+// 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 <vector>
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "Vector.h"
+#include "ImageDecoder.h"
+
+// If CALCULATE_MD5_SUMS is not defined, then this test decodes a handful of
+// image files and compares their MD5 sums to the stored sums on disk.
+//
+// To recalculate the MD5 sums, uncommment CALCULATE_MD5_SUMS.
+//
+// The image files and corresponding MD5 sums live in the directory
+// chrome/test/data/*_decoder (where "*" is the format being tested).
+//
+// Note: The MD5 sums calculated in this test by little- and big-endian systems
+// will differ, since no endianness correction is done. If we start compiling
+// for big endian machines this should be fixed.
+
+//#define CALCULATE_MD5_SUMS
+
+// Reads the contents of the specified file into the specified vector.
+void ReadFileToVector(const std::wstring& path, Vector<char>* contents);
+
+// Returns the path the decoded data is saved at.
+std::wstring GetMD5SumPath(const std::wstring& path);
+
+#ifdef CALCULATE_MD5_SUMS
+// Saves the MD5 sum to the specified file.
+void SaveMD5Sum(const std::wstring& path, WebCore::RGBA32Buffer* buffer);
+#else
+// Verifies the image. |path| identifies the path the image was loaded from.
+void VerifyImage(WebCore::ImageDecoder* decoder,
+ const std::wstring& path,
+ const std::wstring& md5_sum_path);
+#endif
+
+class ImageDecoderTest : public testing::Test {
+ public:
+ explicit ImageDecoderTest(const std::wstring& format) : format_(format) { }
+
+ protected:
+ virtual void SetUp();
+
+ // Returns the vector of image files for testing.
+ std::vector<std::wstring> GetImageFiles() const;
+
+ // Returns true if the image is bogus and should not be successfully decoded.
+ bool ShouldImageFail(const std::wstring& path) const;
+
+ // Verifies each of the test image files is decoded correctly and matches the
+ // expected state.
+ void TestDecoding() const;
+
+#ifndef CALCULATE_MD5_SUMS
+ // Verifies that decoding still works correctly when the files are split into
+ // pieces at a random point.
+ void TestChunkedDecoding() const;
+#endif
+
+ // Returns the correct type of image decoder for this test.
+ virtual WebCore::ImageDecoder* CreateDecoder() const = 0;
+
+ // The format to be decoded, like "bmp" or "ico".
+ std::wstring format_;
+
+ protected:
+ // Path to the test files.
+ std::wstring data_dir_;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(ImageDecoderTest);
+};
diff --git a/webkit/tools/test_shell/keyboard_unittest.cc b/webkit/tools/test_shell/keyboard_unittest.cc
new file mode 100644
index 0000000..5f19e27
--- /dev/null
+++ b/webkit/tools/test_shell/keyboard_unittest.cc
@@ -0,0 +1,293 @@
+// 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 "config.h"
+
+#pragma warning(push, 0)
+#include "EventNames.h"
+#include "EventTarget.h"
+#include "KeyboardEvent.h"
+#pragma warning(pop)
+
+#include "webkit/glue/editor_client_impl.h"
+#include "webkit/glue/event_conversion.h"
+#include "webkit/glue/webinputevent.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(KeyboardUnitTestKeyDown, TestCtrlReturn) {
+ WebCore::EventNames::init();
+
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 0xD;
+ keyboard_event.key_data = 0xD;
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertNewline");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestCtrlZ) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 'Z';
+ keyboard_event.key_data = 'Z';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "Undo");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestCtrlA) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 'A';
+ keyboard_event.key_data = 'A';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "SelectAll");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestCtrlX) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 'X';
+ keyboard_event.key_data = 'X';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+
+ EXPECT_STREQ(result, "Cut");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestCtrlC) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 'C';
+ keyboard_event.key_data = 'C';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "Copy");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestCtrlV) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 'V';
+ keyboard_event.key_data = 'V';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "Paste");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestEscape) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = VK_ESCAPE;
+ keyboard_event.key_data = VK_ESCAPE;
+ keyboard_event.modifiers = 0;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "Cancel");
+}
+
+TEST(KeyboardUnitTestKeyDown, TestRedo) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = 'Y';
+ keyboard_event.key_data = 'Y';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown);
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "Redo");
+}
+
+
+TEST(KeyboardUnitTestKeyPress, TestInsertTab) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\t';
+ keyboard_event.key_data = '\t';
+ keyboard_event.modifiers = 0;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertTab");
+}
+
+TEST(KeyboardUnitTestKeyPress, TestInsertBackTab) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\t';
+ keyboard_event.key_data = '\t';
+ keyboard_event.modifiers = WebInputEvent::SHIFT_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertBacktab");
+}
+
+TEST(KeyboardUnitTestKeyPress, TestInsertNewline) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\r';
+ keyboard_event.key_data = '\r';
+ keyboard_event.modifiers = 0;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertNewline");
+}
+
+TEST(KeyboardUnitTestKeyPress, TestInsertNewline2) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\r';
+ keyboard_event.key_data = '\r';
+ keyboard_event.modifiers = WebInputEvent::CTRL_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertNewline");
+}
+
+TEST(KeyboardUnitTestKeyPress, TestInsertlinebreak) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\r';
+ keyboard_event.key_data = '\r';
+ keyboard_event.modifiers = WebInputEvent::SHIFT_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertLineBreak");
+}
+
+TEST(KeyboardUnitTestKeyPress, TestInsertNewline3) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\r';
+ keyboard_event.key_data = '\r';
+ keyboard_event.modifiers = WebInputEvent::ALT_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertNewline");
+}
+
+TEST(KeyboardUnitTestKeyPress, TestInsertNewline4) {
+ EditorClientImpl editor_impl(NULL);
+
+ WebKeyboardEvent keyboard_event;
+ keyboard_event.key_code = '\r';
+ keyboard_event.key_data = '\r';
+ keyboard_event.modifiers = WebInputEvent::ALT_KEY | WebInputEvent::SHIFT_KEY;
+ keyboard_event.type = WebInputEvent::KEY_DOWN;
+
+ MakePlatformKeyboardEvent evt(keyboard_event);
+ evt.SetKeyType(WebCore::PlatformKeyboardEvent::Char);
+
+ WebCore::KeyboardEvent keyboardEvent(evt, NULL);
+ const char* result = editor_impl.interpretKeyEvent(&keyboardEvent);
+ EXPECT_STREQ(result, "InsertNewline");
+} \ No newline at end of file
diff --git a/webkit/tools/test_shell/layout_test_controller.cc b/webkit/tools/test_shell/layout_test_controller.cc
new file mode 100644
index 0000000..ff48763
--- /dev/null
+++ b/webkit/tools/test_shell/layout_test_controller.cc
@@ -0,0 +1,595 @@
+// 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.
+
+// This file contains the definition for LayoutTestController.
+
+#include <vector>
+
+#include "webkit/tools/test_shell/layout_test_controller.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/path_service.h"
+#include "webkit/glue/webframe.h"
+#include "webkit/glue/webpreferences.h"
+#include "webkit/glue/webview.h"
+#include "webkit/tools/test_shell/test_shell.h"
+
+using std::string;
+using std::wstring;
+
+namespace {
+
+// Stops the test from running and prints a brief warning to stdout. Called
+// when the timer for loading a layout test expires.
+VOID CALLBACK TestTimeout(HWND hwnd, UINT msg, UINT_PTR timer_id, DWORD ms) {
+ reinterpret_cast<TestShell*>(timer_id)->TestFinished();
+ // Print a warning to be caught by the layout-test script.
+ puts("#TEST_TIMED_OUT\n");
+}
+
+}
+
+
+TestShell* LayoutTestController::shell_ = NULL;
+bool LayoutTestController::dump_as_text_ = false;
+bool LayoutTestController::dump_editing_callbacks_ = false;
+bool LayoutTestController::dump_frame_load_callbacks_ = false;
+bool LayoutTestController::dump_resource_load_callbacks_ = false;
+bool LayoutTestController::dump_back_forward_list_ = false;
+bool LayoutTestController::dump_child_frame_scroll_positions_ = false;
+bool LayoutTestController::dump_child_frames_as_text_ = false;
+bool LayoutTestController::dump_title_changes_ = false;
+bool LayoutTestController::accepts_editing_ = true;
+bool LayoutTestController::wait_until_done_ = false;
+bool LayoutTestController::can_open_windows_ = false;
+bool LayoutTestController::close_remaining_windows_ = true;
+bool LayoutTestController::should_add_file_to_pasteboard_ = false;
+LayoutTestController::WorkQueue LayoutTestController::work_queue_;
+CppVariant LayoutTestController::globalFlag_;
+
+LayoutTestController::LayoutTestController(TestShell* shell) {
+ // Set static shell_ variable since we can't do it in an initializer list.
+ // We also need to be careful not to assign shell_ to new windows which are
+ // temporary.
+ if (NULL == shell_)
+ shell_ = shell;
+
+ // Initialize the map that associates methods of this class with the names
+ // they will use when called by JavaScript. The actual binding of those
+ // names to their methods will be done by calling BindToJavaScript() (defined
+ // by CppBoundClass, the parent to LayoutTestController).
+ BindMethod("dumpAsText", &LayoutTestController::dumpAsText);
+ BindMethod("dumpChildFrameScrollPositions", &LayoutTestController::dumpChildFrameScrollPositions);
+ BindMethod("dumpChildFramesAsText", &LayoutTestController::dumpChildFramesAsText);
+ BindMethod("dumpEditingCallbacks", &LayoutTestController::dumpEditingCallbacks);
+ BindMethod("dumpBackForwardList", &LayoutTestController::dumpBackForwardList);
+ BindMethod("dumpFrameLoadCallbacks", &LayoutTestController::dumpFrameLoadCallbacks);
+ BindMethod("dumpResourceLoadCallbacks", &LayoutTestController::dumpResourceLoadCallbacks);
+ BindMethod("dumpTitleChanges", &LayoutTestController::dumpTitleChanges);
+ BindMethod("setAcceptsEditing", &LayoutTestController::setAcceptsEditing);
+ BindMethod("waitUntilDone", &LayoutTestController::waitUntilDone);
+ BindMethod("notifyDone", &LayoutTestController::notifyDone);
+ BindMethod("queueReload", &LayoutTestController::queueReload);
+ BindMethod("queueScript", &LayoutTestController::queueScript);
+ BindMethod("queueLoad", &LayoutTestController::queueLoad);
+ BindMethod("queueBackNavigation", &LayoutTestController::queueBackNavigation);
+ BindMethod("queueForwardNavigation", &LayoutTestController::queueForwardNavigation);
+ BindMethod("windowCount", &LayoutTestController::windowCount);
+ BindMethod("setCanOpenWindows", &LayoutTestController::setCanOpenWindows);
+ BindMethod("setCloseRemainingWindowsWhenComplete", &LayoutTestController::setCloseRemainingWindowsWhenComplete);
+ BindMethod("objCIdentityIsEqual", &LayoutTestController::objCIdentityIsEqual);
+ BindMethod("setWindowIsKey", &LayoutTestController::setWindowIsKey);
+ BindMethod("setTabKeyCyclesThroughElements", &LayoutTestController::setTabKeyCyclesThroughElements);
+ BindMethod("setUserStyleSheetLocation", &LayoutTestController::setUserStyleSheetLocation);
+ BindMethod("setUserStyleSheetEnabled", &LayoutTestController::setUserStyleSheetEnabled);
+ BindMethod("pathToLocalResource", &LayoutTestController::pathToLocalResource);
+ BindMethod("addFileToPasteboardOnDrag", &LayoutTestController::addFileToPasteboardOnDrag);
+ BindMethod("execCommand", &LayoutTestController::execCommand);
+
+ // The following are stubs.
+ BindMethod("dumpAsWebArchive", &LayoutTestController::dumpAsWebArchive);
+ BindMethod("setMainFrameIsFirstResponder", &LayoutTestController::setMainFrameIsFirstResponder);
+ BindMethod("dumpSelectionRect", &LayoutTestController::dumpSelectionRect);
+ BindMethod("display", &LayoutTestController::display);
+ BindMethod("testRepaint", &LayoutTestController::testRepaint);
+ BindMethod("repaintSweepHorizontally", &LayoutTestController::repaintSweepHorizontally);
+ BindMethod("clearBackForwardList", &LayoutTestController::clearBackForwardList);
+ BindMethod("keepWebHistory", &LayoutTestController::keepWebHistory);
+ BindMethod("storeWebScriptObject", &LayoutTestController::storeWebScriptObject);
+ BindMethod("accessStoredWebScriptObject", &LayoutTestController::accessStoredWebScriptObject);
+ BindMethod("objCClassNameOf", &LayoutTestController::objCClassNameOf);
+ BindMethod("addDisallowedURL", &LayoutTestController::addDisallowedURL);
+ BindMethod("setCallCloseOnWebViews", &LayoutTestController::setCallCloseOnWebViews);
+ BindMethod("setPrivateBrowsingEnabled", &LayoutTestController::setPrivateBrowsingEnabled);
+ BindMethod("setUseDashboardCompatibilityMode", &LayoutTestController::setUseDashboardCompatibilityMode);
+ BindMethod("setCustomPolicyDelegate", &LayoutTestController::setCustomPolicyDelegate);
+
+ // This typo (missing 'i') is intentional as it matches the typo in the layout test
+ // see: LayoutTests/fast/canvas/fill-stroke-clip-reset-path.html.
+ // If Apple ever fixes this, we'll need to update it.
+ BindMethod("setUseDashboardCompatiblityMode", &LayoutTestController::setUseDashboardCompatibilityMode);
+
+ // The fallback method is called when an unknown method is invoked.
+ BindFallbackMethod(&LayoutTestController::fallbackMethod);
+
+ // Shared property used by a number of layout tests in
+ // LayoutTests\http\tests\security\dataURL.
+ BindProperty("globalFlag", &globalFlag_);
+}
+
+LayoutTestController::WorkQueue::~WorkQueue() {
+ Reset();
+}
+
+void LayoutTestController::WorkQueue::ProcessWork() {
+ // Quit doing work once a load is in progress.
+ while (!queue_.empty() && !shell_->delegate()->top_loading_frame()) {
+ queue_.front()->Run(shell_);
+ delete queue_.front();
+ queue_.pop();
+ }
+
+ if (!shell_->delegate()->top_loading_frame() && !wait_until_done_) {
+ shell_->TestFinished();
+ }
+}
+
+void LayoutTestController::WorkQueue::Reset() {
+ frozen_ = false;
+ while (!queue_.empty()) {
+ delete queue_.front();
+ queue_.pop();
+ }
+}
+
+void LayoutTestController::WorkQueue::AddWork(WorkItem* work) {
+ if (frozen_) {
+ delete work;
+ return;
+ }
+ queue_.push(work);
+}
+
+void LayoutTestController::dumpAsText(const CppArgumentList& args,
+ CppVariant* result) {
+ dump_as_text_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpEditingCallbacks(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_editing_callbacks_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpBackForwardList(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_back_forward_list_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpFrameLoadCallbacks(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_frame_load_callbacks_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpResourceLoadCallbacks(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_resource_load_callbacks_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpChildFrameScrollPositions(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_child_frame_scroll_positions_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpChildFramesAsText(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_child_frames_as_text_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::dumpTitleChanges(
+ const CppArgumentList& args, CppVariant* result) {
+ dump_title_changes_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::setAcceptsEditing(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool())
+ accepts_editing_ = args[0].value.boolValue;
+ result->SetNull();
+}
+
+void LayoutTestController::waitUntilDone(
+ const CppArgumentList& args, CppVariant* result) {
+ // Set a timer in case something hangs. We use a custom timer rather than
+ // the one managed by the message loop so we can kill it when the load
+ // finishes successfully.
+ if (!::IsDebuggerPresent()) {
+ UINT_PTR timer_id = reinterpret_cast<UINT_PTR>(shell_);
+ SetTimer(shell_->mainWnd(), timer_id, shell_->GetFileTestTimeout(),
+ &TestTimeout);
+ }
+ wait_until_done_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::notifyDone(
+ const CppArgumentList& args, CppVariant* result) {
+ if (!shell_->interactive() && wait_until_done_ &&
+ !shell_->delegate()->top_loading_frame() && work_queue_.empty()) {
+ shell_->TestFinished();
+ }
+ wait_until_done_ = false;
+ result->SetNull();
+}
+
+class WorkItemBackForward : public LayoutTestController::WorkItem {
+ public:
+ WorkItemBackForward(int distance) : distance_(distance) {}
+ void Run(TestShell* shell) {
+ shell->GoBackOrForward(distance_);
+ }
+ private:
+ int distance_;
+};
+
+void LayoutTestController::queueBackNavigation(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isNumber())
+ work_queue_.AddWork(new WorkItemBackForward(-args[0].ToInt32()));
+ result->SetNull();
+}
+
+void LayoutTestController::queueForwardNavigation(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isNumber())
+ work_queue_.AddWork(new WorkItemBackForward(args[0].ToInt32()));
+ result->SetNull();
+}
+
+class WorkItemReload : public LayoutTestController::WorkItem {
+ public:
+ void Run(TestShell* shell) {
+ shell->Reload();
+ }
+};
+
+void LayoutTestController::queueReload(
+ const CppArgumentList& args, CppVariant* result) {
+ work_queue_.AddWork(new WorkItemReload);
+ result->SetNull();
+}
+
+class WorkItemScript : public LayoutTestController::WorkItem {
+ public:
+ WorkItemScript(const string& script) : script_(script) {}
+ void Run(TestShell* shell) {
+ wstring url = L"javascript:" + UTF8ToWide(script_);
+ shell->LoadURL(url.c_str());
+ }
+ private:
+ string script_;
+};
+
+void LayoutTestController::queueScript(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isString())
+ work_queue_.AddWork(new WorkItemScript(args[0].ToString()));
+ result->SetNull();
+}
+
+class WorkItemLoad : public LayoutTestController::WorkItem {
+ public:
+ WorkItemLoad(const GURL& url, const string& target)
+ : url_(url), target_(target) {}
+ void Run(TestShell* shell) {
+ shell->LoadURLForFrame(UTF8ToWide(url_.spec()).c_str(),
+ UTF8ToWide(target_).c_str());
+ }
+ private:
+ GURL url_;
+ string target_;
+};
+
+void LayoutTestController::queueLoad(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isString()) {
+ GURL current_url = shell_->webView()->GetMainFrame()->GetURL();
+ GURL full_url = current_url.Resolve(args[0].ToString());
+
+ string target = "";
+ if (args.size() > 1 && args[1].isString())
+ target = args[1].ToString();
+
+ work_queue_.AddWork(new WorkItemLoad(full_url, target));
+ }
+ result->SetNull();
+}
+
+void LayoutTestController::objCIdentityIsEqual(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() < 2) {
+ // This is the best we can do to return an error.
+ result->SetNull();
+ return;
+ }
+ result->Set(args[0].isEqual(args[1]));
+}
+
+void LayoutTestController::Reset() {
+ if (shell_) {
+ shell_->webView()->MakeTextStandardSize();
+ shell_->webView()->SetTabKeyCyclesThroughElements(true);
+ }
+ dump_as_text_ = false;
+ dump_editing_callbacks_ = false;
+ dump_frame_load_callbacks_ = false;
+ dump_resource_load_callbacks_ = false;
+ dump_back_forward_list_ = false;
+ dump_child_frame_scroll_positions_ = false;
+ dump_child_frames_as_text_ = false;
+ dump_title_changes_ = false;
+ accepts_editing_ = true;
+ wait_until_done_ = false;
+ can_open_windows_ = false;
+ should_add_file_to_pasteboard_ = false;
+ globalFlag_.Set(false);
+
+ if (close_remaining_windows_) {
+ // Iterate through the window list and close everything except the original
+ // shell. We don't want to delete elements as we're iterating, so we copy
+ // to a temp vector first.
+ WindowList* windows = TestShell::windowList();
+ std::vector<HWND> windows_to_delete;
+ for (WindowList::iterator i = windows->begin(); i != windows->end(); ++i) {
+ if (*i != shell_->mainWnd())
+ windows_to_delete.push_back(*i);
+ }
+ DCHECK(windows_to_delete.size() + 1 == windows->size());
+ for (size_t i = 0; i < windows_to_delete.size(); ++i) {
+ DestroyWindow(windows_to_delete[i]);
+ }
+ DCHECK(windows->size() == 1);
+ } else {
+ // Reset the value
+ close_remaining_windows_ = true;
+ }
+ work_queue_.Reset();
+}
+
+void LayoutTestController::LocationChangeDone() {
+ // no more new work after the first complete load.
+ work_queue_.set_frozen(true);
+
+ if (!wait_until_done_)
+ work_queue_.ProcessWork();
+}
+
+void LayoutTestController::setCanOpenWindows(
+ const CppArgumentList& args, CppVariant* result) {
+ can_open_windows_ = true;
+ result->SetNull();
+}
+
+void LayoutTestController::setTabKeyCyclesThroughElements(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool()) {
+ shell_->webView()->SetTabKeyCyclesThroughElements(args[0].ToBoolean());
+ }
+ result->SetNull();
+}
+
+void LayoutTestController::windowCount(
+ const CppArgumentList& args, CppVariant* result) {
+ int num_windows = static_cast<int>(TestShell::windowList()->size());
+ result->Set(num_windows);
+}
+
+void LayoutTestController::setCloseRemainingWindowsWhenComplete(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool()) {
+ close_remaining_windows_ = args[0].value.boolValue;
+ }
+ result->SetNull();
+}
+
+void LayoutTestController::setWindowIsKey(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool()) {
+ shell_->SetFocus(shell_->webViewHost(), args[0].value.boolValue);
+ }
+ result->SetNull();
+}
+
+void LayoutTestController::setUserStyleSheetEnabled(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool()) {
+ shell_->delegate()->SetUserStyleSheetEnabled(args[0].value.boolValue);
+ }
+
+ result->SetNull();
+}
+
+void LayoutTestController::setUserStyleSheetLocation(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isString()) {
+ GURL location(TestShell::RewriteLocalUrl(args[0].ToString()));
+ shell_->delegate()->SetUserStyleSheetLocation(location);
+ }
+
+ result->SetNull();
+}
+
+void LayoutTestController::execCommand(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isString()) {
+ std::string command = args[0].ToString();
+ std::string value("");
+
+ // Ignore the second parameter (which is userInterface)
+ // since this command emulates a manual action.
+ if (args.size() >= 3 && args[2].isString())
+ value = args[2].ToString();
+
+ // Note: webkit's version does not return the boolean, so neither do we.
+ shell_->webView()->GetFocusedFrame()->ExecuteCoreCommandByName(command,
+ value);
+ }
+ result->SetNull();
+}
+
+void LayoutTestController::setUseDashboardCompatibilityMode(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool()) {
+ shell_->delegate()->SetDashboardCompatibilityMode(args[0].value.boolValue);
+ }
+
+ result->SetNull();
+}
+
+void LayoutTestController::setCustomPolicyDelegate(
+ const CppArgumentList& args, CppVariant* result) {
+ if (args.size() > 0 && args[0].isBool()) {
+ shell_->delegate()->SetCustomPolicyDelegate(args[0].value.boolValue);
+ }
+
+ result->SetNull();
+}
+
+void LayoutTestController::pathToLocalResource(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+ if (args.size() <= 0 || !args[0].isString())
+ return;
+
+ std::string url = args[0].ToString();
+ // Some layout tests use file://// which we resolve as a UNC path. Normalize
+ // them to just file:///.
+ while (StartsWithASCII(url, "file:////", false)) {
+ url = url.substr(0, 8) + url.substr(9);
+ }
+ GURL location(TestShell::RewriteLocalUrl(url));
+ result->Set(location.spec());
+}
+
+void LayoutTestController::addFileToPasteboardOnDrag(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+ should_add_file_to_pasteboard_ = true;
+}
+
+//
+// Unimplemented stubs
+//
+
+void LayoutTestController::dumpAsWebArchive(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::setMainFrameIsFirstResponder(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::dumpSelectionRect(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::display(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::testRepaint(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::repaintSweepHorizontally(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::clearBackForwardList(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::keepWebHistory(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::storeWebScriptObject(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::accessStoredWebScriptObject(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::objCClassNameOf(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+void LayoutTestController::addDisallowedURL(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+void LayoutTestController::setCallCloseOnWebViews(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+void LayoutTestController::setPrivateBrowsingEnabled(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+}
+
+void LayoutTestController::fallbackMethod(
+ const CppArgumentList& args, CppVariant* result) {
+ std::wstring message(L"JavaScript ERROR: unknown method called on LayoutTestController");
+ if (shell_->interactive()) {
+ logging::LogMessage("CONSOLE:", 0).stream() << message;
+ } else {
+ printf("CONSOLE MESSAGE: %S\n", message.c_str());
+ }
+ result->SetNull();
+}
diff --git a/webkit/tools/test_shell/layout_test_controller.h b/webkit/tools/test_shell/layout_test_controller.h
new file mode 100644
index 0000000..5924a6e
--- /dev/null
+++ b/webkit/tools/test_shell/layout_test_controller.h
@@ -0,0 +1,306 @@
+// 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.
+
+/*
+ LayoutTestController class:
+ Bound to a JavaScript window.layoutTestController object using the
+ CppBoundClass::BindToJavascript(), this allows layout tests that are run in
+ the test_shell (or, in principle, any web page loaded into a client app built
+ with this class) to control various aspects of how the tests are run and what
+ sort of output they produce.
+*/
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_LAYOUT_TEST_CONTROLLER_H__
+#define WEBKIT_TOOLS_TEST_SHELL_LAYOUT_TEST_CONTROLLER_H__
+
+#include <queue>
+
+#include "webkit/glue/cpp_bound_class.h"
+
+class TestShell;
+
+class LayoutTestController : public CppBoundClass {
+ public:
+ // Builds the property and method lists needed to bind this class to a JS
+ // object.
+ LayoutTestController(TestShell* shell);
+
+ // This function sets a flag that tells the test_shell to dump pages as
+ // plain text, rather than as a text representation of the renderer's state.
+ // It takes no arguments, and ignores any that may be present.
+ void dumpAsText(const CppArgumentList& args, CppVariant* result);
+
+ // This function sets a flag that tells the test_shell to print a line of
+ // descriptive test for each editing command. It takes no arguments, and
+ // ignores any that may be present.
+ void dumpEditingCallbacks(const CppArgumentList& args, CppVariant* result);
+
+ // This function sets a flag that tells the test_shell to print a line of
+ // descriptive test for each frame load callback. It takes no arguments, and
+ // ignores any that may be present.
+ void dumpFrameLoadCallbacks(const CppArgumentList& args, CppVariant* result);
+
+ // This function sets a flag that tells the test_shell to print out a text
+ // representation of the back/forward list. It ignores all args.
+ void dumpBackForwardList(const CppArgumentList& args, CppVariant* result);
+
+ // This function sets a flag that tells the test_shell to print out the
+ // scroll offsets of the child frames. It ignores all args.
+ void dumpChildFrameScrollPositions(const CppArgumentList& args, CppVariant* result);
+
+ // This function sets a flag that tells the test_shell to recursively
+ // dump all frames as plain text if the dumpAsText flag is set.
+ // It takes no arguments, and ignores any that may be present.
+ void dumpChildFramesAsText(const CppArgumentList& args, CppVariant* result);
+
+ // When called with a boolean argument, this sets a flag that controls
+ // whether content-editable elements accept editing focus when an editing
+ // attempt is made. It ignores any additional arguments.
+ void setAcceptsEditing(const CppArgumentList& args, CppVariant* result);
+
+ // Functions for dealing with windows. By default we block all new windows.
+ void windowCount(const CppArgumentList& args, CppVariant* result);
+ void setCanOpenWindows(const CppArgumentList& args, CppVariant* result);
+ void setCloseRemainingWindowsWhenComplete(const CppArgumentList& args, CppVariant* result);
+
+ // By default, tests end when page load is complete. These methods are used
+ // to delay the completion of the test until notifyDone is called.
+ void waitUntilDone(const CppArgumentList& args, CppVariant* result);
+ void notifyDone(const CppArgumentList& args, CppVariant* result);
+
+ // Methods for adding actions to the work queue. Used in conjunction with
+ // waitUntilDone/notifyDone above.
+ void queueBackNavigation(const CppArgumentList& args, CppVariant* result);
+ void queueForwardNavigation(const CppArgumentList& args, CppVariant* result);
+ void queueReload(const CppArgumentList& args, CppVariant* result);
+ void queueScript(const CppArgumentList& args, CppVariant* result);
+ void queueLoad(const CppArgumentList& args, CppVariant* result);
+
+ // Although this is named "objC" to match the Mac version, it actually tests
+ // the identity of its two arguments in C++.
+ void objCIdentityIsEqual(const CppArgumentList& args, CppVariant* result);
+
+ // Gives focus to the window.
+ void setWindowIsKey(const CppArgumentList& args, CppVariant* result);
+
+ // Method that controls whether pressing Tab key cycles through page elements
+ // or inserts a '\t' char in text area
+ void setTabKeyCyclesThroughElements(const CppArgumentList& args, CppVariant* result);
+
+ // Passes through to WebPreferences which allows the user to have a custom
+ // style sheet.
+ void setUserStyleSheetEnabled(const CppArgumentList& args, CppVariant* result);
+ void setUserStyleSheetLocation(const CppArgumentList& args, CppVariant* result);
+
+ // Puts Webkit in "dashboard compatibility mode", which is used in obscure
+ // Mac-only circumstances. It's not really necessary, and will most likely
+ // never be used by Chrome, but some layout tests depend on its presence.
+ void setUseDashboardCompatibilityMode(const CppArgumentList& args, CppVariant* result);
+
+ // Causes navigation actions just printout the intended navigation instead
+ // of taking you to the page. This is used for cases like mailto, where you
+ // don't actually want to open the mail program.
+ void setCustomPolicyDelegate(const CppArgumentList& args, CppVariant* result);
+
+ // Converts a URL starting with file:///tmp/ to the local mapping.
+ void pathToLocalResource(const CppArgumentList& args, CppVariant* result);
+
+ // Sets a bool such that when a drag is started, we fill the drag clipboard
+ // with a fake file object.
+ void addFileToPasteboardOnDrag(const CppArgumentList& args, CppVariant* result);
+
+ // Executes an internal command (superset of document.execCommand() commands)
+ void execCommand(const CppArgumentList& args, CppVariant* result);;
+
+ // The following are only stubs. TODO(pamg): Implement any of these that
+ // are needed to pass the layout tests.
+ void dumpAsWebArchive(const CppArgumentList& args, CppVariant* result);
+ void dumpTitleChanges(const CppArgumentList& args, CppVariant* result);
+ void dumpResourceLoadCallbacks(const CppArgumentList& args, CppVariant* result);
+ void setMainFrameIsFirstResponder(const CppArgumentList& args, CppVariant* result);
+ void dumpSelectionRect(const CppArgumentList& args, CppVariant* result);
+ void display(const CppArgumentList& args, CppVariant* result);
+ void testRepaint(const CppArgumentList& args, CppVariant* result);
+ void repaintSweepHorizontally(const CppArgumentList& args, CppVariant* result);
+ void clearBackForwardList(const CppArgumentList& args, CppVariant* result);
+ void keepWebHistory(const CppArgumentList& args, CppVariant* result);
+ void storeWebScriptObject(const CppArgumentList& args, CppVariant* result);
+ void accessStoredWebScriptObject(const CppArgumentList& args, CppVariant* result);
+ void objCClassNameOf(const CppArgumentList& args, CppVariant* result);
+ void addDisallowedURL(const CppArgumentList& args, CppVariant* result);
+ void setCallCloseOnWebViews(const CppArgumentList& args, CppVariant* result);
+ void setPrivateBrowsingEnabled(const CppArgumentList& args, CppVariant* result);
+
+ // The fallback method is called when a nonexistent method is called on
+ // the layout test controller object.
+ // It is usefull to catch typos in the JavaScript code (a few layout tests
+ // do have typos in them) and it allows the script to continue running in
+ // that case (as the Mac does).
+ void fallbackMethod(const CppArgumentList& args, CppVariant* result);
+
+ public:
+ // The following methods are not exposed to JavaScript.
+ void SetWorkQueueFrozen(bool frozen) { work_queue_.set_frozen(frozen); }
+
+ bool ShouldDumpAsText() { return dump_as_text_; }
+ bool ShouldDumpEditingCallbacks() { return dump_editing_callbacks_; }
+ bool ShouldDumpFrameLoadCallbacks() { return dump_frame_load_callbacks_; }
+ void SetShouldDumpFrameLoadCallbacks(bool value) {
+ dump_frame_load_callbacks_ = value;
+ }
+ bool ShouldDumpResourceLoadCallbacks() {
+ return dump_resource_load_callbacks_;
+ }
+ bool ShouldDumpBackForwardList() { return dump_back_forward_list_; }
+ bool ShouldDumpTitleChanges() { return dump_title_changes_; }
+ bool ShouldDumpChildFrameScrollPositions() {
+ return dump_child_frame_scroll_positions_;
+ }
+ bool ShouldDumpChildFramesAsText() {
+ return dump_child_frames_as_text_;
+ }
+ bool AcceptsEditing() { return accepts_editing_; }
+ bool CanOpenWindows() { return can_open_windows_; }
+ bool ShouldAddFileToPasteboard() { return should_add_file_to_pasteboard_; }
+
+ // If we have queued events, fire them and then dump the test output.
+ // Otherwise, just dump the test output.
+ // Used by the layout tests for tests that span more than a single load.
+ // This is called by the test webview delegate when a page finishes
+ // loading (successful or not). Once all the work has been processed, we
+ // dump the test output.
+ void ProcessWork() { work_queue_.ProcessWork(); }
+
+ // Called by the webview delegate when the toplevel frame load is done.
+ void LocationChangeDone();
+
+ // Reinitializes all static values. The Reset() method should be called
+ // before the start of each test (currently from
+ // TestShell::RunFileTest).
+ void Reset();
+
+ // A single item in the work queue.
+ class WorkItem {
+ public:
+ virtual ~WorkItem() {};
+ virtual void Run(TestShell* shell) = 0;
+ };
+
+ // Used to clear the value of shell_ from test_shell_tests.
+ static void ClearShell() { shell_ = NULL; }
+
+ private:
+ friend class WorkItem;
+
+ // Helper class for managing events queued by methods like queueLoad or
+ // queueScript.
+ class WorkQueue {
+ public:
+ virtual ~WorkQueue();
+ void ProcessWork();
+
+ // Reset the state of the class between tests.
+ void Reset();
+
+ void AddWork(WorkItem* work);
+
+ void set_frozen(bool frozen) { frozen_ = frozen; }
+ bool empty() { return queue_.empty(); }
+
+ private:
+ std::queue<WorkItem*> queue_;
+ bool frozen_;
+ };
+
+ // Non-owning pointer. The LayoutTestController is owned by the host.
+ static TestShell* shell_;
+
+ // If true, the test_shell will produce a plain text dump rather than a
+ // text representation of the renderer.
+ static bool dump_as_text_;
+
+ // If true, the test_shell will write a descriptive line for each editing
+ // command.
+ static bool dump_editing_callbacks_;
+
+ // If true, the test_shell will output a descriptive line for each frame
+ // load callback.
+ static bool dump_frame_load_callbacks_;
+
+ // If true, the test_shell will output a descriptive line for each resource
+ // load callback.
+ static bool dump_resource_load_callbacks_;
+
+ // If true, the test_shell will produce a dump of the back forward list as
+ // well.
+ static bool dump_back_forward_list_;
+
+ // If true, the test_shell will print out the child frame scroll offsets as
+ // well.
+ static bool dump_child_frame_scroll_positions_;
+
+ // If true and if dump_as_text_ is true, the test_shell will recursively
+ // dump all frames as plain text.
+ static bool dump_child_frames_as_text_;
+
+ // If true, output a message when the page title is changed.
+ static bool dump_title_changes_;
+
+ // If true, the element will be treated as editable. This value is returned
+ // from various editing callbacks that are called just before edit operations
+ // are allowed.
+ static bool accepts_editing_;
+
+ // If true, new windows can be opened via javascript or by plugins. By
+ // default, set to false and can be toggled to true using
+ // setCanOpenWindows().
+ static bool can_open_windows_;
+
+ // When reset is called, go through and close all but the main test shell
+ // window. By default, set to true but toggled to false using
+ // setCloseRemainingWindowsWhenComplete().
+ static bool close_remaining_windows_;
+
+ // If true and a drag starts, adds a file to the drag&drop clipboard.
+ static bool should_add_file_to_pasteboard_;
+
+ // If true, don't dump output until notifyDone is called.
+ static bool wait_until_done_;
+
+ // To prevent infinite loops, only the first page of a test can add to a
+ // work queue (since we may well come back to that same page).
+ static bool work_queue_frozen_;
+
+
+ static WorkQueue work_queue_;
+
+ static CppVariant globalFlag_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_LAYOUT_TEST_CONTROLLER_H__
diff --git a/webkit/tools/test_shell/layout_test_controller_unittest.cc b/webkit/tools/test_shell/layout_test_controller_unittest.cc
new file mode 100644
index 0000000..4158b32
--- /dev/null
+++ b/webkit/tools/test_shell/layout_test_controller_unittest.cc
@@ -0,0 +1,114 @@
+// 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 <map>
+
+#include "webkit/tools/test_shell/layout_test_controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+// A subclass of LayoutTestController, with additional accessors.
+class TestLayoutTestController : public LayoutTestController {
+ public:
+ TestLayoutTestController() : LayoutTestController(NULL) {
+ }
+
+ size_t MethodCount() {
+ return methods_.size();
+ }
+
+ void Reset() {
+ LayoutTestController::Reset();
+ }
+};
+
+class LayoutTestControllerTest : public testing::Test {
+};
+} // namespace
+
+TEST(LayoutTestControllerTest, MethodMapIsInitialized) {
+ const char* test_methods[] = {
+ "dumpAsText",
+ "waitUntilDone",
+ "notifyDone",
+ "dumpEditingCallbacks",
+ "queueLoad",
+ "windowCount",
+ NULL
+ };
+ TestLayoutTestController controller;
+ for (const char** method = test_methods; *method; ++method) {
+ EXPECT_TRUE(controller.IsMethodRegistered(*method));
+ }
+
+ // One more case, to test our test.
+ EXPECT_FALSE(controller.IsMethodRegistered("nonexistent_method"));
+}
+
+TEST(LayoutTestControllerTest, DumpAsTextSetAndCleared) {
+ TestLayoutTestController controller;
+ CppArgumentList empty_args;
+ CppVariant ignored_result;
+ EXPECT_FALSE(controller.ShouldDumpAsText());
+ controller.dumpAsText(empty_args, &ignored_result);
+ EXPECT_TRUE(ignored_result.isNull());
+ EXPECT_TRUE(controller.ShouldDumpAsText());
+
+ // Don't worry about closing remaining windows when we call reset.
+ CppArgumentList args;
+ CppVariant bool_false;
+ bool_false.Set(false);
+ args.push_back(bool_false);
+ CppVariant result;
+ controller.setCloseRemainingWindowsWhenComplete(args, &result);
+
+ controller.Reset();
+ EXPECT_FALSE(controller.ShouldDumpAsText());
+}
+
+TEST(LayoutTestControllerTest, DumpChildFramesAsTextSetAndCleared) {
+ TestLayoutTestController controller;
+ CppArgumentList empty_args;
+ CppVariant ignored_result;
+ EXPECT_FALSE(controller.ShouldDumpChildFramesAsText());
+ controller.dumpChildFramesAsText(empty_args, &ignored_result);
+ EXPECT_TRUE(ignored_result.isNull());
+ EXPECT_TRUE(controller.ShouldDumpChildFramesAsText());
+
+ // Don't worry about closing remaining windows when we call reset.
+ CppArgumentList args;
+ CppVariant bool_false;
+ bool_false.Set(false);
+ args.push_back(bool_false);
+ CppVariant result;
+ controller.setCloseRemainingWindowsWhenComplete(args, &result);
+
+ controller.Reset();
+ EXPECT_FALSE(controller.ShouldDumpChildFramesAsText());
+}
diff --git a/webkit/tools/test_shell/node_leak_test.cc b/webkit/tools/test_shell/node_leak_test.cc
new file mode 100644
index 0000000..92ba447
--- /dev/null
+++ b/webkit/tools/test_shell/node_leak_test.cc
@@ -0,0 +1,113 @@
+// 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 "base/command_line.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "net/http/http_cache.h"
+#include "net/url_request/url_request_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_shell.h"
+#include "webkit/tools/test_shell/test_shell_request_context.h"
+#include "webkit/tools/test_shell/test_shell_switches.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+namespace {
+
+const wchar_t kTestUrlSwitch[] = L"test-url";
+
+// A test to help determine if any nodes have been leaked as a result of
+// visiting a given URL. If enabled in WebCore, the number of leaked nodes
+// can be printed upon termination. This is only enabled in debug builds, so
+// it only makes sense to run this using a debug build.
+//
+// It will load a URL, visit about:blank, and then perform garbage collection.
+// The number of remaining (potentially leaked) nodes will be printed on exit.
+class NodeLeakTest : public TestShellTest {
+ public:
+ virtual void SetUp() {
+ CommandLine parsed_command_line;
+
+ std::wstring js_flags =
+ parsed_command_line.GetSwitchValue(test_shell::kJavaScriptFlags);
+ CommandLine::AppendSwitch(&js_flags, L"expose-gc");
+ webkit_glue::SetJavaScriptFlags(js_flags);
+
+ std::wstring cache_path =
+ parsed_command_line.GetSwitchValue(test_shell::kCacheDir);
+ if (cache_path.empty()) {
+ PathService::Get(base::DIR_EXE, &cache_path);
+ file_util::AppendToPath(&cache_path, L"cache");
+ }
+
+ if (parsed_command_line.HasSwitch(test_shell::kTestShellTimeOut)) {
+ const std::wstring timeout_str = parsed_command_line.GetSwitchValue(
+ test_shell::kTestShellTimeOut);
+ int timeout_ms = static_cast<int>(StringToInt64(timeout_str.c_str()));
+ if (timeout_ms > 0)
+ TestShell::SetFileTestTimeout(timeout_ms);
+ }
+
+ // Optionally use playback mode (for instance if running automated tests).
+ net::HttpCache::Mode mode =
+ parsed_command_line.HasSwitch(test_shell::kPlaybackMode) ?
+ net::HttpCache::PLAYBACK : net::HttpCache::NORMAL;
+ SimpleResourceLoaderBridge::Init(
+ new TestShellRequestContext(cache_path, mode));
+
+ TestShellTest::SetUp();
+ }
+
+ virtual void TearDown() {
+ TestShellTest::TearDown();
+
+ SimpleResourceLoaderBridge::Shutdown();
+ }
+
+ void NavigateToURL(const std::wstring& test_url) {
+ test_shell_->LoadURL(test_url.c_str());
+ test_shell_->WaitTestFinished();
+
+ // Depends on TestShellTests::TearDown to load blank page and
+ // the TestShell destructor to call garbage collection.
+ }
+};
+
+} // namespace
+
+TEST_F(NodeLeakTest, TestURL) {
+ CommandLine parsed_command_line;
+
+ if (parsed_command_line.HasSwitch(kTestUrlSwitch)) {
+ NavigateToURL(parsed_command_line.GetSwitchValue(kTestUrlSwitch).c_str());
+ }
+}
diff --git a/webkit/tools/test_shell/plugin_tests.cc b/webkit/tools/test_shell/plugin_tests.cc
new file mode 100644
index 0000000..bfde855
--- /dev/null
+++ b/webkit/tools/test_shell/plugin_tests.cc
@@ -0,0 +1,147 @@
+// 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 <string>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "net/base/cookie_monster.h"
+#include "net/base/net_util.h"
+#include "net/http/http_cache.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_shell.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+static const char kTestCompleteCookie[] = "status";
+static const char kTestCompleteSuccess[] = "OK";
+
+// Provides functionality for creating plugin tests.
+class PluginTest : public TestShellTest {
+ // A basic URLRequestContext that only provides an in-memory cookie store.
+ class RequestContext : public TestURLRequestContext {
+ public:
+ RequestContext() {
+ cookie_store_ = new CookieMonster();
+ }
+
+ virtual ~RequestContext() {
+ delete cookie_store_;
+ }
+ };
+
+ public:
+ PluginTest() {}
+ ~PluginTest() {}
+
+ void NavigateToURL(const std::wstring& test_url) {
+ ASSERT_TRUE(file_util::PathExists(test_url));
+ test_url_ = net_util::FilePathToFileURL(test_url);
+ test_shell_->LoadURL(test_url.c_str());
+ }
+
+ // Waits for the test case to finish.
+ // ASSERTS if there are test failures.
+ void WaitForFinish(const std::string &name, const std::string &id) {
+ test_shell_->WaitTestFinished();
+
+ std::string cookies =
+ request_context_->cookie_store()->GetCookies(test_url_);
+ EXPECT_FALSE(cookies.empty());
+
+ std::string cookieName = name;
+ cookieName.append(".");
+ cookieName.append(id);
+ cookieName.append(".");
+ cookieName.append(kTestCompleteCookie);
+ cookieName.append("=");
+ std::string::size_type idx = cookies.find(cookieName);
+ std::string cookie;
+ if (idx != std::string::npos) {
+ cookies.erase(0, idx + cookieName.length());
+ cookie = cookies.substr(0, cookies.find(";"));
+ }
+
+ EXPECT_EQ(kTestCompleteSuccess, cookie);
+ }
+
+ protected:
+ virtual void SetUp() {
+ // We need to copy our test-plugin into the plugins directory so that
+ // the test can load it.
+ std::wstring current_directory;
+ PathService::Get(base::DIR_EXE, &current_directory);
+ std::wstring plugin_src = current_directory + L"\\npapi_test_plugin.dll";
+ ASSERT_TRUE(file_util::PathExists(plugin_src));
+
+ plugin_dll_path_ = current_directory + L"\\plugins";
+ ::CreateDirectory(plugin_dll_path_.c_str(), NULL);
+
+ plugin_dll_path_ += L"\\npapi_test_plugin.dll";
+ ASSERT_TRUE(CopyFile(plugin_src.c_str(), plugin_dll_path_.c_str(), FALSE));
+
+ // The plugin list has to be refreshed to ensure that the npapi_test_plugin
+ // is loaded by webkit.
+ std::vector<WebPluginInfo> plugin_list;
+ bool refresh = true;
+ NPAPI::PluginList::Singleton()->GetPlugins(refresh, &plugin_list);
+
+ TestShellTest::SetUp();
+
+ plugin_data_dir_ = data_dir_;
+ file_util::AppendToPath(&plugin_data_dir_, L"plugin_tests");
+ ASSERT_TRUE(file_util::PathExists(plugin_data_dir_));
+ }
+
+ virtual void TearDown() {
+ TestShellTest::TearDown();
+
+ // TODO(iyengar) The DeleteFile call fails in some cases as the plugin is
+ // still in use. Needs more investigation.
+ ::DeleteFile(plugin_dll_path_.c_str());
+ }
+
+ std::wstring plugin_data_dir_;
+ std::wstring plugin_dll_path_;
+ RequestContext* request_context_;
+ GURL test_url_;
+};
+
+TEST_F(PluginTest, DISABLED_VerifyPluginWindowRect) {
+ std::wstring test_url = GetTestURL(plugin_data_dir_,
+ L"verify_plugin_window_rect.html");
+ NavigateToURL(test_url);
+ WaitForFinish("checkwindowrect", "1");
+}
diff --git a/webkit/tools/test_shell/resource.h b/webkit/tools/test_shell/resource.h
new file mode 100644
index 0000000..d56339e
--- /dev/null
+++ b/webkit/tools/test_shell/resource.h
@@ -0,0 +1,38 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by test_shell.rc
+//
+
+#define IDS_APP_TITLE 103
+
+#define IDR_MAINFRAME 128
+#define IDD_TESTSHELL_DIALOG 102
+#define IDD_ABOUTBOX 103
+#define IDM_ABOUT 104
+#define IDM_EXIT 105
+#define IDM_DUMP_BODY_TEXT 110
+#define IDM_DUMP_RENDER_TREE 111
+#define IDM_SHOW_WEB_INSPECTOR 112
+#define IDI_TESTSHELL 107
+#define IDI_SMALL 108
+#define IDC_TESTSHELL 109
+#define IDC_MYICON 2
+#define IDC_NAV_BACK 1001
+#define IDC_NAV_FORWARD 1002
+#define IDC_NAV_RELOAD 1003
+#define IDC_NAV_STOP 1004
+#ifndef IDC_STATIC
+#define IDC_STATIC -1
+#endif
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+
+#define _APS_NO_MFC 130
+#define _APS_NEXT_RESOURCE_VALUE 129
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 113
+#endif
+#endif
diff --git a/webkit/tools/test_shell/resources/AHEM____.TTF b/webkit/tools/test_shell/resources/AHEM____.TTF
new file mode 100644
index 0000000..ac81cb0
--- /dev/null
+++ b/webkit/tools/test_shell/resources/AHEM____.TTF
Binary files differ
diff --git a/webkit/tools/test_shell/resources/README.txt b/webkit/tools/test_shell/resources/README.txt
new file mode 100644
index 0000000..c227a5d
--- /dev/null
+++ b/webkit/tools/test_shell/resources/README.txt
@@ -0,0 +1,24 @@
+missingImage.gif was created from Webkit data: WebCore/Resources/missingImage.tiff
+
+Licence text for missingImage.tiff from which missingImage.gif was generated:
+
+Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, 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:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. 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.
+THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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.
diff --git a/webkit/tools/test_shell/resources/missingImage.gif b/webkit/tools/test_shell/resources/missingImage.gif
new file mode 100644
index 0000000..0f7215d
--- /dev/null
+++ b/webkit/tools/test_shell/resources/missingImage.gif
Binary files differ
diff --git a/webkit/tools/test_shell/resources/small.ico b/webkit/tools/test_shell/resources/small.ico
new file mode 100644
index 0000000..d551aa3
--- /dev/null
+++ b/webkit/tools/test_shell/resources/small.ico
Binary files differ
diff --git a/webkit/tools/test_shell/resources/test_shell.ico b/webkit/tools/test_shell/resources/test_shell.ico
new file mode 100644
index 0000000..d551aa3
--- /dev/null
+++ b/webkit/tools/test_shell/resources/test_shell.ico
Binary files differ
diff --git a/webkit/tools/test_shell/resources/test_shell.rc b/webkit/tools/test_shell/resources/test_shell.rc
new file mode 100644
index 0000000..a8fe86e
--- /dev/null
+++ b/webkit/tools/test_shell/resources/test_shell.rc
@@ -0,0 +1,133 @@
+//Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE 9, 1
+#pragma code_page(1252)
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+
+IDI_TESTSHELL ICON "test_shell.ico"
+IDI_SMALL ICON "small.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDC_TESTSHELL MENU
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "E&xit", IDM_EXIT
+ END
+ POPUP "&Debug"
+ BEGIN
+ MENUITEM "Dump body text...", IDM_DUMP_BODY_TEXT
+ MENUITEM "Dump render tree...", IDM_DUMP_RENDER_TREE
+ MENUITEM "Show web inspector...", IDM_SHOW_WEB_INSPECTOR
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "&About ...", IDM_ABOUT
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDC_TESTSHELL ACCELERATORS
+BEGIN
+ "?", IDM_ABOUT, ASCII, ALT
+ "/", IDM_ABOUT, ASCII, ALT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ABOUTBOX DIALOG 22, 17, 230, 75
+STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
+CAPTION "About"
+FONT 8, "System"
+BEGIN
+ ICON IDI_TESTSHELL,IDC_MYICON,14,9,16,16
+ LTEXT "TestShell Version 1.0",IDC_STATIC,49,10,119,8,SS_NOPREFIX
+ LTEXT "Copyright (C) 2006",IDC_STATIC,49,20,119,8
+ DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""windows.h""\r\n"
+ "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDC_TESTSHELL "TESTSHELL"
+ IDS_APP_TITLE "TestShell"
+END
+
+#endif
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
diff --git a/webkit/tools/test_shell/run_all_tests.cc b/webkit/tools/test_shell/run_all_tests.cc
new file mode 100644
index 0000000..05cdc43
--- /dev/null
+++ b/webkit/tools/test_shell/run_all_tests.cc
@@ -0,0 +1,85 @@
+// 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.
+
+// Run all of our test shell tests. This is just an entry point
+// to kick off gTest's RUN_ALL_TESTS().
+
+#include <windows.h>
+#include <commctrl.h>
+
+#include "base/icu_util.h"
+#include "base/message_loop.h"
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_shell.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+const char* TestShellTest::kJavascriptDelayExitScript =
+ "<script>"
+ "window.layoutTestController.waitUntilDone();"
+ "window.addEventListener('load', function() {"
+ " var x = document.body.clientWidth;" // Force a document layout
+ " window.layoutTestController.notifyDone();"
+ "});"
+ "</script>";
+
+int main(int argc, char* argv[]) {
+ TestShell::InitLogging(true); // suppress error dialogs
+
+ // Initialize test shell in non-interactive mode, which will let us load one
+ // request than automatically quit.
+ TestShell::InitializeTestShell(false);
+
+ // Some of the individual tests wind up calling TestShell::WaitTestFinished
+ // which has a timeout in it. For these tests, we don't care about a timeout
+ // so just set it to be a really large number. This is necessary because
+ // when running under Purify, we were hitting those timeouts.
+ TestShell::SetFileTestTimeout(USER_TIMER_MAXIMUM);
+
+ // Allocate a message loop for this thread. Although it is not used
+ // directly, its constructor sets up some necessary state.
+ MessageLoop main_message_loop;
+
+ // Load ICU data tables
+ icu_util::Initialize();
+
+ INITCOMMONCONTROLSEX InitCtrlEx;
+
+ InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ InitCtrlEx.dwICC = ICC_STANDARD_CLASSES;
+ InitCommonControlsEx(&InitCtrlEx);
+
+ // Run the actual tests
+ testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+
+ TestShell::ShutdownTestShell();
+ TestShell::CleanupLogging();
+ return result;
+}
diff --git a/webkit/tools/test_shell/simple_resource_loader_bridge.cc b/webkit/tools/test_shell/simple_resource_loader_bridge.cc
new file mode 100644
index 0000000..e8dcbf2
--- /dev/null
+++ b/webkit/tools/test_shell/simple_resource_loader_bridge.cc
@@ -0,0 +1,584 @@
+// 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.
+//
+// This file contains an implementation of the ResourceLoaderBridge class.
+// The class is implemented using URLRequest, meaning it is a "simple" version
+// that directly issues requests. The more complicated one used in the
+// browser uses IPC.
+//
+// Because URLRequest only provides an asynchronous resource loading API, this
+// file makes use of URLRequest from a background IO thread. Requests for
+// cookies and synchronously loaded resources result in the main thread of the
+// application blocking until the IO thread completes the operation. (See
+// GetCookies and SyncLoad)
+//
+// Main thread IO thread
+// ----------- ---------
+// ResourceLoaderBridge <---o---------> RequestProxy (normal case)
+// \ -> URLRequest
+// o-------> SyncRequestProxy (synchronous case)
+// -> URLRequest
+// SetCookie <------------------------> CookieSetter
+// -> net_util::SetCookie
+// GetCookies <-----------------------> CookieGetter
+// -> net_util::GetCookies
+//
+// NOTE: The implementation in this file may be used to have WebKit fetch
+// resources in-process. For example, it is handy for building a single-
+// process WebKit embedding (e.g., test_shell) that can use URLRequest to
+// perform URL loads. See renderer/resource_dispatcher.h for details on an
+// alternate implementation that defers fetching to another process.
+
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/thread.h"
+#include "net/base/cookie_monster.h"
+#include "net/base/net_util.h"
+#include "net/base/upload_data.h"
+#include "net/url_request/url_request.h"
+#include "webkit/glue/resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_shell_request_context.h"
+
+using webkit_glue::ResourceLoaderBridge;
+using net::HttpResponseHeaders;
+
+namespace {
+
+//-----------------------------------------------------------------------------
+
+URLRequestContext* request_context = NULL;
+Thread* io_thread = NULL;
+
+class IOThread : public Thread {
+ public:
+ IOThread() : Thread("IOThread") {
+ }
+
+ ~IOThread() {
+ // We cannot rely on our base class to stop the thread since we want our
+ // CleanUp function to run.
+ Stop();
+ }
+
+ virtual void CleanUp() {
+ if (request_context) {
+ request_context->Release();
+ request_context = NULL;
+ }
+ }
+};
+
+bool EnsureIOThread() {
+ if (io_thread)
+ return true;
+
+ if (!request_context)
+ SimpleResourceLoaderBridge::Init(NULL);
+
+ io_thread = new IOThread();
+ return io_thread->Start();
+}
+
+//-----------------------------------------------------------------------------
+
+struct RequestParams {
+ std::string method;
+ GURL url;
+ GURL policy_url;
+ GURL referrer;
+ std::string headers;
+ int load_flags;
+ scoped_refptr<net::UploadData> upload;
+};
+
+// The RequestProxy does most of its work on the IO thread. The Start and
+// Cancel methods are proxied over to the IO thread, where an URLRequest object
+// is instantiated.
+class RequestProxy : public URLRequest::Delegate,
+ public base::RefCountedThreadSafe<RequestProxy> {
+ public:
+ // Takes ownership of the params.
+ RequestProxy() {
+ }
+
+ virtual ~RequestProxy() {
+ // If we have a request, then we'd better be on the io thread!
+ DCHECK(!request_.get() ||
+ MessageLoop::current() == io_thread->message_loop());
+ }
+
+ void DropPeer() {
+ peer_ = NULL;
+ }
+
+ void Start(ResourceLoaderBridge::Peer* peer, RequestParams* params) {
+ peer_ = peer;
+ owner_loop_ = MessageLoop::current();
+
+ // proxy over to the io thread
+ io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::AsyncStart, params));
+ }
+
+ void Cancel() {
+ // proxy over to the io thread
+ io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::AsyncCancel));
+ }
+
+ protected:
+ // --------------------------------------------------------------------------
+ // The following methods are called on the owner's thread in response to
+ // various URLRequest callbacks. The event hooks, defined below, trigger
+ // these methods asynchronously.
+
+ void NotifyReceivedRedirect(const GURL& new_url) {
+ if (peer_)
+ peer_->OnReceivedRedirect(new_url);
+ }
+
+ void NotifyReceivedResponse(const ResourceLoaderBridge::ResponseInfo& info) {
+ if (peer_)
+ peer_->OnReceivedResponse(info);
+ }
+
+ void NotifyReceivedData(int bytes_read) {
+ if (!peer_)
+ return;
+
+ // Make a local copy of buf_, since AsyncReadData reuses it.
+ scoped_array<char> buf_copy(new char[bytes_read]);
+ memcpy(buf_copy.get(), buf_, bytes_read);
+
+ // Continue reading more data into buf_
+ // Note: Doing this before notifying our peer ensures our load events get
+ // dispatched in a manner consistent with DumpRenderTree (and also avoids a
+ // race condition). If the order of the next 2 functions were reversed, the
+ // peer could generate new requests in reponse to the received data, which
+ // when run on the io thread, could race against this function in doing
+ // another InvokeLater. See bug 769249.
+ io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::AsyncReadData));
+
+ peer_->OnReceivedData(buf_copy.get(), bytes_read);
+ }
+
+ void NotifyCompletedRequest(const URLRequestStatus& status) {
+ if (peer_) {
+ peer_->OnCompletedRequest(status);
+ DropPeer(); // ensure no further notifications
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // The following methods are called on the io thread. They correspond to
+ // actions performed on the owner's thread.
+
+ void AsyncStart(RequestParams* params) {
+ request_.reset(new URLRequest(params->url, this));
+ request_->set_method(params->method);
+ request_->set_policy_url(params->policy_url);
+ request_->set_referrer(params->referrer.spec());
+ request_->SetExtraRequestHeaders(params->headers);
+ request_->set_load_flags(params->load_flags);
+ request_->set_upload(params->upload.get());
+ request_->set_context(request_context);
+ request_->Start();
+
+ delete params;
+ }
+
+ void AsyncCancel() {
+ // This can be null in cases where the request is already done.
+ if (!request_.get())
+ return;
+
+ request_->Cancel();
+ Done();
+ }
+
+ void AsyncReadData() {
+ // This can be null in cases where the request is already done.
+ if (!request_.get())
+ return;
+
+ if (request_->status().is_success()) {
+ int bytes_read;
+ if (request_->Read(buf_, sizeof(buf_), &bytes_read) && bytes_read) {
+ OnReceivedData(bytes_read);
+ } else if (!request_->status().is_io_pending()) {
+ Done();
+ } // else wait for OnReadCompleted
+ } else {
+ Done();
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // The following methods are event hooks (corresponding to URLRequest
+ // callbacks) that run on the IO thread. They are designed to be overridden
+ // by the SyncRequestProxy subclass.
+
+ virtual void OnReceivedRedirect(const GURL& new_url) {
+ owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::NotifyReceivedRedirect, new_url));
+ }
+
+ virtual void OnReceivedResponse(
+ const ResourceLoaderBridge::ResponseInfo& info) {
+ owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::NotifyReceivedResponse, info));
+ }
+
+ virtual void OnReceivedData(int bytes_read) {
+ owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::NotifyReceivedData, bytes_read));
+ }
+
+ virtual void OnCompletedRequest(const URLRequestStatus& status) {
+ owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RequestProxy::NotifyCompletedRequest, status));
+ }
+
+ // --------------------------------------------------------------------------
+ // URLRequest::Delegate implementation:
+
+ virtual void OnReceivedRedirect(URLRequest* request,
+ const GURL& new_url) {
+ DCHECK(request->status().is_success());
+ OnReceivedRedirect(new_url);
+ }
+
+ virtual void OnResponseStarted(URLRequest* request) {
+ if (request->status().is_success()) {
+ ResourceLoaderBridge::ResponseInfo info;
+ info.request_time = request->request_time();
+ info.response_time = request->response_time();
+ info.headers = request->response_headers();
+ request->GetMimeType(&info.mime_type);
+ request->GetCharset(&info.charset);
+ OnReceivedResponse(info);
+ AsyncReadData(); // start reading
+ } else {
+ Done();
+ }
+ }
+
+ virtual void OnReadCompleted(URLRequest* request, int bytes_read) {
+ if (request->status().is_success() && bytes_read > 0) {
+ OnReceivedData(bytes_read);
+ } else {
+ Done();
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // Helpers and data:
+
+ void Done() {
+ DCHECK(request_.get());
+ OnCompletedRequest(request_->status());
+ request_.reset(); // destroy on the io thread
+ }
+
+ scoped_ptr<URLRequest> request_;
+
+ // Size of our async IO data buffers
+ static const int kDataSize = 16*1024;
+
+ // read buffer for async IO
+ char buf_[kDataSize];
+
+ MessageLoop* owner_loop_;
+
+ // This is our peer in WebKit (implemented as ResourceHandleInternal). We do
+ // not manage its lifetime, and we may only access it from the owner's
+ // message loop (owner_loop_).
+ ResourceLoaderBridge::Peer* peer_;
+};
+
+//-----------------------------------------------------------------------------
+
+class SyncRequestProxy : public RequestProxy {
+ public:
+ explicit SyncRequestProxy(ResourceLoaderBridge::SyncLoadResponse* result)
+ : event_(::CreateEvent(NULL, TRUE, FALSE, NULL)),
+ result_(result) {
+ }
+
+ virtual ~SyncRequestProxy() {
+ CloseHandle(event_);
+ }
+
+ HANDLE event() const {
+ return event_;
+ }
+
+ // --------------------------------------------------------------------------
+ // Event hooks that run on the IO thread:
+
+ virtual void OnReceivedRedirect(const GURL& new_url) {
+ result_->url = new_url;
+ }
+
+ virtual void OnReceivedResponse(
+ const ResourceLoaderBridge::ResponseInfo& info) {
+ *static_cast<ResourceLoaderBridge::ResponseInfo*>(result_) = info;
+ }
+
+ virtual void OnReceivedData(int bytes_read) {
+ result_->data.append(buf_, bytes_read);
+ AsyncReadData(); // read more (may recurse)
+ }
+
+ virtual void OnCompletedRequest(const URLRequestStatus& status) {
+ result_->status = status;
+ ::SetEvent(event_);
+ }
+
+ private:
+ ResourceLoaderBridge::SyncLoadResponse* result_;
+ HANDLE event_;
+};
+
+//-----------------------------------------------------------------------------
+
+class ResourceLoaderBridgeImpl : public ResourceLoaderBridge {
+ public:
+ ResourceLoaderBridgeImpl(const std::string& method,
+ const GURL& url,
+ const GURL& policy_url,
+ const GURL& referrer,
+ const std::string& headers,
+ int load_flags)
+ : params_(new RequestParams),
+ proxy_(NULL) {
+ params_->method = method;
+ params_->url = url;
+ params_->policy_url = policy_url;
+ params_->referrer = referrer;
+ params_->headers = headers;
+ params_->load_flags = load_flags;
+ }
+
+ virtual ~ResourceLoaderBridgeImpl() {
+ if (proxy_) {
+ proxy_->DropPeer();
+ // Let the proxy die on the IO thread
+ io_thread->message_loop()->ReleaseSoon(FROM_HERE, proxy_);
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // ResourceLoaderBridge implementation:
+
+ virtual void AppendDataToUpload(const char* data, int data_len) {
+ DCHECK(params_.get());
+ if (!params_->upload)
+ params_->upload = new net::UploadData();
+ params_->upload->AppendBytes(data, data_len);
+ }
+
+ virtual void AppendFileRangeToUpload(const std::wstring& file_path,
+ uint64 offset, uint64 length) {
+ DCHECK(params_.get());
+ if (!params_->upload)
+ params_->upload = new net::UploadData();
+ params_->upload->AppendFileRange(file_path, offset, length);
+ }
+
+ virtual bool Start(Peer* peer) {
+ DCHECK(!proxy_);
+
+ if (!EnsureIOThread())
+ return false;
+
+ proxy_ = new RequestProxy();
+ proxy_->AddRef();
+
+ proxy_->Start(peer, params_.release());
+
+ return true; // Any errors will be reported asynchronously.
+ }
+
+ virtual void Cancel() {
+ DCHECK(proxy_);
+ proxy_->Cancel();
+ }
+
+ virtual void SetDefersLoading(bool value) {
+ // TODO(darin): implement me
+ }
+
+ virtual void SyncLoad(SyncLoadResponse* response) {
+ DCHECK(!proxy_);
+
+ if (!EnsureIOThread())
+ return;
+
+ // this may change as the result of a redirect
+ response->url = params_->url;
+
+ proxy_ = new SyncRequestProxy(response);
+ proxy_->AddRef();
+
+ proxy_->Start(NULL, params_.release());
+
+ HANDLE event = static_cast<SyncRequestProxy*>(proxy_)->event();
+
+ if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0)
+ NOTREACHED();
+ }
+
+ private:
+ // Ownership of params_ is transfered to the proxy when the proxy is created.
+ scoped_ptr<RequestParams> params_;
+
+ // The request proxy is allocated when we start the request, and then it
+ // sticks around until this ResourceLoaderBridge is destroyed.
+ RequestProxy* proxy_;
+};
+
+//-----------------------------------------------------------------------------
+
+class CookieSetter : public base::RefCountedThreadSafe<CookieSetter> {
+ public:
+ void Set(const GURL& url, const std::string& cookie) {
+ DCHECK(MessageLoop::current() == io_thread->message_loop());
+ request_context->cookie_store()->SetCookie(url, cookie);
+ }
+};
+
+class CookieGetter : public base::RefCountedThreadSafe<CookieGetter> {
+ public:
+ CookieGetter()
+ : event_(::CreateEvent(NULL, FALSE, FALSE, NULL)) {
+ }
+
+ ~CookieGetter() {
+ CloseHandle(event_);
+ }
+
+ void Get(const GURL& url) {
+ result_ = request_context->cookie_store()->GetCookies(url);
+ SetEvent(event_);
+ }
+
+ std::string GetResult() {
+ if (WaitForSingleObject(event_, INFINITE) != WAIT_OBJECT_0)
+ NOTREACHED();
+ return result_;
+ }
+
+ private:
+ HANDLE event_;
+ std::string result_;
+};
+
+} // anonymous namespace
+
+//-----------------------------------------------------------------------------
+
+namespace webkit_glue {
+
+// factory function
+ResourceLoaderBridge* ResourceLoaderBridge::Create(
+ WebFrame* webframe,
+ const std::string& method,
+ const GURL& url,
+ const GURL& policy_url,
+ const GURL& referrer,
+ const std::string& headers,
+ int load_flags,
+ int origin_pid,
+ ResourceType::Type request_type,
+ bool mixed_contents) {
+ return new ResourceLoaderBridgeImpl(method, url, policy_url, referrer,
+ headers, load_flags);
+}
+
+void SetCookie(const GURL& url, const GURL& policy_url,
+ const std::string& cookie) {
+ // Proxy to IO thread to synchronize w/ network loading.
+
+ if (!EnsureIOThread()) {
+ NOTREACHED();
+ return;
+ }
+
+ scoped_refptr<CookieSetter> cookie_setter = new CookieSetter();
+ io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ cookie_setter.get(), &CookieSetter::Set, url, cookie));
+}
+
+std::string GetCookies(const GURL& url, const GURL& policy_url) {
+ // Proxy to IO thread to synchronize w/ network loading
+
+ if (!EnsureIOThread()) {
+ NOTREACHED();
+ return std::string();
+ }
+
+ scoped_refptr<CookieGetter> getter = new CookieGetter();
+
+ io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ getter.get(), &CookieGetter::Get, url));
+
+ return getter->GetResult();
+}
+
+} // namespace webkit_glue
+
+//-----------------------------------------------------------------------------
+
+// static
+void SimpleResourceLoaderBridge::Init(URLRequestContext* context) {
+ // Make sure to stop any existing IO thread since it may be using the
+ // current request context.
+ Shutdown();
+
+ if (context) {
+ request_context = context;
+ } else {
+ request_context = new TestShellRequestContext();
+ }
+ request_context->AddRef();
+}
+
+// static
+void SimpleResourceLoaderBridge::Shutdown() {
+ if (io_thread) {
+ delete io_thread;
+ io_thread = NULL;
+
+ DCHECK(!request_context) << "should have been nulled by thread dtor";
+ }
+}
diff --git a/webkit/tools/test_shell/simple_resource_loader_bridge.h b/webkit/tools/test_shell/simple_resource_loader_bridge.h
new file mode 100644
index 0000000..3c06594
--- /dev/null
+++ b/webkit/tools/test_shell/simple_resource_loader_bridge.h
@@ -0,0 +1,55 @@
+// 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.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_SIMPLE_RESOURCE_LOADER_BRIDGE_H__
+#define WEBKIT_TOOLS_TEST_SHELL_SIMPLE_RESOURCE_LOADER_BRIDGE_H__
+
+#include "base/ref_counted.h"
+
+class URLRequestContext;
+
+class SimpleResourceLoaderBridge {
+ public:
+ // Call this function to initialize the simple resource loader bridge. If
+ // the given context is null, then a default TestShellRequestContext will be
+ // instantiated. Otherwise, a reference is taken to the given request
+ // context, which will be released when Shutdown is called. The caller
+ // should not hold another reference to the request context! It is safe to
+ // call this function multiple times.
+ //
+ // NOTE: If this function is not called, then a default request context will
+ // be initialized lazily.
+ //
+ static void Init(URLRequestContext* context);
+
+ // Call this function to shutdown the simple resource loader bridge.
+ static void Shutdown();
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_SIMPLE_RESOURCE_LOADER_BRIDGE_H__
diff --git a/webkit/tools/test_shell/temp/navigation_controller_base.cc b/webkit/tools/test_shell/temp/navigation_controller_base.cc
new file mode 100644
index 0000000..287e870
--- /dev/null
+++ b/webkit/tools/test_shell/temp/navigation_controller_base.cc
@@ -0,0 +1,301 @@
+// 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 "webkit/tools/test_shell/temp/navigation_controller_base.h"
+
+#include <algorithm>
+
+#include "webkit/tools/test_shell/temp/navigation_entry.h"
+#include "base/logging.h"
+
+NavigationControllerBase::NavigationControllerBase()
+ : pending_entry_(NULL),
+ last_committed_entry_index_(-1),
+ pending_entry_index_(-1) {
+}
+
+NavigationControllerBase::~NavigationControllerBase() {
+ // NOTE: This does NOT invoke Reset as Reset is virtual.
+ ResetInternal();
+}
+
+void NavigationControllerBase::Reset() {
+ ResetInternal();
+
+ last_committed_entry_index_ = -1;
+}
+
+NavigationEntry* NavigationControllerBase::GetActiveEntry() const {
+ NavigationEntry* entry = pending_entry_;
+ if (!entry)
+ entry = GetLastCommittedEntry();
+ return entry;
+}
+
+int NavigationControllerBase::GetCurrentEntryIndex() const {
+ if (pending_entry_index_ != -1)
+ return pending_entry_index_;
+ return last_committed_entry_index_;
+}
+
+NavigationEntry* NavigationControllerBase::GetLastCommittedEntry() const {
+ if (last_committed_entry_index_ == -1)
+ return NULL;
+ return entries_[last_committed_entry_index_];
+}
+
+int NavigationControllerBase::GetEntryIndexWithPageID(
+ TabContentsType type, int32 page_id) const {
+ for (int i = static_cast<int>(entries_.size())-1; i >= 0; --i) {
+ if (entries_[i]->GetType() == type && entries_[i]->GetPageID() == page_id)
+ return i;
+ }
+ return -1;
+}
+
+NavigationEntry* NavigationControllerBase::GetEntryWithPageID(
+ TabContentsType type, int32 page_id) const {
+ int index = GetEntryIndexWithPageID(type, page_id);
+ return (index != -1) ? entries_[index] : NULL;
+}
+
+NavigationEntry* NavigationControllerBase::GetEntryAtOffset(int offset) const {
+ int index = last_committed_entry_index_ + offset;
+ if (index < 0 || index >= GetEntryCount())
+ return NULL;
+
+ return entries_[index];
+}
+
+bool NavigationControllerBase::CanStop() const {
+ // TODO(darin): do we have something pending that we can stop?
+ return false;
+}
+
+bool NavigationControllerBase::CanGoBack() const {
+ return entries_.size() > 1 && GetCurrentEntryIndex() > 0;
+}
+
+bool NavigationControllerBase::CanGoForward() const {
+ int index = GetCurrentEntryIndex();
+ return index >= 0 && index < (static_cast<int>(entries_.size()) - 1);
+}
+
+void NavigationControllerBase::GoBack() {
+ DCHECK(CanGoBack());
+
+ // Base the navigation on where we are now...
+ int current_index = GetCurrentEntryIndex();
+
+ DiscardPendingEntry();
+
+ pending_entry_index_ = current_index - 1;
+ NavigateToPendingEntry(false);
+}
+
+void NavigationControllerBase::GoForward() {
+ DCHECK(CanGoForward());
+
+ // Base the navigation on where we are now...
+ int current_index = GetCurrentEntryIndex();
+
+ DiscardPendingEntry();
+
+ pending_entry_index_ = current_index + 1;
+ NavigateToPendingEntry(false);
+}
+
+void NavigationControllerBase::GoToIndex(int index) {
+ DCHECK(index >= 0);
+ DCHECK(index < static_cast<int>(entries_.size()));
+
+ DiscardPendingEntry();
+
+ pending_entry_index_ = index;
+ NavigateToPendingEntry(false);
+}
+
+void NavigationControllerBase::GoToOffset(int offset) {
+ int index = last_committed_entry_index_ + offset;
+ if (index < 0 || index >= GetEntryCount())
+ return;
+
+ GoToIndex(index);
+}
+
+void NavigationControllerBase::Stop() {
+ DCHECK(CanStop());
+
+ // TODO(darin): we probably want to just call Stop on the active tab
+ // contents, but should we also call DiscardPendingEntry?
+ NOTREACHED() << "implement me";
+}
+
+void NavigationControllerBase::Reload() {
+ // Base the navigation on where we are now...
+ int current_index = GetCurrentEntryIndex();
+
+ // If we are no where, then we can't reload. TODO(darin): We should add a
+ // CanReload method.
+ if (current_index == -1)
+ return;
+
+ DiscardPendingEntryInternal();
+
+ pending_entry_index_ = current_index;
+ entries_[pending_entry_index_]->SetTransition(PageTransition::RELOAD);
+ NavigateToPendingEntry(true);
+}
+
+void NavigationControllerBase::LoadEntry(NavigationEntry* entry) {
+ // When navigating to a new page, we don't know for sure if we will actually
+ // end up leaving the current page. The new page load could for example
+ // result in a download or a 'no content' response (e.g., a mailto: URL).
+
+ DiscardPendingEntryInternal();
+ pending_entry_ = entry;
+ NavigateToPendingEntry(false);
+}
+
+void NavigationControllerBase::DidNavigateToEntry(NavigationEntry* entry) {
+ // If the entry is that of a page with PageID larger than any this Tab has
+ // seen before, then consider it a new navigation.
+ if (entry->GetPageID() > GetMaxPageID()) {
+ InsertEntry(entry);
+ return;
+ }
+
+ // Otherwise, we just need to update an existing entry with matching PageID.
+ // If the existing entry corresponds to the entry which is pending, then we
+ // must update the current entry index accordingly. When navigating to the
+ // same URL, a new PageID is not created.
+
+ int existing_entry_index = GetEntryIndexWithPageID(entry->GetType(),
+ entry->GetPageID());
+ NavigationEntry* existing_entry =
+ (existing_entry_index != -1) ? entries_[existing_entry_index] : NULL;
+ if (!existing_entry) {
+ // No existing entry, then simply ignore this navigation!
+ DLOG(WARNING) << "ignoring navigation for page: " << entry->GetPageID();
+ } else if (existing_entry == pending_entry_) {
+ // The given entry might provide a new URL... e.g., navigating back to a
+ // page in session history could have resulted in a new client redirect.
+ existing_entry->SetURL(entry->GetURL());
+ existing_entry->SetContentState(entry->GetContentState());
+ last_committed_entry_index_ = pending_entry_index_;
+ pending_entry_index_ = -1;
+ pending_entry_ = NULL;
+ IndexOfActiveEntryChanged();
+ } else if (pending_entry_ && pending_entry_->GetPageID() == -1 &&
+ pending_entry_->GetURL() == existing_entry->GetURL()) {
+ // Not a new navigation
+ DiscardPendingEntry();
+ } else {
+ // The given entry might provide a new URL... e.g., navigating to a page
+ // might result in a client redirect, which should override the URL of the
+ // existing entry.
+ existing_entry->SetURL(entry->GetURL());
+ existing_entry->SetContentState(entry->GetContentState());
+
+ // The navigation could have been issued by the renderer, so be sure that
+ // we update our current index.
+ last_committed_entry_index_ = existing_entry_index;
+ IndexOfActiveEntryChanged();
+ }
+
+ delete entry;
+
+ NotifyNavigationStateChanged();
+}
+
+void NavigationControllerBase::DiscardPendingEntry() {
+ DiscardPendingEntryInternal();
+
+ // Derived classes may do additional things in this case.
+}
+
+int NavigationControllerBase::GetIndexOfEntry(
+ const NavigationEntry* entry) const {
+ NavigationEntryList::const_iterator i = find(entries_.begin(), entries_.end(), entry);
+ if (i == entries_.end())
+ return -1;
+ return static_cast<int>(i - entries_.begin());
+}
+
+void NavigationControllerBase::DiscardPendingEntryInternal() {
+ if (pending_entry_index_ == -1)
+ delete pending_entry_;
+ pending_entry_ = NULL;
+ pending_entry_index_ = -1;
+}
+
+void NavigationControllerBase::InsertEntry(NavigationEntry* entry) {
+ DCHECK(entry->GetTransition() != PageTransition::AUTO_SUBFRAME);
+
+ DiscardPendingEntryInternal();
+
+ int current_size = static_cast<int>(entries_.size());
+
+ // Prune any entry which are in front of the current entry
+ if (current_size > 0) {
+ while (last_committed_entry_index_ < (current_size - 1)) {
+ PruneEntryAtIndex(current_size - 1);
+ delete entries_[current_size - 1];
+ entries_.pop_back();
+ current_size--;
+ }
+ NotifyPrunedEntries();
+ }
+
+ entries_.push_back(entry);
+ last_committed_entry_index_ = static_cast<int>(entries_.size()) - 1;
+
+ NotifyNavigationStateChanged();
+}
+
+void NavigationControllerBase::ResetInternal() {
+ // WARNING: this is invoked from the destructor, be sure not to invoke any
+ // virtual methods from this.
+ for (int i = 0, c = static_cast<int>(entries_.size()); i < c; ++i)
+ delete entries_[i];
+ entries_.clear();
+
+ DiscardPendingEntryInternal();
+}
+
+#ifndef NDEBUG
+
+void NavigationControllerBase::Dump() {
+ int i,c;
+ for (i = 1, c = static_cast<int>(entries_.size()); i < c; ++i) {
+ DLOG(INFO) << entries_[i]->GetURL().spec();
+ }
+}
+
+#endif
diff --git a/webkit/tools/test_shell/temp/navigation_controller_base.h b/webkit/tools/test_shell/temp/navigation_controller_base.h
new file mode 100644
index 0000000..d4b3785
--- /dev/null
+++ b/webkit/tools/test_shell/temp/navigation_controller_base.h
@@ -0,0 +1,220 @@
+// 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.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEMP_NAVIGATION_CONTROLLER_BASE_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEMP_NAVIGATION_CONTROLLER_BASE_H__
+
+#include <vector>
+
+#include "webkit/tools/test_shell/temp/page_transition_types.h"
+
+class NavigationEntry;
+
+typedef int TabContentsType;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// NavigationControllerBase class
+//
+// A NavigationControllerBase maintains navigation data (like session history).
+//
+////////////////////////////////////////////////////////////////////////////////
+class NavigationControllerBase {
+ public:
+ NavigationControllerBase();
+ virtual ~NavigationControllerBase();
+
+ // Empties the history list.
+ virtual void Reset();
+
+ // Returns the active entry, which is the pending entry if a navigation is in
+ // progress or the last committed entry otherwise. NOTE: This can be NULL!!
+ //
+ // If you are trying to get the current state of the NavigationControllerBase,
+ // this is the method you will typically want to call.
+ //
+ NavigationEntry* GetActiveEntry() const;
+
+ // Returns the index from which we would go back/forward or reload. This is
+ // the last_committed_entry_index_ if pending_entry_index_ is -1. Otherwise,
+ // it is the pending_entry_index_.
+ int GetCurrentEntryIndex() const;
+
+ // Returns the pending entry corresponding to the navigation that is
+ // currently in progress, or null if there is none.
+ NavigationEntry* GetPendingEntry() const {
+ return pending_entry_;
+ }
+
+ // Returns the index of the pending entry or -1 if the pending entry
+ // corresponds to a new navigation (created via LoadURL).
+ int GetPendingEntryIndex() const {
+ return pending_entry_index_;
+ }
+
+ // Returns the last committed entry, which may be null if there are no
+ // committed entries.
+ NavigationEntry* GetLastCommittedEntry() const;
+
+ // Returns the index of the last committed entry.
+ int GetLastCommittedEntryIndex() const {
+ return last_committed_entry_index_;
+ }
+
+ // Returns the number of entries in the NavigationControllerBase, excluding
+ // the pending entry if there is one.
+ int GetEntryCount() const {
+ return static_cast<int>(entries_.size());
+ }
+
+ NavigationEntry* GetEntryAtIndex(int index) const {
+ return entries_.at(index);
+ }
+
+ // Returns the entry at the specified offset from current. Returns NULL
+ // if out of bounds.
+ NavigationEntry* GetEntryAtOffset(int offset) const;
+
+ bool CanStop() const;
+
+ // Return whether this controller can go back.
+ bool CanGoBack() const;
+
+ // Return whether this controller can go forward.
+ bool CanGoForward() const;
+
+ // Causes the controller to go back.
+ void GoBack();
+
+ // Causes the controller to go forward.
+ void GoForward();
+
+ // Causes the controller to go to the specified index.
+ void GoToIndex(int index);
+
+ // Causes the controller to go to the specified offset from current. Does
+ // nothing if out of bounds.
+ void GoToOffset(int offset);
+
+ // Causes the controller to stop a pending navigation if any.
+ void Stop();
+
+ // Causes the controller to reload the current (or pending) entry.
+ void Reload();
+
+ // Causes the controller to load the specified entry. The controller
+ // assumes ownership of the entry.
+ // NOTE: Do not pass an entry that the controller already owns!
+ void LoadEntry(NavigationEntry* entry);
+
+ // Return the entry with the corresponding type and page_id, or NULL if
+ // not found.
+ NavigationEntry* GetEntryWithPageID(TabContentsType type,
+ int32 page_id) const;
+
+#ifndef NDEBUG
+ void Dump();
+#endif
+
+ // --------------------------------------------------------------------------
+ // For use by NavigationControllerBase clients:
+
+ // Used to inform the NavigationControllerBase of a navigation being committed
+ // for a tab. The controller takes ownership of the entry. Any entry located
+ // forward to the current entry will be deleted. The new entry becomes the
+ // current entry.
+ virtual void DidNavigateToEntry(NavigationEntry* entry);
+
+ // Used to inform the NavigationControllerBase to discard its pending entry.
+ virtual void DiscardPendingEntry();
+
+ // Returns the index of the specified entry, or -1 if entry is not contained
+ // in this NavigationControllerBase.
+ int GetIndexOfEntry(const NavigationEntry* entry) const;
+
+ protected:
+ // Returns the largest page ID seen. When PageIDs come in larger than
+ // this (via DidNavigateToEntry), we know that we've navigated to a new page.
+ virtual int GetMaxPageID() const = 0;
+
+ // Actually issues the navigation held in pending_entry.
+ virtual void NavigateToPendingEntry(bool reload) = 0;
+
+ // Allows the derived class to issue notifications that a load has been
+ // committed.
+ virtual void NotifyNavigationStateChanged() {}
+
+ // Invoked when entries have been pruned, or removed. For example, if the
+ // current entries are [google, digg, yahoo], with the current entry google,
+ // and the user types in cnet, then digg and yahoo are pruned.
+ virtual void NotifyPrunedEntries() {}
+
+ // Invoked when the index of the active entry may have changed.
+ virtual void IndexOfActiveEntryChanged() {}
+
+ // Inserts an entry after the current position, removing all entries after it.
+ // The new entry will become the active one.
+ virtual void InsertEntry(NavigationEntry* entry);
+
+ // Called when navigations cause entries forward of and including the
+ // specified index are pruned.
+ virtual void PruneEntryAtIndex(int prune_index) { }
+
+ // Discards the pending entry without updating active_contents_
+ void DiscardPendingEntryInternal();
+
+ // Return the index of the entry with the corresponding type and page_id,
+ // or -1 if not found.
+ int GetEntryIndexWithPageID(TabContentsType type, int32 page_id) const;
+
+ // List of NavigationEntry for this tab
+ typedef std::vector<NavigationEntry*> NavigationEntryList;
+ typedef NavigationEntryList::iterator NavigationEntryListIterator;
+ NavigationEntryList entries_;
+
+ // An entry we haven't gotten a response for yet. This will be discarded
+ // when we navigate again. It's used only so we know what the currently
+ // displayed tab is.
+ NavigationEntry* pending_entry_;
+
+ // currently visible entry
+ int last_committed_entry_index_;
+
+ // index of pending entry if it is in entries_, or -1 if pending_entry_ is a
+ // new entry (created by LoadURL).
+ int pending_entry_index_;
+
+ private:
+ // Implementation of Reset and the destructor. Deletes entries
+ void ResetInternal();
+
+ DISALLOW_EVIL_CONSTRUCTORS(NavigationControllerBase);
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEMP_NAVIGATION_CONTROLLER_BASE_H__
diff --git a/webkit/tools/test_shell/temp/navigation_entry.h b/webkit/tools/test_shell/temp/navigation_entry.h
new file mode 100644
index 0000000..c40eb5b
--- /dev/null
+++ b/webkit/tools/test_shell/temp/navigation_entry.h
@@ -0,0 +1,154 @@
+// 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.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEMP_NAVIGATION_ENTRY_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEMP_NAVIGATION_ENTRY_H__
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "webkit/tools/test_shell/temp/page_transition_types.h"
+#include "googleurl/src/gurl.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// NavigationEntry class
+//
+// A NavigationEntry is a data structure that captures all the information
+// required to recreate a browsing state. This includes some opaque binary
+// state as provided by the TabContents as well as some clear text title and
+// uri which is used for our user interface.
+//
+////////////////////////////////////////////////////////////////////////////////
+class NavigationEntry {
+ public:
+ // Create a new NavigationEntry.
+ explicit NavigationEntry(TabContentsType type)
+ : type_(type),
+ page_id_(-1),
+ transition_(PageTransition::LINK) {
+ }
+
+ NavigationEntry(TabContentsType type,
+ int page_id,
+ const GURL& url,
+ const std::wstring& title,
+ PageTransition::Type transition)
+ : type_(type),
+ page_id_(page_id),
+ url_(url),
+ title_(title),
+ transition_(transition) {
+ }
+
+ // virtual to allow test_shell to extend the class.
+ virtual ~NavigationEntry() {
+ }
+
+ // Return the TabContents type required to display this entry. Immutable
+ // because a tab can never change its type.
+ TabContentsType GetType() const { return type_; }
+
+ // Set / Get the URI
+ void SetURL(const GURL& url) { url_ = url; }
+ const GURL& GetURL() const { return url_; }
+
+ void SetDisplayURL(const GURL& url) {
+ if (url == url_) {
+ display_url_ = GURL::EmptyGURL();
+ } else {
+ display_url_ = url;
+ }
+ }
+
+ bool HasDisplayURL() { return !display_url_.is_empty(); }
+
+ const GURL& GetDisplayURL() const {
+ if (display_url_.is_empty()) {
+ return url_;
+ } else {
+ return display_url_;
+ }
+ }
+
+ // Set / Get the title
+ void SetTitle(const std::wstring& a_title) { title_ = a_title; }
+ const std::wstring& GetTitle() const { return title_; }
+
+ // Set / Get opaque state.
+ // WARNING: This state is saved to the database and used to restore previous
+ // states. If you use write a custom TabContents and provide your own
+ // state make sure you have the ability to modify the format in the future
+ // while being able to deal with older versions.
+ void SetContentState(const std::string& state) { state_ = state; }
+ const std::string& GetContentState() const { return state_; }
+
+ // Get the page id corresponding to the tab's state.
+ void SetPageID(int page_id) { page_id_ = page_id; }
+ int32 GetPageID() const { return page_id_; }
+
+ // The transition type indicates what the user did to move to this page from
+ // the previous page.
+ void SetTransition(PageTransition::Type transition) {
+ transition_ = transition;
+ }
+ PageTransition::Type GetTransition() const { return transition_; }
+
+ // Set / Get favicon URL.
+ void SetFavIconURL(const GURL& url) { fav_icon_url_ = url; }
+ const GURL& GetFavIconURL() const { return fav_icon_url_; }
+
+ // This is the URL the user typed in. This may be invalid.
+ void SetUserTypedURL(const GURL& url) { user_typed_url_ = url; }
+ const GURL& GetUserTypedURL() const { return user_typed_url_; }
+
+ // If the user typed url is valid it is returned, otherwise url is returned.
+ const GURL& GetUserTypedURLOrURL() const {
+ return user_typed_url_.is_valid() ? user_typed_url_ : url_;
+ }
+
+ private:
+ TabContentsType type_;
+
+ // Describes the current page that the tab represents. This is not relevant
+ // for all tab contents types.
+ int32 page_id_;
+
+ GURL url_;
+ // The URL the user typed in.
+ GURL user_typed_url_;
+ std::wstring title_;
+ GURL fav_icon_url_;
+ GURL display_url_;
+
+ std::string state_;
+
+ PageTransition::Type transition_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEMP_NAVIGATION_ENTRY_H__
diff --git a/webkit/tools/test_shell/temp/page_transition_types.h b/webkit/tools/test_shell/temp/page_transition_types.h
new file mode 100644
index 0000000..1253963
--- /dev/null
+++ b/webkit/tools/test_shell/temp/page_transition_types.h
@@ -0,0 +1,173 @@
+// 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.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEMP_PAGE_TRANSITION_TYPES_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEMP_PAGE_TRANSITION_TYPES_H__
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+// This class is for scoping only.
+class PageTransition {
+ public:
+ // Types of transitions between pages. These are stored in the history
+ // database to separate visits, and are reported by the renderer for page
+ // navigations.
+ //
+ // WARNING: don't change these numbers, they are written directly into the
+ // history database, so future versions will need the same values to match
+ // the enums.
+ //
+ // A type is made of a core value and a set of qualifiers. A type has one
+ // core value and 0 or or more qualifiers.
+ enum Type {
+ // User got to this page by clicking a link on another page.
+ LINK = 0,
+
+ // User got this page by typing the URL in the URL bar. This should not be
+ // used for cases where the user selected a choice that didn't look at all
+ // like a URL; see GENERATED below.
+ //
+ // We also use this for other "explicit" navigation actions such as command
+ // line arguments.
+ TYPED = 1,
+
+ // User got to this page through a suggestion in the UI, for example,
+ // through the destinations page.
+ AUTO_BOOKMARK = 2,
+
+ // This is a subframe navigation. This is any content that is automatically
+ // loaded in a non-toplevel frame. For example, if a page consists of
+ // several frames containing ads, those ad URLs will have this transition
+ // type. The user may not even realize the content in these pages is a
+ // separate frame, so may not care about the URL (see MANUAL below).
+ AUTO_SUBFRAME = 3,
+
+ // For subframe navigations that are explicitly requested by the user and
+ // generate new navigation entries in the back/forward list. These are
+ // probably more important than frames that were automatically loaded in
+ // the background because the user probably cares about the fact that this
+ // link was loaded.
+ MANUAL_SUBFRAME = 4,
+
+ // User got to this page by typing in the URL bar and selecting an entry
+ // that did not look like a URL. For example, a match might have the URL
+ // of a Google search result page, but appear like "Search Google for ...".
+ // These are not quite the same as TYPED navigations because the user
+ // didn't type or see the destination URL.
+ GENERATED = 5,
+
+ // The page was specified in the command line or is the start page.
+ START_PAGE = 6,
+
+ // The user filled out values in a form and submitted it. NOTE that in
+ // some situations submitting a form does not result in this transition
+ // type. This can happen if the form uses script to submit the contents.
+ FORM_SUBMIT = 7,
+
+ // The user "reloaded" the page, either by hitting the reload button or by
+ // hitting enter in the address bar. NOTE: This is distinct from the
+ // concept of whether a particular load uses "reload semantics" (i.e.
+ // bypasses cached data). For this reason, lots of code needs to pass
+ // around the concept of whether a load should be treated as a "reload"
+ // separately from their tracking of this transition type, which is mainly
+ // used for proper scoring for consumers who care about how frequently a
+ // user typed/visited a particular URL.
+ RELOAD = 8,
+
+ // ADDING NEW CORE VALUE? Be sure to update the LAST_CORE and CORE_MASK
+ // values below.
+ LAST_CORE = RELOAD,
+ CORE_MASK = 0xFF,
+
+ // Qualifiers
+ // Any of the core values above can be augmented by one or more qualifiers.
+ // These qualifiers further define the transition
+
+ // The beginning of a navigation chain.
+ CHAIN_START = 0x10000000,
+
+ // The last transition in a redirect chain.
+ CHAIN_END = 0x20000000,
+
+ // Redirects caused by JavaScript or a meta refresh tag on the page.
+ CLIENT_REDIRECT = 0x40000000,
+
+ // Redirects sent from the server by HTTP headers. It might be nice to
+ // break this out into 2 types in the future: permanent or temporary if we
+ // can get that information from WebKit.
+ SERVER_REDIRECT = 0x80000000,
+
+ // Used to test whether a transition involves a redirect
+ IS_REDIRECT_MASK = 0xC0000000,
+
+ // General mask defining the bits used for the qualifiers
+ QUALIFIER_MASK = 0xFFFFFF00
+ };
+
+ static bool ValidType(int32 type) {
+ int32 t = StripQualifier(static_cast<Type>(type));
+ return (t >= 0 && t <= LAST_CORE &&
+ (type & ~(QUALIFIER_MASK | CORE_MASK)) == 0);
+ }
+
+ static Type FromInt(int32 type) {
+ if (!ValidType(type)) {
+ NOTREACHED() << "Invalid transition type " << type;
+
+ // Return a safe default so we don't have corrupt data in release mode.
+ return LINK;
+ }
+ return static_cast<Type>(type);
+ }
+
+ // Returns true if the given transition is a top-level frame transition, or
+ // false if the transition was for a subframe.
+ static bool IsMainFrame(Type type) {
+ int32 t = StripQualifier(type);
+ return (t != AUTO_SUBFRAME && t != MANUAL_SUBFRAME);
+ }
+
+ // Returns whether a transition involves a redirection
+ static bool IsRedirect(Type type) {
+ return (type & IS_REDIRECT_MASK) != 0;
+ }
+
+ // Simplifies the provided transition by removing any qualifier
+ static Type StripQualifier(Type type) {
+ return static_cast<Type>(type & ~QUALIFIER_MASK);
+ }
+
+ // Return the qualifier
+ static int32 GetQualifier(Type type) {
+ return type & QUALIFIER_MASK;
+ }
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEMP_PAGE_TRANSITION_TYPES_H__
diff --git a/webkit/tools/test_shell/test_navigation_controller.cc b/webkit/tools/test_shell/test_navigation_controller.cc
new file mode 100644
index 0000000..8262967
--- /dev/null
+++ b/webkit/tools/test_shell/test_navigation_controller.cc
@@ -0,0 +1,107 @@
+// 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 "webkit/tools/test_shell/test_navigation_controller.h"
+
+#include "base/logging.h"
+#include "webkit/glue/webhistoryitem.h"
+#include "webkit/tools/test_shell/test_shell.h"
+
+// ----------------------------------------------------------------------------
+// TestNavigationEntry
+
+TestNavigationEntry::TestNavigationEntry()
+ : NavigationEntry(GetTabContentsType()) {
+}
+
+TestNavigationEntry::TestNavigationEntry(int page_id,
+ const GURL& url,
+ const std::wstring& title,
+ PageTransition::Type transition,
+ const std::wstring& target_frame)
+ : NavigationEntry(GetTabContentsType(), page_id, url, title, transition),
+ target_frame_(target_frame) {
+}
+
+TestNavigationEntry::~TestNavigationEntry() {
+}
+
+void TestNavigationEntry::SetContentState(const std::string& state) {
+ cached_history_item_ = NULL; // invalidate our cached item
+ NavigationEntry::SetContentState(state);
+}
+
+WebHistoryItem* TestNavigationEntry::GetHistoryItem() const {
+ if (!cached_history_item_) {
+ TestShellExtraRequestData* extra_data =
+ new TestShellExtraRequestData(GetPageID(), GetTransition());
+ cached_history_item_ =
+ WebHistoryItem::Create(GetURL(), GetTitle(), GetContentState(),
+ extra_data);
+ }
+ return cached_history_item_;
+}
+
+// ----------------------------------------------------------------------------
+// TestNavigationController
+
+TestNavigationController::TestNavigationController(TestShell* shell)
+ : shell_(shell),
+ max_page_id_(-1) {
+}
+
+TestNavigationController::~TestNavigationController() {
+}
+
+void TestNavigationController::Reset() {
+ NavigationControllerBase::Reset();
+ max_page_id_ = -1;
+}
+
+void TestNavigationController::NavigateToPendingEntry(bool reload) {
+ // For session history navigations only the pending_entry_index_ is set.
+ if (!pending_entry_) {
+ DCHECK(pending_entry_index_ != -1);
+ pending_entry_ = entries_[pending_entry_index_];
+ }
+
+ if (shell_->Navigate(*pending_entry_, reload)) {
+ // Note: this is redundant if navigation completed synchronously because
+ // DidNavigateToEntry call this as well.
+ NotifyNavigationStateChanged();
+ } else {
+ DiscardPendingEntry();
+ }
+}
+
+void TestNavigationController::NotifyNavigationStateChanged() {
+ NavigationEntry* entry = GetActiveEntry();
+ if (entry)
+ max_page_id_ = std::max(max_page_id_, entry->GetPageID());
+}
diff --git a/webkit/tools/test_shell/test_navigation_controller.h b/webkit/tools/test_shell/test_navigation_controller.h
new file mode 100644
index 0000000..4bdcad8
--- /dev/null
+++ b/webkit/tools/test_shell/test_navigation_controller.h
@@ -0,0 +1,118 @@
+// 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.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEST_NAVIGATION_CONTROLLER_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEST_NAVIGATION_CONTROLLER_H__
+
+#include <vector>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "webkit/glue/weburlrequest.h"
+#include "webkit/tools/test_shell/temp/navigation_controller_base.h"
+#include "webkit/tools/test_shell/temp/navigation_entry.h"
+#include "webkit/tools/test_shell/temp/page_transition_types.h"
+
+class GURL;
+class TestShell;
+class WebHistoryItem;
+
+// Associated with browser-initated navigations to hold tracking data.
+class TestShellExtraRequestData : public WebRequest::ExtraData {
+ public:
+ TestShellExtraRequestData(int32 pending_page_id,
+ PageTransition::Type transition)
+ : WebRequest::ExtraData(),
+ pending_page_id(pending_page_id),
+ transition_type(transition),
+ request_committed(false) {
+ }
+
+ // Contains the page_id for this navigation or -1 if there is none yet.
+ int32 pending_page_id;
+
+ // Contains the transition type that the browser specified when it
+ // initiated the load.
+ PageTransition::Type transition_type;
+
+ // True if we have already processed the "DidCommitLoad" event for this
+ // request. Used by session history.
+ bool request_committed;
+};
+
+// Same as TestNavigationEntry, but caches the HistoryItem.
+class TestNavigationEntry : public NavigationEntry {
+ public:
+ TestNavigationEntry();
+ TestNavigationEntry(int page_id,
+ const GURL& url,
+ const std::wstring& title,
+ PageTransition::Type transition,
+ const std::wstring& target_frame);
+
+ ~TestNavigationEntry();
+
+ // We don't care about tab contents types, so just pick one and use it
+ // everywhere.
+ static TabContentsType GetTabContentsType() {
+ return 0;
+ }
+
+ void SetContentState(const std::string& state);
+ WebHistoryItem* GetHistoryItem() const;
+
+ const std::wstring& GetTargetFrame() const { return target_frame_; }
+
+private:
+ mutable scoped_refptr<WebHistoryItem> cached_history_item_;
+ std::wstring target_frame_;
+};
+
+// Test shell's NavigationController. The goal is to be as close to the Chrome
+// version as possible.
+class TestNavigationController : public NavigationControllerBase {
+ public:
+ TestNavigationController(TestShell* shell);
+ ~TestNavigationController();
+
+ virtual void Reset();
+
+ private:
+ virtual int GetMaxPageID() const { return max_page_id_; }
+ virtual void NavigateToPendingEntry(bool reload);
+ virtual void NotifyNavigationStateChanged();
+
+ TestShell* shell_;
+ int max_page_id_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TestNavigationController);
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEST_NAVIGATION_CONTROLLER_H__
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
diff --git a/webkit/tools/test_shell/test_shell.h b/webkit/tools/test_shell/test_shell.h
new file mode 100644
index 0000000..b9b110a8
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell.h
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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.
+ */
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_H__
+
+#pragma once
+
+#include <string>
+#include <list>
+
+#include "base/ref_counted.h"
+#include "webkit/tools/test_shell/event_sending_controller.h"
+#include "webkit/tools/test_shell/layout_test_controller.h"
+#include "webkit/tools/test_shell/resource.h"
+#include "webkit/tools/test_shell/temp/page_transition_types.h"
+#include "webkit/tools/test_shell/text_input_controller.h"
+#include "webkit/tools/test_shell/test_webview_delegate.h"
+#include "webkit/tools/test_shell/webview_host.h"
+#include "webkit/tools/test_shell/webwidget_host.h"
+
+typedef std::list<HWND> WindowList;
+
+struct WebPreferences;
+class NavigationEntry;
+class TestNavigationController;
+
+class TestShell {
+public:
+ struct TestParams {
+ // Load the test defaults.
+ TestParams() : dump_tree(true), dump_pixels(false) {
+ }
+
+ // The kind of output we want from this test.
+ bool dump_tree;
+ bool dump_pixels;
+
+ // Filename we dump pixels to (when pixel testing is enabled).
+ std::wstring pixel_file_name;
+ };
+
+ TestShell();
+ virtual ~TestShell();
+
+ // Initialization and clean up of logging.
+ static void InitLogging(bool suppress_error_dialogs);
+ static void CleanupLogging();
+
+ // Initialization and clean up of a static member variable.
+ static void InitializeTestShell(bool interactive);
+ static void ShutdownTestShell();
+
+ static bool interactive() { return interactive_; }
+
+ WebView* webView() {
+ return m_webViewHost.get() ? m_webViewHost->webview() : NULL;
+ }
+ WebViewHost* webViewHost() { return m_webViewHost.get(); }
+ WebWidget* popup() { return m_popupHost ? m_popupHost->webwidget() : NULL; }
+ WebWidgetHost* popupHost() { return m_popupHost; }
+
+ // Called by the LayoutTestController to signal test completion.
+ void TestFinished();
+
+ // Called to block the calling thread until TestFinished is called.
+ void WaitTestFinished();
+
+ void Show(WebView* webview, WindowOpenDisposition disposition);
+
+ // We use this to avoid relying on Windows focus during non-interactive
+ // mode.
+ void SetFocus(WebWidgetHost* host, bool enable);
+
+ LayoutTestController* layout_test_controller() {
+ return layout_test_controller_.get();
+ }
+ TestWebViewDelegate* delegate() { return delegate_.get(); }
+ TestNavigationController* navigation_controller() {
+ return navigation_controller_.get();
+ }
+
+ // Resets the LayoutTestController and EventSendingController. Should be
+ // called before loading a page, since some end-editing event notifications
+ // may arrive after the previous page has finished dumping its text and
+ // therefore end up in the next test's results if the messages are still
+ // enabled.
+ void ResetTestController() {
+ layout_test_controller_->Reset();
+ event_sending_controller_->Reset();
+ }
+
+ // Passes options from LayoutTestController through to the delegate (or
+ // any other caller).
+ bool ShouldDumpEditingCallbacks() {
+ return !interactive_ &&
+ layout_test_controller_->ShouldDumpEditingCallbacks();
+ }
+ bool ShouldDumpFrameLoadCallbacks() {
+ return !interactive_ && (test_is_preparing_ || test_is_pending_) &&
+ layout_test_controller_->ShouldDumpFrameLoadCallbacks();
+ }
+ bool ShouldDumpResourceLoadCallbacks() {
+ return !interactive_ && (test_is_preparing_ || test_is_pending_) &&
+ layout_test_controller_->ShouldDumpResourceLoadCallbacks();
+ }
+ bool ShouldDumpTitleChanges() {
+ return !interactive_ &&
+ layout_test_controller_->ShouldDumpTitleChanges();
+ }
+ bool AcceptsEditing() {
+ return layout_test_controller_->AcceptsEditing();
+ }
+
+ void LoadURL(const wchar_t* url);
+ void LoadURLForFrame(const wchar_t* url, const wchar_t* frame_name);
+ void GoBackOrForward(int offset);
+ void Reload();
+ bool Navigate(const NavigationEntry& entry, bool reload);
+
+ bool PromptForSaveFile(const wchar_t* prompt_title, std::wstring* result);
+ std::wstring GetDocumentText();
+ void DumpDocumentText();
+ void DumpRenderTree();
+
+ HWND mainWnd() const { return m_mainWnd; }
+ HWND webViewWnd() const { return m_webViewHost->window_handle(); }
+ HWND editWnd() const { return m_editWnd; }
+ HWND popupWnd() const { return m_popupHost->window_handle(); }
+
+ static WindowList* windowList() { return window_list_; }
+
+ // If shell is non-null, then *shell is assigned upon successful return
+ static bool CreateNewWindow(const std::wstring& startingURL,
+ TestShell** shell = NULL);
+
+ // Implements CreateWebView for TestWebViewDelegate, which in turn
+ // is called as a WebViewDelegate.
+ WebView* CreateWebView(WebView* webview);
+ WebWidget* CreatePopupWidget(WebView* webview);
+ void ClosePopup();
+
+ static ATOM RegisterWindowClass();
+
+ // Called by the WebView delegate WindowObjectCleared() method, this
+ // binds the layout_test_controller_ and other C++ controller classes to
+ // window JavaScript objects so they can be accessed by layout tests.
+ virtual void BindJSObjectsToWindow(WebFrame* frame);
+
+ // Runs a layout test. Loads a single file into the first available
+ // window, then dumps the requested text representation to stdout.
+ // Returns false if the test cannot be run because no windows are open.
+ static bool RunFileTest(const char* filename, const TestParams& params);
+
+ // Writes the back-forward list data for every open window into result.
+ static void DumpBackForwardList(std::wstring* result);
+
+ // Writes the image captured from the given web frame to the given file.
+ // The returned string is the ASCII-ized MD5 sum of the image.
+ static std::string DumpImage(WebFrame* web_frame,
+ const std::wstring& file_name);
+
+ static void ResetWebPreferences();
+
+ WebPreferences* GetWebPreferences() { return web_prefs_; }
+
+ // Some layout tests hardcode a file:///tmp/LayoutTests URL. We get around
+ // this by substituting "tmp" with the path to the LayoutTests parent dir.
+ static std::string RewriteLocalUrl(const std::string& url);
+
+ // Set the timeout for running a test.
+ static void SetFileTestTimeout(int timeout_ms) {
+ file_test_timeout_ms_ = timeout_ms;
+ }
+
+ // Get the timeout for running a test.
+ static int GetFileTestTimeout() { return file_test_timeout_ms_; }
+
+ // Access to the finished event. Used by the static WatchDog
+ // thread.
+ HANDLE finished_event() { return finished_event_; }
+
+ // Have the shell print the StatsTable to stdout on teardown.
+ void DumpStatsTableOnExit() { dump_stats_table_on_exit_ = true; }
+
+ void CallJSGC();
+
+ void set_is_modal(bool value) { is_modal_ = value; }
+ bool is_modal() const { return is_modal_; }
+
+protected:
+ bool Initialize(const std::wstring& startingURL);
+ void SizeToDefault();
+ void SizeTo(int width, int height);
+ void ResizeSubViews();
+
+ static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+ static LRESULT CALLBACK EditWndProc(HWND, UINT, WPARAM, LPARAM);
+
+protected:
+ HWND m_mainWnd;
+ HWND m_editWnd;
+ scoped_ptr<WebViewHost> m_webViewHost;
+ WebWidgetHost* m_popupHost;
+ WNDPROC default_edit_wnd_proc_;
+
+ // Primitive focus controller for layout test mode.
+ WebWidgetHost* m_focusedWidgetHost;
+
+private:
+ // A set of all our windows.
+ static WindowList* window_list_;
+
+ static HINSTANCE instance_handle_;
+
+ // False when the app is being run using the --layout-tests switch.
+ static bool interactive_;
+
+ // Timeout for page load when running non-interactive file tests, in ms.
+ static int file_test_timeout_ms_;
+
+ scoped_ptr<LayoutTestController> layout_test_controller_;
+
+ scoped_ptr<EventSendingController> event_sending_controller_;
+
+ scoped_ptr<TextInputController> text_input_controller_;
+
+ scoped_ptr<TestNavigationController> navigation_controller_;
+
+ scoped_refptr<TestWebViewDelegate> delegate_;
+
+ // True while a test is preparing to run
+ bool test_is_preparing_;
+
+ // True while a test is running
+ bool test_is_pending_;
+
+ // True if driven from a nested message loop.
+ bool is_modal_;
+
+ // The preferences for the test shell.
+ static WebPreferences* web_prefs_;
+
+ // Used by the watchdog to know when it's finished.
+ HANDLE finished_event_;
+
+ // Dump the stats table counters on exit.
+ bool dump_stats_table_on_exit_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_H__
diff --git a/webkit/tools/test_shell/test_shell.vcproj b/webkit/tools/test_shell/test_shell.vcproj
new file mode 100644
index 0000000..06fd305
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell.vcproj
@@ -0,0 +1,546 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="test_shell"
+ ProjectGUID="{FA39524D-3067-4141-888D-28A86C66F2B9}"
+ RootNamespace="test_shell"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ <ToolFile
+ RelativePath="..\..\build\font_file_copy.rules"
+ />
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\test_shell.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="Font file copy"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\test_shell.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="Font file copy"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="resources"
+ >
+ <File
+ RelativePath=".\resources\fonts\ahem-fallback.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Ahem.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Apple_Chancery.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Apple_Symbols.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\AppleMyungjo.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Arial.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Arialb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Ariali.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\comic_sans_ms-fallback.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Comic_Sans_MS.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Comic_Sans_MSb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\courier-fallback.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Courier.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Courier_New.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Courierb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Courierbi.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Courieri.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Geeza_Pro.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Geneva.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Georgia.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Georgiab.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Georgiai.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Helvetica.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Helvetica_Neueb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Helveticab.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Helveticabi.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Helveticai.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Hiragino_Kaku_Gothic_Pro.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Hiragino_Mincho_Pro.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Hiragino_Mincho_Prob.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Impact.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\lucida_grande-fallback.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Lucida_Grande.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Lucida_Grandeb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Monaco.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\MS_Gothic.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\MS_PGothic.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\MS_PMincho.afm"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\net\base\net_resources.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Osaka.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Papyrus.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resource.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\small.ico"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\STKaiti.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Symbol.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\test_shell.ico"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\test_shell.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\times-fallback.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Times.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Times_New_Roman.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Times_New_Romanb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Timesb.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Timesbi.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Timesi.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Trebuchet_MS.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Verdana.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Verdanab.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Verdanabi.afm"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Verdanai.afm"
+ >
+ </File>
+ <File
+ RelativePath="$(IntDir)\..\localized_strings\webkit_strings_en-US.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\fonts\Zapf_Dingbats.afm"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="temp"
+ >
+ <File
+ RelativePath=".\temp\navigation_controller_base.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\temp\navigation_controller_base.h"
+ >
+ </File>
+ <File
+ RelativePath=".\temp\navigation_entry.h"
+ >
+ </File>
+ <File
+ RelativePath=".\temp\page_transition_types.h"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath=".\drag_delegate.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\drag_delegate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\drop_delegate.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\drop_delegate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\event_sending_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\event_sending_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\layout_test_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\layout_test_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\simple_resource_loader_bridge.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_navigation_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_navigation_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_main.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_request_context.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_request_context.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_switches.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_switches.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_webview_delegate.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_webview_delegate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\text_input_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\text_input_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\webview_host.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\webview_host.h"
+ >
+ </File>
+ <File
+ RelativePath=".\webwidget_host.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\webwidget_host.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/webkit/tools/test_shell/test_shell.vsprops b/webkit/tools/test_shell/test_shell.vsprops
new file mode 100644
index 0000000..f2f0b9a
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell.vsprops
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="test_shell"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\third_party\libpng\using_libpng.vsprops;$(SolutionDir)..\breakpad\using_breakpad.vsprops;$(SolutionDir)..\third_party\libxml\build\using_libxml.vsprops;$(SolutionDir)..\third_party\npapi\using_npapi.vsprops;$(SolutionDir)..\skia\using_skia.vsprops"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(OutDir)\WebKit&quot;;&quot;$(SolutionDir)&quot;;&quot;$(IntDir)\..\localized_strings&quot;;&quot;$(SolutionDir)webkit\port\bridge&quot;;&quot;$(SolutionDir)webkit\port\platform&quot;;&quot;$(SolutionDir)webkit\port\platform\network&quot;;&quot;$(SolutionDir)webkit\glue&quot;;&quot;$(SolutionDir)third_party\webkit\src\&quot;;&quot;$(OutDir)\obj\WebCore\JavaScriptHeaders&quot;;&quot;$(OutDir)&quot;"
+ PreprocessorDefinitions="_CRT_SECURE_NO_DEPRECATE;_SCL_SECURE_NO_DEPRECATE"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comctl32.lib shlwapi.lib rpcrt4.lib winmm.lib"
+ AdditionalLibraryDirectories="&quot;$(OutDir)&quot;"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ AdditionalIncludeDirectories="&quot;$(SolutionDir)&quot;;&quot;$(IntDir)\..\&quot;"
+ />
+</VisualStudioPropertySheet>
diff --git a/webkit/tools/test_shell/test_shell_main.cc b/webkit/tools/test_shell/test_shell_main.cc
new file mode 100644
index 0000000..0256fed
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_main.cc
@@ -0,0 +1,360 @@
+// 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.
+
+// Creates an instance of the test_shell.
+
+#include <stdlib.h> // required by _set_abort_behavior
+
+#include <windows.h>
+#include <commctrl.h>
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/event_recorder.h"
+#include "base/file_util.h"
+#include "base/fixed_string.h"
+#include "base/gfx/native_theme.h"
+#include "base/icu_util.h"
+#include "base/memory_debug.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/resource_util.h"
+#include "base/stats_table.h"
+#include "base/string_util.h"
+#include "breakpad/src/client/windows/handler/exception_handler.h"
+#include "net/base/cookie_monster.h"
+#include "net/base/net_module.h"
+#include "net/http/http_cache.h"
+#include "net/url_request/url_request_context.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/window_open_disposition.h"
+#include "webkit/tools/test_shell/foreground_helper.h"
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_shell.h"
+#include "webkit/tools/test_shell/test_shell_request_context.h"
+#include "webkit/tools/test_shell/test_shell_switches.h"
+
+// This is only set for layout tests.
+static wchar_t g_currentTestName[MAX_PATH];
+
+namespace {
+
+// StatsTable initialization parameters.
+static wchar_t* kStatsFile = L"testshell";
+static int kStatsFileThreads = 20;
+static int kStatsFileCounters = 200;
+
+std::string GetDataResource(HMODULE module, int resource_id) {
+ void* data_ptr;
+ size_t data_size;
+ return base::GetDataResourceFromModule(module, resource_id, &data_ptr,
+ &data_size) ?
+ std::string(static_cast<char*>(data_ptr), data_size) : std::string();
+}
+
+// This is called indirectly by the network layer to access resources.
+std::string NetResourceProvider(int key) {
+ return GetDataResource(::GetModuleHandle(NULL), key);
+}
+
+void SetCurrentTestName(char* path)
+{
+ char* lastSlash = strrchr(path, '/');
+ if (lastSlash) {
+ ++lastSlash;
+ } else {
+ lastSlash = path;
+ }
+
+ wcscpy_s(g_currentTestName, arraysize(g_currentTestName),
+ UTF8ToWide(lastSlash).c_str());
+}
+
+bool MinidumpCallback(const wchar_t *dumpPath,
+ const wchar_t *minidumpID,
+ void *context,
+ EXCEPTION_POINTERS *exinfo,
+ MDRawAssertionInfo *assertion,
+ bool succeeded)
+{
+ // Warning: Don't use the heap in this function. It may be corrupted.
+ if (!g_currentTestName[0])
+ return false;
+
+ // Try to rename the minidump file to include the crashed test's name.
+ FixedString<wchar_t, MAX_PATH> origPath;
+ origPath.Append(dumpPath);
+ origPath.Append(file_util::kPathSeparator);
+ origPath.Append(minidumpID);
+ origPath.Append(L".dmp");
+
+ FixedString<wchar_t, MAX_PATH> newPath;
+ newPath.Append(dumpPath);
+ newPath.Append(file_util::kPathSeparator);
+ newPath.Append(g_currentTestName);
+ newPath.Append(L"-");
+ newPath.Append(minidumpID);
+ newPath.Append(L".dmp");
+
+ // May use the heap, but oh well. If this fails, we'll just have the
+ // original dump file lying around.
+ _wrename(origPath.get(), newPath.get());
+
+ return false;
+}
+} // namespace
+
+int main(int argc, char* argv[])
+{
+#ifdef _CRTDBG_MAP_ALLOC
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
+#endif
+
+ CommandLine parsed_command_line;
+ if (parsed_command_line.HasSwitch(test_shell::kStartupDialog))
+ MessageBox(NULL, L"attach to me?", L"test_shell", MB_OK);
+ //webkit_glue::SetLayoutTestMode(true);
+
+ // Allocate a message loop for this thread. Although it is not used
+ // directly, its constructor sets up some necessary state.
+ MessageLoop main_message_loop;
+
+ bool suppress_error_dialogs =
+ (GetEnvironmentVariable(L"CHROME_HEADLESS", NULL, 0) ||
+ parsed_command_line.HasSwitch(test_shell::kNoErrorDialogs) ||
+ parsed_command_line.HasSwitch(test_shell::kLayoutTests));
+ TestShell::InitLogging(suppress_error_dialogs);
+
+ // Suppress abort message in v8 library in debugging mode.
+ // V8 calls abort() when it hits assertion errors.
+ if (suppress_error_dialogs) {
+ _set_abort_behavior(0, _WRITE_ABORT_MSG);
+ }
+
+ bool layout_test_mode =
+ parsed_command_line.HasSwitch(test_shell::kLayoutTests);
+
+ net::HttpCache::Mode cache_mode = net::HttpCache::NORMAL;
+ bool playback_mode =
+ parsed_command_line.HasSwitch(test_shell::kPlaybackMode);
+ bool record_mode =
+ parsed_command_line.HasSwitch(test_shell::kRecordMode);
+
+ if (playback_mode)
+ cache_mode = net::HttpCache::PLAYBACK;
+ else if (record_mode)
+ cache_mode = net::HttpCache::RECORD;
+
+ if (layout_test_mode ||
+ parsed_command_line.HasSwitch(test_shell::kEnableFileCookies))
+ CookieMonster::EnableFileScheme();
+
+ std::wstring cache_path =
+ parsed_command_line.GetSwitchValue(test_shell::kCacheDir);
+ if (cache_path.empty()) {
+ PathService::Get(base::DIR_EXE, &cache_path);
+ file_util::AppendToPath(&cache_path, L"cache");
+ }
+
+ // Initializing with a default context, which means no on-disk cookie DB,
+ // and no support for directory listings.
+ SimpleResourceLoaderBridge::Init(
+ new TestShellRequestContext(cache_path, cache_mode));
+
+ // Load ICU data tables
+ icu_util::Initialize();
+
+ // Config the network module so it has access to a limited set of resources.
+ NetModule::SetResourceProvider(NetResourceProvider);
+
+ INITCOMMONCONTROLSEX InitCtrlEx;
+
+ InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ InitCtrlEx.dwICC = ICC_STANDARD_CLASSES;
+ InitCommonControlsEx(&InitCtrlEx);
+
+ bool interactive = !layout_test_mode;
+ TestShell::InitializeTestShell(interactive);
+
+ // Disable user themes for layout tests so pixel tests are consistent.
+ if (!interactive)
+ gfx::NativeTheme::instance()->DisableTheming();
+
+ if (parsed_command_line.HasSwitch(test_shell::kTestShellTimeOut)) {
+ const std::wstring timeout_str = parsed_command_line.GetSwitchValue(
+ test_shell::kTestShellTimeOut);
+ int timeout_ms = static_cast<int>(StringToInt64(timeout_str.c_str()));
+ if (timeout_ms > 0)
+ TestShell::SetFileTestTimeout(timeout_ms);
+ }
+
+ // Initialize global strings
+ TestShell::RegisterWindowClass();
+
+ // Treat the first loose value as the initial URL to open.
+ std::wstring uri;
+
+ // Default to a homepage if we're interactive.
+ if (interactive) {
+ PathService::Get(base::DIR_SOURCE_ROOT, &uri);
+ file_util::AppendToPath(&uri, L"webkit");
+ file_util::AppendToPath(&uri, L"data");
+ file_util::AppendToPath(&uri, L"test_shell");
+ file_util::AppendToPath(&uri, L"index.html");
+ }
+
+ if (parsed_command_line.GetLooseValueCount() > 0) {
+ CommandLine::LooseValueIterator iter = parsed_command_line.GetLooseValuesBegin();
+ uri = *iter;
+ }
+
+ if (parsed_command_line.HasSwitch(test_shell::kCrashDumps)) {
+ std::wstring dir = parsed_command_line.GetSwitchValue(test_shell::kCrashDumps);
+ new google_breakpad::ExceptionHandler(dir, 0, &MinidumpCallback, 0, true);
+ }
+
+ std::wstring js_flags =
+ parsed_command_line.GetSwitchValue(test_shell::kJavaScriptFlags);
+ // Test shell always exposes the GC.
+ CommandLine::AppendSwitch(&js_flags, L"expose-gc");
+ webkit_glue::SetJavaScriptFlags(js_flags);
+
+ // load and initialize the stats table.
+ StatsTable *table = new StatsTable(kStatsFile, kStatsFileThreads, kStatsFileCounters);
+ StatsTable::set_current(table);
+
+ TestShell* shell;
+ if (TestShell::CreateNewWindow(uri, &shell)) {
+ if (record_mode || playback_mode) {
+ // Move the window to the upper left corner for consistent
+ // record/playback mode. For automation, we want this to work
+ // on build systems where the script invoking us is a background
+ // process. So for this case, make our window the topmost window
+ // as well.
+ ForegroundHelper::SetForeground(shell->mainWnd());
+ ::SetWindowPos(shell->mainWnd(), HWND_TOP, 0, 0, 600, 800, 0);
+ // Tell webkit as well.
+ webkit_glue::SetRecordPlaybackMode(true);
+ }
+
+ shell->Show(shell->webView(), NEW_WINDOW);
+
+ if (parsed_command_line.HasSwitch(test_shell::kDumpStatsTable))
+ shell->DumpStatsTableOnExit();
+
+ bool no_events = parsed_command_line.HasSwitch(test_shell::kNoEvents);
+ if ((record_mode || playback_mode) && !no_events) {
+ std::wstring script_path = cache_path;
+ // Create the cache directory in case it doesn't exist.
+ file_util::CreateDirectory(cache_path);
+ file_util::AppendToPath(&script_path, L"script.log");
+ if (record_mode)
+ base::EventRecorder::current()->StartRecording(script_path);
+ if (playback_mode)
+ base::EventRecorder::current()->StartPlayback(script_path);
+ }
+
+ if (parsed_command_line.HasSwitch(test_shell::kDebugMemoryInUse)) {
+ base::MemoryDebug::SetMemoryInUseEnabled(true);
+ // Dump all in use memory at startup
+ base::MemoryDebug::DumpAllMemoryInUse();
+ }
+
+ // See if we need to run the tests.
+ if (layout_test_mode) {
+ webkit_glue::SetLayoutTestMode(true);
+
+ // Set up for the kind of test requested.
+ TestShell::TestParams params;
+ if (parsed_command_line.HasSwitch(test_shell::kDumpPixels)) {
+ // The pixel test flag also gives the image file name to use.
+ params.dump_pixels = true;
+ params.pixel_file_name = parsed_command_line.GetSwitchValue(
+ test_shell::kDumpPixels);
+ if (params.pixel_file_name.size() == 0) {
+ fprintf(stderr, "No file specified for pixel tests");
+ exit(1);
+ }
+ }
+ if (parsed_command_line.HasSwitch(test_shell::kNoTree)) {
+ params.dump_tree = false;
+ }
+
+ if (uri.length() == 0) {
+ // Watch stdin for URLs.
+ char filenameBuffer[2048];
+ while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
+ char *newLine = strchr(filenameBuffer, '\n');
+ if (newLine)
+ *newLine = '\0';
+ if (!*filenameBuffer)
+ continue;
+
+ SetCurrentTestName(filenameBuffer);
+
+ if (!TestShell::RunFileTest(filenameBuffer, params))
+ break;
+ }
+ } else {
+ TestShell::RunFileTest(WideToUTF8(uri).c_str(), params);
+ }
+
+ shell->CallJSGC();
+ shell->CallJSGC();
+ if (shell) delete shell;
+ } else {
+ MessageLoop::current()->Run();
+ }
+
+ // Flush any remaining messages. This ensures that any
+ // accumulated Task objects get destroyed before we exit,
+ // which avoids noise in purify leak-test results.
+ MessageLoop::current()->Quit();
+ MessageLoop::current()->Run();
+
+ if (record_mode)
+ base::EventRecorder::current()->StopRecording();
+ if (playback_mode)
+ base::EventRecorder::current()->StopPlayback();
+ }
+
+ TestShell::ShutdownTestShell();
+ TestShell::CleanupLogging();
+
+ // Tear down shared StatsTable; prevents unit_tests from leaking it.
+ StatsTable::set_current(NULL);
+ delete table;
+
+#ifdef _CRTDBG_MAP_ALLOC
+ _CrtDumpMemoryLeaks();
+#endif
+ return 0;
+}
+
diff --git a/webkit/tools/test_shell/test_shell_request_context.cc b/webkit/tools/test_shell/test_shell_request_context.cc
new file mode 100644
index 0000000..0406e01
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_request_context.cc
@@ -0,0 +1,69 @@
+// 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 "webkit/tools/test_shell/test_shell_request_context.h"
+
+#include "net/base/cookie_monster.h"
+#include "webkit/glue/webkit_glue.h"
+
+TestShellRequestContext::TestShellRequestContext() {
+ Init(std::wstring(), net::HttpCache::NORMAL);
+}
+
+TestShellRequestContext::TestShellRequestContext(
+ const std::wstring& cache_path,
+ net::HttpCache::Mode cache_mode) {
+ Init(cache_path, cache_mode);
+}
+
+void TestShellRequestContext::Init(
+ const std::wstring& cache_path,
+ net::HttpCache::Mode cache_mode) {
+ cookie_store_ = new CookieMonster();
+
+ user_agent_ = webkit_glue::GetDefaultUserAgent();
+
+ // hard-code A-L and A-C for test shells
+ accept_language_ = "en-us,en";
+ accept_charset_ = "iso-8859-1,*,utf-8";
+
+ net::HttpCache *cache;
+ if (cache_path.empty()) {
+ cache = new net::HttpCache(NULL, 0);
+ } else {
+ cache = new net::HttpCache(NULL, cache_path, 0);
+ }
+ cache->set_mode(cache_mode);
+ http_transaction_factory_ = cache;
+}
+
+TestShellRequestContext::~TestShellRequestContext() {
+ delete cookie_store_;
+ delete http_transaction_factory_;
+}
diff --git a/webkit/tools/test_shell/test_shell_request_context.h b/webkit/tools/test_shell/test_shell_request_context.h
new file mode 100644
index 0000000..255fb64
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_request_context.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_REQUEST_CONTEXT_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_REQUEST_CONTEXT_H__
+
+#include "net/http/http_cache.h"
+#include "net/url_request/url_request_context.h"
+
+// A basic URLRequestContext that only provides an in-memory cookie store.
+class TestShellRequestContext : public URLRequestContext {
+ public:
+ // Use an in-memory cache
+ TestShellRequestContext();
+
+ // Use an on-disk cache at the specified location. Optionally, use the cache
+ // in playback or record mode.
+ TestShellRequestContext(const std::wstring& cache_path,
+ net::HttpCache::Mode cache_mode);
+
+ ~TestShellRequestContext();
+
+ private:
+ void Init(const std::wstring& cache_path, net::HttpCache::Mode cache_mode);
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_REQUEST_CONTEXT_H__
diff --git a/webkit/tools/test_shell/test_shell_switches.cc b/webkit/tools/test_shell/test_shell_switches.cc
new file mode 100644
index 0000000..3560d60
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_switches.cc
@@ -0,0 +1,76 @@
+// 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 "webkit/tools/test_shell/test_shell_switches.h"
+
+namespace test_shell {
+
+// Suppresses all error dialogs when present.
+const wchar_t kNoErrorDialogs[] = L"noerrdialogs";
+
+// Causes the test_shell to run using stdin and stdout for URLs and output,
+// respectively, and interferes with interactive use of the UI.
+const wchar_t kLayoutTests[] = L"layout-tests";
+const wchar_t kCrashDumps[] = L"crash-dumps"; // Enable crash dumps
+
+// Command line flags that control the tests when layout-tests is specified.
+const wchar_t kNoTree[] = L"notree"; // Don't dump the render tree.
+const wchar_t kDumpPixels[] = L"pixel-tests"; // Enable pixel tests.
+// Optional command line switch that specifies timeout time for page load when
+// running non-interactive file tests, in ms.
+const wchar_t kTestShellTimeOut[] = L"time-out-ms";
+
+const wchar_t kStartupDialog[] = L"testshell-startup-dialog";
+
+// JavaScript flags passed to engine.
+const wchar_t kJavaScriptFlags[] = L"js-flags";
+
+// Run the http cache in record mode.
+const wchar_t kRecordMode[] = L"record-mode";
+
+// Run the http cache in playback mode.
+const wchar_t kPlaybackMode[] = L"playback-mode";
+
+// Don't record/playback events when using record & playback.
+const wchar_t kNoEvents[] = L"no-events";
+
+// Dump stats table on exit.
+const wchar_t kDumpStatsTable[] = L"stats";
+
+// Use a specified cache directory.
+const wchar_t kCacheDir[] = L"cache-dir";
+
+// When being run through a memory profiler, trigger memory in use dumps at
+// startup and just prior to shutdown.
+const wchar_t kDebugMemoryInUse[] = L"debug-memory-in-use";
+
+// Enable cookies on the file:// scheme. --layout-tests also enables this.
+const wchar_t kEnableFileCookies[] = L"enable-file-cookies";
+
+} // namespace test_shell
diff --git a/webkit/tools/test_shell/test_shell_switches.h b/webkit/tools/test_shell/test_shell_switches.h
new file mode 100644
index 0000000..c92ce59
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_switches.h
@@ -0,0 +1,55 @@
+// 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.
+
+// Defines all the command-line switches used by Chrome.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_SWITCHES_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_SWITCHES_H__
+
+namespace test_shell {
+
+extern const wchar_t kCrashDumps[];
+extern const wchar_t kDumpPixels[];
+extern const wchar_t kLayoutTests[];
+extern const wchar_t kNoErrorDialogs[];
+extern const wchar_t kNoTree[];
+extern const wchar_t kTestShellTimeOut[];
+extern const wchar_t kStartupDialog[];
+extern const wchar_t kJavaScriptFlags[];
+extern const wchar_t kRecordMode[];
+extern const wchar_t kPlaybackMode[];
+extern const wchar_t kNoEvents[];
+extern const wchar_t kDumpStatsTable[];
+extern const wchar_t kCacheDir[];
+extern const wchar_t kDebugMemoryInUse[];
+extern const wchar_t kEnableFileCookies[];
+
+} // namespace test_shell
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_SWITCHES_H__
diff --git a/webkit/tools/test_shell/test_shell_test.cc b/webkit/tools/test_shell/test_shell_test.cc
new file mode 100644
index 0000000..8b433d2
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_test.cc
@@ -0,0 +1,63 @@
+// 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 "webkit/tools/test_shell/test_shell_test.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+
+std::wstring TestShellTest::GetTestURL(std::wstring test_case_path,
+ const std::wstring& test_case) {
+ file_util::AppendToPath(&test_case_path, test_case);
+ return test_case_path;
+}
+
+void TestShellTest::SetUp() {
+ // Make a test shell for use by the test.
+ TestShell::RegisterWindowClass();
+ CreateEmptyWindow();
+ test_shell_->Show(test_shell_->webView(), NEW_WINDOW);
+
+ // Point data_dir_ to the root of the test case dir
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir_));
+ file_util::AppendToPath(&data_dir_, L"webkit");
+ file_util::AppendToPath(&data_dir_, L"data");
+ ASSERT_TRUE(file_util::PathExists(data_dir_));
+}
+
+void TestShellTest::TearDown() {
+ // Loading a blank url clears the memory in the current page.
+ test_shell_->LoadURL(L"about:blank");
+ DestroyWindow(test_shell_->mainWnd());
+ LayoutTestController::ClearShell();
+}
+
+void TestShellTest::CreateEmptyWindow() {
+ TestShell::CreateNewWindow(L"about:blank", &test_shell_);
+} \ No newline at end of file
diff --git a/webkit/tools/test_shell/test_shell_test.h b/webkit/tools/test_shell/test_shell_test.h
new file mode 100644
index 0000000..b48e22d
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_test.h
@@ -0,0 +1,63 @@
+// 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.
+
+/**
+ * Base test class used by all test shell tests. Provides boiler plate
+ * code to create and destroy a new test shell for each gTest test.
+ */
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_TEST_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_TEST_H__
+
+#include "webkit/glue/window_open_disposition.h"
+#include "webkit/tools/test_shell/test_shell.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TestShellTest : public testing::Test {
+ protected:
+ // Returns the path "test_case_path/test_case".
+ std::wstring GetTestURL(std::wstring test_case_path,
+ const std::wstring& test_case);
+
+ virtual void SetUp();
+ virtual void TearDown();
+
+ // Don't refactor away; some unittests override this!
+ virtual void CreateEmptyWindow();
+
+ static const char* kJavascriptDelayExitScript;
+
+ protected:
+ // Location of SOURCE_ROOT/webkit/data/
+ std::wstring data_dir_;
+
+ TestShell* test_shell_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEST_SHELL_TEST_H__
diff --git a/webkit/tools/test_shell/test_shell_tests.vcproj b/webkit/tools/test_shell/test_shell_tests.vcproj
new file mode 100644
index 0000000..569dbd0
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_tests.vcproj
@@ -0,0 +1,393 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="test_shell_tests"
+ ProjectGUID="{E6766F81-1FCD-4CD7-BC16-E36964A14867}"
+ RootNamespace="test_shell_tests"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\test_shell_tests.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\test_shell_tests.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="support"
+ >
+ <File
+ RelativePath=".\drag_delegate.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\drag_delegate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\drop_delegate.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\drop_delegate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\event_sending_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\event_sending_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\image_decoder_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\image_decoder_unittest.h"
+ >
+ </File>
+ <File
+ RelativePath=".\layout_test_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\layout_test_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\temp\navigation_controller_base.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\net\base\net_resources.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\resource.h"
+ >
+ </File>
+ <File
+ RelativePath=".\run_all_tests.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\simple_resource_loader_bridge.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\small.ico"
+ >
+ </File>
+ <File
+ RelativePath=".\test_navigation_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_navigation_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\test_shell.ico"
+ >
+ </File>
+ <File
+ RelativePath=".\resources\test_shell.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_request_context.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_request_context.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_switches.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_switches.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_test.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_shell_test.h"
+ >
+ </File>
+ <File
+ RelativePath=".\test_webview_delegate.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\test_webview_delegate.h"
+ >
+ </File>
+ <File
+ RelativePath=".\text_input_controller.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\text_input_controller.h"
+ >
+ </File>
+ <File
+ RelativePath=".\webview_host.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\webview_host.h"
+ >
+ </File>
+ <File
+ RelativePath=".\webwidget_host.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\webwidget_host.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="tests"
+ >
+ <File
+ RelativePath="..\..\glue\autocomplete_input_listener_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\port\platform\image-decoders\bmp\BMPImageDecoder_unittest.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\bookmarklet_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\context_menu_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\cpp_bound_class_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\cpp_variant_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\dom_operations_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\dom_serializer_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\port\platform\GKURL_unittest.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\glue_serialize_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\port\platform\image-decoders\ico\ICOImageDecoder_unittest.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\iframe_redirect_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\keyboard_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\layout_test_controller_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\mimetype_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\multipart_response_delegate_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\node_leak_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\password_autocomplete_listener_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\plugin_tests.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\regular_expression_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\resource_fetcher_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\text_input_controller_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\glue\webplugin_impl_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\port\platform\image-decoders\xbm\XBMImageDecoder_unittest.cpp"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/webkit/tools/test_shell/test_shell_tests.vsprops b/webkit/tools/test_shell/test_shell_tests.vsprops
new file mode 100644
index 0000000..0e11457
--- /dev/null
+++ b/webkit/tools/test_shell/test_shell_tests.vsprops
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="test_shell_tests"
+ InheritedPropertySheets=".\test_shell.vsprops;..\..\build\webkit_common.vsprops"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="$(SolutionDir)..\v8\public"
+ PreprocessorDefinitions="_CRT_SECURE_NO_DEPRECATE;_SCL_SECURE_NO_DEPRECATE"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="winmm.lib"
+ />
+</VisualStudioPropertySheet>
diff --git a/webkit/tools/test_shell/test_webview_delegate.cc b/webkit/tools/test_shell/test_webview_delegate.cc
new file mode 100644
index 0000000..231415f
--- /dev/null
+++ b/webkit/tools/test_shell/test_webview_delegate.cc
@@ -0,0 +1,943 @@
+// 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.
+
+// This file contains the implementation of TestWebViewDelegate, which serves
+// as the WebViewDelegate for the TestShellWebHost. The host is expected to
+// have initialized a MessageLoop before these methods are called.
+
+#include "webkit/tools/test_shell/test_webview_delegate.h"
+
+#include <objidl.h>
+#include <shlobj.h>
+
+#include "base/gfx/point.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+#include "webkit/glue/webdatasource.h"
+#include "webkit/glue/webdropdata.h"
+#include "webkit/glue/weberror.h"
+#include "webkit/glue/webframe.h"
+#include "webkit/glue/webpreferences.h"
+#include "webkit/glue/weburlrequest.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/webview.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/webplugin_delegate_impl.h"
+#include "webkit/glue/window_open_disposition.h"
+#include "webkit/tools/test_shell/drag_delegate.h"
+#include "webkit/tools/test_shell/drop_delegate.h"
+#include "webkit/tools/test_shell/test_navigation_controller.h"
+#include "webkit/tools/test_shell/test_shell.h"
+
+namespace {
+
+static int next_page_id_ = 1;
+
+// Used to write a platform neutral file:/// URL by only taking the filename
+// (e.g., converts "file:///tmp/foo.txt" to just "foo.txt").
+std::wstring UrlSuitableForTestResult(const std::wstring& url) {
+ if (url.empty() || std::wstring::npos == url.find(L"file://"))
+ return url;
+
+ return PathFindFileNameW(url.c_str());
+}
+
+// Adds a file called "DRTFakeFile" to |data_object| (CF_HDROP). Use to fake
+// dragging a file.
+void AddDRTFakeFileToDataObject(IDataObject* data_object) {
+ STGMEDIUM medium = {0};
+ medium.tymed = TYMED_HGLOBAL;
+
+ const char filename[] = "DRTFakeFile";
+ const int filename_len = arraysize(filename);
+
+ // Allocate space for the DROPFILES struct, filename, and 2 null characters.
+ medium.hGlobal = GlobalAlloc(GPTR, sizeof(DROPFILES) + filename_len + 2);
+ DCHECK(medium.hGlobal);
+ DROPFILES* drop_files = static_cast<DROPFILES*>(GlobalLock(medium.hGlobal));
+ drop_files->pFiles = sizeof(DROPFILES);
+ drop_files->fWide = 0; // Filenames are ascii
+ strcpy_s(reinterpret_cast<char*>(drop_files) + sizeof(DROPFILES),
+ filename_len, filename);
+ GlobalUnlock(medium.hGlobal);
+
+ FORMATETC file_desc_fmt = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ data_object->SetData(&file_desc_fmt, &medium, TRUE);
+}
+
+} // namespace
+
+// WebViewDelegate -----------------------------------------------------------
+
+TestWebViewDelegate::~TestWebViewDelegate() {
+ if (custom_cursor_)
+ DestroyIcon(custom_cursor_);
+ RevokeDragDrop(shell_->webViewWnd());
+}
+
+WebView* TestWebViewDelegate::CreateWebView(WebView* webview,
+ bool user_gesture) {
+ return shell_->CreateWebView(webview);
+}
+
+WebWidget* TestWebViewDelegate::CreatePopupWidget(WebView* webview) {
+ return shell_->CreatePopupWidget(webview);
+}
+
+WebPluginDelegate* TestWebViewDelegate::CreatePluginDelegate(
+ WebView* webview,
+ const GURL& url,
+ const std::string& mime_type,
+ const std::string& clsid,
+ std::string* actual_mime_type) {
+ HWND hwnd = GetContainingWindow(webview);
+ if (!hwnd)
+ return NULL;
+
+ bool allow_wildcard = true;
+ WebPluginInfo info;
+ if (!NPAPI::PluginList::Singleton()->GetPluginInfo(url, mime_type, clsid,
+ allow_wildcard, &info,
+ actual_mime_type))
+ return NULL;
+
+ if (actual_mime_type && !actual_mime_type->empty())
+ return WebPluginDelegateImpl::Create(info.file, *actual_mime_type, hwnd);
+ else
+ return WebPluginDelegateImpl::Create(info.file, mime_type, hwnd);
+}
+
+void TestWebViewDelegate::OpenURL(WebView* webview, const GURL& url,
+ WindowOpenDisposition disposition) {
+ DCHECK_NE(disposition, CURRENT_TAB); // No code for this
+ if (disposition == SUPPRESS_OPEN)
+ return;
+ TestShell* shell;
+ if (TestShell::CreateNewWindow(UTF8ToWide(url.spec()), &shell))
+ shell->Show(shell->webView(), disposition);
+}
+
+void TestWebViewDelegate::DidStartLoading(WebView* webview) {
+ if (page_is_loading_) {
+ LOG(ERROR) << "DidStartLoading called while loading";
+ return;
+ }
+ page_is_loading_ = true;
+}
+
+void TestWebViewDelegate::DidStopLoading(WebView* webview) {
+ if (!page_is_loading_) {
+ LOG(ERROR) << "DidStopLoading called while not loading";
+ return;
+ }
+ page_is_loading_ = false;
+}
+
+void TestWebViewDelegate::WindowObjectCleared(WebFrame* webframe) {
+ shell_->BindJSObjectsToWindow(webframe);
+}
+
+WindowOpenDisposition TestWebViewDelegate::DispositionForNavigationAction(
+ WebView* webview,
+ WebFrame* frame,
+ const WebRequest* request,
+ WebNavigationType type,
+ WindowOpenDisposition disposition,
+ bool is_redirect) {
+ if (is_custom_policy_delegate_) {
+ std::wstring frame_name = frame->GetName();
+ printf("Policy delegate: attempt to load %s\n",
+ request->GetURL().spec().c_str());
+ return IGNORE_ACTION;
+ } else {
+ return WebViewDelegate::DispositionForNavigationAction(
+ webview, frame, request, type, disposition, is_redirect);
+ }
+}
+
+void TestWebViewDelegate::SetCustomPolicyDelegate(bool isCustom) {
+ is_custom_policy_delegate_ = isCustom;
+}
+
+void TestWebViewDelegate::AssignIdentifierToRequest(WebView* webview,
+ uint32 identifier,
+ const WebRequest& request) {
+ if (shell_->ShouldDumpResourceLoadCallbacks()) {
+ resource_identifier_map_[identifier] = request.GetURL().possibly_invalid_spec();
+ }
+}
+
+std::string TestWebViewDelegate::GetResourceDescription(uint32 identifier) {
+ ResourceMap::iterator it = resource_identifier_map_.find(identifier);
+ return it != resource_identifier_map_.end() ? it->second : "<unknown>";
+}
+
+void TestWebViewDelegate::WillSendRequest(WebView* webview,
+ uint32 identifier,
+ WebRequest* request) {
+ std::string request_url = request->GetURL().possibly_invalid_spec();
+
+ if (shell_->ShouldDumpResourceLoadCallbacks()) {
+ printf("%s - willSendRequest <WebRequest URL \"%s\">\n",
+ GetResourceDescription(identifier).c_str(),
+ request_url.c_str());
+ }
+
+ // Set the new substituted URL.
+ request->SetURL(GURL(TestShell::RewriteLocalUrl(request_url)));
+}
+
+void TestWebViewDelegate::DidFinishLoading(WebView* webview,
+ uint32 identifier) {
+ if (shell_->ShouldDumpResourceLoadCallbacks()) {
+ printf("%s - didFinishLoading\n",
+ GetResourceDescription(identifier).c_str());
+ }
+
+ resource_identifier_map_.erase(identifier);
+}
+
+void TestWebViewDelegate::DidFailLoadingWithError(WebView* webview,
+ uint32 identifier,
+ const WebError& error) {
+ if (shell_->ShouldDumpResourceLoadCallbacks()) {
+ printf("%s - didFailLoadingWithError <WebError code %d,"
+ " failing URL \"%s\">\n",
+ GetResourceDescription(identifier).c_str(),
+ error.GetErrorCode(),
+ error.GetFailedURL().spec().c_str());
+ }
+
+ resource_identifier_map_.erase(identifier);
+}
+
+void TestWebViewDelegate::DidStartProvisionalLoadForFrame(
+ WebView* webview,
+ WebFrame* frame,
+ NavigationGesture gesture) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didStartProvisionalLoadForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ if (!top_loading_frame_) {
+ top_loading_frame_ = frame;
+ }
+ UpdateAddressBar(webview);
+}
+
+void TestWebViewDelegate::DidReceiveServerRedirectForProvisionalLoadForFrame(
+ WebView* webview,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didReceiveServerRedirectForProvisionalLoadForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ UpdateAddressBar(webview);
+}
+
+void TestWebViewDelegate::DidFailProvisionalLoadWithError(
+ WebView* webview,
+ const WebError& error,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didFailProvisionalLoadWithError\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ if (page_is_loading_)
+ DidStopLoading(webview);
+ LocationChangeDone(frame->GetProvisionalDataSource());
+
+ // Don't display an error page if we're running layout tests, because
+ // DumpRenderTree doesn't.
+ if (!shell_->interactive())
+ return;
+
+ // Don't display an error page if this is simply a cancelled load. Aside
+ // from being dumb, WebCore doesn't expect it and it will cause a crash.
+ if (error.GetErrorCode() == net::ERR_ABORTED)
+ return;
+
+ const WebRequest& failed_request =
+ frame->GetProvisionalDataSource()->GetRequest();
+ TestShellExtraRequestData* extra_data =
+ static_cast<TestShellExtraRequestData*>(failed_request.GetExtraData());
+ bool replace = extra_data && extra_data->pending_page_id != -1;
+
+ scoped_ptr<WebRequest> request(failed_request.Clone());
+ request->SetURL(GURL("testshell-error:"));
+
+ std::string error_text =
+ StringPrintf("Error loading url: %d", error.GetErrorCode());
+
+ frame->LoadAlternateHTMLString(request.get(), error_text,
+ error.GetFailedURL(), replace);
+}
+
+void TestWebViewDelegate::DidCommitLoadForFrame(WebView* webview,
+ WebFrame* frame,
+ bool is_new_navigation) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didCommitLoadForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ UpdateForCommittedLoad(frame, is_new_navigation);
+}
+
+void TestWebViewDelegate::DidReceiveTitle(WebView* webview,
+ const std::wstring& title,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didReceiveTitle\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ if (shell_->ShouldDumpTitleChanges()) {
+ printf("TITLE CHANGED: %S\n", title.c_str());
+ }
+}
+
+void TestWebViewDelegate::DidFinishLoadForFrame(WebView* webview,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didFinishLoadForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ UpdateAddressBar(webview);
+ LocationChangeDone(frame->GetDataSource());
+}
+
+void TestWebViewDelegate::DidFailLoadWithError(WebView* webview,
+ const WebError& error,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didFailLoadWithError\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ if (page_is_loading_)
+ DidStopLoading(webview);
+ LocationChangeDone(frame->GetDataSource());
+}
+
+void TestWebViewDelegate::DidFinishDocumentLoadForFrame(WebView* webview,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didFinishDocumentLoadForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+}
+
+void TestWebViewDelegate::DidHandleOnloadEventsForFrame(WebView* webview,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didHandleOnloadEventsForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+}
+
+void TestWebViewDelegate::DidChangeLocationWithinPageForFrame(
+ WebView* webview, WebFrame* frame, bool is_new_navigation) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didChangeLocationWithinPageForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+
+ UpdateForCommittedLoad(frame, is_new_navigation);
+}
+
+void TestWebViewDelegate::DidReceiveIconForFrame(WebView* webview,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didReceiveIconForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+}
+
+void TestWebViewDelegate::WillPerformClientRedirect(WebView* webview,
+ WebFrame* frame,
+ const std::wstring& dest_url,
+ unsigned int delay_seconds,
+ unsigned int fire_date) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ // FIXME: prettyprint the url?
+ printf("%S - willPerformClientRedirectToURL: %S\n",
+ GetFrameDescription(frame).c_str(), dest_url.c_str());
+ }
+}
+
+void TestWebViewDelegate::DidCancelClientRedirect(WebView* webview,
+ WebFrame* frame) {
+ if (shell_->ShouldDumpFrameLoadCallbacks()) {
+ printf("%S - didCancelClientRedirectForFrame\n",
+ GetFrameDescription(frame).c_str());
+ }
+}
+
+void TestWebViewDelegate::AddMessageToConsole(WebView* webview,
+ const std::wstring& message,
+ unsigned int line_no,
+ const std::wstring& source_id) {
+ if (shell_->interactive()) {
+ logging::LogMessage("CONSOLE", 0).stream() << "\""
+ << message.c_str()
+ << ",\" source: "
+ << source_id.c_str()
+ << "("
+ << line_no
+ << ")";
+ } else {
+ // This matches win DumpRenderTree's UIDelegate.cpp.
+ std::wstring new_message = message;
+ if (!message.empty()) {
+ new_message = message;
+ size_t file_protocol = new_message.find(L"file://");
+ if (file_protocol != std::wstring::npos) {
+ new_message = new_message.substr(0, file_protocol) +
+ UrlSuitableForTestResult(new_message);
+ }
+ }
+
+ std::string utf8 = WideToUTF8(new_message);
+ printf("CONSOLE MESSAGE: line %d: %s\n", line_no, utf8.c_str());
+ }
+}
+
+void TestWebViewDelegate::RunJavaScriptAlert(WebView* webview,
+ const std::wstring& message) {
+ if (shell_->interactive()) {
+ MessageBox(shell_->mainWnd(),
+ message.c_str(),
+ L"JavaScript Alert",
+ MB_OK);
+ } else {
+ std::string utf8 = WideToUTF8(message);
+ printf("ALERT: %s\n", utf8.c_str());
+ }
+}
+
+bool TestWebViewDelegate::RunJavaScriptConfirm(WebView* webview,
+ const std::wstring& message) {
+ if (!shell_->interactive()) {
+ // When running tests, write to stdout.
+ std::string utf8 = WideToUTF8(message);
+ printf("CONFIRM: %s\n", utf8.c_str());
+ return true;
+ }
+ return false;
+}
+
+bool TestWebViewDelegate::RunJavaScriptPrompt(WebView* webview,
+ const std::wstring& message, const std::wstring& default_value,
+ std::wstring* result) {
+ if (!shell_->interactive()) {
+ // When running tests, write to stdout.
+ std::string utf8_message = WideToUTF8(message);
+ std::string utf8_default_value = WideToUTF8(default_value);
+ printf("PROMPT: %s, default text: %s\n", utf8_message.c_str(),
+ utf8_default_value.c_str());
+ return true;
+ }
+ return false;
+}
+
+void TestWebViewDelegate::StartDragging(WebView* webview,
+ const WebDropData& drop_data) {
+
+ if (!drag_delegate_)
+ drag_delegate_ = new TestDragDelegate(shell_->webViewWnd(),
+ shell_->webView());
+ if (webkit_glue::IsLayoutTestMode()) {
+ if (shell_->layout_test_controller()->ShouldAddFileToPasteboard()) {
+ // Add a file called DRTFakeFile to the drag&drop clipboard.
+ AddDRTFakeFileToDataObject(drop_data.data_object);
+ }
+
+ // When running a test, we need to fake a drag drop operation otherwise
+ // Windows waits for real mouse events to know when the drag is over.
+ EventSendingController::DoDragDrop(drop_data.data_object);
+ } else {
+ const DWORD ok_effect = DROPEFFECT_COPY | DROPEFFECT_LINK | DROPEFFECT_MOVE;
+ DWORD effect;
+ HRESULT res = DoDragDrop(drop_data.data_object, drag_delegate_.get(),
+ ok_effect, &effect);
+ DCHECK(DRAGDROP_S_DROP == res || DRAGDROP_S_CANCEL == res);
+ }
+ webview->DragSourceSystemDragEnded();
+}
+
+void TestWebViewDelegate::ShowContextMenu(WebView* webview,
+ ContextNode::Type type,
+ int x,
+ int y,
+ const GURL& link_url,
+ const GURL& image_url,
+ const GURL& page_url,
+ const GURL& frame_url,
+ const std::wstring& selection_text,
+ const std::wstring& misspelled_word,
+ int edit_flags) {
+ CapturedContextMenuEvent context(type, x, y);
+ captured_context_menu_events_.push_back(context);
+}
+
+// The output from these methods in non-interactive mode should match that
+// expected by the layout tests. See EditingDelegate.m in DumpRenderTree.
+bool TestWebViewDelegate::ShouldBeginEditing(WebView* webview,
+ std::wstring range) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8 = WideToUTF8(range);
+ printf("EDITING DELEGATE: shouldBeginEditingInDOMRange:%s\n",
+ utf8.c_str());
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldEndEditing(WebView* webview,
+ std::wstring range) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8 = WideToUTF8(range);
+ printf("EDITING DELEGATE: shouldEndEditingInDOMRange:%s\n",
+ utf8.c_str());
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldInsertNode(WebView* webview,
+ std::wstring node,
+ std::wstring range,
+ std::wstring action) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8_node = WideToUTF8(node);
+ std::string utf8_range = WideToUTF8(range);
+ std::string utf8_action = WideToUTF8(action);
+ printf("EDITING DELEGATE: shouldInsertNode:%s "
+ "replacingDOMRange:%s givenAction:%s\n",
+ utf8_node.c_str(), utf8_range.c_str(), utf8_action.c_str());
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldInsertText(WebView* webview,
+ std::wstring text,
+ std::wstring range,
+ std::wstring action) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8_text = WideToUTF8(text);
+ std::string utf8_range = WideToUTF8(range);
+ std::string utf8_action = WideToUTF8(action);
+ printf("EDITING DELEGATE: shouldInsertText:%s "
+ "replacingDOMRange:%s givenAction:%s\n",
+ utf8_text.c_str(), utf8_range.c_str(), utf8_action.c_str());
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldChangeSelectedRange(WebView* webview,
+ std::wstring fromRange,
+ std::wstring toRange,
+ std::wstring affinity,
+ bool stillSelecting) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8_from = WideToUTF8(fromRange);
+ std::string utf8_to = WideToUTF8(toRange);
+ std::string utf8_affinity = WideToUTF8(affinity);
+ printf("EDITING DELEGATE: shouldChangeSelectedDOMRange:%s "
+ "toDOMRange:%s affinity:%s stillSelecting:%s\n",
+ utf8_from.c_str(),
+ utf8_to.c_str(),
+ utf8_affinity.c_str(),
+ (stillSelecting ? "TRUE" : "FALSE"));
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldDeleteRange(WebView* webview,
+ std::wstring range) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8 = WideToUTF8(range);
+ printf("EDITING DELEGATE: shouldDeleteDOMRange:%s\n", utf8.c_str());
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldApplyStyle(WebView* webview,
+ std::wstring style,
+ std::wstring range) {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ std::string utf8_style = WideToUTF8(style);
+ std::string utf8_range = WideToUTF8(range);
+ printf("EDITING DELEGATE: shouldApplyStyle:%s toElementsInDOMRange:%s\n",
+ utf8_style.c_str(), utf8_range.c_str());
+ }
+ return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::SmartInsertDeleteEnabled() {
+ return true;
+}
+
+void TestWebViewDelegate::DidBeginEditing() {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ printf("EDITING DELEGATE: "
+ "webViewDidBeginEditing:WebViewDidBeginEditingNotification\n");
+ }
+}
+
+void TestWebViewDelegate::DidChangeSelection() {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ printf("EDITING DELEGATE: "
+ "webViewDidChangeSelection:WebViewDidChangeSelectionNotification\n");
+ }
+}
+
+void TestWebViewDelegate::DidChangeContents() {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ printf("EDITING DELEGATE: "
+ "webViewDidChange:WebViewDidChangeNotification\n");
+ }
+}
+
+void TestWebViewDelegate::DidEndEditing() {
+ if (shell_->ShouldDumpEditingCallbacks()) {
+ printf("EDITING DELEGATE: "
+ "webViewDidEndEditing:WebViewDidEndEditingNotification\n");
+ }
+}
+
+WebHistoryItem* TestWebViewDelegate::GetHistoryEntryAtOffset(int offset) {
+ TestNavigationEntry* entry = static_cast<TestNavigationEntry*>(
+ shell_->navigation_controller()->GetEntryAtOffset(offset));
+ if (!entry)
+ return NULL;
+
+ return entry->GetHistoryItem();
+}
+
+void TestWebViewDelegate::GoToEntryAtOffsetAsync(int offset) {
+ shell_->navigation_controller()->GoToOffset(offset);
+}
+
+int TestWebViewDelegate::GetHistoryBackListCount() {
+ int current_index =
+ shell_->navigation_controller()->GetLastCommittedEntryIndex();
+ return current_index;
+}
+
+int TestWebViewDelegate::GetHistoryForwardListCount() {
+ int current_index =
+ shell_->navigation_controller()->GetLastCommittedEntryIndex();
+ return shell_->navigation_controller()->GetEntryCount() - current_index - 1;
+}
+
+void TestWebViewDelegate::SetUserStyleSheetEnabled(bool is_enabled) {
+ WebPreferences* prefs = shell_->GetWebPreferences();
+ prefs->user_style_sheet_enabled = is_enabled;
+ shell_->webView()->SetPreferences(*prefs);
+}
+
+void TestWebViewDelegate::SetUserStyleSheetLocation(const GURL& location) {
+ WebPreferences* prefs = shell_->GetWebPreferences();
+ prefs->user_style_sheet_enabled = true;
+ prefs->user_style_sheet_location = location;
+ shell_->webView()->SetPreferences(*prefs);
+}
+
+void TestWebViewDelegate::SetDashboardCompatibilityMode(bool use_mode) {
+ WebPreferences* prefs = shell_->GetWebPreferences();
+ prefs->dashboard_compatibility_mode = use_mode;
+ shell_->webView()->SetPreferences(*prefs);
+}
+
+// WebWidgetDelegate ---------------------------------------------------------
+
+HWND TestWebViewDelegate::GetContainingWindow(WebWidget* webwidget) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget))
+ return host->window_handle();
+
+ return NULL;
+}
+
+void TestWebViewDelegate::DidInvalidateRect(WebWidget* webwidget,
+ const gfx::Rect& rect) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget))
+ host->DidInvalidateRect(rect);
+}
+
+void TestWebViewDelegate::DidScrollRect(WebWidget* webwidget, int dx, int dy,
+ const gfx::Rect& clip_rect) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget))
+ host->DidScrollRect(dx, dy, clip_rect);
+}
+
+void TestWebViewDelegate::Show(WebWidget* webwidget, WindowOpenDisposition) {
+ if (webwidget == shell_->webView()) {
+ ShowWindow(shell_->mainWnd(), SW_SHOW);
+ UpdateWindow(shell_->mainWnd());
+ } else if (webwidget == shell_->popup()) {
+ ShowWindow(shell_->popupWnd(), SW_SHOW);
+ UpdateWindow(shell_->popupWnd());
+ }
+}
+
+void TestWebViewDelegate::CloseWidgetSoon(WebWidget* webwidget) {
+ if (webwidget == shell_->webView()) {
+ PostMessage(shell_->mainWnd(), WM_CLOSE, 0, 0);
+ } else if (webwidget == shell_->popup()) {
+ shell_->ClosePopup();
+ }
+}
+
+void TestWebViewDelegate::Focus(WebWidget* webwidget) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget))
+ shell_->SetFocus(host, true);
+}
+
+void TestWebViewDelegate::Blur(WebWidget* webwidget) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget))
+ shell_->SetFocus(host, false);
+}
+
+void TestWebViewDelegate::SetCursor(WebWidget* webwidget,
+ const WebCursor& cursor) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget)) {
+ if (custom_cursor_) {
+ DestroyIcon(custom_cursor_);
+ custom_cursor_ = NULL;
+ }
+ if (cursor.type() == WebCursor::CUSTOM) {
+ custom_cursor_ = cursor.GetCustomCursor();
+ host->SetCursor(custom_cursor_);
+ } else {
+ HINSTANCE mod_handle = GetModuleHandle(NULL);
+ host->SetCursor(cursor.GetCursor(mod_handle));
+ }
+ }
+}
+
+void TestWebViewDelegate::GetWindowLocation(WebWidget* webwidget,
+ gfx::Point* origin) {
+ if (WebWidgetHost* host = GetHostForWidget(webwidget)) {
+ RECT rect;
+ GetWindowRect(host->window_handle(), &rect);
+ origin->SetPoint(rect.left, rect.top);
+ }
+}
+
+void TestWebViewDelegate::SetWindowRect(WebWidget* webwidget,
+ const gfx::Rect& rect) {
+ if (webwidget == shell_->webView()) {
+ // ignored
+ } else if (webwidget == shell_->popup()) {
+ MoveWindow(shell_->popupWnd(),
+ rect.x(), rect.y(), rect.width(), rect.height(), FALSE);
+ }
+}
+
+void TestWebViewDelegate::DidMove(WebWidget* webwidget,
+ const WebPluginGeometry& move) {
+ WebPluginDelegateImpl::MoveWindow(
+ move.window, move.window_rect, move.clip_rect, move.visible);
+}
+
+void TestWebViewDelegate::RunModal(WebWidget* webwidget) {
+ Show(webwidget, NEW_WINDOW);
+
+ WindowList* wl = TestShell::windowList();
+ for (WindowList::const_iterator i = wl->begin(); i != wl->end(); ++i) {
+ if (*i != shell_->mainWnd())
+ EnableWindow(*i, FALSE);
+ }
+
+ shell_->set_is_modal(true);
+ MessageLoop::current()->Run();
+
+ for (WindowList::const_iterator i = wl->begin(); i != wl->end(); ++i)
+ EnableWindow(*i, TRUE);
+}
+
+void TestWebViewDelegate::RegisterDragDrop() {
+ DCHECK(!drop_delegate_);
+ drop_delegate_ = new TestDropDelegate(shell_->webViewWnd(),
+ shell_->webView());
+}
+
+// Private methods -----------------------------------------------------------
+
+void TestWebViewDelegate::UpdateAddressBar(WebView* webView) {
+ WebFrame* mainFrame = webView->GetMainFrame();
+
+ WebDataSource* dataSource = mainFrame->GetDataSource();
+ if (!dataSource)
+ dataSource = mainFrame->GetProvisionalDataSource();
+ if (!dataSource)
+ return;
+
+ std::wstring frameURL =
+ UTF8ToWide(dataSource->GetRequest().GetMainDocumentURL().spec());
+ SendMessage(shell_->editWnd(), WM_SETTEXT, 0,
+ reinterpret_cast<LPARAM>(frameURL.c_str()));
+}
+
+void TestWebViewDelegate::LocationChangeDone(WebDataSource* data_source) {
+ if (data_source->GetWebFrame() == top_loading_frame_) {
+ top_loading_frame_ = NULL;
+
+ if (!shell_->interactive())
+ shell_->layout_test_controller()->LocationChangeDone();
+ }
+}
+
+WebWidgetHost* TestWebViewDelegate::GetHostForWidget(WebWidget* webwidget) {
+ if (webwidget == shell_->webView())
+ return shell_->webViewHost();
+ if (webwidget == shell_->popup())
+ return shell_->popupHost();
+ return NULL;
+}
+
+void TestWebViewDelegate::UpdateForCommittedLoad(WebFrame* frame,
+ bool is_new_navigation) {
+ WebView* webview = shell_->webView();
+
+ // Code duplicated from RenderView::DidCommitLoadForFrame.
+ const WebRequest& request =
+ webview->GetMainFrame()->GetDataSource()->GetRequest();
+ TestShellExtraRequestData* extra_data =
+ static_cast<TestShellExtraRequestData*>(request.GetExtraData());
+
+ if (is_new_navigation) {
+ // New navigation.
+ UpdateSessionHistory(frame);
+ page_id_ = next_page_id_++;
+ } else if (extra_data && extra_data->pending_page_id != -1 &&
+ !extra_data->request_committed) {
+ // This is a successful session history navigation!
+ UpdateSessionHistory(frame);
+ page_id_ = extra_data->pending_page_id;
+ }
+
+ // Don't update session history multiple times.
+ if (extra_data)
+ extra_data->request_committed = true;
+
+ UpdateURL(frame);
+}
+
+void TestWebViewDelegate::UpdateURL(WebFrame* frame) {
+ WebDataSource* ds = frame->GetDataSource();
+ DCHECK(ds);
+
+ const WebRequest& request = ds->GetRequest();
+
+ // We don't hold a reference to the extra data. The request's reference will
+ // be sufficient because we won't modify it during our call. MAY BE NULL.
+ TestShellExtraRequestData* extra_data =
+ static_cast<TestShellExtraRequestData*>(request.GetExtraData());
+
+ // Type is unused.
+ scoped_ptr<TestNavigationEntry> entry(new TestNavigationEntry);
+
+ // Bug 654101: the referrer will be empty on https->http transitions. It
+ // would be nice if we could get the real referrer from somewhere.
+ entry->SetPageID(page_id_);
+ if (ds->HasUnreachableURL()) {
+ entry->SetURL(GURL(ds->GetUnreachableURL()));
+ } else {
+ entry->SetURL(GURL(request.GetURL()));
+ }
+
+ if (shell_->webView()->GetMainFrame() == frame) {
+ // Top-level navigation.
+
+ PageTransition::Type transition = extra_data ?
+ extra_data->transition_type : PageTransition::LINK;
+ if (!PageTransition::IsMainFrame(transition)) {
+ transition = PageTransition::LINK;
+ }
+ entry->SetTransition(transition);
+ } else {
+ PageTransition::Type transition;
+ if (page_id_ > last_page_id_updated_)
+ transition = PageTransition::MANUAL_SUBFRAME;
+ else
+ transition = PageTransition::AUTO_SUBFRAME;
+ entry->SetTransition(transition);
+ }
+
+ shell_->navigation_controller()->DidNavigateToEntry(entry.release());
+
+ last_page_id_updated_ = std::max(last_page_id_updated_, page_id_);
+}
+
+void TestWebViewDelegate::UpdateSessionHistory(WebFrame* frame) {
+ // If we have a valid page ID at this point, then it corresponds to the page
+ // we are navigating away from. Otherwise, this is the first navigation, so
+ // there is no past session history to record.
+ if (page_id_ == -1)
+ return;
+
+ TestNavigationEntry* entry = static_cast<TestNavigationEntry*>(
+ shell_->navigation_controller()->GetEntryWithPageID(
+ TestNavigationEntry::GetTabContentsType(), page_id_));
+ if (!entry)
+ return;
+
+ GURL url;
+ std::wstring title;
+ std::string state;
+ if (!shell_->webView()->GetMainFrame()->
+ GetPreviousState(&url, &title, &state))
+ return;
+
+ entry->SetURL(url);
+ entry->SetTitle(title);
+ entry->SetContentState(state);
+}
+
+std::wstring TestWebViewDelegate::GetFrameDescription(WebFrame* webframe) {
+ std::wstring name = webframe->GetName();
+
+ if (webframe == shell_->webView()->GetMainFrame()) {
+ if (name.length())
+ return L"main frame \"" + name + L"\"";
+ else
+ return L"main frame";
+ } else {
+ if (name.length())
+ return L"frame \"" + name + L"\"";
+ else
+ return L"frame (anonymous)";
+ }
+}
diff --git a/webkit/tools/test_shell/test_webview_delegate.h b/webkit/tools/test_shell/test_webview_delegate.h
new file mode 100644
index 0000000..9a6f5e7
--- /dev/null
+++ b/webkit/tools/test_shell/test_webview_delegate.h
@@ -0,0 +1,303 @@
+// 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.
+
+// TestWebViewDelegate class:
+// This class implements the WebViewDelegate methods for the test shell. One
+// instance is owned by each TestShell.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEST_WEBVIEW_DELEGATE_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEST_WEBVIEW_DELEGATE_H__
+
+#include <windows.h>
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "webkit/glue/webview_delegate.h"
+#include "webkit/glue/webwidget_delegate.h"
+#include "webkit/tools/test_shell/drag_delegate.h"
+#include "webkit/tools/test_shell/drop_delegate.h"
+
+struct WebPreferences;
+class GURL;
+class TestShell;
+class WebDataSource;
+class WebWidgetHost;
+
+class TestWebViewDelegate : public base::RefCounted<TestWebViewDelegate>, public WebViewDelegate {
+ public:
+ struct CapturedContextMenuEvent {
+ CapturedContextMenuEvent(ContextNode::Type in_type,
+ int in_x,
+ int in_y)
+ : type(in_type),
+ x(in_x),
+ y(in_y) {
+ }
+
+ ContextNode::Type type;
+ int x;
+ int y;
+ };
+
+ typedef std::vector<CapturedContextMenuEvent> CapturedContextMenuEvents;
+
+ TestWebViewDelegate(TestShell* shell)
+ : shell_(shell),
+ top_loading_frame_(NULL),
+ page_id_(-1),
+ last_page_id_updated_(-1),
+ page_is_loading_(false),
+ is_custom_policy_delegate_(false),
+ custom_cursor_(NULL) {
+ }
+ virtual ~TestWebViewDelegate();
+
+ // WebViewDelegate
+ virtual WebView* CreateWebView(WebView* webview, bool user_gesture);
+ virtual WebWidget* CreatePopupWidget(WebView* webview);
+ virtual WebPluginDelegate* CreatePluginDelegate(
+ WebView* webview,
+ const GURL& url,
+ const std::string& mime_type,
+ const std::string& clsid,
+ std::string* actual_mime_type);
+ virtual void OpenURL(WebView* webview,
+ const GURL& url,
+ WindowOpenDisposition disposition);
+ virtual void RunJavaScriptAlert(WebView* webview,
+ const std::wstring& message);
+ virtual bool RunJavaScriptConfirm(WebView* webview,
+ const std::wstring& message);
+ virtual bool RunJavaScriptPrompt(WebView* webview,
+ const std::wstring& message,
+ const std::wstring& default_value,
+ std::wstring* result);
+ virtual void AddMessageToConsole(WebView* webview,
+ const std::wstring& message,
+ unsigned int line_no,
+ const std::wstring& source_id);
+ virtual void StartDragging(WebView* webview,
+ const WebDropData& drop_data);
+ virtual void ShowContextMenu(WebView* webview,
+ ContextNode::Type type,
+ int x,
+ int y,
+ const GURL& link_url,
+ const GURL& image_url,
+ const GURL& page_url,
+ const GURL& frame_url,
+ const std::wstring& selection_text,
+ const std::wstring& misspelled_word,
+ int edit_flags);
+ virtual void DidStartProvisionalLoadForFrame(
+ WebView* webview,
+ WebFrame* frame,
+ NavigationGesture gesture);
+ virtual void DidReceiveServerRedirectForProvisionalLoadForFrame(
+ WebView* webview, WebFrame* frame);
+ virtual void DidFailProvisionalLoadWithError(WebView* webview,
+ const WebError& error,
+ WebFrame* frame);
+ virtual void DidCommitLoadForFrame(WebView* webview, WebFrame* frame,
+ bool is_new_navigation);
+ virtual void DidReceiveTitle(WebView* webview,
+ const std::wstring& title,
+ WebFrame* frame);
+ virtual void DidFinishDocumentLoadForFrame(WebView* webview,
+ WebFrame* frame);
+ virtual void DidHandleOnloadEventsForFrame(WebView* webview,
+ WebFrame* frame);
+ virtual void DidChangeLocationWithinPageForFrame(WebView* webview,
+ WebFrame* frame,
+ bool is_new_navigation);
+ virtual void DidReceiveIconForFrame(WebView* webview, WebFrame* frame);
+
+ virtual void WillPerformClientRedirect(WebView* webview,
+ WebFrame* frame,
+ const std::wstring& dest_url,
+ unsigned int delay_seconds,
+ unsigned int fire_date);
+ virtual void DidCancelClientRedirect(WebView* webview,
+ WebFrame* frame);
+
+ virtual void DidFinishLoadForFrame(WebView* webview, WebFrame* frame);
+ virtual void DidFailLoadWithError(WebView* webview,
+ const WebError& error,
+ WebFrame* forFrame);
+
+ virtual void AssignIdentifierToRequest(WebView* webview,
+ uint32 identifier,
+ const WebRequest& request);
+ virtual void WillSendRequest(WebView* webview,
+ uint32 identifier,
+ WebRequest* request);
+ virtual void DidFinishLoading(WebView* webview, uint32 identifier);
+ virtual void DidFailLoadingWithError(WebView* webview,
+ uint32 identifier,
+ const WebError& error);
+
+ virtual bool ShouldBeginEditing(WebView* webview, std::wstring range);
+ virtual bool ShouldEndEditing(WebView* webview, std::wstring range);
+ virtual bool ShouldInsertNode(WebView* webview,
+ std::wstring node,
+ std::wstring range,
+ std::wstring action);
+ virtual bool ShouldInsertText(WebView* webview,
+ std::wstring text,
+ std::wstring range,
+ std::wstring action);
+ virtual bool ShouldChangeSelectedRange(WebView* webview,
+ std::wstring fromRange,
+ std::wstring toRange,
+ std::wstring affinity,
+ bool stillSelecting);
+ virtual bool ShouldDeleteRange(WebView* webview, std::wstring range);
+ virtual bool ShouldApplyStyle(WebView* webview,
+ std::wstring style,
+ std::wstring range);
+ virtual bool SmartInsertDeleteEnabled();
+ virtual void DidBeginEditing();
+ virtual void DidChangeSelection();
+ virtual void DidChangeContents();
+ virtual void DidEndEditing();
+
+ virtual void DidStartLoading(WebView* webview);
+ virtual void DidStopLoading(WebView* webview);
+
+ virtual void WindowObjectCleared(WebFrame* webframe);
+ virtual WindowOpenDisposition DispositionForNavigationAction(
+ WebView* webview,
+ WebFrame* frame,
+ const WebRequest* request,
+ WebNavigationType type,
+ WindowOpenDisposition disposition,
+ bool is_redirect);
+ void TestWebViewDelegate::SetCustomPolicyDelegate(bool isCustom);
+ virtual WebHistoryItem* GetHistoryEntryAtOffset(int offset);
+ virtual void GoToEntryAtOffsetAsync(int offset);
+ virtual int GetHistoryBackListCount();
+ virtual int GetHistoryForwardListCount();
+
+ // WebWidgetDelegate
+ virtual HWND GetContainingWindow(WebWidget* webwidget);
+ virtual void DidInvalidateRect(WebWidget* webwidget, const gfx::Rect& rect);
+ virtual void DidScrollRect(WebWidget* webwidget, int dx, int dy,
+ const gfx::Rect& clip_rect);
+ virtual void Show(WebWidget* webview, WindowOpenDisposition disposition);
+ virtual void CloseWidgetSoon(WebWidget* webwidget);
+ virtual void Focus(WebWidget* webwidget);
+ virtual void Blur(WebWidget* webwidget);
+ virtual void SetCursor(WebWidget* webwidget,
+ const WebCursor& cursor);
+ virtual void GetWindowLocation(WebWidget* webwidget, gfx::Point* origin);
+ virtual void SetWindowRect(WebWidget* webwidget, const gfx::Rect& rect);
+ virtual void DidMove(WebWidget* webwidget, const WebPluginGeometry& move);
+ virtual void RunModal(WebWidget* webwidget);
+ virtual void AddRef() {
+ RefCounted<TestWebViewDelegate>::AddRef();
+ }
+ virtual void Release() {
+ RefCounted<TestWebViewDelegate>::Release();
+ }
+
+ // Additional accessors
+ WebFrame* top_loading_frame() { return top_loading_frame_; }
+ IDropTarget* drop_delegate() { return drop_delegate_.get(); }
+ IDropSource* drag_delegate() { return drag_delegate_.get(); }
+ const CapturedContextMenuEvents& captured_context_menu_events() const {
+ return captured_context_menu_events_;
+ }
+ void clear_captured_context_menu_events() {
+ captured_context_menu_events_.clear();
+ }
+
+ // Methods for modifying WebPreferences
+ void SetUserStyleSheetEnabled(bool is_enabled);
+ void SetUserStyleSheetLocation(const GURL& location);
+ void SetDashboardCompatibilityMode(bool use_mode);
+
+ // Sets the webview as a drop target.
+ void RegisterDragDrop();
+
+ protected:
+ void UpdateAddressBar(WebView* webView);
+
+ // In the Mac code, this is called to trigger the end of a test after the
+ // page has finished loading. From here, we can generate the dump for the
+ // test.
+ void LocationChangeDone(WebDataSource* data_source);
+
+ WebWidgetHost* GetHostForWidget(WebWidget* webwidget);
+
+ void UpdateForCommittedLoad(WebFrame* webframe, bool is_new_navigation);
+ void UpdateURL(WebFrame* frame);
+ void UpdateSessionHistory(WebFrame* frame);
+
+ // Get a string suitable for dumping a frame to the console.
+ std::wstring GetFrameDescription(WebFrame* webframe);
+
+ private:
+ // True while a page is in the process of being loaded. This flag should
+ // not be necessary, but it helps guard against mismatched messages for
+ // starting and ending loading frames.
+ bool page_is_loading_;
+
+ // Causes navigation actions just printout the intended navigation instead
+ // of taking you to the page. This is used for cases like mailto, where you
+ // don't actually want to open the mail program.
+ bool is_custom_policy_delegate_;
+
+ // Non-owning pointer. The delegate is owned by the host.
+ TestShell* shell_;
+
+ // This is non-NULL IFF a load is in progress.
+ WebFrame* top_loading_frame_;
+
+ // For tracking session history. See RenderView.
+ int page_id_;
+ int last_page_id_updated_;
+
+ // Maps resource identifiers to a descriptive string.
+ typedef std::map<uint32, std::string> ResourceMap;
+ ResourceMap resource_identifier_map_;
+ std::string GetResourceDescription(uint32 identifier);
+
+ HCURSOR custom_cursor_;
+
+ // Classes needed by drag and drop.
+ scoped_refptr<TestDragDelegate> drag_delegate_;
+ scoped_refptr<TestDropDelegate> drop_delegate_;
+
+ CapturedContextMenuEvents captured_context_menu_events_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TestWebViewDelegate);
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEST_WEBVIEW_DELEGATE_H__
diff --git a/webkit/tools/test_shell/text_input_controller.cc b/webkit/tools/test_shell/text_input_controller.cc
new file mode 100644
index 0000000..d74346b
--- /dev/null
+++ b/webkit/tools/test_shell/text_input_controller.cc
@@ -0,0 +1,249 @@
+// 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 "webkit/tools/test_shell/text_input_controller.h"
+
+#include "webkit/glue/webview.h"
+#include "webkit/glue/webframe.h"
+#include "webkit/glue/webtextinput.h"
+#include "webkit/tools/test_shell/test_shell.h"
+
+TestShell* TextInputController::shell_ = NULL;
+
+TextInputController::TextInputController(TestShell* shell) {
+ // Set static shell_ variable. Be careful not to assign shell_ to new
+ // windows which are temporary.
+ if (NULL == shell_)
+ shell_ = shell;
+
+ BindMethod("insertText", &TextInputController::insertText);
+ BindMethod("doCommand", &TextInputController::doCommand);
+ BindMethod("setMarkedText", &TextInputController::setMarkedText);
+ BindMethod("unmarkText", &TextInputController::unmarkText);
+ BindMethod("hasMarkedText", &TextInputController::hasMarkedText);
+ BindMethod("conversationIdentifier", &TextInputController::conversationIdentifier);
+ BindMethod("substringFromRange", &TextInputController::substringFromRange);
+ BindMethod("attributedSubstringFromRange", &TextInputController::attributedSubstringFromRange);
+ BindMethod("markedRange", &TextInputController::markedRange);
+ BindMethod("selectedRange", &TextInputController::selectedRange);
+ BindMethod("firstRectForCharacterRange", &TextInputController::firstRectForCharacterRange);
+ BindMethod("characterIndexForPoint", &TextInputController::characterIndexForPoint);
+ BindMethod("validAttributesForMarkedText", &TextInputController::validAttributesForMarkedText);
+ BindMethod("makeAttributedString", &TextInputController::makeAttributedString);
+}
+
+/* static */ WebView* TextInputController::webview() {
+ return shell_->webView();
+}
+
+/* static */ WebTextInput* TextInputController::GetTextInput() {
+ return webview()->GetMainFrame()->GetTextInput();
+}
+
+void TextInputController::insertText(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 1 && args[0].isString()) {
+ text_input->InsertText(args[0].ToString());
+ }
+}
+
+void TextInputController::doCommand(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 1 && args[0].isString()) {
+ text_input->DoCommand(args[0].ToString());
+ }
+}
+
+void TextInputController::setMarkedText(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 3 && args[0].isString()
+ && args[1].isNumber() && args[2].isNumber()) {
+ text_input->SetMarkedText(args[0].ToString(),
+ args[1].ToInt32(),
+ args[2].ToInt32());
+ }
+}
+
+void TextInputController::unmarkText(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ text_input->UnMarkText();
+}
+
+void TextInputController::hasMarkedText(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ result->Set(text_input->HasMarkedText());
+}
+
+void TextInputController::conversationIdentifier(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ text_input->ConversationIdentifier();
+}
+
+void TextInputController::substringFromRange(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 2 && args[0].isNumber() && args[1].isNumber()) {
+ text_input->SubstringFromRange(args[0].ToInt32(), args[1].ToInt32());
+ }
+}
+
+void TextInputController::attributedSubstringFromRange(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 2 && args[0].isNumber() && args[1].isNumber()) {
+ text_input->AttributedSubstringFromRange(args[0].ToInt32(),
+ args[1].ToInt32());
+ }
+}
+
+void TextInputController::markedRange(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ std::string range_str;
+ text_input->MarkedRange(&range_str);
+ result->Set(range_str);
+}
+
+void TextInputController::selectedRange(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ std::string range_str;
+ text_input->SelectedRange(&range_str);
+ result->Set(range_str);
+}
+
+void TextInputController::firstRectForCharacterRange(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 2 && args[0].isNumber() && args[1].isNumber()) {
+ text_input->FirstRectForCharacterRange(args[0].ToInt32(),
+ args[1].ToInt32());
+ }
+}
+
+void TextInputController::characterIndexForPoint(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 2 && args[0].isDouble() && args[1].isDouble()) {
+ text_input->CharacterIndexForPoint(args[0].ToDouble(),
+ args[1].ToDouble());
+ }
+}
+
+void TextInputController::validAttributesForMarkedText(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ std::string attributes_str;
+ text_input->ValidAttributesForMarkedText(&attributes_str);
+ result->Set(attributes_str);
+}
+
+void TextInputController::makeAttributedString(
+ const CppArgumentList& args, CppVariant* result) {
+ result->SetNull();
+
+ WebTextInput* text_input = GetTextInput();
+ if (!text_input)
+ return;
+
+ if (args.size() >= 1 && args[0].isString()) {
+ text_input->MakeAttributedString(args[0].ToString());
+ }
+}
diff --git a/webkit/tools/test_shell/text_input_controller.h b/webkit/tools/test_shell/text_input_controller.h
new file mode 100644
index 0000000..f54a307
--- /dev/null
+++ b/webkit/tools/test_shell/text_input_controller.h
@@ -0,0 +1,74 @@
+// 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.
+
+// TextInputController is bound to window.textInputController in Javascript
+// when test_shell is running noninteractively. Layout tests use it to
+// exercise various corners of text input.
+//
+// Mac equivalent: WebKit/WebKitTools/DumpRenderTree/TextInputController.{h,m}
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_TEXT_INPUT_CONTROLLER_H__
+#define WEBKIT_TOOLS_TEST_SHELL_TEXT_INPUT_CONTROLLER_H__
+
+#include "webkit/glue/cpp_bound_class.h"
+
+class TestShell;
+class WebView;
+class WebTextInput;
+
+class TextInputController : public CppBoundClass {
+ public:
+ TextInputController(TestShell* shell);
+
+ void insertText(const CppArgumentList& args, CppVariant* result);
+ void doCommand(const CppArgumentList& args, CppVariant* result);
+ void setMarkedText(const CppArgumentList& args, CppVariant* result);
+ void unmarkText(const CppArgumentList& args, CppVariant* result);
+ void hasMarkedText(const CppArgumentList& args, CppVariant* result);
+ void conversationIdentifier(const CppArgumentList& args, CppVariant* result);
+ void substringFromRange(const CppArgumentList& args, CppVariant* result);
+ void attributedSubstringFromRange(const CppArgumentList& args, CppVariant* result);
+ void markedRange(const CppArgumentList& args, CppVariant* result);
+ void selectedRange(const CppArgumentList& args, CppVariant* result);
+ void firstRectForCharacterRange(const CppArgumentList& args, CppVariant* result);
+ void characterIndexForPoint(const CppArgumentList& args, CppVariant* result);
+ void validAttributesForMarkedText(const CppArgumentList& args, CppVariant* result);
+ void makeAttributedString(const CppArgumentList& args, CppVariant* result);
+
+ private:
+ static WebTextInput* GetTextInput();
+
+ // Returns the test shell's webview.
+ static WebView* webview();
+
+ // Non-owning pointer. The LayoutTestController is owned by the host.
+ static TestShell* shell_;
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_TEXT_INPUT_CONTROLLER_H__
diff --git a/webkit/tools/test_shell/text_input_controller_unittest.cc b/webkit/tools/test_shell/text_input_controller_unittest.cc
new file mode 100644
index 0000000..1e2da90
--- /dev/null
+++ b/webkit/tools/test_shell/text_input_controller_unittest.cc
@@ -0,0 +1,82 @@
+// 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 "webkit/tools/test_shell/text_input_controller.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Test class to let us check TextInputController's method table.
+class TestTextInputController : public TextInputController {
+ public:
+ TestTextInputController() : TextInputController(NULL) {}
+
+ size_t MethodCount() {
+ return methods_.size();
+ }
+};
+
+
+TEST(TextInputControllerTest, MethodMapIsInitialized) {
+ TestTextInputController text_input_controller;
+
+ EXPECT_EQ(14, text_input_controller.MethodCount());
+
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "insertText"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "doCommand"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "setMarkedText"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "unmarkText"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "hasMarkedText"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "conversationIdentifier"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "substringFromRange"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "attributedSubstringFromRange"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "markedRange"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "selectedRange"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "firstRectForCharacterRange"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "characterIndexForPoint"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "validAttributesForMarkedText"));
+ EXPECT_TRUE(text_input_controller.IsMethodRegistered(
+ "makeAttributedString"));
+
+ // Negative test.
+ EXPECT_FALSE(text_input_controller.IsMethodRegistered(
+ "momeRathsOutgrabe"));
+}
diff --git a/webkit/tools/test_shell/webview_host.cc b/webkit/tools/test_shell/webview_host.cc
new file mode 100644
index 0000000..61f66f0
--- /dev/null
+++ b/webkit/tools/test_shell/webview_host.cc
@@ -0,0 +1,72 @@
+// 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 "webkit/tools/test_shell/webview_host.h"
+
+#include "base/gfx/platform_canvas.h"
+#include "base/gfx/rect.h"
+#include "base/gfx/size.h"
+#include "base/win_util.h"
+#include "webkit/glue/webinputevent.h"
+#include "webkit/glue/webview.h"
+
+static const wchar_t kWindowClassName[] = L"WebViewHost";
+
+/*static*/
+WebViewHost* WebViewHost::Create(HWND parent_window, WebViewDelegate* delegate,
+ const WebPreferences& prefs) {
+ WebViewHost* host = new WebViewHost();
+
+ static bool registered_class = false;
+ if (!registered_class) {
+ WNDCLASSEX wcex = {0};
+ wcex.cbSize = sizeof(wcex);
+ wcex.style = CS_DBLCLKS;
+ wcex.lpfnWndProc = WebWidgetHost::WndProc;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcex.lpszClassName = kWindowClassName;
+ RegisterClassEx(&wcex);
+ registered_class = true;
+ }
+
+ host->hwnd_ = CreateWindow(kWindowClassName, NULL,
+ WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS, 0, 0,
+ 0, 0, parent_window, NULL,
+ GetModuleHandle(NULL), NULL);
+ win_util::SetWindowUserData(host->hwnd_, host);
+
+ host->webwidget_ = WebView::Create(delegate, prefs);
+
+ return host;
+}
+
+WebView* WebViewHost::webview() const {
+ return static_cast<WebView*>(webwidget_);
+}
diff --git a/webkit/tools/test_shell/webview_host.h b/webkit/tools/test_shell/webview_host.h
new file mode 100644
index 0000000..e2f84d6
--- /dev/null
+++ b/webkit/tools/test_shell/webview_host.h
@@ -0,0 +1,61 @@
+// 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.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_WEBVIEW_HOST_H__
+#define WEBKIT_TOOLS_TEST_SHELL_WEBVIEW_HOST_H__
+
+#include <windows.h>
+
+#include "base/gfx/rect.h"
+#include "base/scoped_ptr.h"
+#include "webkit/tools/test_shell/webwidget_host.h"
+
+struct WebPreferences;
+class WebView;
+class WebViewDelegate;
+
+// This class is a simple HWND-based host for a WebView
+class WebViewHost : public WebWidgetHost {
+ public:
+ // The new instance is deleted once the associated HWND is destroyed. The
+ // newly created window should be resized after it is created, using the
+ // MoveWindow (or equivalent) function.
+ static WebViewHost* Create(HWND parent_window,
+ WebViewDelegate* delegate,
+ const WebPreferences& prefs);
+
+ WebView* webview() const;
+
+ protected:
+ virtual bool WndProc(UINT message, WPARAM wparam, LPARAM lparam) {
+ return false;
+ }
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_WEBVIEW_HOST_H__
diff --git a/webkit/tools/test_shell/webwidget_host.cc b/webkit/tools/test_shell/webwidget_host.cc
new file mode 100644
index 0000000..b037ca9
--- /dev/null
+++ b/webkit/tools/test_shell/webwidget_host.cc
@@ -0,0 +1,354 @@
+// 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 "webkit/tools/test_shell/webwidget_host.h"
+
+#include "base/gfx/platform_canvas.h"
+#include "base/gfx/rect.h"
+#include "base/gfx/size.h"
+#include "base/win_util.h"
+#include "webkit/glue/webinputevent.h"
+#include "webkit/glue/webwidget.h"
+
+static const wchar_t kWindowClassName[] = L"WebWidgetHost";
+
+/*static*/
+WebWidgetHost* WebWidgetHost::Create(HWND parent_window, WebWidgetDelegate* delegate) {
+ WebWidgetHost* host = new WebWidgetHost();
+
+ static bool registered_class = false;
+ if (!registered_class) {
+ WNDCLASSEX wcex = {0};
+ wcex.cbSize = sizeof(wcex);
+ wcex.style = CS_DBLCLKS;
+ wcex.lpfnWndProc = WebWidgetHost::WndProc;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcex.lpszClassName = kWindowClassName;
+ RegisterClassEx(&wcex);
+ registered_class = true;
+ }
+
+ host->hwnd_ = CreateWindowEx(WS_EX_TOOLWINDOW,
+ kWindowClassName, kWindowClassName, WS_POPUP,
+ 0, 0, 0, 0,
+ parent_window, NULL, GetModuleHandle(NULL), NULL);
+
+ win_util::SetWindowUserData(host->hwnd_, host);
+
+ host->webwidget_ = WebWidget::Create(delegate);
+
+ return host;
+}
+
+/*static*/
+WebWidgetHost* WebWidgetHost::FromWindow(HWND hwnd) {
+ return reinterpret_cast<WebWidgetHost*>(win_util::GetWindowUserData(hwnd));
+}
+
+/*static*/
+LRESULT CALLBACK WebWidgetHost::WndProc(HWND hwnd, UINT message, WPARAM wparam,
+ LPARAM lparam) {
+ WebWidgetHost* host = FromWindow(hwnd);
+ if (host && !host->WndProc(message, wparam, lparam)) {
+ switch (message) {
+ case WM_DESTROY:
+ delete host;
+ break;
+
+ case WM_PAINT:
+ host->Paint();
+ return 0;
+
+ case WM_ERASEBKGND:
+ // Do nothing here to avoid flashing, the background will be erased
+ // during painting.
+ return 0;
+
+ case WM_SIZE:
+ host->Resize(lparam);
+ return 0;
+
+ case WM_MOUSEMOVE:
+ case WM_MOUSELEAVE:
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+ host->MouseEvent(message, wparam, lparam);
+ break;
+
+ case WM_MOUSEWHEEL:
+ host->WheelEvent(wparam, lparam);
+ break;
+
+ case WM_CAPTURECHANGED:
+ case WM_CANCELMODE:
+ host->CaptureLostEvent();
+ break;
+
+ // TODO(darin): add WM_SYSKEY{DOWN/UP} to capture ALT key actions
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_IME_CHAR:
+ host->KeyEvent(message, wparam, lparam);
+ break;
+
+ case WM_SETFOCUS:
+ host->SetFocus(true);
+ break;
+
+ case WM_KILLFOCUS:
+ host->SetFocus(false);
+ break;
+ }
+ }
+
+ return DefWindowProc(hwnd, message, wparam, lparam);;
+}
+
+void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) {
+ DLOG_IF(WARNING, painting_) << "unexpected invalidation while painting";
+
+ // If this invalidate overlaps with a pending scroll, then we have to
+ // downgrade to invalidating the scroll rect.
+ if (damaged_rect.Intersects(scroll_rect_)) {
+ paint_rect_ = paint_rect_.Union(scroll_rect_);
+ ResetScrollRect();
+ }
+ paint_rect_ = paint_rect_.Union(damaged_rect);
+
+ RECT r = damaged_rect.ToRECT();
+ InvalidateRect(hwnd_, &r, FALSE);
+}
+
+void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) {
+ DCHECK(dx || dy);
+
+ // If we already have a pending scroll operation or if this scroll operation
+ // intersects the existing paint region, then just failover to invalidating.
+ if (!scroll_rect_.IsEmpty() || paint_rect_.Intersects(clip_rect)) {
+ paint_rect_ = paint_rect_.Union(scroll_rect_);
+ ResetScrollRect();
+ paint_rect_ = paint_rect_.Union(clip_rect);
+ }
+
+ // We will perform scrolling lazily, when requested to actually paint.
+ scroll_rect_ = clip_rect;
+ scroll_dx_ = dx;
+ scroll_dy_ = dy;
+
+ RECT r = clip_rect.ToRECT();
+ InvalidateRect(hwnd_, &r, FALSE);
+}
+
+void WebWidgetHost::SetCursor(HCURSOR cursor) {
+ SetClassLong(hwnd_, GCL_HCURSOR,
+ static_cast<LONG>(reinterpret_cast<LONG_PTR>(cursor)));
+ ::SetCursor(cursor);
+}
+
+void WebWidgetHost::DiscardBackingStore() {
+ canvas_.reset();
+}
+
+WebWidgetHost::WebWidgetHost()
+ : hwnd_(NULL),
+ webwidget_(NULL),
+ track_mouse_leave_(false),
+ scroll_dx_(0),
+ scroll_dy_(0) {
+ set_painting(false);
+}
+
+WebWidgetHost::~WebWidgetHost() {
+ win_util::SetWindowUserData(hwnd_, 0);
+
+ TrackMouseLeave(false);
+
+ webwidget_->Close();
+ webwidget_->Release();
+}
+
+bool WebWidgetHost::WndProc(UINT message, WPARAM wparam, LPARAM lparam) {
+ switch (message) {
+ case WM_ACTIVATE:
+ if (wparam == WA_INACTIVE) {
+ PostMessage(hwnd_, WM_CLOSE, 0, 0);
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
+void WebWidgetHost::Paint() {
+ RECT r;
+ GetClientRect(hwnd_, &r);
+ gfx::Rect client_rect(r);
+
+ // Allocate a canvas if necessary
+ if (!canvas_.get()) {
+ ResetScrollRect();
+ paint_rect_ = client_rect;
+ canvas_.reset(new gfx::PlatformCanvas(
+ paint_rect_.width(), paint_rect_.height(), true));
+ }
+
+ // This may result in more invalidation
+ webwidget_->Layout();
+
+ // Scroll the canvas if necessary
+ scroll_rect_ = client_rect.Intersect(scroll_rect_);
+ if (!scroll_rect_.IsEmpty()) {
+ HDC hdc = canvas_->getTopPlatformDevice().getBitmapDC();
+
+ RECT damaged_rect, r = scroll_rect_.ToRECT();
+ ScrollDC(hdc, scroll_dx_, scroll_dy_, NULL, &r, NULL, &damaged_rect);
+
+ PaintRect(gfx::Rect(damaged_rect));
+ }
+ ResetScrollRect();
+
+ // Paint the canvas if necessary. Allow painting to generate extra rects the
+ // first time we call it. This is necessary because some WebCore rendering
+ // objects update their layout only when painted.
+ for (int i = 0; i < 2; ++i) {
+ paint_rect_ = client_rect.Intersect(paint_rect_);
+ if (!paint_rect_.IsEmpty()) {
+ gfx::Rect rect(paint_rect_);
+ paint_rect_ = gfx::Rect();
+
+ DLOG_IF(WARNING, i == 1) << "painting caused additional invalidations";
+ PaintRect(rect);
+ }
+ }
+ DCHECK(paint_rect_.IsEmpty());
+
+ // Paint to the screen
+ PAINTSTRUCT ps;
+ BeginPaint(hwnd_, &ps);
+ canvas_->getTopPlatformDevice().drawToHDC(ps.hdc,
+ ps.rcPaint.left,
+ ps.rcPaint.top,
+ &ps.rcPaint);
+ EndPaint(hwnd_, &ps);
+
+ // Draw children
+ UpdateWindow(hwnd_);
+}
+
+void WebWidgetHost::Resize(LPARAM lparam) {
+ // Force an entire re-paint. TODO(darin): Maybe reuse this memory buffer.
+ DiscardBackingStore();
+
+ webwidget_->Resize(gfx::Size(LOWORD(lparam), HIWORD(lparam)));
+}
+
+void WebWidgetHost::MouseEvent(UINT message, WPARAM wparam, LPARAM lparam) {
+ WebMouseEvent event(hwnd_, message, wparam, lparam);
+ switch (event.type) {
+ case WebInputEvent::MOUSE_MOVE:
+ TrackMouseLeave(true);
+ break;
+ case WebInputEvent::MOUSE_LEAVE:
+ TrackMouseLeave(false);
+ break;
+ case WebInputEvent::MOUSE_DOWN:
+ SetCapture(hwnd_);
+ break;
+ case WebInputEvent::MOUSE_UP:
+ if (GetCapture() == hwnd_)
+ ReleaseCapture();
+ break;
+ }
+ webwidget_->HandleInputEvent(&event);
+}
+
+void WebWidgetHost::WheelEvent(WPARAM wparam, LPARAM lparam) {
+ WebMouseWheelEvent event(hwnd_, WM_MOUSEWHEEL, wparam, lparam);
+ webwidget_->HandleInputEvent(&event);
+}
+
+void WebWidgetHost::KeyEvent(UINT message, WPARAM wparam, LPARAM lparam) {
+ WebKeyboardEvent event(hwnd_, message, wparam, lparam);
+ webwidget_->HandleInputEvent(&event);
+}
+
+void WebWidgetHost::CaptureLostEvent() {
+ webwidget_->MouseCaptureLost();
+}
+
+void WebWidgetHost::SetFocus(bool enable) {
+ webwidget_->SetFocus(enable);
+}
+
+void WebWidgetHost::TrackMouseLeave(bool track) {
+ if (track == track_mouse_leave_)
+ return;
+ track_mouse_leave_ = track;
+
+ DCHECK(hwnd_);
+
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = TME_LEAVE;
+ if (!track_mouse_leave_)
+ tme.dwFlags |= TME_CANCEL;
+ tme.hwndTrack = hwnd_;
+
+ TrackMouseEvent(&tme);
+}
+
+void WebWidgetHost::ResetScrollRect() {
+ scroll_rect_ = gfx::Rect();
+ scroll_dx_ = 0;
+ scroll_dy_ = 0;
+}
+
+void WebWidgetHost::PaintRect(const gfx::Rect& rect) {
+#ifndef NDEBUG
+ DCHECK(!painting_);
+#endif
+ DCHECK(canvas_.get());
+
+ set_painting(true);
+ webwidget_->Paint(canvas_.get(), rect);
+ set_painting(false);
+}
diff --git a/webkit/tools/test_shell/webwidget_host.h b/webkit/tools/test_shell/webwidget_host.h
new file mode 100644
index 0000000..e0dde64
--- /dev/null
+++ b/webkit/tools/test_shell/webwidget_host.h
@@ -0,0 +1,111 @@
+// 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.
+
+#ifndef WEBKIT_TOOLS_TEST_SHELL_WEBWIDGET_HOST_H__
+#define WEBKIT_TOOLS_TEST_SHELL_WEBWIDGET_HOST_H__
+
+#include <windows.h>
+
+#include "base/gfx/rect.h"
+#include "base/scoped_ptr.h"
+
+class WebWidget;
+class WebWidgetDelegate;
+
+namespace gfx {
+class PlatformCanvas;
+class Size;
+}
+
+// This class is a simple HWND-based host for a WebWidget
+class WebWidgetHost {
+ public:
+ // The new instance is deleted once the associated HWND is destroyed. The
+ // newly created window should be resized after it is created, using the
+ // MoveWindow (or equivalent) function.
+ static WebWidgetHost* Create(HWND parent_window, WebWidgetDelegate* delegate);
+
+ static WebWidgetHost* FromWindow(HWND hwnd);
+
+ HWND window_handle() const { return hwnd_; }
+ WebWidget* webwidget() const { return webwidget_; }
+
+ void DidInvalidateRect(const gfx::Rect& rect);
+ void DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect);
+ void SetCursor(HCURSOR cursor);
+
+ void DiscardBackingStore();
+
+ protected:
+ WebWidgetHost();
+ ~WebWidgetHost();
+
+ // Per-class wndproc. Returns true if the event should be swallowed.
+ virtual bool WndProc(UINT message, WPARAM wparam, LPARAM lparam);
+
+ void Paint();
+ void Resize(LPARAM lparam);
+ void MouseEvent(UINT message, WPARAM wparam, LPARAM lparam);
+ void WheelEvent(WPARAM wparam, LPARAM lparam);
+ void KeyEvent(UINT message, WPARAM wparam, LPARAM lparam);
+ void CaptureLostEvent();
+ void SetFocus(bool enable);
+
+ void TrackMouseLeave(bool enable);
+ void ResetScrollRect();
+ void PaintRect(const gfx::Rect& rect);
+
+ void set_painting(bool value) {
+#ifndef NDEBUG
+ painting_ = value;
+#endif
+ }
+
+ static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+
+ HWND hwnd_;
+ WebWidget* webwidget_;
+ scoped_ptr<gfx::PlatformCanvas> canvas_;
+
+ // specifies the portion of the webwidget that needs painting
+ gfx::Rect paint_rect_;
+
+ // specifies the portion of the webwidget that needs scrolling
+ gfx::Rect scroll_rect_;
+ int scroll_dx_;
+ int scroll_dy_;
+
+ bool track_mouse_leave_;
+
+#ifndef NDEBUG
+ bool painting_;
+#endif
+};
+
+#endif // WEBKIT_TOOLS_TEST_SHELL_WEBWIDGET_HOST_H__