diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:20:51 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:20:51 +0000 |
commit | f5b16fed647e941aa66933178da85db2860d639b (patch) | |
tree | f00e9856c04aad3b558a140955e7674add33f051 /webkit/tools/test_shell | |
parent | 920c091ac3ee15079194c82ae8a7a18215f3f23c (diff) | |
download | chromium_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')
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, ¤t_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 Binary files differnew file mode 100644 index 0000000..ac81cb0 --- /dev/null +++ b/webkit/tools/test_shell/resources/AHEM____.TTF 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 Binary files differnew file mode 100644 index 0000000..0f7215d --- /dev/null +++ b/webkit/tools/test_shell/resources/missingImage.gif diff --git a/webkit/tools/test_shell/resources/small.ico b/webkit/tools/test_shell/resources/small.ico Binary files differnew file mode 100644 index 0000000..d551aa3 --- /dev/null +++ b/webkit/tools/test_shell/resources/small.ico diff --git a/webkit/tools/test_shell/resources/test_shell.ico b/webkit/tools/test_shell/resources/test_shell.ico Binary files differnew file mode 100644 index 0000000..d551aa3 --- /dev/null +++ b/webkit/tools/test_shell/resources/test_shell.ico 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=""$(OutDir)\WebKit";"$(SolutionDir)";"$(IntDir)\..\localized_strings";"$(SolutionDir)webkit\port\bridge";"$(SolutionDir)webkit\port\platform";"$(SolutionDir)webkit\port\platform\network";"$(SolutionDir)webkit\glue";"$(SolutionDir)third_party\webkit\src\";"$(OutDir)\obj\WebCore\JavaScriptHeaders";"$(OutDir)"" + PreprocessorDefinitions="_CRT_SECURE_NO_DEPRECATE;_SCL_SECURE_NO_DEPRECATE" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="comctl32.lib shlwapi.lib rpcrt4.lib winmm.lib" + AdditionalLibraryDirectories=""$(OutDir)"" + /> + <Tool + Name="VCResourceCompilerTool" + AdditionalIncludeDirectories=""$(SolutionDir)";"$(IntDir)\..\"" + /> +</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__ |