diff options
author | Steve Block <steveblock@google.com> | 2009-11-05 09:23:40 +0000 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2009-11-10 22:41:12 +0000 |
commit | cac0f67c402d107cdb10971b95719e2ff9c7c76b (patch) | |
tree | d182c7f87211c6f201a5f038e332336493ebdbe7 /WebKitTools | |
parent | 4b2ef0f288e7c6c4602f621b7a0e9feed304b70e (diff) | |
download | external_webkit-cac0f67c402d107cdb10971b95719e2ff9c7c76b.zip external_webkit-cac0f67c402d107cdb10971b95719e2ff9c7c76b.tar.gz external_webkit-cac0f67c402d107cdb10971b95719e2ff9c7c76b.tar.bz2 |
Merge webkit.org at r50258 : Initial merge by git.
Change-Id: I1a9e1dc4ed654b69174ad52a4f031a07240f37b0
Diffstat (limited to 'WebKitTools')
109 files changed, 6938 insertions, 1571 deletions
diff --git a/WebKitTools/ChangeLog b/WebKitTools/ChangeLog index 6a8a912..7dcbf6c 100644 --- a/WebKitTools/ChangeLog +++ b/WebKitTools/ChangeLog @@ -1,3 +1,1028 @@ +2009-10-28 Roland Steiner <rolandsteiner@chromium.org> + + Adding myself to the committers list. + + * Scripts/modules/committers.py: + +2009-10-28 Chris Fleizach <cfleizach@apple.com> + + Adding myself to the committers list. + + * Scripts/modules/committers.py: + +2009-10-28 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk> + + Reviewed by Jan Alonzo. + + [GTK] API to start inspector for a WebView + https://bugs.webkit.org/show_bug.cgi?id=22551 + + Use the new inspector API to implement the LayoutTestController + interfaces used to test the inspector. + + * DumpRenderTree/gtk/DumpRenderTree.cpp: + (webInspectorInspectWebView): + (createWebView): + * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp: + (LayoutTestController::showWebInspector): + (LayoutTestController::closeWebInspector): + (LayoutTestController::evaluateInWebInspector): + +2009-10-28 Shinichiro Hamaji <hamaji@chromium.org> + + Reviewed by Eric Seidel. + + [Qt] WebFrame::counterValueForElementById must not be exposed + https://bugs.webkit.org/show_bug.cgi?id=30882 + + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::counterValueForElementById): + +2009-10-28 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Rubberstamped by Oliver Hunt. + + Fix the warning: + + "warning: ignoring return value of 'char* getcwd(char*, size_t)', + declared with attribute warn_unused_result". + + by actually checking the result. In the case it is null, an + error has occoured, so treat it as the other fatal errors. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::initializeFonts): + +2009-10-27 Shinichiro Hamaji <hamaji@chromium.org> + + Reviewed by Darin Adler. + + Provide a way to get counter values with layoutTestContoller + https://bugs.webkit.org/show_bug.cgi?id=30555 + + Define layoutTestContoller.counterValueForElementById. + + * DumpRenderTree/LayoutTestController.cpp: + (counterValueForElementByIdCallback): + (LayoutTestController::staticFunctions): + * DumpRenderTree/LayoutTestController.h: + * DumpRenderTree/mac/LayoutTestControllerMac.mm: + (LayoutTestController::counterValueForElementById): + +2009-10-27 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Rubberstamped by Oliver Hunt. + + Change two methods to be internal for DRT use only. + + Part of [Qt] Review all new API in Qt 4.6 + https://bugs.webkit.org/show_bug.cgi?id=29843#c11 + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::resetToConsistentStateBeforeTesting): + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::whiteListAccessFromOrigin): + +2009-10-27 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + REGRESSION: svn-apply exits(1) when applying a patch with a file add + https://bugs.webkit.org/show_bug.cgi?id=30826 + + * Scripts/svn-apply: + - Add () around all system() calls. + - Use the correct system() == 0 or die instead of system() or die + - Add descriptive messages to all die statements. + +2009-10-27 Steve Block <steveblock@google.com> + + Reviewed by NOBODY. + + Adds steveblock@google.com to list of committers. + + * Scripts/modules/committers.py: Adds steveblock@google.com to list of committers. + +2009-10-27 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + svn-apply can exit(0) even on patch failure + https://bugs.webkit.org/show_bug.cgi?id=29622 + + * Scripts/svn-apply: + - Add a bunch of "or die" statements, hopefully catching all + possible cases where failure could still exit(0). + +2009-10-27 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + svn-* scripts should share code through VCSUtils.pm + https://bugs.webkit.org/show_bug.cgi?id=30791 + + Just moving code into a shared location. + + * Scripts/VCSUtils.pm: + * Scripts/prepare-ChangeLog: + * Scripts/resolve-ChangeLogs: + * Scripts/svn-apply: + * Scripts/svn-create-patch: + * Scripts/svn-unapply: + * Scripts/update-webkit: + +2009-10-27 Vadim Zeitlin <vadim@wxwidgets.org> + + Suppress a huge number of MSVC warnings when building wxWebKit. + + * wx/build/settings.py: + +2009-10-26 Eric Seidel <eric@webkit.org> + + No review, just adding Mike Belshe to the committers list. + + * Scripts/modules/committers.py: + +2009-10-26 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Darin Adler. + + Make .rc files compile on Windows without depending on MFC headers + https://bugs.webkit.org/show_bug.cgi?id=30750 + + * DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.rc: Use + windows.h instead of afxres.h because it exists even when MFC is not + installed, and is all that's needed here. + + * FindSafari/FindSafari.rc: Ditto + +2009-10-24 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + bugzilla-tool post-diff should know how to mark commit-queue=? + https://bugs.webkit.org/show_bug.cgi?id=29202 + + * Scripts/bugzilla-tool: + - Add --commit-queue option to post-diff, post-commits and create-bug. + * Scripts/modules/bugzilla.py: + - Added support for --commit-queue to add_patch_to_bug and create_bug_with_patch. + - Added _fill_attachment_form to share code between add_patch_to_bug and create_bug_with_patch. + +2009-10-23 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + bugzilla-tool commit-queue does not notice modifications to committers.py + https://bugs.webkit.org/show_bug.cgi?id=30084 + + * Scripts/bugzilla-tool: + - Make commit-queue re-exec itself instead of using while(1). + - Add a --is-relaunch parameter to commit-queue to bypass initialization on re-launch. + - Add a _next_patch() method which calls exec() (and could eventually call update-webkit too). + +2009-10-22 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + commit-queue will get stuck on patches if land-patches terminates unexpectedly + https://bugs.webkit.org/show_bug.cgi?id=30634 + + * Scripts/bugzilla-tool: + - Add a way for land-patches to exit(2) to indicate an error, but one it has handled. + - Make commit-queue auto cq- any patch where land-patches exited anything other than '0' or '2'. + +2009-10-26 Yuzo Fujishima <yuzo@google.com> + + Reviewed by Eric Seidel. + + Upgrade pywebsocket to 0.4.1. This will make reusing LayoutTests/fast/js/resources easier, for example. + + https://bugs.webkit.org/show_bug.cgi?id=30763 + + * pywebsocket/mod_pywebsocket/__init__.py: + * pywebsocket/mod_pywebsocket/dispatch.py: + * pywebsocket/mod_pywebsocket/headerparserhandler.py: + * pywebsocket/mod_pywebsocket/standalone.py: + * pywebsocket/setup.py: + * pywebsocket/test/test_dispatch.py: + +2009-10-26 Carol Szabo <carol.szabo@nokia.com> + + Reviewed by David Levin. + + REGRESSION: 2 failures in run-webkit-unittests + https://bugs.webkit.org/show_bug.cgi?id=30645 + + * Scripts/modules/cpp_style_unittest.py: + Fixed a few test scenarios which apparently lost some spaces from + text literals. + +2009-10-26 Kevin Ollivier <kevino@theolliviers.com> + + wx build fix. Make sure isQt() doesn't return true if --wx was passed to build-webkit. + + * Scripts/webkitdirs.pm: + +2009-10-26 Csaba Osztrogonác <ossy@webkit.org> + + Reviewed by Darin Adler. + + Unification of using null device in perl scripts. + https://bugs.webkit.org/show_bug.cgi?id=30572 + + * Scripts/VCSUtils.pm: + * Scripts/bisect-builds: + * Scripts/resolve-ChangeLogs: + * Scripts/run-iexploder-tests: + * Scripts/run-jsc: + * Scripts/run-mangleme-tests: + * Scripts/run-webkit-tests: + * Scripts/webkitdirs.pm: + Using File::Spec->devnull() instead of hard coded /dev/null. + +2009-10-26 Eric Seidel <eric@webkit.org> + + Reviewed by Holger Freyther. + + Reviewers are missing from committers.py + https://bugs.webkit.org/show_bug.cgi?id=30733 + + * Scripts/modules/committers.py: + +2009-10-23 Eric Seidel <eric@webkit.org> + + No review, only adding Alice to the list of reviewers. + + * Scripts/modules/committers.py: + +2009-10-23 Eric Seidel <eric@webkit.org> + + Reviewed by Eric Carlson. + + fast/media/mq-transform-02.html failed on Leopard Commit Bot + https://bugs.webkit.org/show_bug.cgi?id=30700 + + * DumpRenderTree/mac/DumpRenderTree.mm: + (resetDefaultsToConsistentValues): Update QuickTime version check. + +2009-10-23 Kevin Ollivier <kevino@theolliviers.com> + + wxMac 10.4 build fix, needs to link against WebKitSystemInterfaceTiger to get + character measurement APIs that are private on Tiger. + + * wx/build/settings.py: + +2009-10-22 Gavin Barraclough <barraclough@apple.com> + + Reviewed by NOBODY (build fix). + Build fix following bug #30696. + + * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp: + (LayoutTestController::evaluateScriptInIsolatedWorld): + +2009-10-22 Gavin Barraclough <barraclough@apple.com> + + Reviewed by Sam Weinig & Geoff Garen. + + https://bugs.webkit.org/show_bug.cgi?id=30696 + Enable isolated-worlds tests on mac. + + Add private interface for DRT to invoke execution in a given world. + + * DumpRenderTree/LayoutTestController.cpp: + (evaluateScriptInIsolatedWorldCallback): + (LayoutTestController::staticFunctions): + * DumpRenderTree/LayoutTestController.h: + * DumpRenderTree/mac/LayoutTestControllerMac.mm: + (LayoutTestController::evaluateScriptInIsolatedWorld): + * DumpRenderTree/win/LayoutTestControllerWin.cpp: + (LayoutTestController::evaluateScriptInIsolatedWorld): + +2009-10-21 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + bugzilla-tool's "patch failed to download an apply" error should give more information + https://bugs.webkit.org/show_bug.cgi?id=30632 + + * Scripts/modules/scm.py: + - Use the common run_command method instead of custom POpen code. + - Make run_command know how to take pipes as input. + * Scripts/modules/scm_unittest.py: + - Add new tests to cover change. + - Also move test_error_handlers into new SCMClassTests so we don't run it 3 times. + +2009-10-21 Kent Tamura <tkent@chromium.org> + + Unreviewed. Adding myself to the committers list. + + * Scripts/modules/committers.py: + +2009-10-21 Robin Dunn <robin@alldunn.com> + + Reviewed by Kevin Ollivier. + + Update the Windows installer builder to work with Vista / Win 7 and with git. + + https://bugs.webkit.org/show_bug.cgi?id=30649 + + * wx/build/build_utils.py: + * wx/packaging/build-mac-installer.py: + * wx/packaging/build-win-installer.py: + * wx/packaging/wxWebKitInstaller.iss.in: + +2009-10-21 Alejandro G. Castro <alex@igalia.com> + + Reviewed by Gustavo Noronha. + + [GTK] Added conditional code to avoid using + gdk_window_get_root_coords if we do not have a gtk+ release newer + than 2.17.3. + https://bugs.webkit.org/show_bug.cgi?id=30636 + + * WebKitTools/DumpRenderTree/gtk/EventSender.cpp: + +2009-10-21 Shu Chang <Chang.Shu@nokia.com> + + Reviewed by Eric Seidel. + + [Qt] Added dummy implementation for keepWebHistory() + https://bugs.webkit.org/show_bug.cgi?id=30592 + + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::keepWebHistory): + * DumpRenderTree/qt/LayoutTestControllerQt.h: + +2009-10-21 Alejandro G. Castro <alex@igalia.com> + + Reviewed by Xan Lopez. + + [GTK] Fixed the double click condition, it is not double click if + we move in just in one direction. + https://bugs.webkit.org/show_bug.cgi?id=30636 + + * WebKitTools/DumpRenderTree/gtk/EventSender.cpp: + +2009-10-21 Alejandro G. Castro <alex@igalia.com> + + Reviewed by Xan Lopez. + + [GTK] Initialize the events completly before emitting them. + https://bugs.webkit.org/show_bug.cgi?id=30633 + + * WebKitTools/DumpRenderTree/gtk/EventSender.cpp: + +2009-10-21 Kevin Ollivier <kevino@theolliviers.com> + + wx build fix. Fix for when linking using --as-needed with gcc. + + * wx/browser/wscript: + +2009-10-21 Kevin Ollivier <kevino@theolliviers.com> + + wxMac 10.4 build fix. Build and link against a version of libcurl new enough + to support all the features used by CURL backend. + + * wx/build/settings.py: + * wx/install-unix-extras: + +2009-10-20 Anton Muhin <antonm@chromium.org> + + Reviewed by Adam Barth. + + Add {ager,antonm,yurys}@chromium.org into committers.py + https://bugs.webkit.org/show_bug.cgi?id=30560 + + * Scripts/modules/committers.py: + +2009-10-20 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Tor Arne Vestbø. + + Make the Netscape Test plugin available to the Qt launcher. + + * Scripts/run-launcher: + +2009-10-20 Fumitoshi Ukai <ukai@chromium.org> + + Reviewed by David Levin. + + Removed WebSocket runtime settings. + https://bugs.webkit.org/show_bug.cgi?id=29896 + + WebSocket runtime configuration is supported by chromium/v8 only. + + * DumpRenderTree/mac/DumpRenderTree.mm: + (resetDefaultsToConsistentValues): + * DumpRenderTree/win/DumpRenderTree.cpp: + (resetDefaultsToConsistentValues): + +2009-10-19 Nate Chapin <japhet@chromium.org> + + Reviewed by Adam Barth. + + Add testFail() to test plugin so we can test our handling of a + plugin invoke call returning false. + + https://bugs.webkit.org/show_bug.cgi?id=30239 + + * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp: Add testFail(). + (pluginInvoke): + (testIdentifierToString): Always return true, since returning false will now cause an exception to be thrown. + +2009-10-19 Zan Dobersek <zandobersek@gmail.com> + + Reviewed by Gustavo Noronha. + + Enable DOM pasting when running layout tests. + + * DumpRenderTree/gtk/DumpRenderTree.cpp: + (resetDefaultsToConsistentValues): + +2009-10-18 Kenneth Rohde Christiansen <kenneth@webkit.org> + + Reviewed by Simon Hausmann. + + Use the setPreferredContentsSize method instead + of setFixedContentsSize, as the method has been renamed. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::open): + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::setFixedContentsSize): + +2009-10-16 Steve Falkenburg <sfalken@apple.com> + + Reviewed by Dan Bernstein. + + https://bugs.webkit.org/show_bug.cgi?id=30456 + Fixes for new Debug_All Windows build configuration. + + * DumpRenderTree/win/DumpRenderTree.cpp: + (sharedCFURLCache): Use new DEBUG_ALL preprocessor define for library naming. + * DumpRenderTree/win/DumpRenderTree.vcproj: Remove unused DEBUG_WEBKIT_HAS_SUFFIX. + +2009-10-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + commit-queue status bot should list which buildbot is blocking the queue + https://bugs.webkit.org/show_bug.cgi?id=30452 + + Add new methods and testing. + + * Scripts/bugzilla-tool: + * Scripts/modules/buildbot.py: + * Scripts/modules/buildbot_unittest.py: + +2009-10-16 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + run-webkit-tests fails when CWD is not inside a WebKit checkout + https://bugs.webkit.org/show_bug.cgi?id=30451 + + * Scripts/modules/scm.py: in_working_directory shouldn't throw exceptions on failure. + * Scripts/modules/scm_unittest.py: + - Remove use of original_path (we don't need to restore the CWD). + - Don't use '.' to find the webkit checkout, use __file__ instead. + +2009-10-16 Steve Falkenburg <sfalken@apple.com> + + Reviewed by Adam Roben. + + Add a Debug_All configuration to build entire stack as debug. + Change Debug_Internal to: + - stop using _debug suffix for all WebKit/Safari binaries + - not use _debug as a DLL naming suffix + - use non-debug C runtime lib. + + * DumpRenderTree/DumpRenderTree.sln: Add Debug_All configuration. + * DumpRenderTree/win/DumpRenderTree.vcproj: Add Debug_All configuration. + * DumpRenderTree/win/ImageDiff.vcproj: Add Debug_All configuration. + * DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.vcproj: + Add missing debug.vsprops inherited property sheet. + Add Debug_All configuration. + * FindSafari/FindSafari.vcproj: Renamed single configuration from "Release" to "all". + * WinLauncher/WinLauncher.vcproj: + Removed extraneous definitions inherited from vsprops. + Add Debug_All configuration. + +2009-10-16 Carol Szabo <carol.szabo@nokia.com> + + Reviewed by David Levin. + + check-webkit-style is wrong about indent checking in namespaces + in header files and a few other things + https://bugs.webkit.org/show_bug.cgi?id=30362 + + The few other things include: + + check-webkit-style does not require spaces around the equal sign + inside 'if' statements and around binary operators that take + numeric literals. + + check-webkit-style reports false errors for the / operator + when part of a filename in the #include directive. + + * Scripts/modules/cpp_style.py: + Improved indentation checking and space checking around + binary operators. While the checks are still not perfect, + they are clearly better than before. + * Scripts/modules/cpp_style_unittest.py: + Added test cases for the newly supported checks and modified old + test cases to match the new guidelines + +2009-10-16 Kevin Ollivier <kevino@theolliviers.com> + + wxMSW build fix. Link to MSW library needed by PluginPackageWin.cpp. + + * wx/build/settings.py: + +2009-10-15 Yuzo Fujishima <yuzo@google.com> + + Reviewed by David Levin. + + Add mod_pywebsocket to test Web Sockets. + http://code.google.com/p/pywebsocket/ + https://bugs.webkit.org/show_bug.cgi?id=27490 + + * pywebsocket/COPYING: Added. + * pywebsocket/MANIFEST.in: Added. + * pywebsocket/README: Added. + * pywebsocket/example/echo_client.py: Added. + * pywebsocket/example/echo_wsh.py: Added. + * pywebsocket/mod_pywebsocket/__init__.py: Added. + * pywebsocket/mod_pywebsocket/dispatch.py: Added. + * pywebsocket/mod_pywebsocket/handshake.py: Added. + * pywebsocket/mod_pywebsocket/headerparserhandler.py: Added. + * pywebsocket/mod_pywebsocket/msgutil.py: Added. + * pywebsocket/mod_pywebsocket/standalone.py: Added. + * pywebsocket/mod_pywebsocket/util.py: Added. + * pywebsocket/setup.py: Added. + * pywebsocket/test/config.py: Added. + * pywebsocket/test/mock.py: Added. + * pywebsocket/test/run_all.py: Added. + * pywebsocket/test/test_dispatch.py: Added. + * pywebsocket/test/test_handshake.py: Added. + * pywebsocket/test/test_mock.py: Added. + * pywebsocket/test/test_msgutil.py: Added. + * pywebsocket/test/test_util.py: Added. + * pywebsocket/test/testdata/handlers/blank_wsh.py: Added. + * pywebsocket/test/testdata/handlers/origin_check_wsh.py: Added. + * pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py: Added. + * pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py: Added. + * pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py: Added. + * pywebsocket/test/testdata/handlers/sub/plain_wsh.py: Added. + * pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py: Added. + * pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py: Added. + +2009-10-15 James Robinson <jamesr@google.com> + + Reviewed by David Levin. + + Updates check-webkit-style to reflect that code inside a namespace should not be indented, even in a header file. + + https://bugs.webkit.org/show_bug.cgi?id=30426 + + * Scripts/modules/cpp_style.py: + * Scripts/modules/cpp_style_unittest.py: + +2009-10-15 Brian Weinstein <bweinstein@apple.com> + + Reviewed by Adam Roben. + + Fixes <http://webkit.org/b/30411>. + REGRESSION(49485): pdevenv doesn't compile in parallel for non-chromium builds on Windows. + + Added a check for isChromium() in pdevenv, and pass /useenv if we are not + building Chromium. + + * Scripts/pdevenv: + +2009-10-15 Robin Dunn <robin@alldunn.com> + + Reviewed by Kevin Ollivier. + + Add Mac package building scripts for wx. + + https://bugs.webkit.org/show_bug.cgi?id=30405 + + * wx/build/build_utils.py: + * wx/build/settings.py: + * wx/packaging/build-mac-installer.py: Added. + +2009-10-15 Zan Dobersek <zandobersek@gmail.com> + + Reviewed by Gustavo Noronha. + + Enable Web Sockets support when running layout tests. + + * DumpRenderTree/gtk/DumpRenderTree.cpp: + (resetDefaultsToConsistentValues): + +2009-10-15 Xan Lopez <xlopez@igalia.com> + + Reviewed by Gustavo Noronha. + + Need to initialize event.button.button, since in most cases a + button number is not passed as an argument. + + * DumpRenderTree/gtk/EventSender.cpp: + (mouseDownCallback): + (mouseUpCallback): + +2009-10-15 Kevin Ollivier <kevino@theolliviers.com> + + wx build fix. More SDK fixes for Mac, make sure we use the SDK corresponding to + the OS if none was explicitly set. + + * wx/build/settings.py: + +2009-10-14 Pavel Feldman <pfeldman@chromium.org> + + Reviewed by Timothy Hatcher. + + Web Inspector: enable developers extras within inspector layout tests. + + https://bugs.webkit.org/show_bug.cgi?id=30014 + + * DumpRenderTree/mac/LayoutTestControllerMac.mm: + (LayoutTestController::showWebInspector): + (LayoutTestController::closeWebInspector): + * DumpRenderTree/win/LayoutTestControllerWin.cpp: + (LayoutTestController::showWebInspector): + (LayoutTestController::closeWebInspector): + +2009-10-14 José Millán Soto <jmillan@igalia.com> + + Reviewed by Jan Alonzo. + + GtkLauncher is using a deprecated signal + https://bugs.webkit.org/show_bug.cgi?id=30364 + + Modified GtkLauncher to use notify::title signal instead of + deprecated title-changed signal + + * GtkLauncher/main.c: + (notify_title_cb): + (create_browser): + +2009-10-14 Brady Eidson <beidson@apple.com> + + Reviewed by Sam Weinig. + + Don't set the history delegate on new windows that are opened during a test, as the history delegate: + 1 - Disables WebHistory + 2 - Doesn't make sense in that context anyway. + + * DumpRenderTree/win/DumpRenderTree.cpp: + (createWebViewAndOffscreenWindow): + +2009-10-14 Kevin Ollivier <kevino@theolliviers.com> + + wxMac build fix. Ensure 10.4 compatibility for deps, and allow the user to specify + the SDK to use since Python overrides any user-set value of MACOSX_DEPLOYMENT_TARGET. + + * wx/build/settings.py: + * wx/install-unix-extras: + +2009-10-14 Brady Eidson <beidson@apple.com> + + Reviewed by Sam Weinig. + + WebKit Win API should provide a delegate interface for global history. + https://bugs.webkit.org/show_bug.cgi?id=29905 + + * DumpRenderTree/LayoutTestController.cpp: + (LayoutTestController::LayoutTestController): + * DumpRenderTree/LayoutTestController.h: + (LayoutTestController::dumpHistoryDelegateCallbacks): + (LayoutTestController::setDumpHistoryDelegateCallbacks): + + * DumpRenderTree/win/DumpRenderTree.cpp: + (shouldLogHistoryDelegates): + (runTest): + (createWebViewAndOffscreenWindow): + (main): + * DumpRenderTree/win/DumpRenderTree.vcproj: + + Add the IWebHistoryDelegate to DRT Windows: + * DumpRenderTree/win/HistoryDelegate.cpp: Added. + (wstringFromBSTR): + (HistoryDelegate::HistoryDelegate): + (HistoryDelegate::~HistoryDelegate): + (HistoryDelegate::QueryInterface): + (HistoryDelegate::AddRef): + (HistoryDelegate::Release): + (HistoryDelegate::didNavigateWithNavigationData): + (HistoryDelegate::didPerformClientRedirectFromURL): + (HistoryDelegate::didPerformServerRedirectFromURL): + (HistoryDelegate::updateHistoryTitle): + (HistoryDelegate::populateVisitedLinksForWebView): + * DumpRenderTree/win/HistoryDelegate.h: Added. + + * DumpRenderTree/win/LayoutTestControllerWin.cpp: + (LayoutTestController::removeAllVisitedLinks): + +2009-10-14 Shu Chang <Chang.Shu@nokia.com> + + Reviewed by Simon Hausmann. + + [Qt] Implement support for setPOSIXLocale on Qt. + https://bugs.webkit.org/show_bug.cgi?id=30268 + + * DumpRenderTree/qt/DumpRenderTree.cpp: + (WebCore::DumpRenderTree::resetToConsistentStateBeforeTesting): + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::setPOSIXLocale): + * DumpRenderTree/qt/LayoutTestControllerQt.h: + +2009-10-13 Stephanie Lewis <slewis@apple.com> + + Unreviewed, adding myself to reviewers list. + + * Scripts/modules/committers.py: + +2009-10-13 Stephanie Lewis <slewis@apple.com> + + Reviewed by Mark Rowe. + + Fix null assignment so root tests work again. + + * Scripts/webkitdirs.pm: + +2009-10-13 Andras Becsi <becsi.andras@stud.u-szeged.hu> + + Reviewed by Simon Hausmann. + + [Qt] Refactor LayoutTestController, EventSender, TextInputController and WorkQueueItem classes + out of jsobjects into separate files to get a more structured DumpRenderTree implementation. + This is done in preparation of implementing missing features in DRT. + No functionality changes made yet. + + * DumpRenderTree/qt/DumpRenderTree.cpp: + * DumpRenderTree/qt/DumpRenderTree.pro: + * DumpRenderTree/qt/EventSenderQt.cpp: Added. + (EventSender::EventSender): + (EventSender::mouseDown): + (EventSender::mouseUp): + (EventSender::mouseMoveTo): + (EventSender::leapForward): + (EventSender::keyDown): + (EventSender::contextClick): + (EventSender::scheduleAsynchronousClick): + (EventSender::frameUnderMouse): + * DumpRenderTree/qt/EventSenderQt.h: Added. + (EventSender::clearKillRing): + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: Added. + (LayoutTestController::LayoutTestController): + (LayoutTestController::reset): + (LayoutTestController::processWork): + (LayoutTestController::maybeDump): + (LayoutTestController::waitUntilDone): + (LayoutTestController::notifyDone): + (LayoutTestController::windowCount): + (LayoutTestController::clearBackForwardList): + (LayoutTestController::dumpEditingCallbacks): + (LayoutTestController::dumpResourceLoadCallbacks): + (LayoutTestController::queueBackNavigation): + (LayoutTestController::queueForwardNavigation): + (LayoutTestController::queueLoad): + (LayoutTestController::queueReload): + (LayoutTestController::queueScript): + (LayoutTestController::provisionalLoad): + (LayoutTestController::timerEvent): + (LayoutTestController::encodeHostName): + (LayoutTestController::decodeHostName): + (LayoutTestController::setJavaScriptProfilingEnabled): + (LayoutTestController::setFixedContentsSize): + (LayoutTestController::setPrivateBrowsingEnabled): + (LayoutTestController::setPopupBlockingEnabled): + (LayoutTestController::pauseAnimationAtTimeOnElementWithId): + (LayoutTestController::pauseTransitionAtTimeOnElementWithId): + (LayoutTestController::numberOfActiveAnimations): + (LayoutTestController::disableImageLoading): + (LayoutTestController::dispatchPendingLoadRequests): + (LayoutTestController::setDatabaseQuota): + (LayoutTestController::clearAllDatabases): + (LayoutTestController::whiteListAccessFromOrigin): + (LayoutTestController::waitForPolicyDelegate): + (LayoutTestController::overridePreference): + * DumpRenderTree/qt/LayoutTestControllerQt.h: Added. + (LayoutTestController::isLoading): + (LayoutTestController::setLoading): + (LayoutTestController::shouldDumpAsText): + (LayoutTestController::shouldDumpBackForwardList): + (LayoutTestController::shouldDumpChildrenAsText): + (LayoutTestController::shouldDumpDatabaseCallbacks): + (LayoutTestController::shouldDumpStatusCallbacks): + (LayoutTestController::shouldWaitUntilDone): + (LayoutTestController::canOpenWindows): + (LayoutTestController::shouldDumpTitleChanges): + (LayoutTestController::waitForPolicy): + (LayoutTestController::dumpAsText): + (LayoutTestController::dumpChildFramesAsText): + (LayoutTestController::dumpDatabaseCallbacks): + (LayoutTestController::dumpStatusCallbacks): + (LayoutTestController::setCanOpenWindows): + (LayoutTestController::dumpBackForwardList): + (LayoutTestController::setCloseRemainingWindowsWhenComplete): + (LayoutTestController::display): + (LayoutTestController::dumpTitleChanges): + (LayoutTestController::dumpSelectionRect): + * DumpRenderTree/qt/TextInputControllerQt.cpp: Added. + (TextInputController::TextInputController): + (TextInputController::doCommand): + * DumpRenderTree/qt/TextInputControllerQt.h: Added. + * DumpRenderTree/qt/WorkQueue.cpp: + * DumpRenderTree/qt/WorkQueue.h: + * DumpRenderTree/qt/WorkQueueItem.h: + * DumpRenderTree/qt/WorkQueueItemQt.cpp: Added. + (findFrameNamed): + (LoadItem::invoke): + (ReloadItem::invoke): + (ScriptItem::invoke): + (BackForwardItem::invoke): + * DumpRenderTree/qt/jsobjects.cpp: Move all the above classes into separate files + * DumpRenderTree/qt/jsobjects.h: + +2009-10-13 Dmitry Titov <dimich@chromium.org> + + Not reviewed, adding myself to the list of reviewers. + + * Scripts/modules/committers.py: + +2009-10-13 Anders Carlsson <andersca@apple.com> + + Reviewed by Sam Weinig. + + <rdar://problem/6660507> Add "privacy mode" to Netscape Plug-in API + + Make the private browsing mode testable by the test plug-in. + + * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp: + (pluginGetProperty): + * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h: + * DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp: + (NPP_New): + (NPP_SetValue): + +2009-10-13 Pavel Feldman <pfeldman@chromium.org> + + No review, just adding self to the list of reviewers. + + * Scripts/modules/committers.py: + +2009-10-12 Pavel Feldman <pfeldman@chromium.org> + + Reviewed by Adam Roben. + + Web Inspector: Use proper web view in inspector layout + tests for windows. + + https://bugs.webkit.org/show_bug.cgi?id=30298 + + * DumpRenderTree/win/LayoutTestControllerWin.cpp: + (LayoutTestController::showWebInspector): + (LayoutTestController::closeWebInspector): + (LayoutTestController::evaluateInWebInspector): + +2009-10-12 Fumitoshi Ukai <ukai@chromium.org> + + Reviewed by Sam Weinig. + + Enable experimentalWebSocket in DumpRenderTree for LayoutTest. + https://bugs.webkit.org/show_bug.cgi?id=29841 + + * DumpRenderTree/mac/DumpRenderTree.mm: + (resetDefaultsToConsistentValues): + * DumpRenderTree/win/DumpRenderTree.cpp: + (resetDefaultsToConsistentValues): + +2009-10-12 Yaar Schnitman <yaar@chromium.org> + + Reviewed by Dimitri Glazkov. + + Chromium Port - Windows + https://bugs.webkit.org/show_bug.cgi?id=29969 + + * Scripts/pdevenv: removed msvc's /useenv for chromium builds + * Scripts/webkitdirs.pm: + +2009-10-12 Csaba Osztrogonac <ossy@webkit.org> + + Reviewed by Darin Adler. + + jsc scripts cleanup and Qt/GTK fix + https://bugs.webkit.org/show_bug.cgi?id=30288 + + Duplicated jscPath() moved to webkitdirs.pm. + New jscProductDir() added to webkitdirs.pm instead of duplicated codes. + Configuration added (release/debug) to path for Qt-port on Windows. + + * Scripts/run-javascriptcore-tests: + * Scripts/run-jsc: + * Scripts/run-sunspider: + * Scripts/sunspider-compare-results: + * Scripts/webkitdirs.pm: + +2009-10-11 Adam Barth <abarth@webkit.org> + + Unreviewed. Add Collin to committers.py. + + * Scripts/modules/committers.py: + +2009-10-11 Kevin Ollivier <kevino@theolliviers.com> + + wx build fix, add bindings to source/include dirs now that there are sources there. + + * wx/build/settings.py: + +2009-10-09 Mark Rowe <mrowe@apple.com> + + Reviewed by Brady Eidson. + + Enable plug-in halting in DumpRenderTree. + + We drop the plug-in halting delay to 1 second and opt in the delegate method to never halt plug-ins. + This is sufficient to ensure that the crash covered by <rdar://problem/7290671> no longer occurs. + + * DumpRenderTree/mac/DumpRenderTree.mm: + (resetDefaultsToConsistentValues): + * DumpRenderTree/mac/UIDelegate.mm: + (-[UIDelegate webView:shouldHaltPlugin:]): + +2009-10-08 Brady Eidson <beidson@apple.com> + + Reviewed by Dan Bernstein. + + Ask the History Delegate to populate the visited links hash. + <rdar://problem/7285293> and https://webkit.org/b/29904 + + Add the ability for LayoutTestController to clear all visited links. + Also lets the History Delegate dump visited links, but only if this test specifically cleared them. + + * DumpRenderTree/LayoutTestController.cpp: + (LayoutTestController::LayoutTestController): + (removeAllVisitedLinksCallback): + (LayoutTestController::staticFunctions): + * DumpRenderTree/LayoutTestController.h: + * DumpRenderTree/mac/LayoutTestControllerMac.mm: + (LayoutTestController::removeAllVisitedLinks): + + * DumpRenderTree/mac/HistoryDelegate.mm: + (-[HistoryDelegate populateVisitedLinksForWebView:]): + + * DumpRenderTree/gtk/LayoutTestControllerGtk.cpp: + (LayoutTestController::removeAllVisitedLinks): + * DumpRenderTree/win/LayoutTestControllerWin.cpp: + (LayoutTestController::removeAllVisitedLinks): + * DumpRenderTree/wx/LayoutTestControllerWx.cpp: + (LayoutTestController::removeAllVisitedLinks): + + +2009-10-08 Daniel Bates <dbates@webkit.org> + + Reviewed by Adam Roben. + + https://bugs.webkit.org/show_bug.cgi?id=30175 + + The Windows DRT equivalent of DoDragDrop (i.e. UIDelegate::doDragDrop) does not return + the OLE drag-and-drop return value like the function it emulates. Currently, + UIDelegate::doDragDrop returns a hard-coded S_OK. Hence, the caller cannot determine + whether the drag-and-drop operation was successful or was cancelled. + + This patch fixes this issue by having UIDelegate::doDragDrop return the OLE drag-and-drop + return value according to whether the drop operation was successful or not. + + * DumpRenderTree/win/EventSender.cpp: + (doMouseUp): Added parameter oleDragAndDropReturnValue. + (replaySavedEvents): Ditto. + * DumpRenderTree/win/EventSender.h: + * DumpRenderTree/win/UIDelegate.cpp: + (UIDelegate::doDragDrop): Modified to return OLE drag-and-drop return value. + +2009-10-08 Alejandro G. Castro <alex@igalia.com> + + Reviewed by Xan Lopez. + + [GTK] Added support for a parameter setting the button that was + pressed in the mouseDown function. + https://bugs.webkit.org/show_bug.cgi?id=30220 + + * WebKitTools/DumpRenderTree/gtk/EventSender.cpp: + +2009-10-08 Alejandro G. Castro <alex@igalia.com> + + Reviewed by Xan Lopez. + + [GTK] Added a line to the bottom of the expected result to match + the output of the test. + https://bugs.webkit.org/show_bug.cgi?id=30220 + + * LayoutTests/platform/gtk/editing/pasteboard/middle-click-onpaste- + expected.txt: + 2009-10-08 Adam Roben <aroben@apple.com> Use QueryInterface to get IWebInspectorPrivate diff --git a/WebKitTools/DumpRenderTree/DumpRenderTree.sln b/WebKitTools/DumpRenderTree/DumpRenderTree.sln index 43d2b61..399c002 100644 --- a/WebKitTools/DumpRenderTree/DumpRenderTree.sln +++ b/WebKitTools/DumpRenderTree/DumpRenderTree.sln @@ -11,29 +11,38 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageDiff", "win\ImageDiff. EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug_All|Win32 = Debug_All|Win32
Debug_Internal|Win32 = Debug_Internal|Win32
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Debug_All|Win32.Build.0 = Debug_All|Win32
{6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Debug_Internal|Win32.ActiveCfg = Debug_Internal|Win32
{6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Debug_Internal|Win32.Build.0 = Debug_Internal|Win32
{6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Debug|Win32.ActiveCfg = Debug|Win32
{6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Debug|Win32.Build.0 = Debug|Win32
{6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Release|Win32.ActiveCfg = Release|Win32
{6567DFD4-D6DE-4CD5-825D-17E353D160E1}.Release|Win32.Build.0 = Release|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug_All|Win32.Build.0 = Debug_All|Win32
{C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug_Internal|Win32.ActiveCfg = Debug_Internal|Win32
{C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug_Internal|Win32.Build.0 = Debug_Internal|Win32
{C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug|Win32.ActiveCfg = Debug|Win32
{C0737398-3565-439E-A2B8-AB2BE4D5430C}.Debug|Win32.Build.0 = Debug|Win32
{C0737398-3565-439E-A2B8-AB2BE4D5430C}.Release|Win32.ActiveCfg = Release|Win32
{C0737398-3565-439E-A2B8-AB2BE4D5430C}.Release|Win32.Build.0 = Release|Win32
- {DA31DA52-6675-48D4-89E0-333A7144397C}.Debug_Internal|Win32.ActiveCfg = Debug|Win32
- {DA31DA52-6675-48D4-89E0-333A7144397C}.Debug_Internal|Win32.Build.0 = Debug|Win32
- {DA31DA52-6675-48D4-89E0-333A7144397C}.Debug|Win32.ActiveCfg = Debug|Win32
- {DA31DA52-6675-48D4-89E0-333A7144397C}.Debug|Win32.Build.0 = Debug|Win32
- {DA31DA52-6675-48D4-89E0-333A7144397C}.Release|Win32.ActiveCfg = Release|Win32
- {DA31DA52-6675-48D4-89E0-333A7144397C}.Release|Win32.Build.0 = Release|Win32
+ {DA31DA52-6675-48D4-89E0-333A7144397C}.Debug_All|Win32.ActiveCfg = all|Win32
+ {DA31DA52-6675-48D4-89E0-333A7144397C}.Debug_All|Win32.Build.0 = all|Win32
+ {DA31DA52-6675-48D4-89E0-333A7144397C}.Debug_Internal|Win32.ActiveCfg = all|Win32
+ {DA31DA52-6675-48D4-89E0-333A7144397C}.Debug_Internal|Win32.Build.0 = all|Win32
+ {DA31DA52-6675-48D4-89E0-333A7144397C}.Debug|Win32.ActiveCfg = all|Win32
+ {DA31DA52-6675-48D4-89E0-333A7144397C}.Debug|Win32.Build.0 = all|Win32
+ {DA31DA52-6675-48D4-89E0-333A7144397C}.Release|Win32.ActiveCfg = all|Win32
+ {DA31DA52-6675-48D4-89E0-333A7144397C}.Release|Win32.Build.0 = all|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug_All|Win32.ActiveCfg = Debug_All|Win32
+ {59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug_All|Win32.Build.0 = Debug_All|Win32
{59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug_Internal|Win32.ActiveCfg = Debug_Internal|Win32
{59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug_Internal|Win32.Build.0 = Debug_Internal|Win32
{59CC0547-70AC-499C-9B19-EC01C6F61137}.Debug|Win32.ActiveCfg = Debug|Win32
diff --git a/WebKitTools/DumpRenderTree/LayoutTestController.cpp b/WebKitTools/DumpRenderTree/LayoutTestController.cpp index 1f34325..daf888f 100644 --- a/WebKitTools/DumpRenderTree/LayoutTestController.cpp +++ b/WebKitTools/DumpRenderTree/LayoutTestController.cpp @@ -31,6 +31,7 @@ #include "WorkQueue.h" #include "WorkQueueItem.h" +#include <JavaScriptCore/JSContextRef.h> #include <JavaScriptCore/JSObjectRef.h> #include <JavaScriptCore/JSRetainPtr.h> #include <stdio.h> @@ -48,12 +49,14 @@ LayoutTestController::LayoutTestController(const std::string& testPathOrURL, con , m_dumpDatabaseCallbacks(false) , m_dumpEditingCallbacks(false) , m_dumpFrameLoadCallbacks(false) + , m_dumpHistoryDelegateCallbacks(false) , m_dumpResourceLoadCallbacks(false) , m_dumpResourceResponseMIMETypes(false) , m_dumpSelectionRect(false) , m_dumpSourceAsWebArchive(false) , m_dumpStatusCallbacks(false) , m_dumpTitleChanges(false) + , m_dumpVisitedLinksCallback(false) , m_dumpWillCacheResponse(false) , m_callCloseOnWebViews(true) , m_canOpenWindows(false) @@ -204,6 +207,14 @@ static JSValueRef pathToLocalResourceCallback(JSContextRef context, JSObjectRef return JSValueMakeString(context, convertedPath.get()); } +static JSValueRef removeAllVisitedLinksCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject)); + controller->setDumpVisitedLinksCallback(true); + controller->removeAllVisitedLinks(); + return JSValueMakeUndefined(context); +} + static JSValueRef repaintSweepHorizontallyCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject)); @@ -374,6 +385,22 @@ static JSValueRef execCommandCallback(JSContextRef context, JSObjectRef function return JSValueMakeUndefined(context); } +static JSValueRef counterValueForElementByIdCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + if (argumentCount < 1) + return JSValueMakeUndefined(context); + + JSRetainPtr<JSStringRef> elementId(Adopt, JSValueToStringCopy(context, arguments[0], exception)); + if (*exception) + return JSValueMakeUndefined(context); + + LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject)); + JSRetainPtr<JSStringRef> counterValue(controller->counterValueForElementById(elementId.get())); + if (!counterValue.get()) + return JSValueMakeUndefined(context); + return JSValueMakeString(context, counterValue.get()); +} + static JSValueRef grantDesktopNotificationPermissionCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { // Has Windows implementation @@ -968,6 +995,18 @@ static JSValueRef evaluateInWebInspectorCallback(JSContextRef context, JSObjectR return JSValueMakeUndefined(context); } +static JSValueRef evaluateScriptInIsolatedWorldCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject)); + double worldID = JSValueToNumber(context, arguments[0], exception); + ASSERT(!*exception); + JSRetainPtr<JSStringRef> script(Adopt, JSValueToStringCopy(context, arguments[1], exception)); + ASSERT(!*exception); + + controller->evaluateScriptInIsolatedWorld(static_cast<unsigned>(worldID), JSContextGetGlobalObject(context), script.get()); + return JSValueMakeUndefined(context); +} + static JSValueRef elementDoesAutoCompleteForElementWithIdCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject)); @@ -1176,7 +1215,9 @@ JSStaticFunction* LayoutTestController::staticFunctions() { "elementDoesAutoCompleteForElementWithId", elementDoesAutoCompleteForElementWithIdCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "encodeHostName", encodeHostNameCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "evaluateInWebInspector", evaluateInWebInspectorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "evaluateScriptInIsolatedWorld", evaluateScriptInIsolatedWorldCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "execCommand", execCommandCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "counterValueForElementById", counterValueForElementByIdCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "grantDesktopNotificationPermission", grantDesktopNotificationPermissionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "isCommandEnabled", isCommandEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "keepWebHistory", keepWebHistoryCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, @@ -1193,6 +1234,7 @@ JSStaticFunction* LayoutTestController::staticFunctions() { "queueLoadingScript", queueLoadingScriptCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "queueNonLoadingScript", queueNonLoadingScriptCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "queueReload", queueReloadCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "removeAllVisitedLinks", removeAllVisitedLinksCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "repaintSweepHorizontally", repaintSweepHorizontallyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setAcceptsEditing", setAcceptsEditingCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setAlwaysAcceptCookies", setAlwaysAcceptCookiesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, diff --git a/WebKitTools/DumpRenderTree/LayoutTestController.h b/WebKitTools/DumpRenderTree/LayoutTestController.h index 7c829ef..79ffb99 100644 --- a/WebKitTools/DumpRenderTree/LayoutTestController.h +++ b/WebKitTools/DumpRenderTree/LayoutTestController.h @@ -30,6 +30,7 @@ #define LayoutTestController_h #include <JavaScriptCore/JSObjectRef.h> +#include <JavaScriptCore/JSRetainPtr.h> #include <wtf/RefCounted.h> #include <string> #include <vector> @@ -51,6 +52,7 @@ public: void dispatchPendingLoadRequests(); void display(); void execCommand(JSStringRef name, JSStringRef value); + JSRetainPtr<JSStringRef> counterValueForElementById(JSStringRef id); bool isCommandEnabled(JSStringRef name); void keepWebHistory(); void notifyDone(); @@ -62,6 +64,7 @@ public: void queueLoadingScript(JSStringRef script); void queueNonLoadingScript(JSStringRef script); void queueReload(); + void removeAllVisitedLinks(); void setAcceptsEditing(bool acceptsEditing); void setAppCacheMaximumSize(unsigned long long quota); void setAuthorAndUserStylesEnabled(bool); @@ -120,6 +123,9 @@ public: bool dumpFrameLoadCallbacks() const { return m_dumpFrameLoadCallbacks; } void setDumpFrameLoadCallbacks(bool dumpFrameLoadCallbacks) { m_dumpFrameLoadCallbacks = dumpFrameLoadCallbacks; } + bool dumpHistoryDelegateCallbacks() const { return m_dumpHistoryDelegateCallbacks; } + void setDumpHistoryDelegateCallbacks(bool dumpHistoryDelegateCallbacks) { m_dumpHistoryDelegateCallbacks = dumpHistoryDelegateCallbacks; } + bool dumpResourceLoadCallbacks() const { return m_dumpResourceLoadCallbacks; } void setDumpResourceLoadCallbacks(bool dumpResourceLoadCallbacks) { m_dumpResourceLoadCallbacks = dumpResourceLoadCallbacks; } @@ -137,6 +143,9 @@ public: bool dumpTitleChanges() const { return m_dumpTitleChanges; } void setDumpTitleChanges(bool dumpTitleChanges) { m_dumpTitleChanges = dumpTitleChanges; } + + bool dumpVisitedLinksCallback() const { return m_dumpVisitedLinksCallback; } + void setDumpVisitedLinksCallback(bool dumpVisitedLinksCallback) { m_dumpVisitedLinksCallback = dumpVisitedLinksCallback; } bool dumpWillCacheResponse() const { return m_dumpWillCacheResponse; } void setDumpWillCacheResponse(bool dumpWillCacheResponse) { m_dumpWillCacheResponse = dumpWillCacheResponse; } @@ -206,6 +215,7 @@ public: void showWebInspector(); void closeWebInspector(); void evaluateInWebInspector(long callId, JSStringRef script); + void evaluateScriptInIsolatedWorld(unsigned worldId, JSObjectRef globalObject, JSStringRef script); void setPOSIXLocale(JSStringRef locale); @@ -219,12 +229,14 @@ private: bool m_dumpDatabaseCallbacks; bool m_dumpEditingCallbacks; bool m_dumpFrameLoadCallbacks; + bool m_dumpHistoryDelegateCallbacks; bool m_dumpResourceLoadCallbacks; bool m_dumpResourceResponseMIMETypes; bool m_dumpSelectionRect; bool m_dumpSourceAsWebArchive; bool m_dumpStatusCallbacks; bool m_dumpTitleChanges; + bool m_dumpVisitedLinksCallback; bool m_dumpWillCacheResponse; bool m_callCloseOnWebViews; bool m_canOpenWindows; diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp index 3a04c3c..14280ba 100644 --- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp +++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp @@ -106,13 +106,17 @@ NPClass *getPluginClass(void) static bool identifiersInitialized = false; -#define ID_PROPERTY_PROPERTY 0 -#define ID_PROPERTY_EVENT_LOGGING 1 -#define ID_PROPERTY_HAS_STREAM 2 -#define ID_PROPERTY_TEST_OBJECT 3 -#define ID_PROPERTY_LOG_DESTROY 4 -#define ID_PROPERTY_RETURN_ERROR_FROM_NEWSTREAM 5 -#define NUM_PROPERTY_IDENTIFIERS 6 +enum { + ID_PROPERTY_PROPERTY = 0, + ID_PROPERTY_EVENT_LOGGING, + ID_PROPERTY_HAS_STREAM, + ID_PROPERTY_TEST_OBJECT, + ID_PROPERTY_LOG_DESTROY, + ID_PROPERTY_RETURN_ERROR_FROM_NEWSTREAM, + ID_PROPERTY_PRIVATE_BROWSING_ENABLED, + ID_PROPERTY_CACHED_PRIVATE_BROWSING_ENABLED, + NUM_PROPERTY_IDENTIFIERS +}; static NPIdentifier pluginPropertyIdentifiers[NUM_PROPERTY_IDENTIFIERS]; static const NPUTF8 *pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = { @@ -122,6 +126,8 @@ static const NPUTF8 *pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = { "testObject", "logDestroy", "returnErrorFromNewStream", + "privateBrowsingEnabled", + "cachedPrivateBrowsingEnabled", }; enum { @@ -144,6 +150,7 @@ enum { ID_TEST_POSTURL_FILE, ID_TEST_CONSTRUCT, ID_TEST_THROW_EXCEPTION_METHOD, + ID_TEST_FAIL_METHOD, ID_DESTROY_NULL_STREAM, NUM_METHOD_IDENTIFIERS }; @@ -169,6 +176,7 @@ static const NPUTF8 *pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = { "testPostURLFile", "testConstruct", "testThrowException", + "testFail", "destroyNullStream" }; @@ -226,6 +234,14 @@ static bool pluginGetProperty(NPObject* obj, NPIdentifier name, NPVariant* resul } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_RETURN_ERROR_FROM_NEWSTREAM]) { BOOLEAN_TO_NPVARIANT(plugin->returnErrorFromNewStream, *result); return true; + } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_PRIVATE_BROWSING_ENABLED]) { + NPBool privateBrowsingEnabled = FALSE; + browser->getvalue(plugin->npp, NPNVprivateModeBool, &privateBrowsingEnabled); + BOOLEAN_TO_NPVARIANT(privateBrowsingEnabled, *result); + return true; + } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_CACHED_PRIVATE_BROWSING_ENABLED]) { + BOOLEAN_TO_NPVARIANT(plugin->cachedPrivateBrowsingMode, *result); + return true; } return false; } @@ -309,13 +325,13 @@ static NPIdentifier variantToIdentifier(NPVariant variant) static bool testIdentifierToString(PluginObject*, const NPVariant* args, uint32_t argCount, NPVariant* result) { if (argCount != 1) - return false; + return true; NPIdentifier identifier = variantToIdentifier(args[0]); if (!identifier) - return false; + return true; NPUTF8* utf8String = browser->utf8fromidentifier(identifier); if (!utf8String) - return false; + return true; STRINGZ_TO_NPVARIANT(utf8String, *result); return true; } @@ -669,6 +685,10 @@ static bool pluginInvoke(NPObject* header, NPIdentifier name, const NPVariant* a else if (name == pluginMethodIdentifiers[ID_TEST_THROW_EXCEPTION_METHOD]) { browser->setexception(header, "plugin object testThrowException SUCCESS"); return true; + } else if (name == pluginMethodIdentifiers[ID_TEST_FAIL_METHOD]) { + NPObject* windowScriptObject; + browser->getvalue(plugin->npp, NPNVWindowNPObject, &windowScriptObject); + browser->invoke(plugin->npp, windowScriptObject, name, args, argCount, result); } else if (name == pluginMethodIdentifiers[ID_DESTROY_NULL_STREAM]) return destroyNullStream(plugin, args, argCount, result); diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h index 17b11e1..7437d04 100644 --- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h +++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h @@ -35,6 +35,7 @@ typedef struct { NPBool logSetWindow; NPBool logDestroy; NPBool returnErrorFromNewStream; + NPBool cachedPrivateBrowsingMode; NPObject* testObject; NPStream* stream; char* onStreamLoad; diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp index 88618c3..125d2e8 100644 --- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp +++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp @@ -130,7 +130,9 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, ch return NPERR_INCOMPATIBLE_VERSION_ERROR; } + browser->getvalue(instance, NPNVprivateModeBool, (void *)&obj->cachedPrivateBrowsingMode); browser->setvalue(instance, NPPVpluginEventModel, (void *)obj->eventModel); + return NPERR_NO_ERROR; } @@ -383,5 +385,13 @@ NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) { - return NPERR_GENERIC_ERROR; + PluginObject* obj = static_cast<PluginObject*>(instance->pdata); + + switch (variable) { + case NPNVprivateModeBool: + obj->cachedPrivateBrowsingMode = *(NPBool*)value; + return NPERR_NO_ERROR; + default: + return NPERR_GENERIC_ERROR; + } } diff --git a/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp index 6ecd774..4ed6e36 100644 --- a/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp +++ b/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp @@ -319,6 +319,8 @@ static void resetDefaultsToConsistentValues() "enable-offline-web-application-cache", TRUE, "enable-universal-access-from-file-uris", TRUE, "enable-scripts", TRUE, + "enable-web-sockets", TRUE, + "enable-dom-paste", TRUE, "default-font-family", "Times", "monospace-font-family", "Courier", "serif-font-family", "Times", @@ -706,6 +708,11 @@ static void databaseQuotaExceeded(WebKitWebView* view, WebKitWebFrame* frame, We static WebKitWebView* webViewCreate(WebKitWebView*, WebKitWebFrame*); +static WebKitWebView* webInspectorInspectWebView(WebKitWebInspector*, gpointer data) +{ + return WEBKIT_WEB_VIEW(webkit_web_view_new()); +} + static WebKitWebView* createWebView() { WebKitWebView* view = WEBKIT_WEB_VIEW(webkit_web_view_new()); @@ -730,6 +737,9 @@ static WebKitWebView* createWebView() "signal::database-quota-exceeded", databaseQuotaExceeded, 0, NULL); + WebKitWebInspector* inspector = webkit_web_view_get_inspector(view); + g_signal_connect(inspector, "inspect-web-view", G_CALLBACK(webInspectorInspectWebView), 0); + return view; } diff --git a/WebKitTools/DumpRenderTree/gtk/EventSender.cpp b/WebKitTools/DumpRenderTree/gtk/EventSender.cpp index c3c72c1..f42928c 100644 --- a/WebKitTools/DumpRenderTree/gtk/EventSender.cpp +++ b/WebKitTools/DumpRenderTree/gtk/EventSender.cpp @@ -53,6 +53,7 @@ extern "C" { } static bool down = false; +static bool currentEventButton = 1; static bool dragMode = true; static bool replayingSavedEvents = false; static int lastMousePositionX; @@ -122,12 +123,27 @@ static JSValueRef contextClickCallback(JSContextRef context, JSObjectRef functio static void updateClickCount(int /* button */) { // FIXME: take the last clicked button number and the time of last click into account. - if (lastClickPositionX != lastMousePositionX && lastClickPositionY != lastMousePositionY) + if (lastClickPositionX != lastMousePositionX || lastClickPositionY != lastMousePositionY) clickCount = 1; else clickCount++; } +#if !GTK_CHECK_VERSION(2,17,3) +static void getRootCoords(GtkWidget* view, int* rootX, int* rootY) +{ + GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(view)); + int tmpX, tmpY; + + gtk_widget_translate_coordinates(view, window, lastMousePositionX, lastMousePositionY, &tmpX, &tmpY); + + gdk_window_get_origin(window->window, rootX, rootY); + + *rootX += tmpX; + *rootY += tmpY; +} +#endif + static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); @@ -140,9 +156,29 @@ static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, memset(&event, 0, sizeof(event)); event.type = GDK_BUTTON_PRESS; event.button.button = 1; + + if (argumentCount == 1) { + event.button.button = (int)JSValueToNumber(context, arguments[0], exception) + 1; + g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context)); + } + + currentEventButton = event.button.button; + event.button.x = lastMousePositionX; event.button.y = lastMousePositionY; event.button.window = GTK_WIDGET(view)->window; + event.button.time = GDK_CURRENT_TIME; + event.button.device = gdk_device_get_core_pointer(); + + int x_root, y_root; +#if GTK_CHECK_VERSION(2,17,3) + gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &x_root, &y_root); +#else + getRootCoords(GTK_WIDGET(view), &x_root, &y_root); +#endif + + event.button.x_root = x_root; + event.button.y_root = y_root; updateClickCount(1); @@ -177,9 +213,29 @@ static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JS memset(&event, 0, sizeof(event)); event.type = GDK_BUTTON_RELEASE; event.button.button = 1; + + if (argumentCount == 1) { + event.button.button = (int)JSValueToNumber(context, arguments[0], exception) + 1; + g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context)); + } + + currentEventButton = event.button.button; + event.button.x = lastMousePositionX; event.button.y = lastMousePositionY; event.button.window = GTK_WIDGET(view)->window; + event.button.time = GDK_CURRENT_TIME; + event.button.device = gdk_device_get_core_pointer(); + + int x_root, y_root; +#if GTK_CHECK_VERSION(2,17,3) + gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &x_root, &y_root); +#else + getRootCoords(GTK_WIDGET(view), &x_root, &y_root); +#endif + + event.button.x_root = x_root; + event.button.y_root = y_root; if ((dragMode && !replayingSavedEvents) || msgQueue[endOfQueue].delay) { msgQueue[endOfQueue].event = event; @@ -213,11 +269,33 @@ static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context)); GdkEvent event; + memset(&event, 0, sizeof(event)); event.type = GDK_MOTION_NOTIFY; event.motion.x = lastMousePositionX; event.motion.y = lastMousePositionY; event.motion.time = GDK_CURRENT_TIME; event.motion.window = GTK_WIDGET(view)->window; + event.motion.device = gdk_device_get_core_pointer(); + + int x_root, y_root; +#if GTK_CHECK_VERSION(2,17,3) + gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &x_root, &y_root); +#else + getRootCoords(GTK_WIDGET(view), &x_root, &y_root); +#endif + + event.motion.x_root = x_root; + event.motion.y_root = y_root; + + if (down) { + if (currentEventButton == 1) + event.motion.state = GDK_BUTTON1_MASK; + else if (currentEventButton == 2) + event.motion.state = GDK_BUTTON2_MASK; + else if (currentEventButton == 3) + event.motion.state = GDK_BUTTON3_MASK; + } else + event.motion.state = 0; if (dragMode && down && !replayingSavedEvents) { msgQueue[endOfQueue].event = event; diff --git a/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp b/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp index 0b4a38f..631fc31 100644 --- a/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp +++ b/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2007 Eric Seidel <eric@webkit.org> * Copyright (C) 2008 Nuanti Ltd. * Copyright (C) 2009 Jan Michael Alonzo <jmalonzo@gmail.com> + * Copyright (C) 2009 Collabora Ltd. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -52,6 +53,8 @@ unsigned int webkit_web_frame_number_of_active_animations(WebKitWebFrame* frame) void webkit_application_cache_set_maximum_size(unsigned long long size); unsigned int webkit_worker_thread_count(void); void webkit_white_list_access_from_origin(const gchar* sourceOrigin, const gchar* destinationProtocol, const gchar* destinationHost, bool allowDestinationSubdomains); +gchar* webkit_web_frame_counter_value_for_element_by_id(WebKitWebFrame* frame, const gchar* id); +void webkit_web_inspector_execute_script(WebKitWebInspector* inspector, long callId, const gchar* script); } static gchar* copyWebSettingKey(gchar* preferenceKey) @@ -118,6 +121,17 @@ void LayoutTestController::display() displayWebView(); } +JSRetainPtr<JSStringRef> LayoutTestController::counterValueForElementById(JSStringRef id) +{ + gchar* idGChar = JSStringCopyUTF8CString(id); + gchar* counterValueGChar = webkit_web_frame_counter_value_for_element_by_id(mainFrame, idGChar); + g_free(idGChar); + if (!counterValueGChar) + return 0; + JSRetainPtr<JSStringRef> counterValue(Adopt, JSStringCreateWithUTF8CString(counterValueGChar)); + return counterValue; +} + void LayoutTestController::keepWebHistory() { // FIXME: implement @@ -474,15 +488,40 @@ void LayoutTestController::addUserStyleSheet(JSStringRef source) void LayoutTestController::showWebInspector() { - // FIXME: Implement this. + WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); + WebKitWebSettings* webSettings = webkit_web_view_get_settings(webView); + WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView); + + g_object_set(webSettings, "enable-developer-extras", TRUE, NULL); + webkit_web_inspector_inspect_coordinates(inspector, 0, 0); } void LayoutTestController::closeWebInspector() { - // FIXME: Implement this. + WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); + WebKitWebSettings* webSettings = webkit_web_view_get_settings(webView); + WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView); + + webkit_web_inspector_close(inspector); + g_object_set(webSettings, "enable-developer-extras", FALSE, NULL); } void LayoutTestController::evaluateInWebInspector(long callId, JSStringRef script) { + WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); + WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView); + char* scriptString = JSStringCopyUTF8CString(script); + + webkit_web_inspector_execute_script(inspector, callId, scriptString); + g_free(scriptString); +} + +void LayoutTestController::evaluateScriptInIsolatedWorld(unsigned worldID, JSObjectRef globalObject, JSStringRef script) +{ + // FIXME: Implement this. +} + +void LayoutTestController::removeAllVisitedLinks() +{ // FIXME: Implement this. } diff --git a/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig b/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig index 79d1e14..a72dd7d 100644 --- a/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig +++ b/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig @@ -22,11 +22,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. HEADER_SEARCH_PATHS = ForwardingHeaders mac/InternalHeaders; -FRAMEWORK_SEARCH_PATHS = $(FRAMEWORK_SEARCH_PATHS_$(MAC_OS_X_VERSION_MAJOR)); -FRAMEWORK_SEARCH_PATHS_ = $(SYSTEM_LIBRARY_DIR)/Frameworks/Quartz.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Frameworks -FRAMEWORK_SEARCH_PATHS_1040 = $(SYSTEM_LIBRARY_DIR)/Frameworks/Quartz.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Frameworks -FRAMEWORK_SEARCH_PATHS_1050 = $(SYSTEM_LIBRARY_DIR)/Frameworks/Quartz.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/CoreServices.framework/Frameworks -FRAMEWORK_SEARCH_PATHS_1060 = $(SYSTEM_LIBRARY_DIR)/Frameworks/Quartz.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/CoreServices.framework/Frameworks +FRAMEWORK_SEARCH_PATHS = $(SYSTEM_LIBRARY_DIR)/Frameworks/Quartz.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/CoreServices.framework/Frameworks; GCC_PREPROCESSOR_DEFINITIONS = ENABLE_DASHBOARD_SUPPORT WEBKIT_VERSION_MIN_REQUIRED=WEBKIT_VERSION_LATEST; DEBUG_INFORMATION_FORMAT = dwarf PREBINDING = NO diff --git a/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig b/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig index 05af1e5..96a39a9 100644 --- a/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig +++ b/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig @@ -28,6 +28,7 @@ ARCHS_ = $(ARCHS_1040); ARCHS_1040 = $(NATIVE_ARCH); ARCHS_1050 = $(NATIVE_ARCH); ARCHS_1060 = $(ARCHS_STANDARD_32_64_BIT); +ARCHS_1070 = $(ARCHS_STANDARD_32_64_BIT); ONLY_ACTIVE_ARCH = YES; @@ -36,3 +37,4 @@ MACOSX_DEPLOYMENT_TARGET_ = 10.4; MACOSX_DEPLOYMENT_TARGET_1040 = 10.4; MACOSX_DEPLOYMENT_TARGET_1050 = 10.5; MACOSX_DEPLOYMENT_TARGET_1060 = 10.6; +MACOSX_DEPLOYMENT_TARGET_1070 = 10.7; diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm index 0c33381..98f4f9c 100644 --- a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm @@ -409,7 +409,7 @@ static void resetDefaultsToConsistentValues() [preferences setCacheModel:WebCacheModelDocumentBrowser]; [preferences setXSSAuditorEnabled:NO]; [preferences setExperimentalNotificationsEnabled:NO]; - [preferences setExperimentalWebSocketsEnabled:NO]; + [preferences setPluginAllowedRunTime:1]; [preferences setPrivateBrowsingEnabled:NO]; [preferences setAuthorAndUserStylesEnabled:YES]; @@ -434,9 +434,9 @@ static void resetDefaultsToConsistentValues() SInt32 qtVersion; OSErr err = Gestalt(gestaltQuickTimeVersion, &qtVersion); assert(err == noErr); - // Bug 7228836 exists in at least 7.6.3 and 7.6.4, hopefully it will be fixed in 7.6.5. + // Bug 7228836 exists in at least 7.6.3 through 7.6.4, hopefully it will be fixed in 7.6.5. // FIXME: Once we know the exact versions of QuickTime affected, we can update this check. - if (qtVersion <= 0x07640000) + if (qtVersion <= 0x07648000) // 7.6.4, final release (0x8). See http://developer.apple.com/mac/library/techn [preferences setAcceleratedCompositingEnabled:NO]; else #endif diff --git a/WebKitTools/DumpRenderTree/mac/HistoryDelegate.mm b/WebKitTools/DumpRenderTree/mac/HistoryDelegate.mm index 9e2b836..cbc4093 100644 --- a/WebKitTools/DumpRenderTree/mac/HistoryDelegate.mm +++ b/WebKitTools/DumpRenderTree/mac/HistoryDelegate.mm @@ -29,6 +29,7 @@ #import "LayoutTestController.h" #import <WebKit/WebNavigationData.h> +#import <WebKit/WebView.h> @interface NSURL (DRTExtras) - (NSString *)_drt_descriptionSuitableForTestResult; @@ -71,4 +72,10 @@ printf("WebView updated the title for history URL \"%s\" to \"%s\".\n", [[[NSURL URLWithString:url]_drt_descriptionSuitableForTestResult] UTF8String], [title UTF8String]); } +- (void)populateVisitedLinksForWebView:(WebView *)webView +{ + if (gLayoutTestController->dumpVisitedLinksCallback()) + printf("Asked to populate visited links for WebView \"%s\"\n", [[[NSURL URLWithString:[webView mainFrameURL]] _drt_descriptionSuitableForTestResult] UTF8String]); +} + @end diff --git a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm index 233c5fd..4d6a609 100644 --- a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm +++ b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm @@ -42,6 +42,7 @@ #import <WebKit/DOMElement.h> #import <WebKit/WebApplicationCache.h> #import <WebKit/WebBackForwardList.h> +#import <WebKit/WebCoreStatistics.h> #import <WebKit/WebDatabaseManagerPrivate.h> #import <WebKit/WebDataSource.h> #import <WebKit/WebFrame.h> @@ -151,6 +152,19 @@ void LayoutTestController::display() displayWebView(); } +JSRetainPtr<JSStringRef> LayoutTestController::counterValueForElementById(JSStringRef id) +{ + RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, id)); + NSString *idNS = (NSString *)idCF.get(); + + DOMElement *element = [[mainFrame DOMDocument] getElementById:idNS]; + if (!element) + return 0; + + JSRetainPtr<JSStringRef> counterValue(Adopt, JSStringCreateWithCFString((CFStringRef)[mainFrame counterValueForElement:element])); + return counterValue; +} + void LayoutTestController::keepWebHistory() { if (![WebHistory optionalSharedHistory]) { @@ -333,6 +347,11 @@ void LayoutTestController::overridePreference(JSStringRef key, JSStringRef value [[WebPreferences standardPreferences] _setPreferenceForTestWithValue:valueNS forKey:keyNS]; } +void LayoutTestController::removeAllVisitedLinks() +{ + [WebHistory _removeAllVisitedLinks]; +} + void LayoutTestController::setPersistentUserStyleSheetLocation(JSStringRef jsURL) { RetainPtr<CFStringRef> urlString(AdoptCF, JSStringCopyCFString(0, jsURL)); @@ -482,24 +501,26 @@ void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart) { RetainPtr<CFStringRef> sourceCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, source)); NSString *sourceNS = (NSString *)sourceCF.get(); - [WebView _addUserScriptToGroup:@"org.webkit.DumpRenderTree" source:sourceNS url:nil worldID:1 whitelist:nil blacklist:nil injectionTime:(runAtStart ? WebInjectAtDocumentStart : WebInjectAtDocumentEnd)]; + [WebView _addUserScriptToGroup:@"org.webkit.DumpRenderTree" worldID:1 source:sourceNS url:nil whitelist:nil blacklist:nil injectionTime:(runAtStart ? WebInjectAtDocumentStart : WebInjectAtDocumentEnd)]; } void LayoutTestController::addUserStyleSheet(JSStringRef source) { RetainPtr<CFStringRef> sourceCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, source)); NSString *sourceNS = (NSString *)sourceCF.get(); - [WebView _addUserStyleSheetToGroup:@"org.webkit.DumpRenderTree" source:sourceNS url:nil worldID:1 whitelist:nil blacklist:nil]; + [WebView _addUserStyleSheetToGroup:@"org.webkit.DumpRenderTree" worldID:1 source:sourceNS url:nil whitelist:nil blacklist:nil]; } void LayoutTestController::showWebInspector() { + [[[mainFrame webView] preferences] setDeveloperExtrasEnabled:true]; [[[mainFrame webView] inspector] show:nil]; } void LayoutTestController::closeWebInspector() { [[[mainFrame webView] inspector] close:nil]; + [[[mainFrame webView] preferences] setDeveloperExtrasEnabled:false]; } void LayoutTestController::evaluateInWebInspector(long callId, JSStringRef script) @@ -508,3 +529,10 @@ void LayoutTestController::evaluateInWebInspector(long callId, JSStringRef scrip NSString *scriptNS = (NSString *)scriptCF.get(); [[[mainFrame webView] inspector] evaluateInFrontend:nil callId:callId script:scriptNS]; } + +void LayoutTestController::evaluateScriptInIsolatedWorld(unsigned worldID, JSObjectRef globalObject, JSStringRef script) +{ + RetainPtr<CFStringRef> scriptCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, script)); + NSString *scriptNS = (NSString *)scriptCF.get(); + [mainFrame _stringByEvaluatingJavaScriptInIsolatedWorld:worldID WithGlobalObject:globalObject FromString:scriptNS]; +} diff --git a/WebKitTools/DumpRenderTree/mac/UIDelegate.mm b/WebKitTools/DumpRenderTree/mac/UIDelegate.mm index a52d5be..393899e 100644 --- a/WebKitTools/DumpRenderTree/mac/UIDelegate.mm +++ b/WebKitTools/DumpRenderTree/mac/UIDelegate.mm @@ -157,6 +157,11 @@ DumpRenderTreeDraggingInfo *draggingInfo = nil; [geolocation setIsAllowed:gLayoutTestController->geolocationPermission()]; } +- (BOOL)webView:(WebView *)sender shouldHaltPlugin:(DOMNode *)pluginNode +{ + return NO; +} + - (void)dealloc { [draggingInfo release]; diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.cpp index 83626ac..9faa37f 100644 --- a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.cpp +++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.cpp @@ -30,6 +30,9 @@ */ #include "DumpRenderTree.h" +#include "EventSenderQt.h" +#include "LayoutTestControllerQt.h" +#include "TextInputControllerQt.h" #include "jsobjects.h" #include "testplugin.h" #include "WorkQueue.h" @@ -65,6 +68,7 @@ extern void qt_dump_set_accepts_editing(bool b); extern void qt_dump_frame_loader(bool b); extern void qt_drt_clearFrameName(QWebFrame* qFrame); extern void qt_drt_overwritePluginDirectories(); +extern void qt_drt_resetOriginAccessWhiteLists(); namespace WebCore { @@ -285,9 +289,10 @@ void DumpRenderTree::resetToConsistentStateBeforeTesting() //WorkQueue::shared()->setFrozen(false); m_controller->reset(); - QWebSecurityOrigin::resetOriginAccessWhiteLists(); + qt_drt_resetOriginAccessWhiteLists(); - setlocale(LC_ALL, ""); + QLocale qlocale; + QLocale::setDefault(qlocale); } void DumpRenderTree::open(const QUrl& aurl) @@ -312,7 +317,7 @@ void DumpRenderTree::open(const QUrl& aurl) int width = isW3CTest ? 480 : maxViewWidth; int height = isW3CTest ? 360 : maxViewHeight; m_page->view()->resize(QSize(width, height)); - m_page->setFixedContentsSize(QSize()); + m_page->setPreferredContentsSize(QSize()); m_page->setViewportSize(QSize(width, height)); QFocusEvent ev(QEvent::FocusIn); @@ -591,7 +596,8 @@ void DumpRenderTree::initializeFonts() exit(1); } char currentPath[PATH_MAX+1]; - getcwd(currentPath, PATH_MAX); + if (!getcwd(currentPath, PATH_MAX)) + qFatal("Couldn't get current working directory"); QByteArray configFile = currentPath; FcConfig *config = FcConfigCreate(); configFile += "/WebKitTools/DumpRenderTree/qt/fonts.conf"; diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro index 0daf8bd..571313c 100644 --- a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro +++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro @@ -12,8 +12,23 @@ PKGCONFIG += fontconfig QT = core gui network macx: QT += xml -HEADERS = WorkQueue.h WorkQueueItem.h DumpRenderTree.h jsobjects.h testplugin.h -SOURCES = WorkQueue.cpp DumpRenderTree.cpp main.cpp jsobjects.cpp testplugin.cpp +HEADERS = WorkQueue.h \ + WorkQueueItem.h \ + DumpRenderTree.h \ + EventSenderQt.h \ + TextInputControllerQt.h \ + LayoutTestControllerQt.h \ + jsobjects.h \ + testplugin.h +SOURCES = WorkQueue.cpp \ + DumpRenderTree.cpp \ + EventSenderQt.cpp \ + TextInputControllerQt.cpp \ + WorkQueueItemQt.cpp \ + LayoutTestControllerQt.cpp \ + main.cpp \ + jsobjects.cpp \ + testplugin.cpp unix:!mac { QMAKE_RPATHDIR = $$OUTPUT_DIR/lib $$QMAKE_RPATHDIR diff --git a/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp b/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp new file mode 100644 index 0000000..bbefed9 --- /dev/null +++ b/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ + * + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "EventSenderQt.h" + +//#include <QtDebug> +EventSender::EventSender(QWebPage* parent) + : QObject(parent) +{ + m_page = parent; +} + +void EventSender::mouseDown(int button) +{ + Qt::MouseButton mouseButton; + switch (button) { + case 0: + mouseButton = Qt::LeftButton; + break; + case 1: + mouseButton = Qt::MidButton; + break; + case 2: + mouseButton = Qt::RightButton; + break; + case 3: + // fast/events/mouse-click-events expects the 4th button to be treated as the middle button + mouseButton = Qt::MidButton; + break; + default: + mouseButton = Qt::LeftButton; + break; + } + +// qDebug() << "EventSender::mouseDown" << frame; + QMouseEvent event(QEvent::MouseButtonPress, m_mousePos, mouseButton, mouseButton, Qt::NoModifier); + QApplication::sendEvent(m_page, &event); +} + +void EventSender::mouseUp(int button) +{ + Qt::MouseButton mouseButton; + switch (button) { + case 0: + mouseButton = Qt::LeftButton; + break; + case 1: + mouseButton = Qt::MidButton; + break; + case 2: + mouseButton = Qt::RightButton; + break; + case 3: + // fast/events/mouse-click-events expects the 4th button to be treated as the middle button + mouseButton = Qt::MidButton; + break; + default: + mouseButton = Qt::LeftButton; + break; + } + +// qDebug() << "EventSender::mouseUp" << frame; + QMouseEvent event(QEvent::MouseButtonRelease, m_mousePos, mouseButton, mouseButton, Qt::NoModifier); + QApplication::sendEvent(m_page, &event); +} + +void EventSender::mouseMoveTo(int x, int y) +{ +// qDebug() << "EventSender::mouseMoveTo" << x << y; + m_mousePos = QPoint(x, y); + QMouseEvent event(QEvent::MouseMove, m_mousePos, Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QApplication::sendEvent(m_page, &event); +} + +void EventSender::leapForward(int ms) +{ + m_timeLeap += ms; + //qDebug() << "EventSender::leapForward" << ms; +} + +void EventSender::keyDown(const QString& string, const QStringList& modifiers) +{ + QString s = string; + Qt::KeyboardModifiers modifs = 0; + for (int i = 0; i < modifiers.size(); ++i) { + const QString& m = modifiers.at(i); + if (m == "ctrlKey") + modifs |= Qt::ControlModifier; + else if (m == "shiftKey") + modifs |= Qt::ShiftModifier; + else if (m == "altKey") + modifs |= Qt::AltModifier; + else if (m == "metaKey") + modifs |= Qt::MetaModifier; + } + int code = 0; + if (string.length() == 1) { + code = string.unicode()->unicode(); + //qDebug() << ">>>>>>>>> keyDown" << code << (char)code; + // map special keycodes used by the tests to something that works for Qt/X11 + if (code == '\t') { + code = Qt::Key_Tab; + if (modifs == Qt::ShiftModifier) + code = Qt::Key_Backtab; + s = QString(); + } else if (code == 127) { + code = Qt::Key_Backspace; + if (modifs == Qt::AltModifier) + modifs = Qt::ControlModifier; + s = QString(); + } else if (code == 'o' && modifs == Qt::ControlModifier) { + s = QLatin1String("\n"); + code = '\n'; + modifs = 0; + } else if (code == 'y' && modifs == Qt::ControlModifier) { + s = QLatin1String("c"); + code = 'c'; + } else if (code == 'k' && modifs == Qt::ControlModifier) { + s = QLatin1String("x"); + code = 'x'; + } else if (code == 'a' && modifs == Qt::ControlModifier) { + s = QString(); + code = Qt::Key_Home; + modifs = 0; + } else if (code == 0xf702) { + s = QString(); + code = Qt::Key_Left; + if (modifs & Qt::MetaModifier) { + code = Qt::Key_Home; + modifs &= ~Qt::MetaModifier; + } + } else if (code == 0xf703) { + s = QString(); + code = Qt::Key_Right; + if (modifs & Qt::MetaModifier) { + code = Qt::Key_End; + modifs &= ~Qt::MetaModifier; + } + } else if (code == 0xf700) { + s = QString(); + code = Qt::Key_Up; + if (modifs & Qt::MetaModifier) { + code = Qt::Key_PageUp; + modifs &= ~Qt::MetaModifier; + } + } else if (code == 0xf701) { + s = QString(); + code = Qt::Key_Down; + if (modifs & Qt::MetaModifier) { + code = Qt::Key_PageDown; + modifs &= ~Qt::MetaModifier; + } + } else if (code == 'a' && modifs == Qt::ControlModifier) { + s = QString(); + code = Qt::Key_Home; + modifs = 0; + } else + code = string.unicode()->toUpper().unicode(); + } else { + //qDebug() << ">>>>>>>>> keyDown" << string; + + if (string.startsWith(QLatin1Char('F')) && string.count() <= 3) { + s = s.mid(1); + int functionKey = s.toInt(); + Q_ASSERT(functionKey >= 1 && functionKey <= 35); + code = Qt::Key_F1 + (functionKey - 1); + // map special keycode strings used by the tests to something that works for Qt/X11 + } else if (string == QLatin1String("leftArrow")) { + s = QString(); + code = Qt::Key_Left; + } else if (string == QLatin1String("rightArrow")) { + s = QString(); + code = Qt::Key_Right; + } else if (string == QLatin1String("upArrow")) { + s = QString(); + code = Qt::Key_Up; + } else if (string == QLatin1String("downArrow")) { + s = QString(); + code = Qt::Key_Down; + } else if (string == QLatin1String("pageUp")) { + s = QString(); + code = Qt::Key_PageUp; + } else if (string == QLatin1String("pageDown")) { + s = QString(); + code = Qt::Key_PageDown; + } else if (string == QLatin1String("home")) { + s = QString(); + code = Qt::Key_Home; + } else if (string == QLatin1String("end")) { + s = QString(); + code = Qt::Key_End; + } else if (string == QLatin1String("delete")) { + s = QString(); + code = Qt::Key_Delete; + } + } + QKeyEvent event(QEvent::KeyPress, code, modifs, s); + QApplication::sendEvent(m_page, &event); + QKeyEvent event2(QEvent::KeyRelease, code, modifs, s); + QApplication::sendEvent(m_page, &event2); +} + +void EventSender::contextClick() +{ + QMouseEvent event(QEvent::MouseButtonPress, m_mousePos, Qt::RightButton, Qt::RightButton, Qt::NoModifier); + QApplication::sendEvent(m_page, &event); + QMouseEvent event2(QEvent::MouseButtonRelease, m_mousePos, Qt::RightButton, Qt::RightButton, Qt::NoModifier); + QApplication::sendEvent(m_page, &event2); +} + +void EventSender::scheduleAsynchronousClick() +{ + QMouseEvent* event = new QMouseEvent(QEvent::MouseButtonPress, m_mousePos, Qt::LeftButton, Qt::RightButton, Qt::NoModifier); + QApplication::postEvent(m_page, event); + QMouseEvent* event2 = new QMouseEvent(QEvent::MouseButtonRelease, m_mousePos, Qt::LeftButton, Qt::RightButton, Qt::NoModifier); + QApplication::postEvent(m_page, event2); +} + +QWebFrame* EventSender::frameUnderMouse() const +{ + QWebFrame* frame = m_page->mainFrame(); + +redo: + QList<QWebFrame*> children = frame->childFrames(); + for (int i = 0; i < children.size(); ++i) { + if (children.at(i)->geometry().contains(m_mousePos)) { + frame = children.at(i); + goto redo; + } + } + if (frame->geometry().contains(m_mousePos)) + return frame; + return 0; +} diff --git a/WebKitTools/DumpRenderTree/qt/EventSenderQt.h b/WebKitTools/DumpRenderTree/qt/EventSenderQt.h new file mode 100644 index 0000000..fd74455 --- /dev/null +++ b/WebKitTools/DumpRenderTree/qt/EventSenderQt.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ + * + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 EventSenderQt_h +#define EventSenderQt_h + +#include <QApplication> +#include <QEvent> +#include <QMouseEvent> +#include <QObject> +#include <QPoint> +#include <QString> +#include <QStringList> + +#include <qwebpage.h> +#include <qwebframe.h> + +class EventSender : public QObject { + Q_OBJECT +public: + EventSender(QWebPage* parent); + +public slots: + void mouseDown(int button = 0); + void mouseUp(int button = 0); + void mouseMoveTo(int x, int y); + void leapForward(int ms); + void keyDown(const QString& string, const QStringList& modifiers = QStringList()); + void clearKillRing() {} + void contextClick(); + void scheduleAsynchronousClick(); + +private: + QPoint m_mousePos; + QWebPage* m_page; + int m_timeLeap; + QWebFrame* frameUnderMouse() const; +}; +#endif // EventSenderQt_h diff --git a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp new file mode 100644 index 0000000..b8de6a5 --- /dev/null +++ b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ + * + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "LayoutTestControllerQt.h" + +#include "DumpRenderTree.h" +#include "WorkQueue.h" +#include "WorkQueueItem.h" + +extern void qt_dump_editing_callbacks(bool b); +extern void qt_dump_resource_load_callbacks(bool b); +extern void qt_drt_setJavaScriptProfilingEnabled(QWebFrame*, bool enabled); +extern bool qt_drt_pauseAnimation(QWebFrame*, const QString& name, double time, const QString& elementId); +extern bool qt_drt_pauseTransitionOfProperty(QWebFrame*, const QString& name, double time, const QString& elementId); +extern int qt_drt_numberOfActiveAnimations(QWebFrame*); +extern void qt_drt_whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains); +extern QString qt_drt_counterValueForElementById(QWebFrame* qFrame, const QString& id); + +LayoutTestController::LayoutTestController(WebCore::DumpRenderTree* drt) + : QObject() + , m_drt(drt) +{ + reset(); +} + +void LayoutTestController::reset() +{ + m_isLoading = true; + m_textDump = false; + m_dumpBackForwardList = false; + m_dumpChildrenAsText = false; + m_canOpenWindows = false; + m_waitForDone = false; + m_dumpTitleChanges = false; + m_dumpDatabaseCallbacks = false; + m_dumpStatusCallbacks = false; + m_timeoutTimer.stop(); + m_topLoadingFrame = 0; + m_waitForPolicy = false; + qt_dump_editing_callbacks(false); + qt_dump_resource_load_callbacks(false); +} + +void LayoutTestController::processWork() +{ + // qDebug() << ">>>processWork"; + + // if we didn't start a new load, then we finished all the commands, so we're ready to dump state + if (!WorkQueue::shared()->processWork() && !shouldWaitUntilDone()) { + emit done(); + m_isLoading = false; + } +} + +// Called on loadFinished on mainFrame. +void LayoutTestController::maybeDump(bool success) +{ + Q_ASSERT(sender() == m_topLoadingFrame); + + // as the function is called on loadFinished, the test might + // already have dumped and thus no longer be active, thus + // bail out here. + if (!m_isLoading) + return; + + m_topLoadingFrame = 0; + WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test + + if (!shouldWaitUntilDone()) { + if (WorkQueue::shared()->count()) + QTimer::singleShot(0, this, SLOT(processWork())); + else { + if (success) + emit done(); + m_isLoading = false; + } + } +} + +void LayoutTestController::waitUntilDone() +{ + //qDebug() << ">>>>waitForDone"; + m_waitForDone = true; + m_timeoutTimer.start(11000, this); +} + +QString LayoutTestController::counterValueForElementById(const QString& id) +{ + return qt_drt_counterValueForElementById(m_drt->webPage()->mainFrame(), id); +} + +void LayoutTestController::keepWebHistory() +{ + // FIXME: implement +} + +void LayoutTestController::notifyDone() +{ + qDebug() << ">>>>notifyDone"; + if (!m_timeoutTimer.isActive()) + return; + m_timeoutTimer.stop(); + emit done(); + m_isLoading = false; + m_waitForDone = false; + m_waitForPolicy = false; +} + +int LayoutTestController::windowCount() +{ + return m_drt->windowCount(); +} + +void LayoutTestController::clearBackForwardList() +{ + m_drt->webPage()->history()->clear(); +} + +void LayoutTestController::dumpEditingCallbacks() +{ + qDebug() << ">>>dumpEditingCallbacks"; + qt_dump_editing_callbacks(true); +} + +void LayoutTestController::dumpResourceLoadCallbacks() +{ + qt_dump_resource_load_callbacks(true); +} + +void LayoutTestController::queueBackNavigation(int howFarBackward) +{ + //qDebug() << ">>>queueBackNavigation" << howFarBackward; + WorkQueue::shared()->queue(new BackItem(howFarBackward, m_drt->webPage())); +} + +void LayoutTestController::queueForwardNavigation(int howFarForward) +{ + //qDebug() << ">>>queueForwardNavigation" << howFarForward; + WorkQueue::shared()->queue(new ForwardItem(howFarForward, m_drt->webPage())); +} + +void LayoutTestController::queueLoad(const QString& url, const QString& target) +{ + //qDebug() << ">>>queueLoad" << url << target; + QUrl mainResourceUrl = m_drt->webPage()->mainFrame()->url(); + QString absoluteUrl = mainResourceUrl.resolved(QUrl(url)).toEncoded(); + WorkQueue::shared()->queue(new LoadItem(absoluteUrl, target, m_drt->webPage())); +} + +void LayoutTestController::queueReload() +{ + //qDebug() << ">>>queueReload"; + WorkQueue::shared()->queue(new ReloadItem(m_drt->webPage())); +} + +void LayoutTestController::queueScript(const QString &url) +{ + //qDebug() << ">>>queueScript" << url; + WorkQueue::shared()->queue(new ScriptItem(url, m_drt->webPage())); +} + +void LayoutTestController::provisionalLoad() +{ + QWebFrame* frame = qobject_cast<QWebFrame*>(sender()); + if (!m_topLoadingFrame && m_isLoading) + m_topLoadingFrame = frame; +} + +void LayoutTestController::timerEvent(QTimerEvent *ev) +{ + if (ev->timerId() == m_timeoutTimer.timerId()) { + //qDebug() << ">>>>>>>>>>>>> timeout"; + notifyDone(); + } else + QObject::timerEvent(ev); +} + +QString LayoutTestController::encodeHostName(const QString& host) +{ + QString encoded = QString::fromLatin1(QUrl::toAce(host + QLatin1String(".no"))); + encoded.truncate(encoded.length() - 3); // strip .no + return encoded; +} + +QString LayoutTestController::decodeHostName(const QString& host) +{ + QString decoded = QUrl::fromAce(host.toLatin1() + QByteArray(".no")); + decoded.truncate(decoded.length() - 3); + return decoded; +} + +void LayoutTestController::setJavaScriptProfilingEnabled(bool enable) +{ + m_topLoadingFrame->page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); + qt_drt_setJavaScriptProfilingEnabled(m_topLoadingFrame, enable); +} + +void LayoutTestController::setFixedContentsSize(int width, int height) +{ + m_topLoadingFrame->page()->setPreferredContentsSize(QSize(width, height)); +} + +void LayoutTestController::setPrivateBrowsingEnabled(bool enable) +{ + m_drt->webPage()->settings()->setAttribute(QWebSettings::PrivateBrowsingEnabled, enable); +} + +void LayoutTestController::setPopupBlockingEnabled(bool enable) +{ + m_drt->webPage()->settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, !enable); +} + +void LayoutTestController::setPOSIXLocale(const QString& locale) +{ + QLocale qlocale(locale); + QLocale::setDefault(qlocale); +} + +bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(const QString& animationName, + double time, + const QString& elementId) +{ + QWebFrame* frame = m_drt->webPage()->mainFrame(); + Q_ASSERT(frame); + return qt_drt_pauseAnimation(frame, animationName, time, elementId); +} + +bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(const QString& propertyName, + double time, + const QString& elementId) +{ + QWebFrame* frame = m_drt->webPage()->mainFrame(); + Q_ASSERT(frame); + return qt_drt_pauseTransitionOfProperty(frame, propertyName, time, elementId); +} + +unsigned LayoutTestController::numberOfActiveAnimations() const +{ + QWebFrame* frame = m_drt->webPage()->mainFrame(); + Q_ASSERT(frame); + return qt_drt_numberOfActiveAnimations(frame); +} + +void LayoutTestController::disableImageLoading() +{ + // FIXME: Implement for testing fix for https://bugs.webkit.org/show_bug.cgi?id=27896 + // Also need to make sure image loading is re-enabled for each new test. +} + +void LayoutTestController::dispatchPendingLoadRequests() +{ + // FIXME: Implement for testing fix for 6727495 +} + +void LayoutTestController::setDatabaseQuota(int size) +{ + if (!m_topLoadingFrame) + return; + m_topLoadingFrame->securityOrigin().setDatabaseQuota(size); +} + +void LayoutTestController::clearAllDatabases() +{ + QWebDatabase::removeAllDatabases(); +} + +void LayoutTestController::whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains) +{ + qt_drt_whiteListAccessFromOrigin(sourceOrigin, destinationProtocol, destinationHost, allowDestinationSubdomains); +} + +void LayoutTestController::waitForPolicyDelegate() +{ + m_waitForPolicy = true; + waitUntilDone(); +} + +void LayoutTestController::overridePreference(const QString& name, const QVariant& value) +{ + QWebSettings* settings = m_topLoadingFrame->page()->settings(); + + if (name == "WebKitJavaScriptEnabled") + settings->setAttribute(QWebSettings::JavascriptEnabled, value.toBool()); + else if (name == "WebKitTabToLinksPreferenceKey") + settings->setAttribute(QWebSettings::LinksIncludedInFocusChain, value.toBool()); + else if (name == "WebKitOfflineWebApplicationCacheEnabled") + settings->setAttribute(QWebSettings::OfflineWebApplicationCacheEnabled, value.toBool()); + else if (name == "WebKitDefaultFontSize") + settings->setFontSize(QWebSettings::DefaultFontSize, value.toInt()); +} diff --git a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h new file mode 100644 index 0000000..cab014c --- /dev/null +++ b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ + * + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 LayoutTestControllerQt_h +#define LayoutTestControllerQt_h + +#include <QBasicTimer> +#include <QObject> +#include <QSize> +#include <QString> +#include <QtDebug> +#include <QTimer> +#include <QTimerEvent> +#include <QVariant> + +#include <qwebdatabase.h> +#include <qwebframe.h> +#include <qwebhistory.h> +#include <qwebpage.h> +#include <qwebsecurityorigin.h> + +class QWebFrame; +namespace WebCore { + class DumpRenderTree; +} +class LayoutTestController : public QObject { + Q_OBJECT +public: + LayoutTestController(WebCore::DumpRenderTree* drt); + + bool isLoading() const { return m_isLoading; } + void setLoading(bool loading) { m_isLoading = loading; } + + bool shouldDumpAsText() const { return m_textDump; } + bool shouldDumpBackForwardList() const { return m_dumpBackForwardList; } + bool shouldDumpChildrenAsText() const { return m_dumpChildrenAsText; } + bool shouldDumpDatabaseCallbacks() const { return m_dumpDatabaseCallbacks; } + bool shouldDumpStatusCallbacks() const { return m_dumpStatusCallbacks; } + bool shouldWaitUntilDone() const { return m_waitForDone; } + bool canOpenWindows() const { return m_canOpenWindows; } + bool shouldDumpTitleChanges() const { return m_dumpTitleChanges; } + bool waitForPolicy() const { return m_waitForPolicy; } + + void reset(); + +protected: + void timerEvent(QTimerEvent*); + +signals: + void done(); + +public slots: + void maybeDump(bool ok); + void dumpAsText() { m_textDump = true; } + void dumpChildFramesAsText() { m_dumpChildrenAsText = true; } + void dumpDatabaseCallbacks() { m_dumpDatabaseCallbacks = true; } + void dumpStatusCallbacks() { m_dumpStatusCallbacks = true; } + void setCanOpenWindows() { m_canOpenWindows = true; } + void waitUntilDone(); + QString counterValueForElementById(const QString& id); + void keepWebHistory(); + void notifyDone(); + void dumpBackForwardList() { m_dumpBackForwardList = true; } + void dumpEditingCallbacks(); + void dumpResourceLoadCallbacks(); + void queueBackNavigation(int howFarBackward); + void queueForwardNavigation(int howFarForward); + void queueLoad(const QString& url, const QString& target = QString()); + void queueReload(); + void queueScript(const QString& url); + void provisionalLoad(); + void setCloseRemainingWindowsWhenComplete(bool = false) {} + int windowCount(); + void display() {} + void clearBackForwardList(); + void dumpTitleChanges() { m_dumpTitleChanges = true; } + QString encodeHostName(const QString& host); + QString decodeHostName(const QString& host); + void dumpSelectionRect() const {} + void setJavaScriptProfilingEnabled(bool enable); + void setFixedContentsSize(int width, int height); + void setPrivateBrowsingEnabled(bool enable); + void setPopupBlockingEnabled(bool enable); + void setPOSIXLocale(const QString& locale); + + bool pauseAnimationAtTimeOnElementWithId(const QString& animationName, double time, const QString& elementId); + bool pauseTransitionAtTimeOnElementWithId(const QString& propertyName, double time, const QString& elementId); + unsigned numberOfActiveAnimations() const; + + void whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains); + + void dispatchPendingLoadRequests(); + void disableImageLoading(); + + void setDatabaseQuota(int size); + void clearAllDatabases(); + + void waitForPolicyDelegate(); + void overridePreference(const QString& name, const QVariant& value); + +private slots: + void processWork(); + +private: + bool m_isLoading; + bool m_textDump; + bool m_dumpBackForwardList; + bool m_dumpChildrenAsText; + bool m_canOpenWindows; + bool m_waitForDone; + bool m_dumpTitleChanges; + bool m_dumpDatabaseCallbacks; + bool m_dumpStatusCallbacks; + bool m_waitForPolicy; + + QBasicTimer m_timeoutTimer; + QWebFrame* m_topLoadingFrame; + WebCore::DumpRenderTree* m_drt; +}; + +#endif // LayoutTestControllerQt_h diff --git a/WebKitTools/DumpRenderTree/qt/TextInputControllerQt.cpp b/WebKitTools/DumpRenderTree/qt/TextInputControllerQt.cpp new file mode 100644 index 0000000..7b8f120 --- /dev/null +++ b/WebKitTools/DumpRenderTree/qt/TextInputControllerQt.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ + * + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "TextInputControllerQt.h" + +#include <QApplication> +#include <QKeyEvent> + +TextInputController::TextInputController(QWebPage* parent) + : QObject(parent) +{ +} + +void TextInputController::doCommand(const QString& command) +{ + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + int keycode = 0; + if (command == "moveBackwardAndModifySelection:") { + modifiers |= Qt::ShiftModifier; + keycode = Qt::Key_Left; + } else if (command =="moveDown:") { + keycode = Qt::Key_Down; + } else if (command =="moveDownAndModifySelection:") { + modifiers |= Qt::ShiftModifier; + keycode = Qt::Key_Down; + } else if (command =="moveForward:") { + keycode = Qt::Key_Right; + } else if (command =="moveForwardAndModifySelection:") { + modifiers |= Qt::ShiftModifier; + keycode = Qt::Key_Right; + } else if (command =="moveLeft:") { + keycode = Qt::Key_Left; + } else if (command =="moveLeftAndModifySelection:") { + modifiers |= Qt::ShiftModifier; + keycode = Qt::Key_Left; + } else if (command =="moveRight:") { + keycode = Qt::Key_Right; + } else if (command =="moveRightAndModifySelection:") { + modifiers |= Qt::ShiftModifier; + keycode = Qt::Key_Right; + } else if (command =="moveToBeginningOfDocument:") { + modifiers |= Qt::ControlModifier; + keycode = Qt::Key_Home; + } else if (command =="moveToBeginningOfLine:") { + keycode = Qt::Key_Home; +// } else if (command =="moveToBeginningOfParagraph:") { + } else if (command =="moveToEndOfDocument:") { + modifiers |= Qt::ControlModifier; + keycode = Qt::Key_End; + } else if (command =="moveToEndOfLine:") { + keycode = Qt::Key_End; +// } else if (command =="moveToEndOfParagraph:") { + } else if (command =="moveUp:") { + keycode = Qt::Key_Up; + } else if (command =="moveUpAndModifySelection:") { + modifiers |= Qt::ShiftModifier; + keycode = Qt::Key_Up; + } else if (command =="moveWordBackward:") { + modifiers |= Qt::ControlModifier; + keycode = Qt::Key_Up; + } else if (command =="moveWordBackwardAndModifySelection:") { + modifiers |= Qt::ShiftModifier; + modifiers |= Qt::ControlModifier; + keycode = Qt::Key_Left; + } else if (command =="moveWordForward:") { + modifiers |= Qt::ControlModifier; + keycode = Qt::Key_Right; + } else if (command =="moveWordForwardAndModifySelection:") { + modifiers |= Qt::ControlModifier; + modifiers |= Qt::ShiftModifier; + keycode = Qt::Key_Right; + } else if (command =="moveWordLeft:") { + modifiers |= Qt::ControlModifier; + keycode = Qt::Key_Left; + } else if (command =="moveWordRight:") { + modifiers |= Qt::ControlModifier; + keycode = Qt::Key_Left; + } else if (command =="moveWordRightAndModifySelection:") { + modifiers |= Qt::ShiftModifier; + modifiers |= Qt::ControlModifier; + keycode = Qt::Key_Right; + } else if (command =="moveWordLeftAndModifySelection:") { + modifiers |= Qt::ShiftModifier; + modifiers |= Qt::ControlModifier; + keycode = Qt::Key_Left; + } else if (command =="pageDown:") { + keycode = Qt::Key_PageDown; + } else if (command =="pageUp:") { + keycode = Qt::Key_PageUp; + } else if (command == "deleteWordBackward:") { + modifiers |= Qt::ControlModifier; + keycode = Qt::Key_Backspace; + } else if (command == "deleteBackward:") { + keycode = Qt::Key_Backspace; + } else if (command == "deleteForward:") { + keycode = Qt::Key_Delete; + } + + QKeyEvent event(QEvent::KeyPress, keycode, modifiers); + QApplication::sendEvent(parent(), &event); + QKeyEvent event2(QEvent::KeyRelease, keycode, modifiers); + QApplication::sendEvent(parent(), &event2); +} diff --git a/WebKitTools/DumpRenderTree/qt/TextInputControllerQt.h b/WebKitTools/DumpRenderTree/qt/TextInputControllerQt.h new file mode 100644 index 0000000..7c7433e --- /dev/null +++ b/WebKitTools/DumpRenderTree/qt/TextInputControllerQt.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ + * + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 TextInputControllerQt_h +#define TextInputControllerQt_h + +#include <QList> +#include <QObject> +#include <QString> +#include "qwebpage.h" + +class TextInputController : public QObject { + Q_OBJECT +public: + TextInputController(QWebPage* parent); + +public slots: + void doCommand(const QString& command); +// void setMarkedText(const QString& str, int from, int length); +// bool hasMarkedText(); +// void unmarkText(); +// QList<int> markedRange(); +// QList<int> selectedRange(); +// void validAttributesForMarkedText(); +// void inserText(const QString&); +// void firstRectForCharacterRange(); +// void characterIndexForPoint(int, int); +// void substringFromRange(int, int); +// void conversationIdentifier(); +}; +#endif // TextInputControllerQt_h diff --git a/WebKitTools/DumpRenderTree/qt/WorkQueue.cpp b/WebKitTools/DumpRenderTree/qt/WorkQueue.cpp index ae24e0d..f8448e4 100644 --- a/WebKitTools/DumpRenderTree/qt/WorkQueue.cpp +++ b/WebKitTools/DumpRenderTree/qt/WorkQueue.cpp @@ -30,8 +30,6 @@ #include "config.h" #include "WorkQueue.h" -#include "WorkQueueItem.h" - static const unsigned queueLength = 1024; static WorkQueueItem* theQueue[queueLength]; diff --git a/WebKitTools/DumpRenderTree/qt/WorkQueue.h b/WebKitTools/DumpRenderTree/qt/WorkQueue.h index 8adbfcd..902ba0d 100644 --- a/WebKitTools/DumpRenderTree/qt/WorkQueue.h +++ b/WebKitTools/DumpRenderTree/qt/WorkQueue.h @@ -30,7 +30,7 @@ #ifndef WorkQueue_h #define WorkQueue_h -class WorkQueueItem; +#include "WorkQueueItem.h" class WorkQueue { public: diff --git a/WebKitTools/DumpRenderTree/qt/WorkQueueItem.h b/WebKitTools/DumpRenderTree/qt/WorkQueueItem.h index 0e10f62..9819ec0 100644 --- a/WebKitTools/DumpRenderTree/qt/WorkQueueItem.h +++ b/WebKitTools/DumpRenderTree/qt/WorkQueueItem.h @@ -30,8 +30,9 @@ #ifndef WorkQueueItem_h #define WorkQueueItem_h -#include <qstring.h> -#include <qpointer.h> +#include <QPointer> +#include <QString> +#include <qwebframe.h> #include <qwebpage.h> class WorkQueueItem { diff --git a/WebKitTools/DumpRenderTree/qt/WorkQueueItemQt.cpp b/WebKitTools/DumpRenderTree/qt/WorkQueueItemQt.cpp new file mode 100644 index 0000000..807a6a4 --- /dev/null +++ b/WebKitTools/DumpRenderTree/qt/WorkQueueItemQt.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ + * + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "WorkQueueItem.h" + +QWebFrame* findFrameNamed(const QString& frameName, QWebFrame* frame) +{ + if (frame->frameName() == frameName) + return frame; + + foreach (QWebFrame* childFrame, frame->childFrames()) + if (QWebFrame* f = findFrameNamed(frameName, childFrame)) + return f; + + return 0; +} + +bool LoadItem::invoke() const +{ + //qDebug() << ">>>LoadItem::invoke"; + Q_ASSERT(m_webPage); + + QWebFrame* frame = 0; + const QString t = target(); + if (t.isEmpty()) + frame = m_webPage->mainFrame(); + else + frame = findFrameNamed(t, m_webPage->mainFrame()); + + if (!frame) + return false; + + frame->load(url()); + return true; +} + +bool ReloadItem::invoke() const +{ + //qDebug() << ">>>ReloadItem::invoke"; + Q_ASSERT(m_webPage); + m_webPage->triggerAction(QWebPage::Reload); + return true; +} + +bool ScriptItem::invoke() const +{ + //qDebug() << ">>>ScriptItem::invoke"; + Q_ASSERT(m_webPage); + m_webPage->mainFrame()->evaluateJavaScript(script()); + return true; +} + +bool BackForwardItem::invoke() const +{ + //qDebug() << ">>>BackForwardItem::invoke"; + Q_ASSERT(m_webPage); + if (!m_howFar) + return false; + + if (m_howFar > 0) { + for (int i = 0; i != m_howFar; ++i) + m_webPage->triggerAction(QWebPage::Forward); + } else { + for (int i = 0; i != m_howFar; --i) + m_webPage->triggerAction(QWebPage::Back); + } + return true; +} diff --git a/WebKitTools/DumpRenderTree/qt/jsobjects.cpp b/WebKitTools/DumpRenderTree/qt/jsobjects.cpp index b2d8528..e747aeb 100644 --- a/WebKitTools/DumpRenderTree/qt/jsobjects.cpp +++ b/WebKitTools/DumpRenderTree/qt/jsobjects.cpp @@ -27,672 +27,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <jsobjects.h> - -#include "DumpRenderTree.h" -#include "WorkQueue.h" -#include "WorkQueueItem.h" - -#include <qapplication.h> -#include <qevent.h> -#include <qtimer.h> -#include <qwebdatabase.h> -#include <qwebframe.h> -#include <qwebhistory.h> #include <qwebpage.h> -#include <qwebsecurityorigin.h> - -extern void qt_dump_editing_callbacks(bool b); -extern void qt_dump_resource_load_callbacks(bool b); -extern void qt_drt_setJavaScriptProfilingEnabled(QWebFrame*, bool enabled); -extern bool qt_drt_pauseAnimation(QWebFrame*, const QString &name, double time, const QString &elementId); -extern bool qt_drt_pauseTransitionOfProperty(QWebFrame*, const QString &name, double time, const QString &elementId); -extern int qt_drt_numberOfActiveAnimations(QWebFrame*); - -QWebFrame* findFrameNamed(const QString &frameName, QWebFrame* frame) -{ - if (frame->frameName() == frameName) - return frame; - - foreach (QWebFrame *childFrame, frame->childFrames()) - if (QWebFrame *f = findFrameNamed(frameName, childFrame)) - return f; - - return 0; -} - -bool LoadItem::invoke() const -{ - //qDebug() << ">>>LoadItem::invoke"; - Q_ASSERT(m_webPage); - - QWebFrame* frame = 0; - const QString t = target(); - if (t.isEmpty()) - frame = m_webPage->mainFrame(); - else - frame = findFrameNamed(t, m_webPage->mainFrame()); - - if (!frame) - return false; - - frame->load(url()); - return true; -} - -bool ReloadItem::invoke() const -{ - //qDebug() << ">>>ReloadItem::invoke"; - Q_ASSERT(m_webPage); - m_webPage->triggerAction(QWebPage::Reload); - return true; -} - -bool ScriptItem::invoke() const -{ - //qDebug() << ">>>ScriptItem::invoke"; - Q_ASSERT(m_webPage); - m_webPage->mainFrame()->evaluateJavaScript(script()); - return true; -} - -bool BackForwardItem::invoke() const -{ - //qDebug() << ">>>BackForwardItem::invoke"; - Q_ASSERT(m_webPage); - if (!m_howFar) - return false; - - if (m_howFar > 0) { - for (int i = 0; i != m_howFar; ++i) - m_webPage->triggerAction(QWebPage::Forward); - } else { - for (int i = 0; i != m_howFar; --i) - m_webPage->triggerAction(QWebPage::Back); - } - return true; -} - -LayoutTestController::LayoutTestController(WebCore::DumpRenderTree *drt) - : QObject() - , m_drt(drt) -{ - reset(); -} - -void LayoutTestController::reset() -{ - m_isLoading = true; - m_textDump = false; - m_dumpBackForwardList = false; - m_dumpChildrenAsText = false; - m_canOpenWindows = false; - m_waitForDone = false; - m_dumpTitleChanges = false; - m_dumpDatabaseCallbacks = false; - m_dumpStatusCallbacks = false; - m_timeoutTimer.stop(); - m_topLoadingFrame = 0; - m_waitForPolicy = false; - qt_dump_editing_callbacks(false); - qt_dump_resource_load_callbacks(false); -} - -void LayoutTestController::processWork() -{ - qDebug() << ">>>processWork"; - - // if we didn't start a new load, then we finished all the commands, so we're ready to dump state - if (!WorkQueue::shared()->processWork() && !shouldWaitUntilDone()) { - emit done(); - m_isLoading = false; - } -} - -// Called on loadFinished on mainFrame. -void LayoutTestController::maybeDump(bool success) -{ - Q_ASSERT(sender() == m_topLoadingFrame); - - // as the function is called on loadFinished, the test might - // already have dumped and thus no longer be active, thus - // bail out here. - if (!m_isLoading) - return; - - m_topLoadingFrame = 0; - WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test - - if (!shouldWaitUntilDone()) { - if (WorkQueue::shared()->count()) - QTimer::singleShot(0, this, SLOT(processWork())); - else { - if (success) - emit done(); - m_isLoading = false; - } - } -} - -void LayoutTestController::waitUntilDone() -{ - //qDebug() << ">>>>waitForDone"; - m_waitForDone = true; - m_timeoutTimer.start(11000, this); -} - -void LayoutTestController::notifyDone() -{ - //qDebug() << ">>>>notifyDone"; - if (!m_timeoutTimer.isActive()) - return; - m_timeoutTimer.stop(); - emit done(); - m_isLoading = false; - m_waitForDone = false; - m_waitForPolicy = false; -} - -int LayoutTestController::windowCount() -{ - return m_drt->windowCount(); -} - -void LayoutTestController::clearBackForwardList() -{ - m_drt->webPage()->history()->clear(); -} - -void LayoutTestController::dumpEditingCallbacks() -{ - qDebug() << ">>>dumpEditingCallbacks"; - qt_dump_editing_callbacks(true); -} - -void LayoutTestController::dumpResourceLoadCallbacks() -{ - qt_dump_resource_load_callbacks(true); -} - -void LayoutTestController::queueBackNavigation(int howFarBackward) -{ - //qDebug() << ">>>queueBackNavigation" << howFarBackward; - WorkQueue::shared()->queue(new BackItem(howFarBackward, m_drt->webPage())); -} - -void LayoutTestController::queueForwardNavigation(int howFarForward) -{ - //qDebug() << ">>>queueForwardNavigation" << howFarForward; - WorkQueue::shared()->queue(new ForwardItem(howFarForward, m_drt->webPage())); -} - -void LayoutTestController::queueLoad(const QString &url, const QString &target) -{ - //qDebug() << ">>>queueLoad" << url << target; - QUrl mainResourceUrl = m_drt->webPage()->mainFrame()->url(); - QString absoluteUrl = mainResourceUrl.resolved(QUrl(url)).toEncoded(); - WorkQueue::shared()->queue(new LoadItem(absoluteUrl, target, m_drt->webPage())); -} - -void LayoutTestController::queueReload() -{ - //qDebug() << ">>>queueReload"; - WorkQueue::shared()->queue(new ReloadItem(m_drt->webPage())); -} - -void LayoutTestController::queueScript(const QString &url) -{ - //qDebug() << ">>>queueScript" << url; - WorkQueue::shared()->queue(new ScriptItem(url, m_drt->webPage())); -} - -void LayoutTestController::provisionalLoad() -{ - QWebFrame* frame = qobject_cast<QWebFrame*>(sender()); - if (!m_topLoadingFrame && m_isLoading) - m_topLoadingFrame = frame; -} - -void LayoutTestController::timerEvent(QTimerEvent *ev) -{ - if (ev->timerId() == m_timeoutTimer.timerId()) { - qDebug() << ">>>>>>>>>>>>> timeout"; - notifyDone(); - } else - QObject::timerEvent(ev); -} - -QString LayoutTestController::encodeHostName(const QString &host) -{ - QString encoded = QString::fromLatin1(QUrl::toAce(host + QLatin1String(".no"))); - encoded.truncate(encoded.length() - 3); // strip .no - return encoded; -} - -QString LayoutTestController::decodeHostName(const QString &host) -{ - QString decoded = QUrl::fromAce(host.toLatin1() + QByteArray(".no")); - decoded.truncate(decoded.length() - 3); - return decoded; -} - -void LayoutTestController::setJavaScriptProfilingEnabled(bool enable) -{ - m_topLoadingFrame->page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); - qt_drt_setJavaScriptProfilingEnabled(m_topLoadingFrame, enable); -} - -void LayoutTestController::setFixedContentsSize(int width, int height) -{ - m_topLoadingFrame->page()->setFixedContentsSize(QSize(width, height)); -} - -void LayoutTestController::setPrivateBrowsingEnabled(bool enable) -{ - m_drt->webPage()->settings()->setAttribute(QWebSettings::PrivateBrowsingEnabled, enable); -} - -void LayoutTestController::setPopupBlockingEnabled(bool enable) -{ - m_drt->webPage()->settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, !enable); -} - -bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(const QString &animationName, - double time, - const QString &elementId) -{ - QWebFrame* frame = m_drt->webPage()->mainFrame(); - Q_ASSERT(frame); - return qt_drt_pauseAnimation(frame, animationName, time, elementId); -} - -bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(const QString &propertyName, - double time, - const QString &elementId) -{ - QWebFrame* frame = m_drt->webPage()->mainFrame(); - Q_ASSERT(frame); - return qt_drt_pauseTransitionOfProperty(frame, propertyName, time, elementId); -} - -unsigned LayoutTestController::numberOfActiveAnimations() const -{ - QWebFrame* frame = m_drt->webPage()->mainFrame(); - Q_ASSERT(frame); - return qt_drt_numberOfActiveAnimations(frame); -} - -void LayoutTestController::disableImageLoading() -{ - // FIXME: Implement for testing fix for https://bugs.webkit.org/show_bug.cgi?id=27896 - // Also need to make sure image loading is re-enabled for each new test. -} - -void LayoutTestController::dispatchPendingLoadRequests() -{ - // FIXME: Implement for testing fix for 6727495 -} - -void LayoutTestController::setDatabaseQuota(int size) -{ - if (!m_topLoadingFrame) - return; - m_topLoadingFrame->securityOrigin().setDatabaseQuota(size); -} - -void LayoutTestController::clearAllDatabases() -{ - QWebDatabase::removeAllDatabases(); -} - -void LayoutTestController::whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains) -{ - QWebSecurityOrigin::whiteListAccessFromOrigin(sourceOrigin, destinationProtocol, destinationHost, allowDestinationSubdomains); -} - -void LayoutTestController::waitForPolicyDelegate() -{ - m_waitForPolicy = true; - waitUntilDone(); -} - -void LayoutTestController::overridePreference(const QString& name, const QVariant& value) -{ - QWebSettings* settings = m_topLoadingFrame->page()->settings(); - - if (name == "WebKitJavaScriptEnabled") - settings->setAttribute(QWebSettings::JavascriptEnabled, value.toBool()); - else if (name == "WebKitTabToLinksPreferenceKey") - settings->setAttribute(QWebSettings::LinksIncludedInFocusChain, value.toBool()); - else if (name == "WebKitOfflineWebApplicationCacheEnabled") - settings->setAttribute(QWebSettings::OfflineWebApplicationCacheEnabled, value.toBool()); - else if (name == "WebKitDefaultFontSize") - settings->setFontSize(QWebSettings::DefaultFontSize, value.toInt()); -} - -EventSender::EventSender(QWebPage *parent) - : QObject(parent) -{ - m_page = parent; -} - -void EventSender::mouseDown(int button) -{ - Qt::MouseButton mouseButton; - switch (button) { - case 0: - mouseButton = Qt::LeftButton; - break; - case 1: - mouseButton = Qt::MidButton; - break; - case 2: - mouseButton = Qt::RightButton; - break; - case 3: - // fast/events/mouse-click-events expects the 4th button to be treated as the middle button - mouseButton = Qt::MidButton; - break; - default: - mouseButton = Qt::LeftButton; - break; - } - -// qDebug() << "EventSender::mouseDown" << frame; - QMouseEvent event(QEvent::MouseButtonPress, m_mousePos, mouseButton, mouseButton, Qt::NoModifier); - QApplication::sendEvent(m_page, &event); -} - -void EventSender::mouseUp(int button) -{ - Qt::MouseButton mouseButton; - switch (button) { - case 0: - mouseButton = Qt::LeftButton; - break; - case 1: - mouseButton = Qt::MidButton; - break; - case 2: - mouseButton = Qt::RightButton; - break; - case 3: - // fast/events/mouse-click-events expects the 4th button to be treated as the middle button - mouseButton = Qt::MidButton; - break; - default: - mouseButton = Qt::LeftButton; - break; - } - -// qDebug() << "EventSender::mouseUp" << frame; - QMouseEvent event(QEvent::MouseButtonRelease, m_mousePos, mouseButton, mouseButton, Qt::NoModifier); - QApplication::sendEvent(m_page, &event); -} - -void EventSender::mouseMoveTo(int x, int y) -{ -// qDebug() << "EventSender::mouseMoveTo" << x << y; - m_mousePos = QPoint(x, y); - QMouseEvent event(QEvent::MouseMove, m_mousePos, Qt::NoButton, Qt::NoButton, Qt::NoModifier); - QApplication::sendEvent(m_page, &event); -} - -void EventSender::leapForward(int ms) -{ - m_timeLeap += ms; - qDebug() << "EventSender::leapForward" << ms; -} - -void EventSender::keyDown(const QString &string, const QStringList &modifiers) -{ - QString s = string; - Qt::KeyboardModifiers modifs = 0; - for (int i = 0; i < modifiers.size(); ++i) { - const QString &m = modifiers.at(i); - if (m == "ctrlKey") - modifs |= Qt::ControlModifier; - else if (m == "shiftKey") - modifs |= Qt::ShiftModifier; - else if (m == "altKey") - modifs |= Qt::AltModifier; - else if (m == "metaKey") - modifs |= Qt::MetaModifier; - } - int code = 0; - if (string.length() == 1) { - code = string.unicode()->unicode(); - qDebug() << ">>>>>>>>> keyDown" << code << (char)code; - // map special keycodes used by the tests to something that works for Qt/X11 - if (code == '\t') { - code = Qt::Key_Tab; - if (modifs == Qt::ShiftModifier) - code = Qt::Key_Backtab; - s = QString(); - } else if (code == 127) { - code = Qt::Key_Backspace; - if (modifs == Qt::AltModifier) - modifs = Qt::ControlModifier; - s = QString(); - } else if (code == 'o' && modifs == Qt::ControlModifier) { - s = QLatin1String("\n"); - code = '\n'; - modifs = 0; - } else if (code == 'y' && modifs == Qt::ControlModifier) { - s = QLatin1String("c"); - code = 'c'; - } else if (code == 'k' && modifs == Qt::ControlModifier) { - s = QLatin1String("x"); - code = 'x'; - } else if (code == 'a' && modifs == Qt::ControlModifier) { - s = QString(); - code = Qt::Key_Home; - modifs = 0; - } else if (code == 0xf702) { - s = QString(); - code = Qt::Key_Left; - if (modifs & Qt::MetaModifier) { - code = Qt::Key_Home; - modifs &= ~Qt::MetaModifier; - } - } else if (code == 0xf703) { - s = QString(); - code = Qt::Key_Right; - if (modifs & Qt::MetaModifier) { - code = Qt::Key_End; - modifs &= ~Qt::MetaModifier; - } - } else if (code == 0xf700) { - s = QString(); - code = Qt::Key_Up; - if (modifs & Qt::MetaModifier) { - code = Qt::Key_PageUp; - modifs &= ~Qt::MetaModifier; - } - } else if (code == 0xf701) { - s = QString(); - code = Qt::Key_Down; - if (modifs & Qt::MetaModifier) { - code = Qt::Key_PageDown; - modifs &= ~Qt::MetaModifier; - } - } else if (code == 'a' && modifs == Qt::ControlModifier) { - s = QString(); - code = Qt::Key_Home; - modifs = 0; - } else - code = string.unicode()->toUpper().unicode(); - } else { - qDebug() << ">>>>>>>>> keyDown" << string; - - if (string.startsWith(QLatin1Char('F')) && string.count() <= 3) { - s = s.mid(1); - int functionKey = s.toInt(); - Q_ASSERT(functionKey >= 1 && functionKey <= 35); - code = Qt::Key_F1 + (functionKey - 1); - // map special keycode strings used by the tests to something that works for Qt/X11 - } else if (string == QLatin1String("leftArrow")) { - s = QString(); - code = Qt::Key_Left; - } else if (string == QLatin1String("rightArrow")) { - s = QString(); - code = Qt::Key_Right; - } else if (string == QLatin1String("upArrow")) { - s = QString(); - code = Qt::Key_Up; - } else if (string == QLatin1String("downArrow")) { - s = QString(); - code = Qt::Key_Down; - } else if (string == QLatin1String("pageUp")) { - s = QString(); - code = Qt::Key_PageUp; - } else if (string == QLatin1String("pageDown")) { - s = QString(); - code = Qt::Key_PageDown; - } else if (string == QLatin1String("home")) { - s = QString(); - code = Qt::Key_Home; - } else if (string == QLatin1String("end")) { - s = QString(); - code = Qt::Key_End; - } else if (string == QLatin1String("delete")) { - s = QString(); - code = Qt::Key_Delete; - } - } - QKeyEvent event(QEvent::KeyPress, code, modifs, s); - QApplication::sendEvent(m_page, &event); - QKeyEvent event2(QEvent::KeyRelease, code, modifs, s); - QApplication::sendEvent(m_page, &event2); -} - -void EventSender::contextClick() -{ - QMouseEvent event(QEvent::MouseButtonPress, m_mousePos, Qt::RightButton, Qt::RightButton, Qt::NoModifier); - QApplication::sendEvent(m_page, &event); - QMouseEvent event2(QEvent::MouseButtonRelease, m_mousePos, Qt::RightButton, Qt::RightButton, Qt::NoModifier); - QApplication::sendEvent(m_page, &event2); -} - -void EventSender::scheduleAsynchronousClick() -{ - QMouseEvent* event = new QMouseEvent(QEvent::MouseButtonPress, m_mousePos, Qt::LeftButton, Qt::RightButton, Qt::NoModifier); - QApplication::postEvent(m_page, event); - QMouseEvent* event2 = new QMouseEvent(QEvent::MouseButtonRelease, m_mousePos, Qt::LeftButton, Qt::RightButton, Qt::NoModifier); - QApplication::postEvent(m_page, event2); -} - -QWebFrame* EventSender::frameUnderMouse() const -{ - QWebFrame* frame = m_page->mainFrame(); - -redo: - QList<QWebFrame*> children = frame->childFrames(); - for (int i = 0; i < children.size(); ++i) { - if (children.at(i)->geometry().contains(m_mousePos)) { - frame = children.at(i); - goto redo; - } - } - if (frame->geometry().contains(m_mousePos)) - return frame; - return 0; -} - - -TextInputController::TextInputController(QWebPage *parent) - : QObject(parent) -{ -} - -void TextInputController::doCommand(const QString &command) -{ - Qt::KeyboardModifiers modifiers = Qt::NoModifier; - int keycode = 0; - if (command == "moveBackwardAndModifySelection:") { - modifiers |= Qt::ShiftModifier; - keycode = Qt::Key_Left; - } else if (command =="moveDown:") { - keycode = Qt::Key_Down; - } else if (command =="moveDownAndModifySelection:") { - modifiers |= Qt::ShiftModifier; - keycode = Qt::Key_Down; - } else if (command =="moveForward:") { - keycode = Qt::Key_Right; - } else if (command =="moveForwardAndModifySelection:") { - modifiers |= Qt::ShiftModifier; - keycode = Qt::Key_Right; - } else if (command =="moveLeft:") { - keycode = Qt::Key_Left; - } else if (command =="moveLeftAndModifySelection:") { - modifiers |= Qt::ShiftModifier; - keycode = Qt::Key_Left; - } else if (command =="moveRight:") { - keycode = Qt::Key_Right; - } else if (command =="moveRightAndModifySelection:") { - modifiers |= Qt::ShiftModifier; - keycode = Qt::Key_Right; - } else if (command =="moveToBeginningOfDocument:") { - modifiers |= Qt::ControlModifier; - keycode = Qt::Key_Home; - } else if (command =="moveToBeginningOfLine:") { - keycode = Qt::Key_Home; -// } else if (command =="moveToBeginningOfParagraph:") { - } else if (command =="moveToEndOfDocument:") { - modifiers |= Qt::ControlModifier; - keycode = Qt::Key_End; - } else if (command =="moveToEndOfLine:") { - keycode = Qt::Key_End; -// } else if (command =="moveToEndOfParagraph:") { - } else if (command =="moveUp:") { - keycode = Qt::Key_Up; - } else if (command =="moveUpAndModifySelection:") { - modifiers |= Qt::ShiftModifier; - keycode = Qt::Key_Up; - } else if (command =="moveWordBackward:") { - modifiers |= Qt::ControlModifier; - keycode = Qt::Key_Up; - } else if (command =="moveWordBackwardAndModifySelection:") { - modifiers |= Qt::ShiftModifier; - modifiers |= Qt::ControlModifier; - keycode = Qt::Key_Left; - } else if (command =="moveWordForward:") { - modifiers |= Qt::ControlModifier; - keycode = Qt::Key_Right; - } else if (command =="moveWordForwardAndModifySelection:") { - modifiers |= Qt::ControlModifier; - modifiers |= Qt::ShiftModifier; - keycode = Qt::Key_Right; - } else if (command =="moveWordLeft:") { - modifiers |= Qt::ControlModifier; - keycode = Qt::Key_Left; - } else if (command =="moveWordRight:") { - modifiers |= Qt::ControlModifier; - keycode = Qt::Key_Left; - } else if (command =="moveWordRightAndModifySelection:") { - modifiers |= Qt::ShiftModifier; - modifiers |= Qt::ControlModifier; - keycode = Qt::Key_Right; - } else if (command =="moveWordLeftAndModifySelection:") { - modifiers |= Qt::ShiftModifier; - modifiers |= Qt::ControlModifier; - keycode = Qt::Key_Left; - } else if (command =="pageDown:") { - keycode = Qt::Key_PageDown; - } else if (command =="pageUp:") { - keycode = Qt::Key_PageUp; - } else if (command == "deleteWordBackward:") { - modifiers |= Qt::ControlModifier; - keycode = Qt::Key_Backspace; - } else if (command == "deleteBackward:") { - keycode = Qt::Key_Backspace; - } else if (command == "deleteForward:") { - keycode = Qt::Key_Delete; - } - QKeyEvent event(QEvent::KeyPress, keycode, modifiers); - QApplication::sendEvent(parent(), &event); - QKeyEvent event2(QEvent::KeyRelease, keycode, modifiers); - QApplication::sendEvent(parent(), &event2); -} GCController::GCController(QWebPage* parent) : QObject(parent) diff --git a/WebKitTools/DumpRenderTree/qt/jsobjects.h b/WebKitTools/DumpRenderTree/qt/jsobjects.h index c076bb9..3ee566b 100644 --- a/WebKitTools/DumpRenderTree/qt/jsobjects.h +++ b/WebKitTools/DumpRenderTree/qt/jsobjects.h @@ -29,156 +29,9 @@ #ifndef JSOBJECTS_H #define JSOBJECTS_H -#include <qobject.h> -#include <qdebug.h> -#include <qpoint.h> -#include <qstringlist.h> -#include <qsize.h> -#include <qbasictimer.h> - -class QWebFrame; -namespace WebCore { - class DumpRenderTree; -} -class LayoutTestController : public QObject -{ - Q_OBJECT -public: - LayoutTestController(WebCore::DumpRenderTree *drt); - - bool isLoading() const { return m_isLoading; } - void setLoading(bool loading) { m_isLoading = loading; } - - bool shouldDumpAsText() const { return m_textDump; } - bool shouldDumpBackForwardList() const { return m_dumpBackForwardList; } - bool shouldDumpChildrenAsText() const { return m_dumpChildrenAsText; } - bool shouldDumpDatabaseCallbacks() const { return m_dumpDatabaseCallbacks; } - bool shouldDumpStatusCallbacks() const { return m_dumpStatusCallbacks; } - bool shouldWaitUntilDone() const { return m_waitForDone; } - bool canOpenWindows() const { return m_canOpenWindows; } - bool shouldDumpTitleChanges() const { return m_dumpTitleChanges; } - bool waitForPolicy() const { return m_waitForPolicy; } - - void reset(); - -protected: - void timerEvent(QTimerEvent *); - -signals: - void done(); - -public slots: - void maybeDump(bool ok); - void dumpAsText() { m_textDump = true; } - void dumpChildFramesAsText() { m_dumpChildrenAsText = true; } - void dumpDatabaseCallbacks() { m_dumpDatabaseCallbacks = true; } - void dumpStatusCallbacks() { m_dumpStatusCallbacks = true; } - void setCanOpenWindows() { m_canOpenWindows = true; } - void waitUntilDone(); - void notifyDone(); - void dumpBackForwardList() { m_dumpBackForwardList = true; } - void dumpEditingCallbacks(); - void dumpResourceLoadCallbacks(); - void queueBackNavigation(int howFarBackward); - void queueForwardNavigation(int howFarForward); - void queueLoad(const QString &url, const QString &target = QString()); - void queueReload(); - void queueScript(const QString &url); - void provisionalLoad(); - void setCloseRemainingWindowsWhenComplete(bool=false) {} - int windowCount(); - void display() {} - void clearBackForwardList(); - void dumpTitleChanges() { m_dumpTitleChanges = true; } - QString encodeHostName(const QString &host); - QString decodeHostName(const QString &host); - void dumpSelectionRect() const {} - void setJavaScriptProfilingEnabled(bool enable); - void setFixedContentsSize(int width, int height); - void setPrivateBrowsingEnabled(bool enable); - void setPopupBlockingEnabled(bool enable); - - bool pauseAnimationAtTimeOnElementWithId(const QString &animationName, double time, const QString &elementId); - bool pauseTransitionAtTimeOnElementWithId(const QString &propertyName, double time, const QString &elementId); - unsigned numberOfActiveAnimations() const; - - void whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains); - - void dispatchPendingLoadRequests(); - void disableImageLoading(); - - void setDatabaseQuota(int size); - void clearAllDatabases(); - - void waitForPolicyDelegate(); - void overridePreference(const QString& name, const QVariant& value); - -private slots: - void processWork(); - -private: - bool m_isLoading; - bool m_textDump; - bool m_dumpBackForwardList; - bool m_dumpChildrenAsText; - bool m_canOpenWindows; - bool m_waitForDone; - bool m_dumpTitleChanges; - bool m_dumpDatabaseCallbacks; - bool m_dumpStatusCallbacks; - bool m_waitForPolicy; - - QBasicTimer m_timeoutTimer; - QWebFrame *m_topLoadingFrame; - WebCore::DumpRenderTree *m_drt; -}; +#include <QObject> class QWebPage; -class QWebFrame; - -class EventSender : public QObject -{ - Q_OBJECT -public: - EventSender(QWebPage *parent); - -public slots: - void mouseDown(int button = 0); - void mouseUp(int button = 0); - void mouseMoveTo(int x, int y); - void leapForward(int ms); - void keyDown(const QString &string, const QStringList &modifiers=QStringList()); - void clearKillRing() {} - void contextClick(); - void scheduleAsynchronousClick(); - -private: - QPoint m_mousePos; - QWebPage *m_page; - int m_timeLeap; - QWebFrame *frameUnderMouse() const; -}; - -class TextInputController : public QObject -{ - Q_OBJECT -public: - TextInputController(QWebPage *parent); - -public slots: - void doCommand(const QString &command); -// void setMarkedText(const QString &str, int from, int length); -// bool hasMarkedText(); -// void unmarkText(); -// QList<int> markedRange(); -// QList<int> selectedRange(); -// void validAttributesForMarkedText(); -// void inserText(const QString &); -// void firstRectForCharacterRange(); -// void characterIndexForPoint(int, int); -// void substringFromRange(int, int); -// void conversationIdentifier(); -}; class GCController : public QObject { diff --git a/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp index 7e013a7..d486d06 100644 --- a/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp +++ b/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp @@ -31,6 +31,7 @@ #include "EditingDelegate.h" #include "FrameLoadDelegate.h" +#include "HistoryDelegate.h" #include "LayoutTestController.h" #include "PixelDumpSupport.h" #include "PolicyDelegate.h" @@ -97,6 +98,7 @@ COMPtr<FrameLoadDelegate> sharedFrameLoadDelegate; COMPtr<UIDelegate> sharedUIDelegate; COMPtr<EditingDelegate> sharedEditingDelegate; COMPtr<ResourceLoadDelegate> sharedResourceLoadDelegate; +COMPtr<HistoryDelegate> sharedHistoryDelegate; IWebFrame* frame; HWND webViewWindow; @@ -199,7 +201,7 @@ static const wstring& fontsPath() return path; } -#ifdef DEBUG_WEBKIT_HAS_SUFFIX +#ifdef DEBUG_ALL #define WEBKITDLL TEXT("WebKit_debug.dll") #else #define WEBKITDLL TEXT("WebKit.dll") @@ -681,6 +683,11 @@ static bool shouldLogFrameLoadDelegates(const char* pathOrURL) return strstr(pathOrURL, "/loading/") || strstr(pathOrURL, "\\loading\\"); } +static bool shouldLogHistoryDelegates(const char* pathOrURL) +{ + return strstr(pathOrURL, "/globalhistory/") || strstr(pathOrURL, "\\globalhistory\\"); +} + static void resetDefaultsToConsistentValues(IWebPreferences* preferences) { #ifdef USE_MAC_FONTS @@ -737,7 +744,6 @@ static void resetDefaultsToConsistentValues(IWebPreferences* preferences) prefsPrivate->setAuthorAndUserStylesEnabled(TRUE); prefsPrivate->setDeveloperExtrasEnabled(FALSE); prefsPrivate->setExperimentalNotificationsEnabled(TRUE); - prefsPrivate->setExperimentalWebSocketsEnabled(FALSE); prefsPrivate->setShouldPaintNativeControls(FALSE); // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592> prefsPrivate->setXSSAuditorEnabled(FALSE); prefsPrivate->setOfflineWebApplicationCacheEnabled(TRUE); @@ -836,6 +842,17 @@ static void runTest(const string& testPathOrURL) if (shouldLogFrameLoadDelegates(pathOrURL.c_str())) gLayoutTestController->setDumpFrameLoadCallbacks(true); + COMPtr<IWebView> webView; + if (SUCCEEDED(frame->webView(&webView))) { + COMPtr<IWebViewPrivate> viewPrivate; + if (SUCCEEDED(webView->QueryInterface(&viewPrivate))) { + if (shouldLogHistoryDelegates(pathOrURL.c_str())) { + gLayoutTestController->setDumpHistoryDelegateCallbacks(true); + viewPrivate->setHistoryDelegate(sharedHistoryDelegate.get()); + } else + viewPrivate->setHistoryDelegate(0); + } + } COMPtr<IWebHistory> history; if (SUCCEEDED(WebKitCreateInstance(CLSID_WebHistory, 0, __uuidof(history), reinterpret_cast<void**>(&history)))) history->setOptionalSharedHistory(0); @@ -843,8 +860,7 @@ static void runTest(const string& testPathOrURL) resetWebViewToConsistentStateBeforeTesting(); prevTestBFItem = 0; - COMPtr<IWebView> webView; - if (SUCCEEDED(frame->webView(&webView))) { + if (webView) { COMPtr<IWebBackForwardList> bfList; if (SUCCEEDED(webView->backForwardList(&bfList))) bfList->currentItem(&prevTestBFItem); @@ -1095,9 +1111,11 @@ IWebView* createWebViewAndOffscreenWindow(HWND* webViewWindow) #if USE(CFNETWORK) RetainPtr<CFURLCacheRef> sharedCFURLCache() { +#ifndef DEBUG_ALL + HMODULE module = GetModuleHandle(TEXT("CFNetwork.dll")); +#else HMODULE module = GetModuleHandle(TEXT("CFNetwork_debug.dll")); - if (!module) - module = GetModuleHandle(TEXT("CFNetwork.dll")); +#endif if (!module) return 0; @@ -1153,6 +1171,7 @@ int main(int argc, char* argv[]) sharedUIDelegate.adoptRef(new UIDelegate); sharedEditingDelegate.adoptRef(new EditingDelegate); sharedResourceLoadDelegate.adoptRef(new ResourceLoadDelegate); + sharedHistoryDelegate.adoptRef(new HistoryDelegate); // FIXME - need to make DRT pass with Windows native controls <http://bugs.webkit.org/show_bug.cgi?id=25592> COMPtr<IWebPreferences> tmpPreferences; diff --git a/WebKitTools/DumpRenderTree/win/DumpRenderTree.vcproj b/WebKitTools/DumpRenderTree/win/DumpRenderTree.vcproj index dea2467..ba3640c 100644 --- a/WebKitTools/DumpRenderTree/win/DumpRenderTree.vcproj +++ b/WebKitTools/DumpRenderTree/win/DumpRenderTree.vcproj @@ -185,7 +185,7 @@ <Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""$(ProjectDir)\.";"$(ProjectDir)\..";"$(ProjectDir)\..\cg";"$(WebKitOutputDir)\Include";"$(WebKitOutputDir)\Include\private";"$(WebKitOutputDir)\Include\DumpRenderTree\ForwardingHeaders";"$(WebKitOutputDir)\Include\JavaScriptCore";"$(WebKitOutputDir)\Include\private\JavaScriptCore";"$(WebKitLibrariesDir)\Include";"$(WebKitLibrariesDir)\Include\private";"$(WebKitLibrariesDir)\include\pthreads";"$(WebKitOutputDir)\Include\WebCore";"$(WebKitLibrariesDir)\Include\WebCore";"$(WebKitLibrariesDir)\Include\CoreFoundation\OSXCompatibilityHeaders";"$(WebKitLibrariesDir)\Include\CoreFoundation\OSXCompatibilityHeaders\GNUCompatibility""
- PreprocessorDefinitions="_CONSOLE;DEBUG_WEBKIT_HAS_SUFFIX"
+ PreprocessorDefinitions="_CONSOLE"
DisableSpecificWarnings="4146"
ForcedIncludeFiles="DumpRenderTreePrefix.h"
/>
@@ -376,6 +376,77 @@ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

if not defined ARCHIVE_BUILD (if defined PRODUCTION exit /b)

mkdir 2>NUL "$(WebKitOutputDir)\bin"

if not exist "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" exit /b

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
/>
</Configuration>
+ <Configuration
+ Name="Debug_All|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(WebKitLibrariesDir)\tools\vsprops\common.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug_internal.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug_all.vsprops"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine="%SystemDrive%\cygwin\bin\which.exe bash
if errorlevel 1 set PATH=%SystemDrive%\cygwin\bin;%PATH%
cmd /c
if exist "$(WebKitOutputDir)\buildfailed" grep XX$(ProjectName)XX "$(WebKitOutputDir)\buildfailed"
if errorlevel 1 exit 1
echo XX$(ProjectName)XX > "$(WebKitOutputDir)\buildfailed"

mkdir 2>NUL "$(WebKitOutputDir)\include\DumpRenderTree"
mkdir 2>NUL "$(WebKitOutputDir)\include\DumpRenderTree\ForwardingHeaders"
mkdir 2>NUL "$(WebKitOutputDir)\include\DumpRenderTree\ForwardingHeaders\wtf"

xcopy /y /d "$(ProjectDir)\..\ForwardingHeaders\wtf\*.h" "$(WebKitOutputDir)\include\DumpRenderTree\ForwardingHeaders\wtf"
"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""$(ProjectDir)\.";"$(ProjectDir)\..";"$(ProjectDir)\..\cg";"$(WebKitOutputDir)\Include";"$(WebKitOutputDir)\Include\private";"$(WebKitOutputDir)\Include\DumpRenderTree\ForwardingHeaders";"$(WebKitOutputDir)\Include\JavaScriptCore";"$(WebKitOutputDir)\Include\private\JavaScriptCore";"$(WebKitLibrariesDir)\Include";"$(WebKitLibrariesDir)\Include\private";"$(WebKitLibrariesDir)\include\pthreads";"$(WebKitOutputDir)\Include\WebCore";"$(WebKitLibrariesDir)\Include\WebCore";"$(WebKitLibrariesDir)\Include\CoreFoundation\OSXCompatibilityHeaders";"$(WebKitLibrariesDir)\Include\CoreFoundation\OSXCompatibilityHeaders\GNUCompatibility""
+ PreprocessorDefinitions="_CONSOLE"
+ DisableSpecificWarnings="4146"
+ ForcedIncludeFiles="DumpRenderTreePrefix.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="JavaScriptCore$(WebKitDLLConfigSuffix).lib WebKitGUID$(WebKitConfigSuffix).lib WebKit$(WebKitDLLConfigSuffix).lib CoreGraphics$(LibraryConfigSuffix).lib CoreFoundation$(LibraryConfigSuffix).lib CFNetwork$(LibraryConfigSuffix).lib pthreadVC2$(LibraryConfigSuffix).lib gdi32.lib ole32.lib oleaut32.lib user32.lib shlwapi.lib oleacc.lib comsuppw.lib"
+ AdditionalLibraryDirectories=""
+ DelayLoadDLLs=""
+ 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"
+ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

if not defined ARCHIVE_BUILD (if defined PRODUCTION exit /b)

mkdir 2>NUL "$(WebKitOutputDir)\bin"

if not exist "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" exit /b

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CFNetwork$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CFNetwork.resources" "$(WebKitOutputDir)\bin\CFNetwork.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CharacterSets" "$(WebKitOutputDir)\bin\CharacterSets"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\dnssd.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxml2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\libxslt$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\SQLite3$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\zlib1$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
+ />
+ </Configuration>
</Configurations>
<References>
</References>
@@ -456,6 +527,14 @@ >
</File>
<File
+ RelativePath=".\HistoryDelegate.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\HistoryDelegate.h"
+ >
+ </File>
+ <File
RelativePath=".\PolicyDelegate.cpp"
>
</File>
@@ -555,6 +634,14 @@ Name="VCCLCompilerTool"
/>
</FileConfiguration>
+ <FileConfiguration
+ Name="Debug_All|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
</File>
<File
RelativePath="..\cairo\PixelDumpSupportCairo.h"
@@ -583,6 +670,14 @@ Name="VCCustomBuildTool"
/>
</FileConfiguration>
+ <FileConfiguration
+ Name="Debug_All|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ </FileConfiguration>
</File>
<File
RelativePath="..\cg\PixelDumpSupportCG.cpp"
diff --git a/WebKitTools/DumpRenderTree/win/EventSender.cpp b/WebKitTools/DumpRenderTree/win/EventSender.cpp index 721b238..dd5bf9d 100644 --- a/WebKitTools/DumpRenderTree/win/EventSender.cpp +++ b/WebKitTools/DumpRenderTree/win/EventSender.cpp @@ -194,7 +194,7 @@ static inline POINTL pointl(const POINT& point) return result; } -static void doMouseUp(MSG msg) +static void doMouseUp(MSG msg, HRESULT* oleDragAndDropReturnValue = 0) { COMPtr<IWebFramePrivate> framePrivate; if (SUCCEEDED(frame->QueryInterface(&framePrivate))) @@ -215,6 +215,8 @@ static void doMouseUp(MSG msg) didDragEnter = true; } HRESULT hr = draggingInfo->dropSource()->QueryContinueDrag(0, 0); + if (oleDragAndDropReturnValue) + *oleDragAndDropReturnValue = hr; webViewDropTarget->DragOver(0, pointl(screenPoint), &effect); if (hr == DRAGDROP_S_DROP && effect != DROPEFFECT_NONE) { DWORD effect = 0; @@ -314,7 +316,7 @@ static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function return JSValueMakeUndefined(context); } -void replaySavedEvents() +void replaySavedEvents(HRESULT* oleDragAndDropReturnValue) { replayingSavedEvents = true; @@ -326,7 +328,7 @@ void replaySavedEvents() case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: - doMouseUp(msg); + doMouseUp(msg, oleDragAndDropReturnValue); break; case WM_MOUSEMOVE: doMouseMove(msg); @@ -370,7 +372,7 @@ void replaySavedEvents() case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: - doMouseUp(msg); + doMouseUp(msg, oleDragAndDropReturnValue); break; case WM_MOUSEMOVE: doMouseMove(msg); diff --git a/WebKitTools/DumpRenderTree/win/EventSender.h b/WebKitTools/DumpRenderTree/win/EventSender.h index 9ae0aec..79d7dab 100644 --- a/WebKitTools/DumpRenderTree/win/EventSender.h +++ b/WebKitTools/DumpRenderTree/win/EventSender.h @@ -31,11 +31,12 @@ class DraggingInfo; +typedef long HRESULT; typedef const struct OpaqueJSContext* JSContextRef; typedef struct OpaqueJSValue* JSObjectRef; JSObjectRef makeEventSender(JSContextRef context); -void replaySavedEvents(); +void replaySavedEvents(HRESULT* oleDragAndDropReturnValue = 0); extern DraggingInfo* draggingInfo; diff --git a/WebKitTools/DumpRenderTree/win/HistoryDelegate.cpp b/WebKitTools/DumpRenderTree/win/HistoryDelegate.cpp new file mode 100644 index 0000000..8a41fac --- /dev/null +++ b/WebKitTools/DumpRenderTree/win/HistoryDelegate.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2009 Apple 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. + */ + +#include "config.h" +#include "HistoryDelegate.h" + +#include "DumpRenderTree.h" +#include "DumpRenderTreeWin.h" +#include "LayoutTestController.h" +#include <string> +#include <WebKit/WebKit.h> + +using std::wstring; + +static inline wstring wstringFromBSTR(BSTR str) +{ + return wstring(str, ::SysStringLen(str)); +} + +HistoryDelegate::HistoryDelegate() + : m_refCount(1) +{ +} + +HistoryDelegate::~HistoryDelegate() +{ +} + + // IUnknown +HRESULT HistoryDelegate::QueryInterface(REFIID riid, void** ppvObject) +{ + *ppvObject = 0; + if (IsEqualGUID(riid, IID_IUnknown)) + *ppvObject = static_cast<IWebHistoryDelegate*>(this); + else if (IsEqualGUID(riid, IID_IWebHistoryDelegate)) + *ppvObject = static_cast<IWebHistoryDelegate*>(this); + else + return E_NOINTERFACE; + + AddRef(); + return S_OK; +} + +ULONG HistoryDelegate::AddRef(void) +{ + return ++m_refCount; +} + +ULONG HistoryDelegate::Release(void) +{ + ULONG newRef = --m_refCount; + if (!newRef) + delete(this); + + return newRef; +} + +// IWebHistoryDelegate +HRESULT HistoryDelegate::didNavigateWithNavigationData(IWebView* webView, IWebNavigationData* navigationData, IWebFrame* webFrame) +{ + if (!gLayoutTestController->dumpHistoryDelegateCallbacks()) + return S_OK; + + BSTR urlBSTR; + if (FAILED(navigationData->url(&urlBSTR))) + return E_FAIL; + wstring url; + if (urlBSTR) + url = urlSuitableForTestResult(wstringFromBSTR(urlBSTR)); + SysFreeString(urlBSTR); + + BSTR titleBSTR; + if (FAILED(navigationData->title(&titleBSTR))) + return E_FAIL; + wstring title; + if (titleBSTR) + title = wstringFromBSTR(titleBSTR); + SysFreeString(titleBSTR); + + COMPtr<IWebURLRequest> request; + if (FAILED(navigationData->originalRequest(&request))) + return E_FAIL; + + BSTR httpMethodBSTR; + if (FAILED(request->HTTPMethod(&httpMethodBSTR))) + return E_FAIL; + wstring httpMethod; + if (httpMethodBSTR) + httpMethod = wstringFromBSTR(httpMethodBSTR); + SysFreeString(httpMethodBSTR); + + COMPtr<IWebURLResponse> response; + if (FAILED(navigationData->response(&response))) + return E_FAIL; + + COMPtr<IWebHTTPURLResponse> httpResponse; + if (FAILED(response->QueryInterface(&httpResponse))) + return E_FAIL; + + int statusCode = 0; + if (FAILED(httpResponse->statusCode(&statusCode))) + return E_FAIL; + + BOOL hasSubstituteData; + if (FAILED(navigationData->hasSubstituteData(&hasSubstituteData))) + return E_FAIL; + + BSTR clientRedirectSourceBSTR; + if (FAILED(navigationData->clientRedirectSource(&clientRedirectSourceBSTR))) + return E_FAIL; + bool hasClientRedirect = clientRedirectSourceBSTR && SysStringLen(clientRedirectSourceBSTR); + wstring redirectSource; + if (clientRedirectSourceBSTR) + redirectSource = urlSuitableForTestResult(wstringFromBSTR(clientRedirectSourceBSTR)); + SysFreeString(clientRedirectSourceBSTR); + + bool wasFailure = hasSubstituteData || (httpResponse && statusCode >= 400); + + printf("WebView navigated to url \"%S\" with title \"%S\" with HTTP equivalent method \"%S\". The navigation was %s and was %s%S.\n", + url.c_str(), + title.c_str(), + httpMethod.c_str(), + wasFailure ? "a failure" : "successful", + hasClientRedirect ? "a client redirect from " : "not a client redirect", + redirectSource.c_str()); + + return S_OK; +} + +HRESULT HistoryDelegate::didPerformClientRedirectFromURL(IWebView*, BSTR sourceURL, BSTR destinationURL, IWebFrame*) +{ + if (!gLayoutTestController->dumpHistoryDelegateCallbacks()) + return S_OK; + + wstring source; + if (sourceURL) + source = urlSuitableForTestResult(wstringFromBSTR(sourceURL)); + + wstring destination; + if (destinationURL) + destination = urlSuitableForTestResult(wstringFromBSTR(destinationURL)); + + printf("WebView performed a client redirect from \"%S\" to \"%S\".\n", source.c_str(), destination.c_str()); + return S_OK; +} + +HRESULT HistoryDelegate::didPerformServerRedirectFromURL(IWebView* webView, BSTR sourceURL, BSTR destinationURL, IWebFrame* webFrame) +{ + if (!gLayoutTestController->dumpHistoryDelegateCallbacks()) + return S_OK; + + wstring source; + if (sourceURL) + source = urlSuitableForTestResult(wstringFromBSTR(sourceURL)); + + wstring destination; + if (destinationURL) + destination = urlSuitableForTestResult(wstringFromBSTR(destinationURL)); + + printf("WebView performed a server redirect from \"%S\" to \"%S\".\n", source.c_str(), destination.c_str()); + return S_OK; +} + +HRESULT HistoryDelegate::updateHistoryTitle(IWebView* webView, BSTR titleBSTR, BSTR urlBSTR) +{ + if (!gLayoutTestController->dumpHistoryDelegateCallbacks()) + return S_OK; + + wstring url; + if (urlBSTR) + url = urlSuitableForTestResult(wstringFromBSTR(urlBSTR)); + + wstring title; + if (titleBSTR) + title = wstringFromBSTR(titleBSTR); + + printf("WebView updated the title for history URL \"%S\" to \"%S\".\n", url.c_str(), title.c_str()); + return S_OK; +} + +HRESULT HistoryDelegate::populateVisitedLinksForWebView(IWebView* webView) +{ + if (!gLayoutTestController->dumpHistoryDelegateCallbacks()) + return S_OK; + + BSTR urlBSTR; + if (FAILED(webView->mainFrameURL(&urlBSTR))) + return E_FAIL; + + wstring url; + if (urlBSTR) + url = urlSuitableForTestResult(wstringFromBSTR(urlBSTR)); + SysFreeString(urlBSTR); + + if (gLayoutTestController->dumpVisitedLinksCallback()) + printf("Asked to populate visited links for WebView \"%S\"\n", url.c_str()); + + return S_OK; +} diff --git a/WebKitTools/DumpRenderTree/win/HistoryDelegate.h b/WebKitTools/DumpRenderTree/win/HistoryDelegate.h new file mode 100644 index 0000000..41be670 --- /dev/null +++ b/WebKitTools/DumpRenderTree/win/HistoryDelegate.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2009 Apple 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 HistoryDelegate_h +#define HistoryDelegate_h + +#include <WebKit/WebKit.h> +#include <wtf/OwnPtr.h> + +class HistoryDelegate : public IWebHistoryDelegate { +public: + HistoryDelegate(); + virtual ~HistoryDelegate(); + + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(void); + virtual ULONG STDMETHODCALLTYPE Release(void); + + // IWebHistoryDelegate + virtual HRESULT STDMETHODCALLTYPE didNavigateWithNavigationData( + /* [in] */ IWebView* webView, + /* [in] */ IWebNavigationData* navigationData, + /* [in] */ IWebFrame* webFrame); + + virtual HRESULT STDMETHODCALLTYPE didPerformClientRedirectFromURL( + /* [in] */ IWebView* webView, + /* [in] */ BSTR sourceURL, + /* [in] */ BSTR destinationURL, + /* [in] */ IWebFrame* webFrame); + + virtual HRESULT STDMETHODCALLTYPE didPerformServerRedirectFromURL( + /* [in] */ IWebView* webView, + /* [in] */ BSTR sourceURL, + /* [in] */ BSTR destinationURL, + /* [in] */ IWebFrame* webFrame); + + virtual HRESULT STDMETHODCALLTYPE updateHistoryTitle( + /* [in] */ IWebView* webView, + /* [in] */ BSTR title, + /* [in] */ BSTR url); + + virtual HRESULT STDMETHODCALLTYPE populateVisitedLinksForWebView( + /* [in] */ IWebView* webView); + +private: + ULONG m_refCount; +}; + +#endif // HistoryDelegate_h diff --git a/WebKitTools/DumpRenderTree/win/ImageDiff.vcproj b/WebKitTools/DumpRenderTree/win/ImageDiff.vcproj index 37bddeb..d9f5225 100644 --- a/WebKitTools/DumpRenderTree/win/ImageDiff.vcproj +++ b/WebKitTools/DumpRenderTree/win/ImageDiff.vcproj @@ -216,6 +216,73 @@ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

mkdir 2>NUL "$(WebKitOutputDir)\bin"

if not defined ARCHIVE_BUILD (if defined PRODUCTION exit /b)

if not exist "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" exit /b

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
/>
</Configuration>
+ <Configuration
+ Name="Debug_All|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(WebKitLibrariesDir)\tools\vsprops\common.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug_internal.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug_all.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine="%SystemDrive%\cygwin\bin\which.exe bash
if errorlevel 1 set PATH=%SystemDrive%\cygwin\bin;%PATH%
cmd /c
if exist "$(WebKitOutputDir)\buildfailed" grep XX$(ProjectName)XX "$(WebKitOutputDir)\buildfailed"
if errorlevel 1 exit 1
echo XX$(ProjectName)XX > "$(WebKitOutputDir)\buildfailed"
"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""$(WebKitOutputDir)\include";"$(WebKitOutputDir)\include\private";"$(WebKitOutputDir)\include\WebCore\ForwardingHeaders";"$(WebKitLibrariesDir)\include";"$(WebKitLibrariesDir)\include\private";"$(WebKitLibrariesDir)\Include\CoreFoundation\OSXCompatibilityHeaders";"$(WebKitLibrariesDir)\Include\CoreFoundation\OSXCompatibilityHeaders\GNUCompatibility""
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="CoreGraphics$(LibraryConfigSuffix).lib CoreFoundation$(LibraryConfigSuffix).lib"
+ AdditionalLibraryDirectories=""
+ 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"
+ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

mkdir 2>NUL "$(WebKitOutputDir)\bin"

if not defined ARCHIVE_BUILD (if defined PRODUCTION exit /b)

if not exist "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" exit /b

xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreGraphics$(LibraryConfigSuffix).pdb" "$(WebKitOutputDir)\bin"
"
+ />
+ </Configuration>
</Configurations>
<References>
</References>
diff --git a/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp b/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp index cf3ac85..d3cac7a 100644 --- a/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp +++ b/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp @@ -876,34 +876,68 @@ void LayoutTestController::addUserStyleSheet(JSStringRef source) void LayoutTestController::showWebInspector() { - COMPtr<IWebViewPrivate> webView; - if (FAILED(WebKitCreateInstance(__uuidof(WebView), 0, __uuidof(webView), reinterpret_cast<void**>(&webView)))) + COMPtr<IWebView> webView; + if (FAILED(frame->webView(&webView))) + return; + + COMPtr<IWebPreferences> preferences; + if (FAILED(webView->preferences(&preferences))) + return; + + COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences); + if (!prefsPrivate) + return; + + prefsPrivate->setDeveloperExtrasEnabled(true); + + COMPtr<IWebViewPrivate> viewPrivate(Query, webView); + if (!viewPrivate) return; COMPtr<IWebInspector> inspector; - if (SUCCEEDED(webView->inspector(&inspector))) + if (SUCCEEDED(viewPrivate->inspector(&inspector))) inspector->show(); } void LayoutTestController::closeWebInspector() { - COMPtr<IWebViewPrivate> webView; - if (FAILED(WebKitCreateInstance(__uuidof(WebView), 0, __uuidof(webView), reinterpret_cast<void**>(&webView)))) + COMPtr<IWebView> webView; + if (FAILED(frame->webView(&webView))) + return; + + COMPtr<IWebViewPrivate> viewPrivate(Query, webView); + if (!viewPrivate) return; COMPtr<IWebInspector> inspector; - if (SUCCEEDED(webView->inspector(&inspector))) - inspector->close(); + if (FAILED(viewPrivate->inspector(&inspector))) + return; + + inspector->close(); + + COMPtr<IWebPreferences> preferences; + if (FAILED(webView->preferences(&preferences))) + return; + + COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences); + if (!prefsPrivate) + return; + + prefsPrivate->setDeveloperExtrasEnabled(false); } void LayoutTestController::evaluateInWebInspector(long callId, JSStringRef script) { - COMPtr<IWebViewPrivate> webView; - if (FAILED(WebKitCreateInstance(__uuidof(WebView), 0, __uuidof(webView), reinterpret_cast<void**>(&webView)))) + COMPtr<IWebView> webView; + if (FAILED(frame->webView(&webView))) + return; + + COMPtr<IWebViewPrivate> viewPrivate(Query, webView); + if (!viewPrivate) return; COMPtr<IWebInspector> inspector; - if (FAILED(webView->inspector(&inspector))) + if (FAILED(viewPrivate->inspector(&inspector))) return; COMPtr<IWebInspectorPrivate> inspectorPrivate(Query, inspector); @@ -912,3 +946,52 @@ void LayoutTestController::evaluateInWebInspector(long callId, JSStringRef scrip inspectorPrivate->evaluateInFrontend(callId, bstrT(script).GetBSTR()); } + +void LayoutTestController::evaluateScriptInIsolatedWorld(unsigned worldId, JSObjectRef globalObject, JSStringRef script) +{ + COMPtr<IWebFramePrivate> framePrivate(Query, frame); + if (!framePrivate) + return; + + BSTR result; + if (FAILED(framePrivate->stringByEvaluatingJavaScriptInIsolatedWorld(worldId, reinterpret_cast<OLE_HANDLE>(globalObject), bstrT(script).GetBSTR(), &result))) + return; + SysFreeString(result); +} + +void LayoutTestController::removeAllVisitedLinks() +{ + COMPtr<IWebHistory> history; + if (FAILED(WebKitCreateInstance(CLSID_WebHistory, 0, __uuidof(history), reinterpret_cast<void**>(&history)))) + return; + + COMPtr<IWebHistory> sharedHistory; + if (FAILED(history->optionalSharedHistory(&sharedHistory)) || !sharedHistory) + return; + + COMPtr<IWebHistoryPrivate> sharedHistoryPrivate; + if (FAILED(sharedHistory->QueryInterface(&sharedHistoryPrivate))) + return; + + sharedHistoryPrivate->removeAllVisitedLinks(); +} + +JSRetainPtr<JSStringRef> LayoutTestController::counterValueForElementById(JSStringRef id) +{ + COMPtr<IWebFramePrivate> framePrivate(Query, frame); + if (!framePrivate) + return 0; + + wstring idWstring = jsStringRefToWString(id); + BSTR idBSTR = SysAllocStringLen((OLECHAR*)idWstring.c_str(), idWstring.length()); + BSTR counterValueBSTR; + if (FAILED(framePrivate->counterValueForElementById(idBSTR, &counterValueBSTR))) + return 0; + + wstring counterValue(counterValueBSTR, SysStringLen(counterValueBSTR)); + SysFreeString(idBSTR); + SysFreeString(counterValueBSTR); + JSRetainPtr<JSStringRef> counterValueJS(Adopt, JSStringCreateWithCharacters(counterValue.data(), counterValue.length())); + return counterValueJS; +} + diff --git a/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.rc b/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.rc index cccc69c..a8fbbcd 100644 --- a/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.rc +++ b/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.rc @@ -7,7 +7,7 @@ //
// Generated from the TEXTINCLUDE 2 resource.
//
-#include "afxres.h"
+#include "windows.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
@@ -34,7 +34,7 @@ END 2 TEXTINCLUDE
BEGIN
- "#include ""afxres.h""\r\n"
+ "#include ""windows.h""\r\n"
"\0"
END
diff --git a/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.vcproj b/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.vcproj index 0e0918d..b1f2073 100644 --- a/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.vcproj +++ b/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.vcproj @@ -157,7 +157,7 @@ <Configuration
Name="Debug_Internal|Win32"
ConfigurationType="2"
- InheritedPropertySheets="$(WebKitLibrariesDir)\tools\vsprops\common.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug_internal.vsprops"
+ InheritedPropertySheets="$(WebKitLibrariesDir)\tools\vsprops\common.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug_internal.vsprops"
CharacterSet="1"
>
<Tool
@@ -293,6 +293,76 @@ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed""
/>
</Configuration>
+ <Configuration
+ Name="Debug_All|Win32"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(WebKitLibrariesDir)\tools\vsprops\common.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug_internal.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug_all.vsprops"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine="%SystemDrive%\cygwin\bin\which.exe bash
if errorlevel 1 set PATH=%SystemDrive%\cygwin\bin;%PATH%
cmd /c
if exist "$(WebKitOutputDir)\buildfailed" grep XX$(ProjectName)XX "$(WebKitOutputDir)\buildfailed"
if errorlevel 1 exit 1
echo XX$(ProjectName)XX > "$(WebKitOutputDir)\buildfailed"
"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""$(WebKitOutputDir)\Include";"$(WebKitOutputDir)\Include\JavaScriptCore";"$(WebKitOutputDir)\Include\WebCore\ForwardingHeaders";"$(ProjectDir)..\..\TestNetscapePlugin.subproj";"$(WebKitLibrariesDir)\include";"$(WebKitLibrariesDir)\include\CoreFoundation\OSXCompatibilityHeaders";"$(WebKitLibrariesDir)\include\CoreFoundation\OSXCompatibilityHeaders\GNUCompatibility""
+ PreprocessorDefinitions="_USRDLL;TESTNETSCAPEPLUGIN_EXPORTS;snprintf=_snprintf"
+ RuntimeLibrary="3"
+ DisableSpecificWarnings="4819"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ AdditionalIncludeDirectories=""
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)\$(ProjectName)$(WebKitConfigSuffix)\np$(ProjectName)$(WebKitConfigSuffix).dll"
+ ModuleDefinitionFile="TestNetscapePlugin$(WebKitConfigSuffix).def"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed""
+ />
+ </Configuration>
</Configurations>
<References>
</References>
diff --git a/WebKitTools/DumpRenderTree/win/UIDelegate.cpp b/WebKitTools/DumpRenderTree/win/UIDelegate.cpp index b78fd3e..16724d7 100755 --- a/WebKitTools/DumpRenderTree/win/UIDelegate.cpp +++ b/WebKitTools/DumpRenderTree/win/UIDelegate.cpp @@ -502,13 +502,14 @@ HRESULT STDMETHODCALLTYPE UIDelegate::doDragDrop( *performedEffect = 0; draggingInfo = new DraggingInfo(object, source); - replaySavedEvents(); + HRESULT oleDragAndDropReturnValue = DRAGDROP_S_CANCEL; + replaySavedEvents(&oleDragAndDropReturnValue); if (draggingInfo) { *performedEffect = draggingInfo->performedDropEffect(); delete draggingInfo; draggingInfo = 0; } - return S_OK; + return oleDragAndDropReturnValue; } HRESULT STDMETHODCALLTYPE UIDelegate::webViewGetDlgCode( diff --git a/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp b/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp index bfe1d99..72f85ae 100644 --- a/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp +++ b/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp @@ -313,3 +313,8 @@ void LayoutTestController::evaluateInWebInspector(long callId, JSStringRef scrip { // FIXME: Implement this. } + +void LayoutTestController::removeAllVisitedLinks() +{ + // FIXME: Implement this. +} diff --git a/WebKitTools/FindSafari/FindSafari.rc b/WebKitTools/FindSafari/FindSafari.rc index 20c2da2..7d4abac 100644 --- a/WebKitTools/FindSafari/FindSafari.rc +++ b/WebKitTools/FindSafari/FindSafari.rc @@ -7,7 +7,7 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "afxres.h" +#include "windows.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS @@ -34,7 +34,7 @@ END 2 TEXTINCLUDE BEGIN - "#include ""afxres.h""\r\n" + "#include ""windows.h""\r\n" "\0" END diff --git a/WebKitTools/FindSafari/FindSafari.vcproj b/WebKitTools/FindSafari/FindSafari.vcproj index 48ea3df..083fa4d 100644 --- a/WebKitTools/FindSafari/FindSafari.vcproj +++ b/WebKitTools/FindSafari/FindSafari.vcproj @@ -15,7 +15,7 @@ </ToolFiles>
<Configurations>
<Configuration
- Name="Release|Win32"
+ Name="all|Win32"
ConfigurationType="1"
InheritedPropertySheets="$(WebKitLibrariesDir)\tools\vsprops\common.vsprops;$(WebKitLibrariesDir)\tools\vsprops\release.vsprops"
CharacterSet="1"
diff --git a/WebKitTools/GtkLauncher/main.c b/WebKitTools/GtkLauncher/main.c index 6994674..2f9350d 100644 --- a/WebKitTools/GtkLauncher/main.c +++ b/WebKitTools/GtkLauncher/main.c @@ -65,11 +65,11 @@ link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpoin } static void -title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) +notify_title_cb (WebKitWebView* web_view, GParamSpec* pspec, gpointer data) { if (main_title) g_free (main_title); - main_title = g_strdup (title); + main_title = g_strdup (webkit_web_view_get_title(web_view)); update_title (GTK_WINDOW (main_window)); } @@ -118,7 +118,7 @@ create_browser () web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ()); gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (web_view)); - g_signal_connect (web_view, "title-changed", G_CALLBACK (title_change_cb), web_view); + g_signal_connect (web_view, "notify::title", G_CALLBACK (notify_title_cb), web_view); g_signal_connect (web_view, "notify::load-status", G_CALLBACK (notify_load_status_cb), web_view); g_signal_connect (web_view, "notify::progress", G_CALLBACK (notify_progress_cb), web_view); g_signal_connect (web_view, "hovering-over-link", G_CALLBACK (link_hover_cb), web_view); diff --git a/WebKitTools/Scripts/VCSUtils.pm b/WebKitTools/Scripts/VCSUtils.pm index 6ec12c9..e1e0bc2 100644 --- a/WebKitTools/Scripts/VCSUtils.pm +++ b/WebKitTools/Scripts/VCSUtils.pm @@ -40,10 +40,13 @@ BEGIN { $VERSION = 1.00; @ISA = qw(Exporter); @EXPORT = qw( + &canonicalizePath &chdirReturningRelativePath &determineSVNRoot &determineVCSRoot + &fixChangeLogPatch &gitBranch + &gitdiff2svndiff &isGit &isGitBranchBuild &isGitDirectory @@ -51,8 +54,10 @@ BEGIN { &isSVNDirectory &isSVNVersion16OrNewer &makeFilePathRelative + &normalizePath &pathRelativeToSVNRepositoryRootForPath &svnRevisionForDirectory + &svnStatus ); %EXPORT_TAGS = ( ); @EXPORT_OK = (); @@ -70,7 +75,7 @@ my $svnVersion; sub isGitDirectory($) { my ($dir) = @_; - return system("cd $dir && git rev-parse > /dev/null 2>&1") == 0; + return system("cd $dir && git rev-parse > " . File::Spec->devnull() . " 2>&1") == 0; } sub isGit() @@ -159,7 +164,6 @@ sub determineGitRoot() sub determineSVNRoot() { - my $devNull = File::Spec->devnull(); my $last = ''; my $path = '.'; my $parent = '..'; @@ -169,7 +173,7 @@ sub determineSVNRoot() my $thisRoot; my $thisUUID; # Ignore error messages in case we've run past the root of the checkout. - open INFO, "svn info '$path' 2> $devNull |" or die; + open INFO, "svn info '$path' 2> " . File::Spec->devnull() . " |" or die; while (<INFO>) { if (/^Repository Root: (.+)/) { $thisRoot = $1; @@ -268,4 +272,131 @@ sub makeFilePathRelative($) return $gitRoot . $path; } +sub normalizePath($) +{ + my ($path) = @_; + $path =~ s/\\/\//g; + return $path; +} + +sub canonicalizePath($) +{ + my ($file) = @_; + + # Remove extra slashes and '.' directories in path + $file = File::Spec->canonpath($file); + + # Remove '..' directories in path + my @dirs = (); + foreach my $dir (File::Spec->splitdir($file)) { + if ($dir eq '..' && $#dirs >= 0 && $dirs[$#dirs] ne '..') { + pop(@dirs); + } else { + push(@dirs, $dir); + } + } + return ($#dirs >= 0) ? File::Spec->catdir(@dirs) : "."; +} + +sub svnStatus($) +{ + my ($fullPath) = @_; + my $svnStatus; + open SVN, "svn status --non-interactive --non-recursive '$fullPath' |" or die; + if (-d $fullPath) { + # When running "svn stat" on a directory, we can't assume that only one + # status will be returned (since any files with a status below the + # directory will be returned), and we can't assume that the directory will + # be first (since any files with unknown status will be listed first). + my $normalizedFullPath = File::Spec->catdir(File::Spec->splitdir($fullPath)); + while (<SVN>) { + # Input may use a different EOL sequence than $/, so avoid chomp. + $_ = removeEOL($_); + my $normalizedStatPath = File::Spec->catdir(File::Spec->splitdir(substr($_, 7))); + if ($normalizedFullPath eq $normalizedStatPath) { + $svnStatus = "$_\n"; + last; + } + } + # Read the rest of the svn command output to avoid a broken pipe warning. + local $/ = undef; + <SVN>; + } + else { + # Files will have only one status returned. + $svnStatus = removeEOL(<SVN>) . "\n"; + } + close SVN; + return $svnStatus; +} + +sub gitdiff2svndiff($) +{ + $_ = shift @_; + if (m#^diff --git a/(.+) b/(.+)#) { + return "Index: $1"; + } elsif (m/^new file.*/) { + return ""; + } elsif (m#^index [0-9a-f]{7}\.\.[0-9a-f]{7} [0-9]{6}#) { + return "==================================================================="; + } elsif (m#^--- a/(.+)#) { + return "--- $1"; + } elsif (m#^\+\+\+ b/(.+)#) { + return "+++ $1"; + } + return $_; +} + +sub fixChangeLogPatch($) +{ + my $patch = shift; + my $contextLineCount = 3; + + return $patch if $patch !~ /\n@@ -1,(\d+) \+1,(\d+) @@\n( .*\n)+(\+.*\n)+( .*\n){$contextLineCount}$/m; + my ($oldLineCount, $newLineCount) = ($1, $2); + return $patch if $oldLineCount <= $contextLineCount; + + # The diff(1) command is greedy when matching lines, so a new ChangeLog entry will + # have lines of context at the top of a patch when the existing entry has the same + # date and author as the new entry. This nifty loop alters a ChangeLog patch so + # that the added lines ("+") in the patch always start at the beginning of the + # patch and there are no initial lines of context. + my $newPatch; + my $lineCountInState = 0; + my $oldContentLineCountReduction = $oldLineCount - $contextLineCount; + my $newContentLineCountWithoutContext = $newLineCount - $oldLineCount - $oldContentLineCountReduction; + my ($stateHeader, $statePreContext, $stateNewChanges, $statePostContext) = (1..4); + my $state = $stateHeader; + foreach my $line (split(/\n/, $patch)) { + $lineCountInState++; + if ($state == $stateHeader && $line =~ /^@@ -1,$oldLineCount \+1,$newLineCount @\@$/) { + $line = "@@ -1,$contextLineCount +1," . ($newLineCount - $oldContentLineCountReduction) . " @@"; + $lineCountInState = 0; + $state = $statePreContext; + } elsif ($state == $statePreContext && substr($line, 0, 1) eq " ") { + $line = "+" . substr($line, 1); + if ($lineCountInState == $oldContentLineCountReduction) { + $lineCountInState = 0; + $state = $stateNewChanges; + } + } elsif ($state == $stateNewChanges && substr($line, 0, 1) eq "+") { + # No changes to these lines + if ($lineCountInState == $newContentLineCountWithoutContext) { + $lineCountInState = 0; + $state = $statePostContext; + } + } elsif ($state == $statePostContext) { + if (substr($line, 0, 1) eq "+" && $lineCountInState <= $oldContentLineCountReduction) { + $line = " " . substr($line, 1); + } elsif ($lineCountInState > $contextLineCount && substr($line, 0, 1) eq " ") { + next; # Discard + } + } + $newPatch .= $line . "\n"; + } + + return $newPatch; +} + + 1; diff --git a/WebKitTools/Scripts/bisect-builds b/WebKitTools/Scripts/bisect-builds index 93e9223..55bf238 100755 --- a/WebKitTools/Scripts/bisect-builds +++ b/WebKitTools/Scripts/bisect-builds @@ -368,7 +368,7 @@ sub mountAndRunNightly($$$$) while (-e $mountPath) { $i++; usleep 100 if $i > 1; - `hdiutil detach '$mountPath' 2> /dev/null`; + exec "hdiutil", "detach '$mountPath' 2> " . File::Spec->devnull(); die "Could not unmount $diskImage at $mountPath" if $i > 100; } die "Can't mount $diskImage: $mountPath already exists!" if -e $mountPath; @@ -393,7 +393,7 @@ sub mountAndRunNightly($$$$) $tempFile ||= ""; `DYLD_FRAMEWORK_PATH=$frameworkPath WEBKIT_UNSET_DYLD_FRAMEWORK_PATH=YES $safari $tempFile`; - `hdiutil detach '$mountPath' 2> /dev/null`; + exec "hdiutil", "detach '$mountPath' 2> " . File::Spec->devnull(); } sub parseRevisions($$;$) diff --git a/WebKitTools/Scripts/bugzilla-tool b/WebKitTools/Scripts/bugzilla-tool index ec5aa0d..8e899b5 100755 --- a/WebKitTools/Scripts/bugzilla-tool +++ b/WebKitTools/Scripts/bugzilla-tool @@ -73,7 +73,7 @@ def commit_message_for_this_commit(scm): log("Parsing ChangeLog: %s" % changelog_path) changelog_entry = ChangeLog(changelog_path).latest_entry() if not changelog_entry: - error("Failed to parse ChangeLog: " + os.path.abspath(changelog_path)) + raise ScriptError(message="Failed to parse ChangeLog: " + os.path.abspath(changelog_path)) changelog_messages.append(changelog_entry) # FIXME: We should sort and label the ChangeLog messages like commit-log-editor does. @@ -325,6 +325,11 @@ class LandPatchesFromBugs(Command): options += WebKitLandingScripts.land_options() Command.__init__(self, 'Lands all patches on a bug optionally testing them first', 'BUGID', options=options) + @staticmethod + def handled_error(error): + log(error) + exit(2) # Exit 2 insted of 1 to indicate to the commit-queue to indicate we handled the error, and that the queue should keep looping. + @classmethod def land_patches(cls, bug_id, patches, options, tool): try: @@ -344,11 +349,11 @@ class LandPatchesFromBugs(Command): except CheckoutNeedsUpdate, e: log("Commit was rejected because the checkout is out of date. Please update and try again.") log("You can pass --no-build to skip building/testing after update if you believe the new commits did not affect the results.") - error(e) + cls.handled_error(e) except ScriptError, e: # Mark the patch as commit-queue- and comment in the bug. tool.bugs.reject_patch_from_commit_queue(patch['id'], e.message_with_output()) - error(e) + cls.handled_error(e) @staticmethod def _fetch_list_of_patches_to_land(options, args, tool): @@ -420,6 +425,7 @@ class PostDiffAsPatchToBug(Command): return [ make_option("--no-obsolete", action="store_false", dest="obsolete_patches", default=True, help="Do not obsolete old patches before posting this one."), make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review."), + make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review."), ] @staticmethod @@ -443,7 +449,7 @@ class PostDiffAsPatchToBug(Command): diff_file = StringIO.StringIO(diff) # add_patch_to_bug expects a file-like object description = options.description or "Patch v1" - tool.bugs.add_patch_to_bug(bug_id, diff_file, description, mark_for_review=options.review) + tool.bugs.add_patch_to_bug(bug_id, diff_file, description, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) class PostCommitsAsPatchesToBug(Command): @@ -493,7 +499,7 @@ class PostCommitsAsPatchesToBug(Command): diff_file = self._diff_file_for_commit(tool, commit_id) description = options.description or commit_message.description(lstrip=True, strip_url=True) comment_text = self._comment_text_for_commit(options, commit_message, tool, commit_id) - tool.bugs.add_patch_to_bug(bug_id, diff_file, description, comment_text, mark_for_review=options.review) + tool.bugs.add_patch_to_bug(bug_id, diff_file, description, comment_text, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) class RolloutCommit(Command): @@ -560,6 +566,7 @@ class CreateBug(Command): make_option("--component", action="store", type="string", dest="component", help="Component for the new bug."), make_option("--no-prompt", action="store_false", dest="prompt", default=True, help="Do not prompt for bug title and comment; use commit log instead."), make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review."), + make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review."), ] Command.__init__(self, 'Create a bug from local changes or local commits.', '[COMMITISH]', options=options) @@ -583,7 +590,7 @@ class CreateBug(Command): diff = tool.scm().create_patch_from_local_commit(commit_id) diff_file = StringIO.StringIO(diff) # create_bug_with_patch expects a file-like object - bug_id = tool.bugs.create_bug_with_patch(bug_title, comment_text, options.component, diff_file, "Patch v1", cc=options.cc, mark_for_review=options.review) + bug_id = tool.bugs.create_bug_with_patch(bug_title, comment_text, options.component, diff_file, "Patch v1", cc=options.cc, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) if bug_id and len(commit_ids) > 1: options.bug_id = bug_id @@ -603,7 +610,7 @@ class CreateBug(Command): diff = tool.scm().create_patch() diff_file = StringIO.StringIO(diff) # create_bug_with_patch expects a file-like object - bug_id = tool.bugs.create_bug_with_patch(bug_title, comment_text, options.component, diff_file, "Patch v1", cc=options.cc, mark_for_review=options.review) + bug_id = tool.bugs.create_bug_with_patch(bug_title, comment_text, options.component, diff_file, "Patch v1", cc=options.cc, mark_for_review=options.review, mark_for_commit_queue=options.request_commit) def prompt_for_bug_title_and_comment(self): bug_title = raw_input("Bug title: ") @@ -644,6 +651,7 @@ class CheckTreeStatus(Command): class LandPatchesFromCommitQueue(Command): def __init__(self): options = [ + make_option("--is-relaunch", action="store_true", dest="is_relaunch", default=False, help="Internal: Used by the queue to indicate that it's relaunching itself."), make_option("--no-confirm", action="store_false", dest="confirm", default=True, help="Do not ask the user for confirmation before running the queue. Dangerous!"), make_option("--status-host", action="store", type="string", dest="status_host", default=StatusBot.default_host, help="Do not ask the user for confirmation before running the queue. Dangerous!"), ] @@ -675,16 +683,25 @@ class LandPatchesFromCommitQueue(Command): wake_time = datetime.now() + timedelta(seconds=cls.seconds_to_sleep) return "%s Sleeping until %s (%s)." % (message, wake_time.strftime(cls.log_date_format), cls.sleep_duration_text) - @classmethod - def _sleep(cls, message): - log(cls._sleep_message(message)) - time.sleep(cls.seconds_to_sleep) + def _sleep(self, message): + log(self._sleep_message(message)) + time.sleep(self.seconds_to_sleep) + self._next_patch() def _update_status_and_sleep(self, message): status_message = self._sleep_message(message) self.status_bot.update_status(status_message) log(status_message) time.sleep(self.seconds_to_sleep) + self._next_patch() + + def _next_patch(self): + # Re-exec this script to catch any updates to the script. + # Make sure that the re-execed commit-queue does not wait for the user. + args = sys.argv[:] + if args.count("--is-relaunch") == 0: + args.append("--is-relaunch") + os.execvp(sys.argv[0], args) @staticmethod def _open_log_file(log_path): @@ -705,50 +722,55 @@ class LandPatchesFromCommitQueue(Command): log_file.close() def execute(self, options, args, tool): - log("CAUTION: commit-queue will discard all local changes in %s" % tool.scm().checkout_root) - if options.confirm: - response = raw_input("Are you sure? Type 'yes' to continue: ") - if (response != 'yes'): - error("User declined.") + if not options.is_relaunch: + log("CAUTION: commit-queue will discard all local changes in %s" % tool.scm().checkout_root) + if options.confirm: + response = raw_input("Are you sure? Type 'yes' to continue: ") + if (response != 'yes'): + error("User declined.") queue_log = self._add_log_to_output_tee(self.queue_log_path) - log("Running WebKit Commit Queue. %s" % datetime.now().strftime(self.log_date_format)) + if not options.is_relaunch: + log("Running WebKit Commit Queue. %s" % datetime.now().strftime(self.log_date_format)) self.status_bot = StatusBot(host=options.status_host) - while (True): - # Either of these calls could throw URLError which shouldn't stop the queue. - # We catch all exceptions just in case. - try: - # Fetch patches instead of just bug ids to that we validate reviewer/committer flags on every patch. - patches = tool.bugs.fetch_patches_from_commit_queue(reject_invalid_patches=True) - if not len(patches): - self._update_status_and_sleep("Empty queue.") - continue - patch_ids = map(lambda patch: patch['id'], patches) - first_bug_id = patches[0]['bug_id'] - log("%s in commit queue [%s]" % (pluralize('patch', len(patches)), ", ".join(patch_ids))) - - if not tool.buildbot.core_builders_are_green(): - self._update_status_and_sleep("Builders (http://build.webkit.org) are red.") - continue - - self.status_bot.update_status("Landing patches from bug %s." % first_bug_id, bug_id=first_bug_id) - except Exception, e: - # Don't try tell the status bot, in case telling it causes an exception. - self._sleep("Exception while checking queue and bots: %s." % e) - continue - - # Try to land patches on the first bug in the queue before looping - bug_log_path = os.path.join(self.bug_logs_directory, "%s.log" % first_bug_id) - bug_log = self._add_log_to_output_tee(bug_log_path) - bugzilla_tool_path = __file__ # re-execute this script - bugzilla_tool_args = [bugzilla_tool_path, 'land-patches', '--force-clean', '--commit-queue', '--quiet', first_bug_id] - WebKitLandingScripts.run_command_with_teed_output(bugzilla_tool_args, sys.stdout) - self._remove_log_from_output_tee(bug_log) - - log("Finished WebKit Commit Queue. %s" % datetime.now().strftime(self.log_date_format)) - self._remove_log_from_output_tee(queue_log) + # Either of these calls could throw URLError which shouldn't stop the queue. + # We catch all exceptions just in case. + try: + # Fetch patches instead of just bug ids to that we validate reviewer/committer flags on every patch. + patches = tool.bugs.fetch_patches_from_commit_queue(reject_invalid_patches=True) + if not len(patches): + self._update_status_and_sleep("Empty queue.") + patch_ids = map(lambda patch: patch['id'], patches) + first_bug_id = patches[0]['bug_id'] + log("%s in commit queue [%s]" % (pluralize('patch', len(patches)), ", ".join(patch_ids))) + + red_builders_names = tool.buildbot.red_core_builders_names() + if red_builders_names: + red_builders_names = map(lambda name: '"%s"' % name, red_builders_names) # Add quotes around the names. + self._update_status_and_sleep("Builders [%s] are red. See http://build.webkit.org." % ", ".join(red_builders_names)) + + self.status_bot.update_status("Landing patches from bug %s." % first_bug_id, bug_id=first_bug_id) + except Exception, e: + # Don't try tell the status bot, in case telling it causes an exception. + self._sleep("Exception while checking queue and bots: %s." % e) + + # Try to land patches on the first bug in the queue before looping + bug_log_path = os.path.join(self.bug_logs_directory, "%s.log" % first_bug_id) + bug_log = self._add_log_to_output_tee(bug_log_path) + bugzilla_tool_path = __file__ # re-execute this script + bugzilla_tool_args = [bugzilla_tool_path, 'land-patches', '--force-clean', '--commit-queue', '--quiet', first_bug_id] + try: + WebKitLandingScripts.run_and_throw_if_fail(bugzilla_tool_args) + except ScriptError, e: + # Unexpected failure! Mark the patch as commit-queue- and comment in the bug. + # exit(2) is a special exit code we use to indicate that the error was already handled by land-patches and we should keep looping anyway. + if e.exit_code != 2: + tool.bugs.reject_patch_from_commit_queue(patch['id'], "Unexpected failure when landing patch! Please file a bug against bugzilla-tool.\n%s" % e.message_with_output()) + self._remove_log_from_output_tee(bug_log) + # self._remove_log_from_output_tee(queue_log) # implicit in the exec() + self._next_patch() class NonWrappingEpilogIndentedHelpFormatter(IndentedHelpFormatter): diff --git a/WebKitTools/Scripts/modules/bugzilla.py b/WebKitTools/Scripts/modules/bugzilla.py index daf3f19..fe81b48 100644 --- a/WebKitTools/Scripts/modules/bugzilla.py +++ b/WebKitTools/Scripts/modules/bugzilla.py @@ -294,7 +294,18 @@ class Bugzilla: self.authenticated = True - def add_patch_to_bug(self, bug_id, patch_file_object, description, comment_text=None, mark_for_review=False): + def _fill_attachment_form(self, description, patch_file_object, comment_text=None, mark_for_review=False, mark_for_commit_queue=False, bug_id=None): + self.browser['description'] = description + self.browser['ispatch'] = ("1",) + self.browser['flag_type-1'] = ('?',) if mark_for_review else ('X',) + self.browser['flag_type-3'] = ('?',) if mark_for_commit_queue else ('X',) + if bug_id: + patch_name = "bug-%s-%s.patch" % (bug_id, timestamp()) + else: + patch_name ="%s.patch" % timestamp() + self.browser.add_file(patch_file_object, "text/plain", patch_name, 'data') + + def add_patch_to_bug(self, bug_id, patch_file_object, description, comment_text=None, mark_for_review=False, mark_for_commit_queue=False): self.authenticate() log('Adding patch "%s" to bug %s' % (description, bug_id)) @@ -304,13 +315,10 @@ class Bugzilla: self.browser.open("%sattachment.cgi?action=enter&bugid=%s" % (self.bug_server_url, bug_id)) self.browser.select_form(name="entryform") - self.browser['description'] = description - self.browser['ispatch'] = ("1",) + self._fill_attachment_form(description, patch_file_object, mark_for_review=mark_for_review, mark_for_commit_queue=mark_for_commit_queue, bug_id=bug_id) if comment_text: log(comment_text) self.browser['comment'] = comment_text - self.browser['flag_type-1'] = ('?',) if mark_for_review else ('X',) - self.browser.add_file(patch_file_object, "text/plain", "bug-%s-%s.patch" % (bug_id, timestamp())) self.browser.submit() def prompt_for_component(self, components): @@ -334,7 +342,7 @@ class Bugzilla: error_message = "\n" + '\n'.join([" " + line.strip() for line in text_lines if line.strip()]) raise BugzillaError("Bug not created: %s" % error_message) - def create_bug_with_patch(self, bug_title, bug_description, component, patch_file_object, patch_description, cc, mark_for_review=False): + def create_bug_with_patch(self, bug_title, bug_description, component, patch_file_object, patch_description, cc, mark_for_review=False, mark_for_commit_queue=False): self.authenticate() log('Creating bug with patch description "%s"' % patch_description) @@ -355,10 +363,8 @@ class Bugzilla: if bug_description: log(bug_description) self.browser['comment'] = bug_description - self.browser['description'] = patch_description - self.browser['ispatch'] = ("1",) - self.browser['flag_type-1'] = ('?',) if mark_for_review else ('X',) - self.browser.add_file(patch_file_object, "text/plain", "%s.patch" % timestamp(), 'data') + + self._fill_attachment_form(patch_description, patch_file_object, mark_for_review=mark_for_review, mark_for_commit_queue=mark_for_commit_queue) response = self.browser.submit() bug_id = self._check_create_bug_response(response.read()) diff --git a/WebKitTools/Scripts/modules/buildbot.py b/WebKitTools/Scripts/modules/buildbot.py index 4478429..e948d8c 100644 --- a/WebKitTools/Scripts/modules/buildbot.py +++ b/WebKitTools/Scripts/modules/buildbot.py @@ -83,11 +83,19 @@ class BuildBot: builders.append(builder) return builders - def core_builders_are_green(self): + def red_core_builders(self): + red_builders = [] for builder in self._builder_statuses_with_names_matching_regexps(self.builder_statuses(), self.core_builder_names_regexps): if not builder['is_green']: - return False - return True + red_builders.append(builder) + return red_builders + + def red_core_builders_names(self): + red_builders = self.red_core_builders() + return map(lambda builder: builder['name'], red_builders) + + def core_builders_are_green(self): + return not self.red_core_builders() def builder_statuses(self): build_status_url = self.buildbot_server_url + 'one_box_per_builder' diff --git a/WebKitTools/Scripts/modules/buildbot_unittest.py b/WebKitTools/Scripts/modules/buildbot_unittest.py index 461e5a2..a85f2ea 100644 --- a/WebKitTools/Scripts/modules/buildbot_unittest.py +++ b/WebKitTools/Scripts/modules/buildbot_unittest.py @@ -91,6 +91,22 @@ class BuildBotTest(unittest.TestCase): for key, expected_value in expected_parsing.items(): self.assertEquals(builder[key], expected_value, ("Builder %d parse failure for key: %s: Actual='%s' Expected='%s'" % (x, key, builder[key], expected_value))) + def test_core_builder_methods(self): + buildbot = BuildBot() + + # Override builder_statuses function to not touch the network. + def example_builder_statuses(): # We could use instancemethod() to bind 'self' but we don't need to. + return BuildBotTest._expected_example_one_box_parsings + buildbot.builder_statuses = example_builder_statuses + + buildbot.core_builder_names_regexps = [ 'Leopard', "Windows.*Build" ] + self.assertEquals(buildbot.red_core_builders_names(), []) + self.assertTrue(buildbot.core_builders_are_green()) + + buildbot.core_builder_names_regexps = [ 'SnowLeopard', 'Qt' ] + self.assertEquals(buildbot.red_core_builders_names(), [ u'SnowLeopard Intel Release', u'Qt Linux Release' ]) + self.assertFalse(buildbot.core_builders_are_green()) + def test_builder_name_regexps(self): buildbot = BuildBot() diff --git a/WebKitTools/Scripts/modules/committers.py b/WebKitTools/Scripts/modules/committers.py index e157fb4..fc263eb 100644 --- a/WebKitTools/Scripts/modules/committers.py +++ b/WebKitTools/Scripts/modules/committers.py @@ -42,22 +42,28 @@ class Reviewer(Committer): Committer.__init__(self, name, email) self.can_review = True -# All reviewers are committers, so this list is only of committers -# who are not reviewers. +# This is intended as a cannonical, machine-readable list of all non-reviewer committers for WebKit. +# If your name is missing here and you are a committer, please add it. No review needed. +# All reviewers are committers, so this list is only of committers who are not reviewers. committers_unable_to_review = [ Committer("Aaron Boodman", "aa@chromium.org"), Committer("Adam Langley", "agl@chromium.org"), Committer("Albert J. Wong", "ajwong@chromium.org"), + Committer("Anton Muhin", "antonm@chromium.org"), Committer("Antonio Gomes", "tonikitoo@webkit.org"), Committer("Anthony Ricaud", "rik@webkit.org"), Committer("Ben Murdoch", "benm@google.com"), + Committer("Chris Fleizach", "cfleizach@apple.com"), Committer("Brent Fulgham", "bfulgham@webkit.org"), Committer("Brian Weinstein", "bweinstein@apple.com"), Committer("Cameron McCormack", "cam@webkit.org"), + Committer("Collin Jackson", "collinj@webkit.org"), + Committer("Csaba Osztrogonac", "ossy@webkit.org"), Committer("Daniel Bates", "dbates@webkit.org"), Committer("Drew Wilson", "atwilson@chromium.org"), Committer("Dirk Schulze", "krit@webkit.org"), Committer("Dmitry Titov", "dimich@chromium.org"), + Committer("Dumitru Daniliuc", "dumi@chromium.org"), Committer("Eli Fidler", "eli@staikos.net"), Committer("Eric Roman", "eroman@chromium.org"), Committer("Fumitoshi Ukai", "ukai@chromium.org"), @@ -69,38 +75,53 @@ committers_unable_to_review = [ Committer("Joseph Pecoraro", "joepeck@webkit.org"), Committer("Julie Parent", "jparent@google.com"), Committer("Kenneth Rohde Christiansen", "kenneth@webkit.org"), + Committer("Kent Tamura", "tkent@chromium.org"), Committer("Laszlo Gombos", "laszlo.1.gombos@nokia.com"), + Committer("Mads Ager", "ager@chromium.org"), + Committer("Mike Belshe", "mike@belshe.com"), Committer("Nate Chapin", "japhet@chromium.org"), Committer("Ojan Vafai", "ojan@chromium.org"), Committer("Pam Greene", "pam@chromium.org"), Committer("Peter Kasting", "pkasting@google.com"), Committer("Pierre d'Herbemont", "pdherbemont@free.fr"), + Committer("Roland Steiner", "rolandsteiner@chromium.org"), Committer("Ryosuke Niwa", "rniwa@webkit.org"), Committer("Scott Violet", "sky@chromium.org"), Committer("Shinichiro Hamaji", "hamaji@chromium.org"), + Committer("Steve Block", "steveblock@google.com"), Committer("Tony Chang", "tony@chromium.org"), Committer("Yael Aharon", "yael.aharon@nokia.com"), Committer("Yong Li", "yong.li@torchmobile.com"), + Committer("Yury Semikhatsky", "yurys@chromium.org"), Committer("Zoltan Horvath", "zoltan@webkit.org"), ] +# This is intended as a cannonical, machine-readable list of all reviewers for WebKit. +# If your name is missing here and you are a reviewer, please add it. No review needed. reviewers_list = [ Reviewer("Adam Barth", "abarth@webkit.org"), + Reviewer("Ada Chan", "adachan@apple.com"), Reviewer("Adam Roben", "aroben@apple.com"), Reviewer("Adam Treat", "treat@kde.org"), Reviewer("Adele Peterson", "adele@apple.com"), Reviewer("Alexey Proskuryakov", "ap@webkit.org"), + Reviewer("Alice Liu", "alice.liu@apple.com"), + Reviewer("Alp Toker", "alp@nuanti.com"), Reviewer("Anders Carlsson", "andersca@apple.com"), Reviewer("Antti Koivisto", "koivisto@iki.fi"), Reviewer("Ariya Hidayat", "ariya.hidayat@trolltech.com"), Reviewer("Brady Eidson", "beidson@apple.com"), + Reviewer("Cameron Zwarich", "zwarich@apple.com"), Reviewer("Dan Bernstein", "mitz@webkit.org"), Reviewer("Darin Adler", "darin@apple.com"), Reviewer("Darin Fisher", "fishd@chromium.org"), + Reviewer("David Harrison", "harrison@apple.com"), Reviewer("David Hyatt", "hyatt@apple.com"), Reviewer("David Kilzer", "ddkilzer@webkit.org"), Reviewer("David Levin", "levin@chromium.org"), Reviewer("Dimitri Glazkov", "dglazkov@chromium.org"), + Reviewer("Don Melton", "gramps@apple.com"), + Reviewer("Dmitri Titov", "dimich@chromium.org"), Reviewer("Eric Carlson", "eric.carlson@apple.com"), Reviewer("Eric Seidel", "eric@webkit.org"), Reviewer("Gavin Barraclough", "barraclough@apple.com"), @@ -110,16 +131,22 @@ reviewers_list = [ Reviewer("Holger Freyther", "zecke@selfish.org"), Reviewer("Jan Alonzo", "jmalonzo@gmail.com"), Reviewer("John Sullivan", "sullivan@apple.com"), + Reviewer("Jon Honeycutt", "jhoneycutt@apple.com"), Reviewer("Justin Garcia", "justin.garcia@apple.com"), + Reviewer("Kevin Decker", "kdecker@apple.com"), Reviewer("Kevin McCullough", "kmccullough@apple.com"), Reviewer("Kevin Ollivier", "kevino@theolliviers.com"), + Reviewer("Lars Knoll", "lars@trolltech.com"), Reviewer("Maciej Stachowiak", "mjs@apple.com"), Reviewer("Mark Rowe", "mrowe@apple.com"), Reviewer("Nikolas Zimmermann", "zimmermann@kde.org"), Reviewer("Oliver Hunt", "oliver@apple.com"), + Reviewer("Pavel Feldman", "pfeldman@chromium.org"), + Reviewer("Rob Buis", "rwlbuis@gmail.com"), Reviewer("Sam Weinig", "sam@webkit.org"), Reviewer("Simon Fraser", "simon.fraser@apple.com"), Reviewer("Simon Hausmann", "hausmann@webkit.org"), + Reviewer("Stephanie Lewis", "slewis@apple.com"), Reviewer("Steve Falkenburg", "sfalken@apple.com"), Reviewer("Timothy Hatcher", "timothy@hatcher.name"), Reviewer(u'Tor Arne Vestb\xf8', "vestbo@webkit.org"), diff --git a/WebKitTools/Scripts/modules/cpp_style.py b/WebKitTools/Scripts/modules/cpp_style.py index 0c9dfa0..485b07c 100644 --- a/WebKitTools/Scripts/modules/cpp_style.py +++ b/WebKitTools/Scripts/modules/cpp_style.py @@ -1533,16 +1533,15 @@ def check_spacing(filename, clean_lines, line_number, error): # Don't try to do spacing checks for operator methods line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) - - # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". - # Otherwise not. Note we only check for non-spaces on *both* sides; - # sometimes people put non-spaces on one side when aligning ='s among - # many lines (not that this is behavior that I approve of...) - if search(r'[\w.]=[\w.]', line) and not search(r'\b(if|while) ', line): + # Don't try to do spacing checks for #include statements at minimum it + # messes up checks for spacing around / + if match(r'\s*#\s*include', line): + return + if search(r'[\w.]=[\w.]', line): error(filename, line_number, 'whitespace/operators', 4, 'Missing spaces around =') - # FIXME: It's not ok to have spaces around binary operators like + - * / . + # FIXME: It's not ok to have spaces around binary operators like . # You should always have whitespace around binary operators. # Alas, we can't test < or > because they're legitimately used sans spaces @@ -1559,12 +1558,6 @@ def check_spacing(filename, clean_lines, line_number, error): if matched: error(filename, line_number, 'whitespace/operators', 3, 'Missing spaces around %s' % matched.group(1)) - # We allow no-spaces around << and >> when used like this: 10<<20, but - # not otherwise (particularly, not when used as streams) - matched = search(r'[^0-9\s](<<|>>)[^0-9\s=]', line) - if matched: - error(filename, line_number, 'whitespace/operators', 3, - 'Missing spaces around %s' % matched.group(1)) # There shouldn't be space around unary operators matched = search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) @@ -1699,50 +1692,32 @@ def check_namespace_indentation(filename, clean_lines, line_number, file_extensi if not namespace_match: return - namespace_indentation = namespace_match.group('namespace_indentation') - - is_header_file = file_extension == 'h' - is_implementation_file = not is_header_file + current_indentation_level = len(namespace_match.group('namespace_indentation')) + if current_indentation_level > 0: + error(filename, line_number, 'whitespace/indent', 4, + 'namespace should never be indented.') + return + looking_for_semicolon = False; line_offset = 0 - - if is_header_file: - inner_indentation = namespace_indentation + ' ' * 4 - - for current_line in clean_lines.raw_lines[line_number + 1:]: - line_offset += 1 - - # Skip not only empty lines but also those with preprocessor directives. - # Goto labels don't occur in header files, so no need to check for those. - if current_line.strip() == '' or current_line.startswith('#'): - continue - - if not current_line.startswith(inner_indentation): - # If something unindented was discovered, make sure it's a closing brace. - if not current_line.startswith(namespace_indentation + '}'): + in_preprocessor_directive = False; + for current_line in clean_lines.elided[line_number + 1:]: + line_offset += 1 + if not current_line.strip(): + continue + if not current_indentation_level: + if not (in_preprocessor_directive or looking_for_semicolon): + if not match(r'\S', current_line): error(filename, line_number + line_offset, 'whitespace/indent', 4, - 'In a header, code inside a namespace should be indented.') - break - - if is_implementation_file: - for current_line in clean_lines.raw_lines[line_number + 1:]: - line_offset += 1 - - # Skip not only empty lines but also those with (goto) labels. - # The goto label regexp accepts spaces or the beginning of a - # comment (if anything) after the initial colon. - if current_line.strip() == '' or match(r'\w+\s*:([\s\/].*)?$', current_line): - continue - - remaining_line = current_line[len(namespace_indentation):] - if not match(r'\S', remaining_line): - error(filename, line_number + line_offset, 'whitespace/indent', 4, - 'In an implementation file, code inside a namespace should not be indented.') - - # Just check the first non-empty line in any case, because - # otherwise we would need to count opened and closed braces, - # which is obviously a lot more complicated. - break - + 'Code inside a namespace should not be indented.') + if in_preprocessor_directive or (current_line.strip()[0] == '#'): # This takes care of preprocessor directive syntax. + in_preprocessor_directive = current_line[-1] == '\\' + else: + looking_for_semicolon = ((current_line.find(';') == -1) and (current_line.strip()[-1] != '}')) or (current_line[-1] == '\\') + else: + looking_for_semicolon = False; # If we have a brace we may not need a semicolon. + current_indentation_level += current_line.count('{') - current_line.count('}') + if current_indentation_level < 0: + break; def check_using_std(filename, clean_lines, line_number, error): """Looks for 'using std::foo;' statements which should be replaced with 'using namespace std;'. diff --git a/WebKitTools/Scripts/modules/cpp_style_unittest.py b/WebKitTools/Scripts/modules/cpp_style_unittest.py index 322356e..d5637f4 100644 --- a/WebKitTools/Scripts/modules/cpp_style_unittest.py +++ b/WebKitTools/Scripts/modules/cpp_style_unittest.py @@ -1265,6 +1265,12 @@ class CppStyleTest(CppStyleTestBase): self.assert_lint('a<Foo&> t <<= &b | &c;', '') self.assert_lint('a<Foo*> t <<= &b & &c; // Test', '') self.assert_lint('a<Foo*> t <<= *b / &c; // Test', '') + self.assert_lint('if (a=b == 1)', 'Missing spaces around = [whitespace/operators] [4]') + self.assert_lint('a = 1<<20', 'Missing spaces around << [whitespace/operators] [3]') + self.assert_lint('if (a = b == 1)', '') + self.assert_lint('a = 1 << 20', '') + self.assert_multi_line_lint('#include "config.h"\n#include <sys/io.h>\n', + '') def test_spacing_before_last_semicolon(self): self.assert_lint('call_function() ;', @@ -2806,40 +2812,39 @@ class WebKitStyleTest(CppStyleTestBase): 'Weird number of spaces at line-start. Are you using a 4-space indent? [whitespace/indent] [3]') # FIXME: No tests for 8-spaces. - # 3. In a header, code inside a namespace should be indented. + # 3. In a header, code inside a namespace should not be indented. self.assert_multi_line_lint( 'namespace WebCore {\n\n' - ' class Document {\n' - ' int myVariable;\n' - ' };\n' + 'class Document {\n' + ' int myVariable;\n' + '};\n' '}', '', 'foo.h') self.assert_multi_line_lint( 'namespace OuterNamespace {\n' ' namespace InnerNamespace {\n' - ' class Document {\n' - ' };\n' - ' };\n' + ' class Document {\n' + '};\n' + '};\n' '}', - '', + ['Code inside a namespace should not be indented. [whitespace/indent] [4]', 'namespace should never be indented. [whitespace/indent] [4]'], 'foo.h') self.assert_multi_line_lint( 'namespace WebCore {\n' '#if 0\n' ' class Document {\n' - ' };\n' + '};\n' '#endif\n' '}', - '', + 'Code inside a namespace should not be indented. [whitespace/indent] [4]', 'foo.h') self.assert_multi_line_lint( 'namespace WebCore {\n' 'class Document {\n' '};\n' '}', - 'In a header, code inside a namespace should be indented.' - ' [whitespace/indent] [4]', + '', 'foo.h') # 4. In an implementation file (files with the extension .cpp, .c @@ -2858,14 +2863,52 @@ class WebKitStyleTest(CppStyleTestBase): 'namespace OuterNamespace {\n' 'namespace InnerNamespace {\n' 'Document::Foo() { }\n' - '}', - '', + ' void* p;\n' + '}\n' + '}\n', + 'Code inside a namespace should not be indented. [whitespace/indent] [4]', 'foo.cpp') self.assert_multi_line_lint( - ' namespace WebCore {\n\n' - 'start: // Pointless code, but tests the label regexp.\n' - ' goto start;\n' - ' }', + 'namespace OuterNamespace {\n' + 'namespace InnerNamespace {\n' + 'Document::Foo() { }\n' + '}\n' + ' void* p;\n' + '}\n', + 'Code inside a namespace should not be indented. [whitespace/indent] [4]', + 'foo.cpp') + self.assert_multi_line_lint( + 'namespace WebCore {\n\n' + ' const char* foo = "start:;"\n' + ' "dfsfsfs";\n' + '}\n', + 'Code inside a namespace should not be indented. [whitespace/indent] [4]', + 'foo.cpp') + self.assert_multi_line_lint( + 'namespace WebCore {\n\n' + 'const char* foo(void* a = ";", // ;\n' + ' void* b);\n' + ' void* p;\n' + '}\n', + 'Code inside a namespace should not be indented. [whitespace/indent] [4]', + 'foo.cpp') + self.assert_multi_line_lint( + 'namespace WebCore {\n\n' + 'const char* foo[] = {\n' + ' "void* b);", // ;\n' + ' "asfdf",\n' + ' }\n' + ' void* p;\n' + '}\n', + 'Code inside a namespace should not be indented. [whitespace/indent] [4]', + 'foo.cpp') + self.assert_multi_line_lint( + 'namespace WebCore {\n\n' + 'const char* foo[] = {\n' + ' "void* b);", // }\n' + ' "asfdf",\n' + ' }\n' + '}\n', '', 'foo.cpp') self.assert_multi_line_lint( @@ -2875,15 +2918,30 @@ class WebKitStyleTest(CppStyleTestBase): 'start: // infinite loops are fun!\n' ' goto start;\n' ' }', - '', + 'namespace should never be indented. [whitespace/indent] [4]', 'foo.cpp') self.assert_multi_line_lint( 'namespace WebCore {\n' ' Document::Foo() { }\n' '}', - 'In an implementation file, code inside a namespace should not be indented.' + 'Code inside a namespace should not be indented.' ' [whitespace/indent] [4]', 'foo.cpp') + self.assert_multi_line_lint( + 'namespace WebCore {\n' + '#define abc(x) x; \\\n' + ' x\n' + '}', + '', + 'foo.cpp') + self.assert_multi_line_lint( + 'namespace WebCore {\n' + '#define abc(x) x; \\\n' + ' x\n' + ' void* x;' + '}', + 'Code inside a namespace should not be indented. [whitespace/indent] [4]', + 'foo.cpp') # 5. A case label should line up with its switch statement. The # case statement is indented. @@ -3246,7 +3304,7 @@ class WebKitStyleTest(CppStyleTestBase): '') self.assert_multi_line_lint( 'namespace WebCore {\n' - ' int foo;\n' + 'int foo;\n' '};\n', '') self.assert_multi_line_lint( diff --git a/WebKitTools/Scripts/modules/scm.py b/WebKitTools/Scripts/modules/scm.py index 3daecbc..3ffa23b 100644 --- a/WebKitTools/Scripts/modules/scm.py +++ b/WebKitTools/Scripts/modules/scm.py @@ -124,9 +124,14 @@ class SCM: @staticmethod def run_command(args, cwd=None, input=None, error_handler=default_error_handler, return_exit_code=False): - stdin = subprocess.PIPE if input else None + if hasattr(input, 'read'): # Check if the input is a file. + stdin = input + string_to_communicate = None + else: + stdin = subprocess.PIPE if input else None + string_to_communicate = input process = subprocess.Popen(args, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=cwd) - output = process.communicate(input)[0].rstrip() + output = process.communicate(string_to_communicate)[0].rstrip() exit_code = process.wait() if exit_code: script_error = ScriptError(script_args=args, exit_code=exit_code, output=output, cwd=cwd) @@ -166,11 +171,8 @@ class SCM: args = [self.script_path('svn-apply'), '--reviewer', patch['reviewer']] if force: args.append('--force') - patch_apply_process = subprocess.Popen(args, stdin=curl_process.stdout) - return_code = patch_apply_process.wait() - if return_code: - raise ScriptError(message="Patch %s from bug %s failed to download and apply." % (patch['url'], patch['bug_id'])) + self.run_command(args, input=curl_process.stdout) def run_status_and_extract_filenames(self, status_command, status_regexp): filenames = [] @@ -392,7 +394,7 @@ class Git(SCM): @classmethod def in_working_directory(cls, path): - return cls.run_command(['git', 'rev-parse', '--is-inside-work-tree'], cwd=path) == "true" + return cls.run_command(['git', 'rev-parse', '--is-inside-work-tree'], cwd=path, error_handler=ignore_error) == "true" @classmethod def find_checkout_root(cls, path): diff --git a/WebKitTools/Scripts/modules/scm_unittest.py b/WebKitTools/Scripts/modules/scm_unittest.py index 58494a0..784303f 100644 --- a/WebKitTools/Scripts/modules/scm_unittest.py +++ b/WebKitTools/Scripts/modules/scm_unittest.py @@ -95,8 +95,6 @@ class SVNTestRepository: @classmethod def setup(cls, test_object): - test_object.original_path = os.path.abspath('.') - # Create an test SVN repository test_object.svn_repo_path = tempfile.mkdtemp(suffix="svn_test_repo") test_object.svn_repo_url = "file://%s" % test_object.svn_repo_path # Not sure this will work on windows @@ -115,23 +113,30 @@ class SVNTestRepository: run(['rm', '-rf', test_object.svn_repo_path]) run(['rm', '-rf', test_object.svn_checkout_path]) +# For testing the SCM baseclass directly. +class SCMClassTests(unittest.TestCase): + def setUp(self): + self.dev_null = open(os.devnull, "w") # Used to make our Popen calls quiet. -class SCMTest(unittest.TestCase): - def _create_patch(self, patch_contents): - patch_path = os.path.join(self.svn_checkout_path, 'patch.diff') - write_into_file_at_path(patch_path, patch_contents) - patch = {} - patch['reviewer'] = 'Joe Cool' - patch['bug_id'] = '12345' - patch['url'] = 'file://%s' % urllib.pathname2url(patch_path) - return patch + def tearDown(self): + self.dev_null.close() - def _setup_webkittools_scripts_symlink(self, local_scm): - webkit_scm = detect_scm_system(self.original_path) - webkit_scripts_directory = webkit_scm.scripts_directory() - local_scripts_directory = local_scm.scripts_directory() - os.mkdir(os.path.dirname(local_scripts_directory)) - os.symlink(webkit_scripts_directory, local_scripts_directory) + def test_run_command_with_pipe(self): + input_process = subprocess.Popen(['/bin/echo', 'foo\nbar'], stdout=subprocess.PIPE, stderr=self.dev_null) + self.assertEqual(SCM.run_command(['/usr/bin/grep', 'bar'], input=input_process.stdout), "bar") + + # Test the non-pipe case too: + self.assertEqual(SCM.run_command(['/usr/bin/grep', 'bar'], input="foo\nbar"), "bar") + + command_returns_non_zero = ['/bin/sh', '--invalid-option'] + # Test when the input pipe process fails. + input_process = subprocess.Popen(command_returns_non_zero, stdout=subprocess.PIPE, stderr=self.dev_null) + self.assertTrue(input_process.poll() != 0) + self.assertRaises(ScriptError, SCM.run_command, ['/usr/bin/grep', 'bar'], input=input_process.stdout) + + # Test when the run_command process fails. + input_process = subprocess.Popen(['/bin/echo', 'foo\nbar'], stdout=subprocess.PIPE, stderr=self.dev_null) # grep shows usage and calls exit(2) when called w/o arguments. + self.assertRaises(ScriptError, SCM.run_command, command_returns_non_zero, input=input_process.stdout) def test_error_handlers(self): git_failure_message="Merge conflict during commit: Your file or directory 'WebCore/ChangeLog' is probably out-of-date: resource out of date; try updating at /usr/local/libexec/git-core//git-svn line 469" @@ -152,6 +157,24 @@ svn: resource out of date; try updating self.assertRaises(ScriptError, commit_error_handler, ScriptError(output='blah blah blah')) +# GitTest and SVNTest inherit from this so any test_ methods here will be run once for this class and then once for each subclass. +class SCMTest(unittest.TestCase): + def _create_patch(self, patch_contents): + patch_path = os.path.join(self.svn_checkout_path, 'patch.diff') + write_into_file_at_path(patch_path, patch_contents) + patch = {} + patch['reviewer'] = 'Joe Cool' + patch['bug_id'] = '12345' + patch['url'] = 'file://%s' % urllib.pathname2url(patch_path) + return patch + + def _setup_webkittools_scripts_symlink(self, local_scm): + webkit_scm = detect_scm_system(os.path.dirname(os.path.abspath(__file__))) + webkit_scripts_directory = webkit_scm.scripts_directory() + local_scripts_directory = local_scm.scripts_directory() + os.mkdir(os.path.dirname(local_scripts_directory)) + os.symlink(webkit_scripts_directory, local_scripts_directory) + # Tests which both GitTest and SVNTest should run. # FIXME: There must be a simpler way to add these w/o adding a wrapper method to both subclasses def _shared_test_commit_with_message(self): @@ -188,7 +211,6 @@ class SVNTest(SCMTest): def tearDown(self): SVNTestRepository.tear_down(self) - os.chdir(self.original_path) def test_create_patch_is_full_patch(self): test_dir_path = os.path.join(self.svn_checkout_path, 'test_dir') @@ -284,7 +306,6 @@ class GitTest(SCMTest): def tearDown(self): SVNTestRepository.tear_down(self) self._tear_down_git_clone_of_svn_repository() - os.chdir(self.original_path) def test_detection(self): scm = detect_scm_system(self.git_checkout_path) diff --git a/WebKitTools/Scripts/pdevenv b/WebKitTools/Scripts/pdevenv index 818e4ee..cab8b16 100755 --- a/WebKitTools/Scripts/pdevenv +++ b/WebKitTools/Scripts/pdevenv @@ -6,6 +6,9 @@ use warnings; use File::Temp qw/tempfile/; use FindBin; +use lib $FindBin::Bin; +use webkitdirs; + my ($fh, $path) = tempfile(UNLINK => 0, SUFFIX => '.cmd') or die; chomp(my $vcBin = `cygpath -w "$FindBin::Bin/../vcbin"`); @@ -28,8 +31,15 @@ if ($ENV{'VS80COMNTOOLS'}) { print $fh "\@echo off\n\n"; print $fh "call \"\%" . $vsToolsVar . "\%\\vsvars32.bat\"\n\n"; print $fh "set PATH=$vcBin;$scriptsPath;\%PATH\%\n\n"; -print $fh "IF EXIST \"\%VSINSTALLDIR\%\\Common7\\IDE\\devenv.com\" (devenv.com /useenv " . join(" ", @ARGV) . ") ELSE "; -print $fh "VCExpress.exe /useenv " . join(" ", @ARGV) . "\n"; + +my $useenv = "/useenv "; +if (isChromium()) { + $useenv = ""; +} + +print $fh "IF EXIST \"\%VSINSTALLDIR\%\\Common7\\IDE\\devenv.com\" (devenv.com " . $useenv . join(" ", @ARGV) . ") ELSE "; +print $fh "VCExpress.exe " . $useenv . join(" ", @ARGV) . "\n"; + close $fh; diff --git a/WebKitTools/Scripts/prepare-ChangeLog b/WebKitTools/Scripts/prepare-ChangeLog index ed31005..dd864df 100755 --- a/WebKitTools/Scripts/prepare-ChangeLog +++ b/WebKitTools/Scripts/prepare-ChangeLog @@ -85,7 +85,6 @@ sub isConflictStatus($); sub statusDescription($$$$); sub propertyChangeDescription($); sub extractLineRange($); -sub canonicalizePath($); sub testListForChangeLog(@); sub get_function_line_ranges($$); sub get_function_line_ranges_for_c($$); @@ -95,7 +94,6 @@ sub method_decl_to_selector($); sub processPaths(\@); sub reviewerAndDescriptionForGitCommit($); sub normalizeLineEndings($$); -sub normalizePath($); sub decodeEntities($); # Project time zone for Cupertino, CA, US @@ -429,24 +427,6 @@ if ($openChangeLogs && @logs) { # Done. exit; -sub canonicalizePath($) -{ - my ($file) = @_; - - # Remove extra slashes and '.' directories in path - $file = File::Spec->canonpath($file); - - # Remove '..' directories in path - my @dirs = (); - foreach my $dir (File::Spec->splitdir($file)) { - if ($dir eq '..' && $#dirs >= 0 && $dirs[$#dirs] ne '..') { - pop(@dirs); - } else { - push(@dirs, $dir); - } - } - return ($#dirs >= 0) ? File::Spec->catdir(@dirs) : "."; -} sub changeLogDate($) { @@ -1717,13 +1697,6 @@ sub normalizeLineEndings($$) return $string; } -sub normalizePath($) -{ - my ($path) = @_; - $path =~ s/\\/\//g; - return $path; -} - sub decodeEntities($) { my ($text) = @_; diff --git a/WebKitTools/Scripts/resolve-ChangeLogs b/WebKitTools/Scripts/resolve-ChangeLogs index db497f9..1a2d2af 100755 --- a/WebKitTools/Scripts/resolve-ChangeLogs +++ b/WebKitTools/Scripts/resolve-ChangeLogs @@ -44,7 +44,6 @@ sub canonicalRelativePath($); sub conflictFiles($); sub findChangeLog($); sub findUnmergedChangeLogs(); -sub fixChangeLogPatch($); sub fixMergedChangeLogs($;@); sub fixOneMergedChangeLog($); sub hasGitUnmergedFiles(); @@ -56,7 +55,6 @@ sub resolveChangeLog($); sub resolveConflict($); sub showStatus($;$); sub usageAndExit(); -sub normalizePath($); my $isGit = isGit(); my $isSVN = isSVN(); @@ -281,57 +279,6 @@ sub findUnmergedChangeLogs() return @results; } -sub fixChangeLogPatch($) -{ - my $patch = shift; - my $contextLineCount = 3; - - return $patch if $patch !~ /\n@@ -1,(\d+) \+1,(\d+) @@\n( .*\n)+(\+.*\n)+( .*\n){$contextLineCount}$/m; - my ($oldLineCount, $newLineCount) = ($1, $2); - return $patch if $oldLineCount <= $contextLineCount; - - # The diff(1) command is greedy when matching lines, so a new ChangeLog entry will - # have lines of context at the top of a patch when the existing entry has the same - # date and author as the new entry. This nifty loop alters a ChangeLog patch so - # that the added lines ("+") in the patch always start at the beginning of the - # patch and there are no initial lines of context. - my $newPatch; - my $lineCountInState = 0; - my $oldContentLineCountReduction = $oldLineCount - $contextLineCount; - my $newContentLineCountWithoutContext = $newLineCount - $oldLineCount - $oldContentLineCountReduction; - my ($stateHeader, $statePreContext, $stateNewChanges, $statePostContext) = (1..4); - my $state = $stateHeader; - foreach my $line (split(/\n/, $patch)) { - $lineCountInState++; - if ($state == $stateHeader && $line =~ /^@@ -1,$oldLineCount \+1,$newLineCount @\@$/) { - $line = "@@ -1,$contextLineCount +1," . ($newLineCount - $oldContentLineCountReduction) . " @@"; - $lineCountInState = 0; - $state = $statePreContext; - } elsif ($state == $statePreContext && substr($line, 0, 1) eq " ") { - $line = "+" . substr($line, 1); - if ($lineCountInState == $oldContentLineCountReduction) { - $lineCountInState = 0; - $state = $stateNewChanges; - } - } elsif ($state == $stateNewChanges && substr($line, 0, 1) eq "+") { - # No changes to these lines - if ($lineCountInState == $newContentLineCountWithoutContext) { - $lineCountInState = 0; - $state = $statePostContext; - } - } elsif ($state == $statePostContext) { - if (substr($line, 0, 1) eq "+" && $lineCountInState <= $oldContentLineCountReduction) { - $line = " " . substr($line, 1); - } elsif ($lineCountInState > $contextLineCount && substr($line, 0, 1) eq " ") { - next; # Discard - } - } - $newPatch .= $line . "\n"; - } - - return $newPatch; -} - sub fixMergedChangeLogs($;@) { my $revisionRange = shift; @@ -408,7 +355,7 @@ sub fixOneMergedChangeLog($) close FILE; # Apply the new patch - open(PATCH, "| patch -p1 $file > /dev/null") or die $!; + open(PATCH, "| patch -p1 $file > " . File::Spec->devnull()) or die $!; print PATCH $newPatch; close(PATCH) or die $!; @@ -460,7 +407,7 @@ sub mergeChanges($$$) unlink("${fileNewer}.orig"); unlink("${fileNewer}.rej"); - open(PATCH, "| patch --fuzz=3 --binary $fileNewer > /dev/null") or die $!; + open(PATCH, "| patch --fuzz=3 --binary $fileNewer > " . File::Spec->devnull()) or die $!; print PATCH fixChangeLogPatch($patch); close(PATCH); @@ -571,9 +518,3 @@ sub showStatus($;$) } } -sub normalizePath($) -{ - my ($path) = @_; - $path =~ s/\\/\//g; - return $path; -} diff --git a/WebKitTools/Scripts/run-iexploder-tests b/WebKitTools/Scripts/run-iexploder-tests index f5e8a6c..ed5ecd6 100755 --- a/WebKitTools/Scripts/run-iexploder-tests +++ b/WebKitTools/Scripts/run-iexploder-tests @@ -32,6 +32,7 @@ use strict; use warnings; use Cwd; +use File::Spec; use FindBin; use Getopt::Long; use IPC::Open2; @@ -149,7 +150,7 @@ sub openHTTPDIfNeeded() "-c", "User \"#$<\""); my $retryCount = 20; - while (system("/usr/bin/curl -q --silent --stderr - --output /dev/null $listen") && $retryCount) { + while (system("/usr/bin/curl -q --silent --stderr - --output " . File::Spec->devnull() . " $listen") && $retryCount) { sleep 1; --$retryCount; } diff --git a/WebKitTools/Scripts/run-javascriptcore-tests b/WebKitTools/Scripts/run-javascriptcore-tests index 865ae1d..fb4c388 100755 --- a/WebKitTools/Scripts/run-javascriptcore-tests +++ b/WebKitTools/Scripts/run-javascriptcore-tests @@ -100,21 +100,10 @@ if (!defined($root) && !$skipBuild) { } -my $productDir = productDir(); - -$productDir .= "/JavaScriptCore" if isQt(); -$productDir .= "/Programs" if isGtk(); +my $productDir = jscProductDir(); $ENV{DYLD_FRAMEWORK_PATH} = $productDir; setPathForRunningWebKitApp(\%ENV) if isCygwin(); -sub jscPath($) -{ - my ($productDir) = @_; - my $jscName = "jsc"; - $jscName .= "_debug" if (isCygwin() && ($configuration eq "Debug")); - return "$productDir/$jscName"; -} - sub testapiPath($) { my ($productDir) = @_; diff --git a/WebKitTools/Scripts/run-jsc b/WebKitTools/Scripts/run-jsc index 20dc5e8..e5341c1 100755 --- a/WebKitTools/Scripts/run-jsc +++ b/WebKitTools/Scripts/run-jsc @@ -30,6 +30,7 @@ use strict; use warnings; +use File::Spec; use FindBin; use lib $FindBin::Bin; use Getopt::Long; @@ -43,10 +44,10 @@ GetOptions("count|c=i" => \$count, "verbose|v" => \$verbose); die "$usage\n" if (@ARGV < 1); -my $jsc = productDir() . "/jsc @ARGV"; -$jsc .= " 2> /dev/null" unless $verbose; +my $jsc = jscProductDir() . "/jsc @ARGV"; +$jsc .= " 2> " . File::Spec->devnull() unless $verbose; -my $dyld = productDir(); +my $dyld = jscProductDir(); $ENV{"DYLD_FRAMEWORK_PATH"} = $dyld; print STDERR "Running $count time(s): DYLD_FRAMEWORK_PATH=$dyld $jsc\n"; diff --git a/WebKitTools/Scripts/run-launcher b/WebKitTools/Scripts/run-launcher index ee462ba..e12a64a 100755 --- a/WebKitTools/Scripts/run-launcher +++ b/WebKitTools/Scripts/run-launcher @@ -49,6 +49,8 @@ if (isQt()) { my $libDir = catdir(productDir(), 'lib'); $launcherPath = catdir($launcherPath, "bin", "QtLauncher"); + $ENV{QTWEBKIT_PLUGIN_PATH} = catdir($libDir, 'plugins'); + print "Starting webkit launcher, running against the built WebKit in $libDir...\n"; if (isDarwin()) { $ENV{DYLD_LIBRARY_PATH} = $ENV{DYLD_LIBRARY_PATH} ? "$libDir:$ENV{DYLD_LIBRARY_PATH}" : $libDir; diff --git a/WebKitTools/Scripts/run-mangleme-tests b/WebKitTools/Scripts/run-mangleme-tests index 93b7894..43ac74b 100755 --- a/WebKitTools/Scripts/run-mangleme-tests +++ b/WebKitTools/Scripts/run-mangleme-tests @@ -32,6 +32,7 @@ use strict; use warnings; use Cwd; +use File::Spec; use FindBin; use Getopt::Long; use IPC::Open2; @@ -152,7 +153,7 @@ sub openHTTPDIfNeeded() "-c", "User \"#$<\""); my $retryCount = 20; - while (system("/usr/bin/curl -q --silent --stderr - --output /dev/null $listen") && $retryCount) { + while (system("/usr/bin/curl -q --silent --stderr - --output " . File::Spec->devnull() . " $listen") && $retryCount) { sleep 1; --$retryCount; } diff --git a/WebKitTools/Scripts/run-sunspider b/WebKitTools/Scripts/run-sunspider index 367fd06..e63f5d1 100755 --- a/WebKitTools/Scripts/run-sunspider +++ b/WebKitTools/Scripts/run-sunspider @@ -103,23 +103,12 @@ sub setupEnvironmentForExecution($) # FIXME: Other platforms may wish to augment this method to use LD_LIBRARY_PATH, etc. } -sub jscPath($) -{ - my ($productDir) = @_; - my $jscName = "jsc"; - $jscName .= "_debug" if (isCygwin() && ($configuration eq "Debug")); - return "$productDir/$jscName"; -} - buildJSC(); chdirWebKit(); chdir("SunSpider"); -my $productDir = productDir(); -# FIXME: This hack should be pushed down into productDir() -$productDir .= "/JavaScriptCore" if isQt(); -$productDir .= "/Programs" if isGtk(); +my $productDir = jscProductDir(); setupEnvironmentForExecution($productDir); my @args = ("--shell", jscPath($productDir), "--runs", $testRuns); diff --git a/WebKitTools/Scripts/run-webkit-tests b/WebKitTools/Scripts/run-webkit-tests index a08a53c..6056035 100755 --- a/WebKitTools/Scripts/run-webkit-tests +++ b/WebKitTools/Scripts/run-webkit-tests @@ -321,7 +321,7 @@ if (!defined($root)) { local *DEVNULL; my ($childIn, $childOut, $childErr); if ($quiet) { - open(DEVNULL, ">", File::Spec->devnull) or die "Failed to open /dev/null"; + open(DEVNULL, ">", File::Spec->devnull()) or die "Failed to open /dev/null"; $childOut = ">&DEVNULL"; $childErr = ">&DEVNULL"; } else { @@ -1351,7 +1351,7 @@ sub openHTTPDIfNeeded() open2(\*HTTPDIN, \*HTTPDOUT, $httpdPath, @args); my $retryCount = 20; - while (system("/usr/bin/curl -q --silent --stderr - --output /dev/null $listen") && $retryCount) { + while (system("/usr/bin/curl -q --silent --stderr - --output " . File::Spec->devnull() . " $listen") && $retryCount) { sleep 1; --$retryCount; } diff --git a/WebKitTools/Scripts/sunspider-compare-results b/WebKitTools/Scripts/sunspider-compare-results index ce87a23..a207d7a 100755 --- a/WebKitTools/Scripts/sunspider-compare-results +++ b/WebKitTools/Scripts/sunspider-compare-results @@ -107,9 +107,7 @@ sub pathToJSC() buildJSC(); - my $productDir = productDir(); - # FIXME: This hack should be pushed down into productDir() - $productDir .= "/JavaScriptCore" if (isQt() or isGtk()); + my $productDir = jscProductDir(); setupEnvironmentForExecution($productDir); return pathToBuiltJSC($productDir); diff --git a/WebKitTools/Scripts/svn-apply b/WebKitTools/Scripts/svn-apply index 19c8c56..7d14e3a 100755 --- a/WebKitTools/Scripts/svn-apply +++ b/WebKitTools/Scripts/svn-apply @@ -74,15 +74,12 @@ use VCSUtils; sub addDirectoriesIfNeeded($); sub applyPatch($$;$); sub checksum($); -sub fixChangeLogPatch($); -sub gitdiff2svndiff($); sub handleBinaryChange($$); sub isDirectoryEmptyForRemoval($); sub patch($); sub removeDirectoriesIfNeeded(); sub setChangeLogDateAndReviewer($$); sub removeEOL($); -sub svnStatus($); # These should be replaced by an scm class/module: sub scmKnowsOfFile($); @@ -178,8 +175,9 @@ if ($merge) { die "--merge is currently only supported for SVN" unless isSVN(); # How do we handle Git patches applied to an SVN checkout here? for my $file (sort keys %versions) { - print "Getting version $versions{$file} of $file\n"; - system "svn", "update", "-r", $versions{$file}, $file; + my $version = $versions{$file}; + print "Getting version $version of $file\n"; + system("svn", "update", "-r", $version, $file) == 0 or die "Failed to run svn update -r $version $file."; } } @@ -258,74 +256,6 @@ sub checksum($) return $checksum; } -sub fixChangeLogPatch($) -{ - my $patch = shift; - my $contextLineCount = 3; - - return $patch if $patch !~ /\n@@ -1,(\d+) \+1,(\d+) @@\r?\n( .*\r?\n)+(\+.*\r?\n)+( .*\r?\n){$contextLineCount}$/m; - my ($oldLineCount, $newLineCount) = ($1, $2); - return $patch if $oldLineCount <= $contextLineCount; - - # The diff(1) command is greedy when matching lines, so a new ChangeLog entry will - # have lines of context at the top of a patch when the existing entry has the same - # date and author as the new entry. This nifty loop alters a ChangeLog patch so - # that the added lines ("+") in the patch always start at the beginning of the - # patch and there are no initial lines of context. - my $newPatch; - my $lineCountInState = 0; - my $oldContentLineCountReduction = $oldLineCount - $contextLineCount; - my $newContentLineCountWithoutContext = $newLineCount - $oldLineCount - $oldContentLineCountReduction; - my ($stateHeader, $statePreContext, $stateNewChanges, $statePostContext) = (1..4); - my $state = $stateHeader; - foreach my $line (split(/\n/, $patch)) { - $lineCountInState++; - if ($state == $stateHeader && $line =~ /^@@ -1,$oldLineCount \+1,$newLineCount @\@$/) { - $line = "@@ -1,$contextLineCount +1," . ($newLineCount - $oldContentLineCountReduction) . " @@"; - $lineCountInState = 0; - $state = $statePreContext; - } elsif ($state == $statePreContext && substr($line, 0, 1) eq " ") { - $line = "+" . substr($line, 1); - if ($lineCountInState == $oldContentLineCountReduction) { - $lineCountInState = 0; - $state = $stateNewChanges; - } - } elsif ($state == $stateNewChanges && substr($line, 0, 1) eq "+") { - # No changes to these lines - if ($lineCountInState == $newContentLineCountWithoutContext) { - $lineCountInState = 0; - $state = $statePostContext; - } - } elsif ($state == $statePostContext) { - if (substr($line, 0, 1) eq "+" && $lineCountInState <= $oldContentLineCountReduction) { - $line = " " . substr($line, 1); - } elsif ($lineCountInState > $contextLineCount && substr($line, 0, 1) eq " ") { - next; # Discard - } - } - $newPatch .= $line . "\n"; - } - - return $newPatch; -} - -sub gitdiff2svndiff($) -{ - $_ = shift @_; - if (m#^diff --git a/(.+) b/(.+)#) { - return "Index: $1"; - } elsif (m/^new file.*/) { - return ""; - } elsif (m#^index [0-9a-f]{7}\.\.[0-9a-f]{7} [0-9]{6}#) { - return "==================================================================="; - } elsif (m#^--- a/(.+)#) { - return "--- $1"; - } elsif (m#^\+\+\+ b/(.+)#) { - return "+++ $1"; - } - return $_; -} - sub handleBinaryChange($$) { my ($fullPath, $contents) = @_; @@ -334,7 +264,7 @@ sub handleBinaryChange($$) # The last line is allowed to have up to two '=' characters at the end (to signify padding). if ($contents =~ m#((\n[A-Za-z0-9+/]{76})*\n[A-Za-z0-9+/]{2,74}?[A-Za-z0-9+/=]{2}\n)#) { # Addition or Modification - open FILE, ">", $fullPath or die; + open FILE, ">", $fullPath or die "Failed to open $fullPath."; print FILE decode_base64($1); close FILE; if (!scmKnowsOfFile($fullPath)) { @@ -373,6 +303,7 @@ sub patch($) unless ($patch =~ m|^Index: ([^\r\n]+)|) { my $separator = '-' x 67; warn "Failed to find 'Index:' in:\n$separator\n$patch\n$separator\n"; + die unless $force; return; } my $fullPath = $1; @@ -413,7 +344,7 @@ sub patch($) unlink("$fullPath.orig") if -e "$fullPath.orig" && checksum($fullPath) eq checksum("$fullPath.orig"); scmAdd($fullPath); # What is this for? - system "svn", "stat", "$fullPath.orig" if isSVN() && -e "$fullPath.orig"; + system("svn", "stat", "$fullPath.orig") if isSVN() && -e "$fullPath.orig"; } } } @@ -455,38 +386,6 @@ sub removeEOL($) return $line; } -sub svnStatus($) -{ - my ($fullPath) = @_; - my $svnStatus; - open SVN, "svn status --non-interactive --non-recursive '$fullPath' |" or die; - if (-d $fullPath) { - # When running "svn stat" on a directory, we can't assume that only one - # status will be returned (since any files with a status below the - # directory will be returned), and we can't assume that the directory will - # be first (since any files with unknown status will be listed first). - my $normalizedFullPath = File::Spec->catdir(File::Spec->splitdir($fullPath)); - while (<SVN>) { - # Input may use a different EOL sequence than $/, so avoid chomp. - $_ = removeEOL($_); - my $normalizedStatPath = File::Spec->catdir(File::Spec->splitdir(substr($_, 7))); - if ($normalizedFullPath eq $normalizedStatPath) { - $svnStatus = "$_\n"; - last; - } - } - # Read the rest of the svn command output to avoid a broken pipe warning. - local $/ = undef; - <SVN>; - } - else { - # Files will have only one status returned. - $svnStatus = removeEOL(<SVN>) . "\n"; - } - close SVN; - return $svnStatus; -} - # This could be made into a more general "status" call, except svn and git # have different ideas about "moving" files which might get confusing. sub scmWillDeleteFile($) @@ -524,10 +423,10 @@ sub scmCopy($$) { my ($source, $destination) = @_; if (isSVN()) { - system "svn", "copy", $source, $destination; + system("svn", "copy", $source, $destination) == 0 or die "Failed to svn copy $source $destination."; } elsif (isGit()) { - system "cp", $source, $destination; - system "git", "add", $destination; + system("cp", $source, $destination) == 0 or die "Failed to copy $source $destination."; + system("git", "add", $destination) == 0 or die "Failed to git add $destination."; } } @@ -535,9 +434,9 @@ sub scmAdd($) { my ($path) = @_; if (isSVN()) { - system "svn", "add", $path; + system("svn", "add", $path) == 0 or die "Failed to svn add $path."; } elsif (isGit()) { - system "git", "add", $path; + system("git", "add", $path) == 0 or die "Failed to git add $path."; } } @@ -555,6 +454,6 @@ sub scmRemove($) close SVN; print $svnOutput if $svnOutput; } elsif (isGit()) { - system "git", "rm", "--force", $path; + system("git", "rm", "--force", $path) == 0 or die "Failed to git rm --force $path."; } } diff --git a/WebKitTools/Scripts/svn-create-patch b/WebKitTools/Scripts/svn-create-patch index 3f40783..768a8ed 100755 --- a/WebKitTools/Scripts/svn-create-patch +++ b/WebKitTools/Scripts/svn-create-patch @@ -56,12 +56,10 @@ use Time::gmtime; use VCSUtils; sub binarycmp($$); -sub canonicalizePath($); sub findBaseUrl($); sub findMimeType($;$); sub findModificationType($); sub findSourceFileAndRevision($); -sub fixChangeLogPatch($); sub generateDiff($$); sub generateFileList($\%); sub isBinaryMimeType($); @@ -132,25 +130,6 @@ sub binarycmp($$) return $fileDataA->{isBinary} <=> $fileDataB->{isBinary}; } -sub canonicalizePath($) -{ - my ($file) = @_; - - # Remove extra slashes and '.' directories in path - $file = File::Spec->canonpath($file); - - # Remove '..' directories in path - my @dirs = (); - foreach my $dir (File::Spec->splitdir($file)) { - if ($dir eq '..' && $#dirs >= 0 && $dirs[$#dirs] ne '..') { - pop(@dirs); - } else { - push(@dirs, $dir); - } - } - return ($#dirs >= 0) ? File::Spec->catdir(@dirs) : "."; -} - sub findBaseUrl($) { my ($infoPath) = @_; @@ -211,57 +190,6 @@ sub findSourceFileAndRevision($) return ($sourceFile, $sourceRevision); } -sub fixChangeLogPatch($) -{ - my $patch = shift; - my $contextLineCount = 3; - - return $patch if $patch !~ /\n@@ -1,(\d+) \+1,(\d+) @@\n( .*\n)+(\+.*\n)+( .*\n){$contextLineCount}$/m; - my ($oldLineCount, $newLineCount) = ($1, $2); - return $patch if $oldLineCount <= $contextLineCount; - - # The diff(1) command is greedy when matching lines, so a new ChangeLog entry will - # have lines of context at the top of a patch when the existing entry has the same - # date and author as the new entry. This nifty loop alters a ChangeLog patch so - # that the added lines ("+") in the patch always start at the beginning of the - # patch and there are no initial lines of context. - my $newPatch; - my $lineCountInState = 0; - my $oldContentLineCountReduction = $oldLineCount - $contextLineCount; - my $newContentLineCountWithoutContext = $newLineCount - $oldLineCount - $oldContentLineCountReduction; - my ($stateHeader, $statePreContext, $stateNewChanges, $statePostContext) = (1..4); - my $state = $stateHeader; - foreach my $line (split(/\n/, $patch)) { - $lineCountInState++; - if ($state == $stateHeader && $line =~ /^@@ -1,$oldLineCount \+1,$newLineCount @\@$/) { - $line = "@@ -1,$contextLineCount +1," . ($newLineCount - $oldContentLineCountReduction) . " @@"; - $lineCountInState = 0; - $state = $statePreContext; - } elsif ($state == $statePreContext && substr($line, 0, 1) eq " ") { - $line = "+" . substr($line, 1); - if ($lineCountInState == $oldContentLineCountReduction) { - $lineCountInState = 0; - $state = $stateNewChanges; - } - } elsif ($state == $stateNewChanges && substr($line, 0, 1) eq "+") { - # No changes to these lines - if ($lineCountInState == $newContentLineCountWithoutContext) { - $lineCountInState = 0; - $state = $statePostContext; - } - } elsif ($state == $statePostContext) { - if (substr($line, 0, 1) eq "+" && $lineCountInState <= $oldContentLineCountReduction) { - $line = " " . substr($line, 1); - } elsif ($lineCountInState > $contextLineCount && substr($line, 0, 1) eq " ") { - next; # Discard - } - } - $newPatch .= $line . "\n"; - } - - return $newPatch; -} - sub generateDiff($$) { my ($fileData, $prefix) = @_; diff --git a/WebKitTools/Scripts/svn-unapply b/WebKitTools/Scripts/svn-unapply index a4cec9a..94bb1ce 100755 --- a/WebKitTools/Scripts/svn-unapply +++ b/WebKitTools/Scripts/svn-unapply @@ -71,12 +71,9 @@ use lib $FindBin::Bin; use VCSUtils; sub checksum($); -sub fixChangeLogPatch($); -sub gitdiff2svndiff($); sub patch($); sub revertDirectories(); sub removeEOL($); -sub svnStatus($); sub unapplyPatch($$;$); sub unsetChangeLogDate($$); @@ -158,74 +155,6 @@ sub checksum($) return $checksum; } -sub fixChangeLogPatch($) -{ - my $patch = shift; - my $contextLineCount = 3; - - return $patch if $patch !~ /\n@@ -1,(\d+) \+1,(\d+) @@\n( .*\n)+(\+.*\n)+( .*\n){$contextLineCount}$/m; - my ($oldLineCount, $newLineCount) = ($1, $2); - return $patch if $oldLineCount <= $contextLineCount; - - # The diff(1) command is greedy when matching lines, so a new ChangeLog entry will - # have lines of context at the top of a patch when the existing entry has the same - # date and author as the new entry. This nifty loop alters a ChangeLog patch so - # that the added lines ("+") in the patch always start at the beginning of the - # patch and there are no initial lines of context. - my $newPatch; - my $lineCountInState = 0; - my $oldContentLineCountReduction = $oldLineCount - $contextLineCount; - my $newContentLineCountWithoutContext = $newLineCount - $oldLineCount - $oldContentLineCountReduction; - my ($stateHeader, $statePreContext, $stateNewChanges, $statePostContext) = (1..4); - my $state = $stateHeader; - foreach my $line (split(/\n/, $patch)) { - $lineCountInState++; - if ($state == $stateHeader && $line =~ /^@@ -1,$oldLineCount \+1,$newLineCount @\@$/) { - $line = "@@ -1,$contextLineCount +1," . ($newLineCount - $oldContentLineCountReduction) . " @@"; - $lineCountInState = 0; - $state = $statePreContext; - } elsif ($state == $statePreContext && substr($line, 0, 1) eq " ") { - $line = "+" . substr($line, 1); - if ($lineCountInState == $oldContentLineCountReduction) { - $lineCountInState = 0; - $state = $stateNewChanges; - } - } elsif ($state == $stateNewChanges && substr($line, 0, 1) eq "+") { - # No changes to these lines - if ($lineCountInState == $newContentLineCountWithoutContext) { - $lineCountInState = 0; - $state = $statePostContext; - } - } elsif ($state == $statePostContext) { - if (substr($line, 0, 1) eq "+" && $lineCountInState <= $oldContentLineCountReduction) { - $line = " " . substr($line, 1); - } elsif ($lineCountInState > $contextLineCount && substr($line, 0, 1) eq " ") { - next; # Discard - } - } - $newPatch .= $line . "\n"; - } - - return $newPatch; -} - -sub gitdiff2svndiff($) -{ - $_ = shift @_; - if (m#^diff --git a/(.+) b/(.+)#) { - return "Index: $1"; - } elsif (m/^new file.*/) { - return ""; - } elsif (m#^index [0-9a-f]{7}\.\.[0-9a-f]{7} [0-9]{6}#) { - return "==================================================================="; - } elsif (m#^--- a/(.+)#) { - return "--- $1"; - } elsif (m#^\+\+\+ b/(.+)#) { - return "+++ $1"; - } - return $_; -} - sub patch($) { my ($patch) = @_; @@ -338,38 +267,6 @@ sub removeEOL($) return $line; } -sub svnStatus($) -{ - my ($fullPath) = @_; - my $svnStatus; - open SVN, "svn status --non-interactive --non-recursive '$fullPath' |" or die; - if (-d $fullPath) { - # When running "svn stat" on a directory, we can't assume that only one - # status will be returned (since any files with a status below the - # directory will be returned), and we can't assume that the directory will - # be first (since any files with unknown status will be listed first). - my $normalizedFullPath = File::Spec->catdir(File::Spec->splitdir($fullPath)); - while (<SVN>) { - # Input may use a different EOL sequence than $/, so avoid chomp. - $_ = removeEOL($_); - my $normalizedStatPath = File::Spec->catdir(File::Spec->splitdir(substr($_, 7))); - if ($normalizedFullPath eq $normalizedStatPath) { - $svnStatus = "$_\n"; - last; - } - } - # Read the rest of the svn command output to avoid a broken pipe warning. - local $/ = undef; - <SVN>; - } - else { - # Files will have only one status returned. - $svnStatus = removeEOL(<SVN>) . "\n"; - } - close SVN; - return $svnStatus; -} - sub unapplyPatch($$;$) { my ($patch, $fullPath, $options) = @_; diff --git a/WebKitTools/Scripts/update-webkit b/WebKitTools/Scripts/update-webkit index e562cc0..b503004 100755 --- a/WebKitTools/Scripts/update-webkit +++ b/WebKitTools/Scripts/update-webkit @@ -39,7 +39,6 @@ use VCSUtils; use webkitdirs; sub runSvnUpdate(); -sub normalizePath($); # Handle options my $quiet = ''; @@ -105,10 +104,3 @@ sub runSvnUpdate() or die "Could not open resolve-ChangeLogs script: $!.\n"; } } - -sub normalizePath($) -{ - my ($path) = @_; - $path =~ s/\\/\//g; - return $path; -} diff --git a/WebKitTools/Scripts/webkitdirs.pm b/WebKitTools/Scripts/webkitdirs.pm index d5177dd..16f9c26 100644 --- a/WebKitTools/Scripts/webkitdirs.pm +++ b/WebKitTools/Scripts/webkitdirs.pm @@ -117,7 +117,7 @@ sub determineBaseProductDir unlink($personalPlistFile) || die "Could not delete $personalPlistFile: $!"; } - open PRODUCT, "defaults read com.apple.Xcode PBXApplicationwideBuildSettings 2> /dev/null |" or die; + open PRODUCT, "defaults read com.apple.Xcode PBXApplicationwideBuildSettings 2> " . File::Spec->devnull() . " |" or die; $baseProductDir = join '', <PRODUCT>; close PRODUCT; @@ -125,7 +125,7 @@ sub determineBaseProductDir undef $baseProductDir unless $baseProductDir =~ /^\//; if (!defined($baseProductDir)) { - open PRODUCT, "defaults read com.apple.Xcode PBXProductDirectory 2> /dev/null |" or die; + open PRODUCT, "defaults read com.apple.Xcode PBXProductDirectory 2> " . File::Spec->devnull() . " |" or die; $baseProductDir = <PRODUCT>; close PRODUCT; if ($baseProductDir) { @@ -215,6 +215,14 @@ sub determineArchitecture } } +sub jscPath($) +{ + my ($productDir) = @_; + my $jscName = "jsc"; + $jscName .= "_debug" if (isCygwin() && ($configuration eq "Debug")); + return "$productDir/$jscName"; +} + sub argumentsForConfiguration() { determineConfiguration(); @@ -300,6 +308,16 @@ sub productDir return $configurationProductDir; } +sub jscProductDir +{ + my $productDir = productDir(); + $productDir .= "/JavaScriptCore" if isQt(); + $productDir .= "/$configuration" if (isQt() && isWindows()); + $productDir .= "/Programs" if isGtk(); + + return $productDir; +} + sub configuration() { determineConfiguration(); @@ -792,7 +810,7 @@ sub determineIsQt() } # The presence of QTDIR only means Qt if --gtk is not on the command-line - if (isGtk()) { + if (isGtk() || isWx()) { $isQt = 0; return; } @@ -1100,7 +1118,12 @@ sub buildVisualStudioProject $action = "/clean"; } - my @command = ($vcBuildPath, $winProjectPath, $action, $config); + my $useenv = "/useenv"; + if (isChromium()) { + $useenv = ""; + } + + my @command = ($vcBuildPath, $useenv, $winProjectPath, $action, $config); print join(" ", @command), "\n"; return system @command; diff --git a/WebKitTools/WinLauncher/WinLauncher.vcproj b/WebKitTools/WinLauncher/WinLauncher.vcproj index 5fddb48..9b7c42c 100644 --- a/WebKitTools/WinLauncher/WinLauncher.vcproj +++ b/WebKitTools/WinLauncher/WinLauncher.vcproj @@ -39,17 +39,10 @@ />
<Tool
Name="VCCLCompilerTool"
- Optimization="0"
AdditionalIncludeDirectories=""$(WebKitOutputDir)\include\WebKit";"$(WebKitOutputDir)\Include";"$(WebKitLibrariesDir)\Include";"$(WebKitOutputDir)\Include\WebCore";"$(WebKitLibrariesDir)\Include\WebCore";"$(WebKitOutputDir)\Include\WebCore\ForwardingHeaders";"$(WebKitLibrariesDir)\Include\WebCore\ForwardingHeaders";"$(WebKitOutputDir)\Include\JavaScriptCore";"$(WebKitLibrariesDir)\Include\JavaScriptCore";"$(ProjectDir)\..";"$(ProjectDir)";"$(IntDir)\Include";"$(WebKitOutputDir)\obj\WebKit\DerivedSources""
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE"
- MinimalRebuild="true"
- ExceptionHandling="0"
- BasicRuntimeChecks="3"
- RuntimeLibrary="3"
- RuntimeTypeInfo="false"
UsePrecompiledHeader="2"
WarningLevel="1"
- Detect64BitPortabilityProblems="false"
/>
<Tool
Name="VCManagedResourceCompilerTool"
@@ -125,12 +118,8 @@ Name="VCCLCompilerTool"
AdditionalIncludeDirectories=""$(WebKitOutputDir)\include\WebKit";"$(WebKitOutputDir)\Include";"$(WebKitLibrariesDir)\Include";"$(WebKitOutputDir)\Include\WebCore";"$(WebKitLibrariesDir)\Include\WebCore";"$(WebKitOutputDir)\Include\WebCore\ForwardingHeaders";"$(WebKitLibrariesDir)\Include\WebCore\ForwardingHeaders";"$(WebKitOutputDir)\Include\JavaScriptCore";"$(WebKitLibrariesDir)\Include\JavaScriptCore";"$(WebKitOutputDir)\Include\icu";"$(WebKitLibrariesDir)\Include\icu";"$(WebKitLibrariesDir)\include\pthreads";"$(WebKitOutputDir)\Include\CoreFoundation\OSXCompatibilityHeaders";"$(WebKitLibrariesDir)\Include\CoreFoundation\OSXCompatibilityHeaders";"$(WebKitOutputDir)\Include\CoreFoundation\OSXCompatibilityHeaders\GNUCompatibility";"$(WebKitLibrariesDir)\Include\CoreFoundation\OSXCompatibilityHeaders\GNUCompatibility";"$(ProjectDir)\..";"$(ProjectDir)";"$(IntDir)\include";"$(WebKitOutputDir)\obj\WebKit\DerivedSources""
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_WIN32_WINNT=0x501"
- ExceptionHandling="0"
- RuntimeLibrary="2"
- RuntimeTypeInfo="false"
UsePrecompiledHeader="2"
WarningLevel="1"
- Detect64BitPortabilityProblems="false"
/>
<Tool
Name="VCManagedResourceCompilerTool"
@@ -206,17 +195,10 @@ />
<Tool
Name="VCCLCompilerTool"
- Optimization="0"
AdditionalIncludeDirectories=""$(WebKitOutputDir)\include\WebKit";"$(WebKitOutputDir)\Include";"$(WebKitLibrariesDir)\Include";"$(WebKitOutputDir)\Include\WebCore";"$(WebKitLibrariesDir)\Include\WebCore";"$(WebKitOutputDir)\Include\WebCore\ForwardingHeaders";"$(WebKitLibrariesDir)\Include\WebCore\ForwardingHeaders";"$(WebKitOutputDir)\Include\JavaScriptCore";"$(WebKitLibrariesDir)\Include\JavaScriptCore";"$(ProjectDir)\..";"$(ProjectDir)";"$(IntDir)\Include";"$(WebKitOutputDir)\obj\WebKit\DerivedSources""
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE"
- MinimalRebuild="true"
- ExceptionHandling="0"
- BasicRuntimeChecks="3"
- RuntimeLibrary="3"
- RuntimeTypeInfo="false"
UsePrecompiledHeader="2"
WarningLevel="1"
- Detect64BitPortabilityProblems="false"
/>
<Tool
Name="VCManagedResourceCompilerTool"
@@ -289,17 +271,86 @@ />
<Tool
Name="VCCLCompilerTool"
- Optimization="0"
AdditionalIncludeDirectories=""$(WebKitOutputDir)\include\WebKit";"$(WebKitOutputDir)\Include";"$(WebKitLibrariesDir)\Include";"$(WebKitOutputDir)\Include\WebCore";"$(WebKitLibrariesDir)\Include\WebCore";"$(WebKitOutputDir)\Include\WebCore\ForwardingHeaders";"$(WebKitLibrariesDir)\Include\WebCore\ForwardingHeaders";"$(WebKitOutputDir)\Include\JavaScriptCore";"$(WebKitLibrariesDir)\Include\JavaScriptCore";"$(ProjectDir)\..";"$(ProjectDir)";"$(IntDir)\Include";"$(WebKitOutputDir)\obj\WebKit\DerivedSources""
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE"
- MinimalRebuild="true"
- ExceptionHandling="0"
- BasicRuntimeChecks="3"
- RuntimeLibrary="3"
- RuntimeTypeInfo="false"
UsePrecompiledHeader="2"
WarningLevel="1"
- Detect64BitPortabilityProblems="false"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkLibraryDependencies="false"
+ AdditionalDependencies="comctl32.lib shlwapi.lib user32.lib ole32.lib oleaut32.lib WebKitGUID$(WebKitConfigSuffix).lib WebKit$(WebKitDLLConfigSuffix).lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories=""$(WebKitOutputDir)\lib";"$(ProjectDir)\..\..\..\""
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ TypeLibraryFile=""
+ ComponentFileName=""
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed""
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug_All|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(WebKitLibrariesDir)\tools\vsprops\common.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug_internal.vsprops;$(WebKitLibrariesDir)\tools\vsprops\debug_all.vsprops"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine="%SystemDrive%\cygwin\bin\which.exe bash
if errorlevel 1 set PATH=%SystemDrive%\cygwin\bin;%PATH%
cmd /c
if exist "$(WebKitOutputDir)\buildfailed" grep XX$(ProjectName)XX "$(WebKitOutputDir)\buildfailed"
if errorlevel 1 exit 1
echo XX$(ProjectName)XX > "$(WebKitOutputDir)\buildfailed"
"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""$(WebKitOutputDir)\include\WebKit";"$(WebKitOutputDir)\Include";"$(WebKitLibrariesDir)\Include";"$(WebKitOutputDir)\Include\WebCore";"$(WebKitLibrariesDir)\Include\WebCore";"$(WebKitOutputDir)\Include\WebCore\ForwardingHeaders";"$(WebKitLibrariesDir)\Include\WebCore\ForwardingHeaders";"$(WebKitOutputDir)\Include\JavaScriptCore";"$(WebKitLibrariesDir)\Include\JavaScriptCore";"$(ProjectDir)\..";"$(ProjectDir)";"$(IntDir)\Include";"$(WebKitOutputDir)\obj\WebKit\DerivedSources""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE"
+ UsePrecompiledHeader="2"
+ WarningLevel="1"
/>
<Tool
Name="VCManagedResourceCompilerTool"
@@ -392,6 +443,14 @@ UsePrecompiledHeader="1"
/>
</FileConfiguration>
+ <FileConfiguration
+ Name="Debug_All|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
</File>
<File
RelativePath=".\WinLauncher.cpp"
diff --git a/WebKitTools/pywebsocket/COPYING b/WebKitTools/pywebsocket/COPYING new file mode 100644 index 0000000..ab9d52d --- /dev/null +++ b/WebKitTools/pywebsocket/COPYING @@ -0,0 +1,28 @@ +Copyright 2009, 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. diff --git a/WebKitTools/pywebsocket/MANIFEST.in b/WebKitTools/pywebsocket/MANIFEST.in new file mode 100644 index 0000000..1925688 --- /dev/null +++ b/WebKitTools/pywebsocket/MANIFEST.in @@ -0,0 +1,6 @@ +include COPYING +include MANIFEST.in +include README +recursive-include example *.py +recursive-include mod_pywebsocket *.py +recursive-include test *.py diff --git a/WebKitTools/pywebsocket/README b/WebKitTools/pywebsocket/README new file mode 100644 index 0000000..1f9f05f --- /dev/null +++ b/WebKitTools/pywebsocket/README @@ -0,0 +1,6 @@ +Install this package by: +$ python setup.py build +$ sudo python setup.py install + +Then read document by: +$ pydoc mod_pywebsocket diff --git a/WebKitTools/pywebsocket/example/echo_client.py b/WebKitTools/pywebsocket/example/echo_client.py new file mode 100644 index 0000000..61b129c --- /dev/null +++ b/WebKitTools/pywebsocket/example/echo_client.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python +# +# Copyright 2009, 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. + + +"""Web Socket Echo client. + +This is an example Web Socket client that talks with echo_wsh.py. +This may be useful for checking mod_pywebsocket installation. + +Note: +This code is far from robust, e.g., we cut corners in handshake. +""" + + +import codecs +from optparse import OptionParser +import socket +import sys + + +_DEFAULT_PORT = 80 +_DEFAULT_SECURE_PORT = 443 +_UNDEFINED_PORT = -1 + +_UPGRADE_HEADER = 'Upgrade: WebSocket\r\n' +_CONNECTION_HEADER = 'Connection: Upgrade\r\n' +_EXPECTED_RESPONSE = ( + 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + + _UPGRADE_HEADER + + _CONNECTION_HEADER) + + +def _method_line(resource): + return 'GET %s HTTP/1.1\r\n' % resource + + +def _origin_header(origin): + return 'Origin: %s\r\n' % origin + + +class _TLSSocket(object): + """Wrapper for a TLS connection.""" + + def __init__(self, raw_socket): + self._ssl = socket.ssl(raw_socket) + + def send(self, bytes): + return self._ssl.write(bytes) + + def recv(self, size=-1): + return self._ssl.read(size) + + def close(self): + # Nothing to do. + pass + + +class EchoClient(object): + """Web Socket echo client.""" + + def __init__(self, options): + self._options = options + self._socket = None + + def run(self): + """Run the client. + + Shake hands and then repeat sending message and receiving its echo. + """ + self._socket = socket.socket() + try: + self._socket.connect((self._options.server_host, + self._options.server_port)) + if self._options.use_tls: + self._socket = _TLSSocket(self._socket) + self._handshake() + for line in self._options.message.split(','): + frame = '\x00' + line.encode('utf-8') + '\xff' + self._socket.send(frame) + if self._options.verbose: + print 'Send: %s' % line + received = self._socket.recv(len(frame)) + if received != frame: + raise Exception('Incorrect echo: %r' % received) + if self._options.verbose: + print 'Recv: %s' % received[1:-1].decode('utf-8') + finally: + self._socket.close() + + def _handshake(self): + self._socket.send(_method_line(self._options.resource)) + self._socket.send(_UPGRADE_HEADER) + self._socket.send(_CONNECTION_HEADER) + self._socket.send(self._format_host_header()) + self._socket.send(_origin_header(self._options.origin)) + self._socket.send('\r\n') + + for expected_char in _EXPECTED_RESPONSE: + received = self._socket.recv(1)[0] + if expected_char != received: + raise Exception('Handshake failure') + # We cut corners and skip other headers. + self._skip_headers() + + def _skip_headers(self): + terminator = '\r\n\r\n' + pos = 0 + while pos < len(terminator): + received = self._socket.recv(1)[0] + if received == terminator[pos]: + pos += 1 + elif received == terminator[0]: + pos = 1 + else: + pos = 0 + + def _format_host_header(self): + host = 'Host: ' + self._options.server_host + if ((not self._options.use_tls and + self._options.server_port != _DEFAULT_PORT) or + (self._options.use_tls and + self._options.server_port != _DEFAULT_SECURE_PORT)): + host += ':' + str(self._options.server_port) + host += '\r\n' + return host + + +def main(): + sys.stdout = codecs.getwriter('utf-8')(sys.stdout) + + parser = OptionParser() + parser.add_option('-s', '--server_host', dest='server_host', type='string', + default='localhost', help='server host') + parser.add_option('-p', '--server_port', dest='server_port', type='int', + default=_UNDEFINED_PORT, help='server port') + parser.add_option('-o', '--origin', dest='origin', type='string', + default='http://localhost/', help='origin') + parser.add_option('-r', '--resource', dest='resource', type='string', + default='/echo', help='resource path') + parser.add_option('-m', '--message', dest='message', type='string', + help='comma-separated messages to send') + parser.add_option('-q', '--quiet', dest='verbose', action='store_false', + default=True, help='suppress messages') + parser.add_option('-t', '--tls', dest='use_tls', action='store_true', + default=False, help='use TLS (wss://)') + (options, unused_args) = parser.parse_args() + + # Default port number depends on whether TLS is used. + if options.server_port == _UNDEFINED_PORT: + if options.use_tls: + options.server_port = _DEFAULT_SECURE_PORT + else: + options.server_port = _DEFAULT_PORT + + # optparse doesn't seem to handle non-ascii default values. + # Set default message here. + if not options.message: + options.message = u'Hello,\u65e5\u672c' # "Japan" in Japanese + + EchoClient(options).run() + + +if __name__ == '__main__': + main() + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/example/echo_wsh.py b/WebKitTools/pywebsocket/example/echo_wsh.py new file mode 100644 index 0000000..f680fa5 --- /dev/null +++ b/WebKitTools/pywebsocket/example/echo_wsh.py @@ -0,0 +1,44 @@ +# Copyright 2009, 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. + + +from mod_pywebsocket import msgutil + + +def web_socket_do_extra_handshake(request): + pass # Always accept. + + +def web_socket_transfer_data(request): + while True: + line = msgutil.receive_message(request) + msgutil.send_message(request, line) + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/__init__.py b/WebKitTools/pywebsocket/mod_pywebsocket/__init__.py new file mode 100644 index 0000000..beacc9e --- /dev/null +++ b/WebKitTools/pywebsocket/mod_pywebsocket/__init__.py @@ -0,0 +1,102 @@ +# Copyright 2009, 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. + + +"""Web Socket extension for Apache HTTP Server. + +mod_pywebsocket is a Web Socket extension for Apache HTTP Server +intended for testing or experimental purposes. mod_python is required. + +Installation: + +0. Prepare an Apache HTTP Server for which mod_python is enabled. + +1. Specify the following Apache HTTP Server directives to suit your + configuration. + + If mod_pywebsocket is not in the Python path, specify the following. + <websock_lib> is the directory where mod_pywebsocket is installed. + + PythonPath "sys.path+['<websock_lib>']" + + Always specify the following. <websock_handlers> is the directory where + user-written Web Socket handlers are placed. + + PythonOption mod_pywebsocket.handler_root <websock_handlers> + PythonHeaderParserHandler mod_pywebsocket.headerparserhandler + + To limit the search for Web Socket handlers to a directory <scan_dir> + under <websock_handlers>, configure as follows: + + PythonOption mod_pywebsocket.handler_scan <scan_dir> + + <scan_dir> is useful in saving scan time when <websock_handlers> + contains many non-Web Socket handler files. + + Example snippet of httpd.conf: + (mod_pywebsocket is in /websock_lib, Web Socket handlers are in + /websock_handlers, port is 80 for ws, 443 for wss.) + + <IfModule python_module> + PythonPath "sys.path+['/websock_lib']" + PythonOption mod_pywebsocket.handler_root /websock_handlers + PythonHeaderParserHandler mod_pywebsocket.headerparserhandler + </IfModule> + +Writing Web Socket handlers: + +When a Web Socket request comes in, the resource name +specified in the handshake is considered as if it is a file path under +<websock_handlers> and the handler defined in +<websock_handlers>/<resource_name>_wsh.py is invoked. + +For example, if the resource name is /example/chat, the handler defined in +<websock_handlers>/example/chat_wsh.py is invoked. + +A Web Socket handler is composed of the following two functions: + + web_socket_do_extra_handshake(request) + web_socket_transfer_data(request) + +where: + request: mod_python request. + +web_socket_do_extra_handshake is called during the handshake after the +headers are successfully parsed and Web Socket properties (ws_location, +ws_origin, ws_protocol, and ws_resource) are added to request. A handler +can reject the request by raising an exception. + +web_socket_transfer_data is called after the handshake completed +successfully. A handler can receive/send messages from/to the client +using request. mod_pywebsocket.msgutil module provides utilities +for data transfer. +""" + + +# vi:sts=4 sw=4 et tw=72 diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/dispatch.py b/WebKitTools/pywebsocket/mod_pywebsocket/dispatch.py new file mode 100644 index 0000000..84422eb --- /dev/null +++ b/WebKitTools/pywebsocket/mod_pywebsocket/dispatch.py @@ -0,0 +1,205 @@ +# Copyright 2009, 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. + + +"""Dispatch Web Socket request. +""" + + +import os +import re + +import util + + +_SOURCE_PATH_PATTERN = re.compile(r'(?i)_wsh\.py$') +_SOURCE_SUFFIX = '_wsh.py' +_DO_EXTRA_HANDSHAKE_HANDLER_NAME = 'web_socket_do_extra_handshake' +_TRANSFER_DATA_HANDLER_NAME = 'web_socket_transfer_data' + + +class DispatchError(Exception): + """Exception in dispatching Web Socket request.""" + + pass + + +def _normalize_path(path): + """Normalize path. + + Args: + path: the path to normalize. + + Path is converted to the absolute path. + The input path can use either '\\' or '/' as the separator. + The normalized path always uses '/' regardless of the platform. + """ + + path = path.replace('\\', os.path.sep) + path = os.path.abspath(path) + path = path.replace('\\', '/') + return path + + +def _path_to_resource_converter(base_dir): + base_dir = _normalize_path(base_dir) + base_len = len(base_dir) + suffix_len = len(_SOURCE_SUFFIX) + def converter(path): + if not path.endswith(_SOURCE_SUFFIX): + return None + path = _normalize_path(path) + if not path.startswith(base_dir): + return None + return path[base_len:-suffix_len] + return converter + + +def _source_file_paths(directory): + """Yield Web Socket Handler source file names in the given directory.""" + + for root, unused_dirs, files in os.walk(directory): + for base in files: + path = os.path.join(root, base) + if _SOURCE_PATH_PATTERN.search(path): + yield path + + +def _source(source_str): + """Source a handler definition string.""" + + global_dic = {} + try: + exec source_str in global_dic + except Exception: + raise DispatchError('Error in sourcing handler:' + + util.get_stack_trace()) + return (_extract_handler(global_dic, _DO_EXTRA_HANDSHAKE_HANDLER_NAME), + _extract_handler(global_dic, _TRANSFER_DATA_HANDLER_NAME)) + + +def _extract_handler(dic, name): + if name not in dic: + raise DispatchError('%s is not defined.' % name) + handler = dic[name] + if not callable(handler): + raise DispatchError('%s is not callable.' % name) + return handler + + +class Dispatcher(object): + """Dispatches Web Socket requests. + + This class maintains a map from resource name to handlers. + """ + + def __init__(self, root_dir, scan_dir=None): + """Construct an instance. + + Args: + root_dir: The directory where handler definition files are + placed. + scan_dir: The directory where handler definition files are + searched. scan_dir must be a directory under root_dir, + including root_dir itself. If scan_dir is None, root_dir + is used as scan_dir. scan_dir can be useful in saving + scan time when root_dir contains many subdirectories. + """ + + self._handlers = {} + self._source_warnings = [] + if scan_dir is None: + scan_dir = root_dir + if not os.path.abspath(scan_dir).startswith(os.path.abspath(root_dir)): + raise DispatchError('scan_dir:%s must be a directory under ' + 'root_dir:%s.' % (scan_dir, root_dir)) + self._source_files_in_dir(root_dir, scan_dir) + + def source_warnings(self): + """Return warnings in sourcing handlers.""" + + return self._source_warnings + + def do_extra_handshake(self, request): + """Do extra checking in Web Socket handshake. + + Select a handler based on request.uri and call its + web_socket_do_extra_handshake function. + + Args: + request: mod_python request. + """ + + do_extra_handshake_, unused_transfer_data = self._handler(request) + try: + do_extra_handshake_(request) + except Exception: + raise DispatchError('%s raised exception: %s' % + (_DO_EXTRA_HANDSHAKE_HANDLER_NAME, util.get_stack_trace())) + + def transfer_data(self, request): + """Let a handler transfer_data with a Web Socket client. + + Select a handler based on request.ws_resource and call its + web_socket_transfer_data function. + + Args: + request: mod_python request. + """ + + unused_do_extra_handshake, transfer_data_ = self._handler(request) + try: + transfer_data_(request) + except Exception: + raise DispatchError('%s raised exception: %s' % + (_TRANSFER_DATA_HANDLER_NAME, util.get_stack_trace())) + + def _handler(self, request): + try: + return self._handlers[request.ws_resource] + except KeyError: + raise DispatchError('No handler for: %r' % request.ws_resource) + + def _source_files_in_dir(self, root_dir, scan_dir): + """Source all the handler source files in the scan_dir directory. + + The resource path is determined relative to root_dir. + """ + + to_resource = _path_to_resource_converter(root_dir) + for path in _source_file_paths(scan_dir): + try: + handlers = _source(open(path).read()) + except DispatchError, e: + self._source_warnings.append('%s: %s' % (path, e)) + continue + self._handlers[to_resource(path)] = handlers + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/handshake.py b/WebKitTools/pywebsocket/mod_pywebsocket/handshake.py new file mode 100644 index 0000000..a67aadd --- /dev/null +++ b/WebKitTools/pywebsocket/mod_pywebsocket/handshake.py @@ -0,0 +1,178 @@ +# Copyright 2009, 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. + + +"""Web Socket handshaking. + +Note: request.connection.write/read are used in this module, even though +mod_python document says that they should be used only in connection handlers. +Unfortunately, we have no other options. For example, request.write/read are +not suitable because they don't allow direct raw bytes writing/reading. +""" + + +import re + + +_DEFAULT_WEB_SOCKET_PORT = 80 +_DEFAULT_WEB_SOCKET_SECURE_PORT = 443 +_WEB_SOCKET_SCHEME = 'ws' +_WEB_SOCKET_SECURE_SCHEME = 'wss' + +_METHOD_LINE = re.compile(r'^GET ([^ ]+) HTTP/1.1\r\n$') + +_MANDATORY_HEADERS = [ + # key, expected value or None + ['Upgrade', 'WebSocket'], + ['Connection', 'Upgrade'], + ['Host', None], + ['Origin', None], +] + + +def _default_port(is_secure): + if is_secure: + return _DEFAULT_WEB_SOCKET_SECURE_PORT + else: + return _DEFAULT_WEB_SOCKET_PORT + + +class HandshakeError(Exception): + """Exception in Web Socket Handshake.""" + + pass + + +def _validate_protocol(protocol): + """Validate WebSocket-Protocol string.""" + + if not protocol: + raise HandshakeError('Invalid WebSocket-Protocol: empty') + for c in protocol: + if not 0x21 <= ord(c) <= 0x7e: + raise HandshakeError('Illegal character in protocol: %r' % c) + + +class Handshaker(object): + """This class performs Web Socket handshake.""" + + def __init__(self, request, dispatcher): + """Construct an instance. + + Args: + request: mod_python request. + dispatcher: Dispatcher (dispatch.Dispatcher). + + Handshaker will add attributes such as ws_resource in performing + handshake. + """ + + self._request = request + self._dispatcher = dispatcher + + def do_handshake(self): + """Perform Web Socket Handshake.""" + + self._check_header_lines() + self._set_resource() + self._set_origin() + self._set_location() + self._set_protocol() + self._dispatcher.do_extra_handshake(self._request) + self._send_handshake() + + def _set_resource(self): + self._request.ws_resource = self._request.uri + + def _set_origin(self): + self._request.ws_origin = self._request.headers_in['Origin'] + + def _set_location(self): + location_parts = [] + if self._request.is_https(): + location_parts.append(_WEB_SOCKET_SECURE_SCHEME) + else: + location_parts.append(_WEB_SOCKET_SCHEME) + location_parts.append('://') + host, port = self._parse_host_header() + connection_port = self._request.connection.local_addr[1] + if port != connection_port: + raise HandshakeError('Header/connection port mismatch: %d/%d' % + (port, connection_port)) + location_parts.append(host) + if (port != _default_port(self._request.is_https())): + location_parts.append(':') + location_parts.append(str(port)) + location_parts.append(self._request.uri) + self._request.ws_location = ''.join(location_parts) + + def _parse_host_header(self): + fields = self._request.headers_in['Host'].split(':', 1) + if len(fields) == 1: + return fields[0], _default_port(self._request.is_https()) + try: + return fields[0], int(fields[1]) + except ValueError, e: + raise HandshakeError('Invalid port number format: %r' % e) + + def _set_protocol(self): + protocol = self._request.headers_in.get('WebSocket-Protocol') + if protocol is not None: + _validate_protocol(protocol) + self._request.ws_protocol = protocol + + def _send_handshake(self): + self._request.connection.write( + 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n') + self._request.connection.write('Upgrade: WebSocket\r\n') + self._request.connection.write('Connection: Upgrade\r\n') + self._request.connection.write('WebSocket-Origin: ') + self._request.connection.write(self._request.ws_origin) + self._request.connection.write('\r\n') + self._request.connection.write('WebSocket-Location: ') + self._request.connection.write(self._request.ws_location) + self._request.connection.write('\r\n') + if self._request.ws_protocol: + self._request.connection.write('WebSocket-Protocol: ') + self._request.connection.write(self._request.ws_protocol) + self._request.connection.write('\r\n') + self._request.connection.write('\r\n') + + def _check_header_lines(self): + for key, expected_value in _MANDATORY_HEADERS: + actual_value = self._request.headers_in.get(key) + if not actual_value: + raise HandshakeError('Header %s is not defined' % key) + if expected_value: + if actual_value != expected_value: + raise HandshakeError('Illegal value for header %s: %s' % + (key, actual_value)) + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/headerparserhandler.py b/WebKitTools/pywebsocket/mod_pywebsocket/headerparserhandler.py new file mode 100644 index 0000000..124b9f1 --- /dev/null +++ b/WebKitTools/pywebsocket/mod_pywebsocket/headerparserhandler.py @@ -0,0 +1,99 @@ +# Copyright 2009, 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. + + +"""PythonHeaderParserHandler for mod_pywebsocket. + +Apache HTTP Server and mod_python must be configured such that this +function is called to handle Web Socket request. +""" + + +from mod_python import apache + +import dispatch +import handshake +import util + + +# PythonOption to specify the handler root directory. +_PYOPT_HANDLER_ROOT = 'mod_pywebsocket.handler_root' + +# PythonOption to specify the handler scan directory. +# This must be a directory under the root directory. +# The default is the root directory. +_PYOPT_HANDLER_SCAN = 'mod_pywebsocket.handler_scan' + + +def _create_dispatcher(): + _HANDLER_ROOT = apache.main_server.get_options().get( + _PYOPT_HANDLER_ROOT, None) + if not _HANDLER_ROOT: + raise Exception('PythonOption %s is not defined' % _PYOPT_HANDLER_ROOT, + apache.APLOG_ERR) + _HANDLER_SCAN = apache.main_server.get_options().get( + _PYOPT_HANDLER_SCAN, _HANDLER_ROOT) + dispatcher = dispatch.Dispatcher(_HANDLER_ROOT, _HANDLER_SCAN) + for warning in dispatcher.source_warnings(): + apache.log_error('mod_pywebsocket: %s' % warning, apache.APLOG_WARNING) + return dispatcher + + +# Initialize +_dispatcher = _create_dispatcher() + + +def headerparserhandler(request): + """Handle request. + + Args: + request: mod_python request. + + This function is named headerparserhandler because it is the default name + for a PythonHeaderParserHandler. + """ + + try: + handshaker = handshake.Handshaker(request, _dispatcher) + handshaker.do_handshake() + request.log_error('mod_pywebsocket: resource: %r' % request.ws_resource, + apache.APLOG_DEBUG) + _dispatcher.transfer_data(request) + except handshake.HandshakeError, e: + # Handshake for ws/wss failed. + # But the request can be valid http/https request. + request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO) + return apache.DECLINED + except dispatch.DispatchError, e: + request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_WARNING) + return apache.DECLINED + return apache.DONE # Return DONE such that no other handlers are invoked. + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/msgutil.py b/WebKitTools/pywebsocket/mod_pywebsocket/msgutil.py new file mode 100644 index 0000000..bdb554d --- /dev/null +++ b/WebKitTools/pywebsocket/mod_pywebsocket/msgutil.py @@ -0,0 +1,223 @@ +# Copyright 2009, 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. + + +"""Message related utilities. + +Note: request.connection.write/read are used in this module, even though +mod_python document says that they should be used only in connection handlers. +Unfortunately, we have no other options. For example, request.write/read are +not suitable because they don't allow direct raw bytes writing/reading. +""" + + +import Queue +import threading + + +def send_message(request, message): + """Send message. + + Args: + request: mod_python request. + message: unicode string to send. + """ + + request.connection.write('\x00' + message.encode('utf-8') + '\xff') + + +def receive_message(request): + """Receive a Web Socket frame and return its payload as unicode string. + + Args: + request: mod_python request. + """ + + while True: + # Read 1 byte. + # mp_conn.read will block if no bytes are available. + # Timeout is controlled by TimeOut directive of Apache. + frame_type_str = request.connection.read(1) + frame_type = ord(frame_type_str[0]) + if (frame_type & 0x80) == 0x80: + # The payload length is specified in the frame. + # Read and discard. + length = _payload_length(request) + _receive_bytes(request, length) + else: + # The payload is delimited with \xff. + bytes = _read_until(request, '\xff') + message = bytes.decode('utf-8') + if frame_type == 0x00: + return message + # Discard data of other types. + + +def _payload_length(request): + length = 0 + while True: + b_str = request.connection.read(1) + b = ord(b_str[0]) + length = length * 128 + (b & 0x7f) + if (b & 0x80) == 0: + break + return length + + +def _receive_bytes(request, length): + bytes = [] + while length > 0: + new_bytes = request.connection.read(length) + bytes.append(new_bytes) + length -= len(new_bytes) + return ''.join(bytes) + + +def _read_until(request, delim_char): + bytes = [] + while True: + ch = request.connection.read(1) + if ch == delim_char: + break + bytes.append(ch) + return ''.join(bytes) + + +class MessageReceiver(threading.Thread): + """This class receives messages from the client. + + This class provides three ways to receive messages: blocking, non-blocking, + and via callback. Callback has the highest precedence. + + Note: This class should not be used with the standalone server for wss + because pyOpenSSL used by the server raises a fatal error if the socket + is accessed from multiple threads. + """ + def __init__(self, request, onmessage=None): + """Construct an instance. + + Args: + request: mod_python request. + onmessage: a function to be called when a message is received. + May be None. If not None, the function is called on + another thread. In that case, MessageReceiver.receive + and MessageReceiver.receive_nowait are useless because + they will never return any messages. + """ + threading.Thread.__init__(self) + self._request = request + self._queue = Queue.Queue() + self._onmessage = onmessage + self._stop_requested = False + self.setDaemon(True) + self.start() + + def run(self): + while not self._stop_requested: + message = receive_message(self._request) + if self._onmessage: + self._onmessage(message) + else: + self._queue.put(message) + + def receive(self): + """ Receive a message from the channel, blocking. + + Returns: + message as a unicode string. + """ + return self._queue.get() + + def receive_nowait(self): + """ Receive a message from the channel, non-blocking. + + Returns: + message as a unicode string if available. None otherwise. + """ + try: + message = self._queue.get_nowait() + except Queue.Empty: + message = None + return message + + def stop(self): + """Request to stop this instance. + + The instance will be stopped after receiving the next message. + This method may not be very useful, but there is no clean way + in Python to forcefully stop a running thread. + """ + self._stop_requested = True + + +class MessageSender(threading.Thread): + """This class sends messages to the client. + + This class provides both synchronous and asynchronous ways to send + messages. + + Note: This class should not be used with the standalone server for wss + because pyOpenSSL used by the server raises a fatal error if the socket + is accessed from multiple threads. + """ + def __init__(self, request): + """Construct an instance. + + Args: + request: mod_python request. + """ + threading.Thread.__init__(self) + self._request = request + self._queue = Queue.Queue() + self.setDaemon(True) + self.start() + + def run(self): + while True: + message, condition = self._queue.get() + condition.acquire() + send_message(self._request, message) + condition.notify() + condition.release() + + def send(self, message): + """Send a message, blocking.""" + + condition = threading.Condition() + condition.acquire() + self._queue.put((message, condition)) + condition.wait() + + def send_nowait(self, message): + """Send a message, non-blocking.""" + + self._queue.put((message, threading.Condition())) + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/standalone.py b/WebKitTools/pywebsocket/mod_pywebsocket/standalone.py new file mode 100644 index 0000000..a4c142b --- /dev/null +++ b/WebKitTools/pywebsocket/mod_pywebsocket/standalone.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python +# +# Copyright 2009, 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. + + +"""Standalone Web Socket server. + +Use this server to run mod_pywebsocket without Apache HTTP Server. + +Usage: + python standalone.py [-p <ws_port>] [-w <websock_handlers>] + [-s <scan_dir>] + [-d <document_root>] + +<ws_port> is the port number to use for ws:// connection. + +<document_root> is the path to the root directory of HTML files. + +<websock_handlers> is the path to the root directory of Web Socket handlers. +See __init__.py for details of <websock_handlers> and how to write Web Socket +handlers. If this path is relative, <document_root> is used as the base. + +<scan_dir> is a path under the root directory. If specified, only the handlers +under scan_dir are scanned. This is useful in saving scan time. + +Note: +This server is derived from SocketServer.ThreadingMixIn. Hence a thread is +used for each request. +""" + +import BaseHTTPServer +import SimpleHTTPServer +import SocketServer +import logging +import optparse +import os +import socket +import sys + +_HAS_OPEN_SSL = False +try: + import OpenSSL.SSL + _HAS_OPEN_SSL = True +except ImportError: + pass + +import dispatch +import handshake + + +class _StandaloneConnection(object): + """Mimic mod_python mp_conn.""" + + def __init__(self, request_handler): + """Construct an instance. + + Args: + request_handler: A WebSocketRequestHandler instance. + """ + self._request_handler = request_handler + + def get_local_addr(self): + """Getter to mimic mp_conn.local_addr.""" + return (self._request_handler.server.server_name, + self._request_handler.server.server_port) + local_addr = property(get_local_addr) + + def write(self, data): + """Mimic mp_conn.write().""" + return self._request_handler.wfile.write(data) + + def read(self, length): + """Mimic mp_conn.read().""" + return self._request_handler.rfile.read(length) + + +class _StandaloneRequest(object): + """Mimic mod_python request.""" + + def __init__(self, request_handler, use_tls): + """Construct an instance. + + Args: + request_handler: A WebSocketRequestHandler instance. + """ + self._request_handler = request_handler + self.connection = _StandaloneConnection(request_handler) + self._use_tls = use_tls + + def get_uri(self): + """Getter to mimic request.uri.""" + return self._request_handler.path + uri = property(get_uri) + + def get_headers_in(self): + """Getter to mimic request.headers_in.""" + return self._request_handler.headers + headers_in = property(get_headers_in) + + def is_https(self): + """Mimic request.is_https().""" + return self._use_tls + + +class WebSocketServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): + """HTTPServer specialized for Web Socket.""" + + SocketServer.ThreadingMixIn.daemon_threads = True + + def __init__(self, server_address, RequestHandlerClass): + """Override SocketServer.BaseServer.__init__.""" + + SocketServer.BaseServer.__init__( + self, server_address, RequestHandlerClass) + self.socket = self._create_socket() + self.server_bind() + self.server_activate() + + def _create_socket(self): + socket_ = socket.socket(self.address_family, self.socket_type) + if WebSocketServer.options.use_tls: + ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) + ctx.use_privatekey_file(WebSocketServer.options.private_key) + ctx.use_certificate_file(WebSocketServer.options.certificate) + socket_ = OpenSSL.SSL.Connection(ctx, socket_) + return socket_ + +class WebSocketRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): + """SimpleHTTPRequestHandler specialized for Web Socket.""" + + def setup(self): + """Override SocketServer.StreamRequestHandler.setup.""" + + self.connection = self.request + self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) + self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) + + def __init__(self, *args, **keywords): + self._request = _StandaloneRequest( + self, WebSocketRequestHandler.options.use_tls) + self._dispatcher = dispatch.Dispatcher( + WebSocketRequestHandler.options.websock_handlers, + WebSocketRequestHandler.options.scan_dir) + self._print_warnings_if_any() + self._handshaker = handshake.Handshaker(self._request, + self._dispatcher) + SimpleHTTPServer.SimpleHTTPRequestHandler.__init__( + self, *args, **keywords) + + def _print_warnings_if_any(self): + warnings = self._dispatcher.source_warnings() + if warnings: + for warning in warnings: + logging.warning('mod_pywebsocket: %s' % warning) + + def parse_request(self): + """Override BaseHTTPServer.BaseHTTPRequestHandler.parse_request. + + Return True to continue processing for HTTP(S), False otherwise. + """ + result = SimpleHTTPServer.SimpleHTTPRequestHandler.parse_request(self) + if result: + try: + self._handshaker.do_handshake() + self._dispatcher.transfer_data(self._request) + return False + except handshake.HandshakeError, e: + # Handshake for ws(s) failed. Assume http(s). + logging.info('mod_pywebsocket: %s' % e) + return True + except dispatch.DispatchError, e: + logging.warning('mod_pywebsocket: %s' % e) + return False + return result + + +def _main(): + logging.basicConfig() + + parser = optparse.OptionParser() + parser.add_option('-p', '--port', dest='port', type='int', + default=handshake._DEFAULT_WEB_SOCKET_PORT, + help='port to listen to') + parser.add_option('-w', '--websock_handlers', dest='websock_handlers', + default='.', + help='Web Socket handlers root directory.') + parser.add_option('-s', '--scan_dir', dest='scan_dir', + default=None, + help=('Web Socket handlers scan directory. ' + 'Must be a directory under websock_handlers.')) + parser.add_option('-d', '--document_root', dest='document_root', + default='.', + help='Document root directory.') + parser.add_option('-t', '--tls', dest='use_tls', action='store_true', + default=False, help='use TLS (wss://)') + parser.add_option('-k', '--private_key', dest='private_key', + default='', help='TLS private key file.') + parser.add_option('-c', '--certificate', dest='certificate', + default='', help='TLS certificate file.') + options = parser.parse_args()[0] + + if options.use_tls: + if not _HAS_OPEN_SSL: + print >>sys.stderr, 'To use TLS, install pyOpenSSL.' + sys.exit(1) + if not options.private_key or not options.certificate: + print >>sys.stderr, ('To use TLS, specify private_key and ' + 'certificate.') + sys.exit(1) + + if not options.scan_dir: + options.scan_dir = options.websock_handlers + + WebSocketRequestHandler.options = options + WebSocketServer.options = options + + os.chdir(options.document_root) + + server = WebSocketServer(('', options.port), WebSocketRequestHandler) + server.serve_forever() + + +if __name__ == '__main__': + _main() + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/mod_pywebsocket/util.py b/WebKitTools/pywebsocket/mod_pywebsocket/util.py new file mode 100644 index 0000000..4835298 --- /dev/null +++ b/WebKitTools/pywebsocket/mod_pywebsocket/util.py @@ -0,0 +1,52 @@ +# Copyright 2009, 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. + + +"""Web Sockets utilities. +""" + + +import StringIO +import traceback + + +def get_stack_trace(): + """Get the current stack trace as string. + + This is needed to support Python 2.3. + TODO: Remove this when we only support Python 2.4 and above. + Use traceback.format_exc instead. + """ + + out = StringIO.StringIO() + traceback.print_exc(file=out) + return out.getvalue() + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/setup.py b/WebKitTools/pywebsocket/setup.py new file mode 100644 index 0000000..1810a6d --- /dev/null +++ b/WebKitTools/pywebsocket/setup.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# +# Copyright 2009, 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. + + +"""Set up script for mod_pywebsocket. +""" + + +from distutils.core import setup +import sys + + +_PACKAGE_NAME = 'mod_pywebsocket' + +if sys.version < '2.3': + print >>sys.stderr, '%s requires Python 2.3 or later.' % _PACKAGE_NAME + sys.exit(1) + +setup(author='Yuzo Fujishima', + author_email='yuzo@chromium.org', + description='Web Socket extension for Apache HTTP Server.', + long_description=( + 'mod_pywebsocket is an Apache HTTP Server extension for ' + 'Web Socket (http://tools.ietf.org/html/' + 'draft-hixie-thewebsocketprotocol). ' + 'See mod_pywebsocket/__init__.py for more detail.'), + license='See COPYING', + name=_PACKAGE_NAME, + packages=[_PACKAGE_NAME], + url='http://code.google.com/p/pywebsocket/', + version='0.4.1', + ) + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/config.py b/WebKitTools/pywebsocket/test/config.py new file mode 100644 index 0000000..5aaab8c --- /dev/null +++ b/WebKitTools/pywebsocket/test/config.py @@ -0,0 +1,45 @@ +# Copyright 2009, 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. + + +"""Configuration for testing. + +Test files should import this module before mod_pywebsocket. +""" + + +import os +import sys + + +# Add the parent directory to sys.path to enable importing mod_pywebsocket. +sys.path += [os.path.join(os.path.split(__file__)[0], '..')] + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/mock.py b/WebKitTools/pywebsocket/test/mock.py new file mode 100644 index 0000000..3b85d64 --- /dev/null +++ b/WebKitTools/pywebsocket/test/mock.py @@ -0,0 +1,205 @@ +# Copyright 2009, 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. + + +"""Mocks for testing. +""" + + +import Queue +import threading + + +class _MockConnBase(object): + """Base class of mocks for mod_python.apache.mp_conn. + + This enables tests to check what is written to a (mock) mp_conn. + """ + + def __init__(self): + self._write_data = [] + + def write(self, data): + """Override mod_python.apache.mp_conn.write.""" + + self._write_data.append(data) + + def written_data(self): + """Get bytes written to this mock.""" + + return ''.join(self._write_data) + + +class MockConn(_MockConnBase): + """Mock for mod_python.apache.mp_conn. + + This enables tests to specify what should be read from a (mock) mp_conn as + well as to check what is written to it. + """ + + def __init__(self, read_data): + """Constructs an instance. + + Args: + read_data: bytes that should be returned when read* methods are + called. + """ + + _MockConnBase.__init__(self) + self._read_data = read_data + self._read_pos = 0 + + def readline(self): + """Override mod_python.apache.mp_conn.readline.""" + + if self._read_pos >= len(self._read_data): + return '' + end_index = self._read_data.find('\n', self._read_pos) + 1 + if not end_index: + end_index = len(self._read_data) + return self._read_up_to(end_index) + + def read(self, length): + """Override mod_python.apache.mp_conn.read.""" + + if self._read_pos >= len(self._read_data): + return '' + end_index = min(len(self._read_data), self._read_pos + length) + return self._read_up_to(end_index) + + def _read_up_to(self, end_index): + line = self._read_data[self._read_pos:end_index] + self._read_pos = end_index + return line + + +class MockBlockingConn(_MockConnBase): + """Blocking mock for mod_python.apache.mp_conn. + + This enables tests to specify what should be read from a (mock) mp_conn as + well as to check what is written to it. + Callers of read* methods will block if there is no bytes available. + """ + + def __init__(self): + _MockConnBase.__init__(self) + self._queue = Queue.Queue() + + def readline(self): + """Override mod_python.apache.mp_conn.readline.""" + line = '' + while True: + c = self._queue.get() + line += c + if c == '\n': + return line + + def read(self, length): + """Override mod_python.apache.mp_conn.read.""" + + data = '' + for unused in range(length): + data += self._queue.get() + return data + + def put_bytes(self, bytes): + """Put bytes to be read from this mock. + + Args: + bytes: bytes to be read. + """ + + for byte in bytes: + self._queue.put(byte) + + +class MockTable(dict): + """Mock table. + + This mimics mod_python mp_table. Note that only the methods used by + tests are overridden. + """ + + def __init__(self, copy_from={}): + if isinstance(copy_from, dict): + copy_from = copy_from.items() + for key, value in copy_from: + self.__setitem__(key, value) + + def __getitem__(self, key): + return super(MockTable, self).__getitem__(key.lower()) + + def __setitem__(self, key, value): + super(MockTable, self).__setitem__(key.lower(), value) + + def get(self, key, def_value=None): + return super(MockTable, self).get(key.lower(), def_value) + + +class MockRequest(object): + """Mock request. + + This mimics mod_python request. + """ + + def __init__(self, uri=None, headers_in={}, connection=None, + is_https=False): + """Construct an instance. + + Arguments: + uri: URI of the request. + headers_in: Request headers. + connection: Connection used for the request. + is_https: Whether this request is over SSL. + + See the document of mod_python Request for details. + """ + self.uri = uri + self.connection = connection + self.headers_in = MockTable(headers_in) + # self.is_https_ needs to be accessible from tests. To avoid name + # conflict with self.is_https(), it is named as such. + self.is_https_ = is_https + + def is_https(self): + """Return whether this request is over SSL.""" + return self.is_https_ + + +class MockDispatcher(object): + """Mock for dispatch.Dispatcher.""" + + def do_extra_handshake(self, conn_context): + pass + + def transfer_data(self, conn_context): + pass + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/run_all.py b/WebKitTools/pywebsocket/test/run_all.py new file mode 100644 index 0000000..3885618 --- /dev/null +++ b/WebKitTools/pywebsocket/test/run_all.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# Copyright 2009, 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 tests in the same directory. +""" + + +import os +import re +import unittest + + +_TEST_MODULE_PATTERN = re.compile(r'^(test_.+)\.py$') + + +def _list_test_modules(directory): + module_names = [] + for filename in os.listdir(directory): + match = _TEST_MODULE_PATTERN.search(filename) + if match: + module_names.append(match.group(1)) + return module_names + + +def _suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromNames( + _list_test_modules(os.path.join(os.path.split(__file__)[0], '.'))) + + +if __name__ == '__main__': + unittest.main(defaultTest='_suite') + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/test_dispatch.py b/WebKitTools/pywebsocket/test/test_dispatch.py new file mode 100644 index 0000000..d617205 --- /dev/null +++ b/WebKitTools/pywebsocket/test/test_dispatch.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python +# +# Copyright 2009, 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. + + +"""Tests for dispatch module.""" + + + +import os +import unittest + +import config # This must be imported before mod_pywebsocket. +from mod_pywebsocket import dispatch + +import mock + + +_TEST_HANDLERS_DIR = os.path.join( + os.path.split(__file__)[0], 'testdata', 'handlers') + +_TEST_HANDLERS_SUB_DIR = os.path.join(_TEST_HANDLERS_DIR, 'sub') + +class DispatcherTest(unittest.TestCase): + def test_normalize_path(self): + self.assertEqual(os.path.abspath('/a/b').replace('\\', '/'), + dispatch._normalize_path('/a/b')) + self.assertEqual(os.path.abspath('/a/b').replace('\\', '/'), + dispatch._normalize_path('\\a\\b')) + self.assertEqual(os.path.abspath('/a/b').replace('\\', '/'), + dispatch._normalize_path('/a/c/../b')) + self.assertEqual(os.path.abspath('abc').replace('\\', '/'), + dispatch._normalize_path('abc')) + + def test_converter(self): + converter = dispatch._path_to_resource_converter('/a/b') + self.assertEqual('/h', converter('/a/b/h_wsh.py')) + self.assertEqual('/c/h', converter('/a/b/c/h_wsh.py')) + self.assertEqual(None, converter('/a/b/h.py')) + self.assertEqual(None, converter('a/b/h_wsh.py')) + + converter = dispatch._path_to_resource_converter('a/b') + self.assertEqual('/h', converter('a/b/h_wsh.py')) + + converter = dispatch._path_to_resource_converter('/a/b///') + self.assertEqual('/h', converter('/a/b/h_wsh.py')) + self.assertEqual('/h', converter('/a/b/../b/h_wsh.py')) + + converter = dispatch._path_to_resource_converter('/a/../a/b/../b/') + self.assertEqual('/h', converter('/a/b/h_wsh.py')) + + converter = dispatch._path_to_resource_converter(r'\a\b') + self.assertEqual('/h', converter(r'\a\b\h_wsh.py')) + self.assertEqual('/h', converter(r'/a/b/h_wsh.py')) + + def test_source_file_paths(self): + paths = list(dispatch._source_file_paths(_TEST_HANDLERS_DIR)) + paths.sort() + self.assertEqual(7, len(paths)) + expected_paths = [ + os.path.join(_TEST_HANDLERS_DIR, 'blank_wsh.py'), + os.path.join(_TEST_HANDLERS_DIR, 'origin_check_wsh.py'), + os.path.join(_TEST_HANDLERS_DIR, 'sub', + 'exception_in_transfer_wsh.py'), + os.path.join(_TEST_HANDLERS_DIR, 'sub', 'non_callable_wsh.py'), + os.path.join(_TEST_HANDLERS_DIR, 'sub', 'plain_wsh.py'), + os.path.join(_TEST_HANDLERS_DIR, 'sub', + 'wrong_handshake_sig_wsh.py'), + os.path.join(_TEST_HANDLERS_DIR, 'sub', + 'wrong_transfer_sig_wsh.py'), + ] + for expected, actual in zip(expected_paths, paths): + self.assertEqual(expected, actual) + + def test_source(self): + self.assertRaises(dispatch.DispatchError, dispatch._source, '') + self.assertRaises(dispatch.DispatchError, dispatch._source, 'def') + self.assertRaises(dispatch.DispatchError, dispatch._source, '1/0') + self.failUnless(dispatch._source( + 'def web_socket_do_extra_handshake(request):pass\n' + 'def web_socket_transfer_data(request):pass\n')) + + def test_source_warnings(self): + dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) + warnings = dispatcher.source_warnings() + warnings.sort() + expected_warnings = [ + (os.path.join(_TEST_HANDLERS_DIR, 'blank_wsh.py') + + ': web_socket_do_extra_handshake is not defined.'), + (os.path.join(_TEST_HANDLERS_DIR, 'sub', + 'non_callable_wsh.py') + + ': web_socket_do_extra_handshake is not callable.'), + (os.path.join(_TEST_HANDLERS_DIR, 'sub', + 'wrong_handshake_sig_wsh.py') + + ': web_socket_do_extra_handshake is not defined.'), + (os.path.join(_TEST_HANDLERS_DIR, 'sub', + 'wrong_transfer_sig_wsh.py') + + ': web_socket_transfer_data is not defined.'), + ] + self.assertEquals(4, len(warnings)) + for expected, actual in zip(expected_warnings, warnings): + self.assertEquals(expected, actual) + + def test_do_extra_handshake(self): + dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) + request = mock.MockRequest() + request.ws_resource = '/origin_check' + request.ws_origin = 'http://example.com' + dispatcher.do_extra_handshake(request) # Must not raise exception. + + request.ws_origin = 'http://bad.example.com' + self.assertRaises(dispatch.DispatchError, + dispatcher.do_extra_handshake, request) + + def test_transfer_data(self): + dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) + request = mock.MockRequest(connection=mock.MockConn('')) + request.ws_resource = '/origin_check' + request.ws_protocol = 'p1' + + dispatcher.transfer_data(request) + self.assertEqual('origin_check_wsh.py is called for /origin_check, p1', + request.connection.written_data()) + + request = mock.MockRequest(connection=mock.MockConn('')) + request.ws_resource = '/sub/plain' + request.ws_protocol = None + dispatcher.transfer_data(request) + self.assertEqual('sub/plain_wsh.py is called for /sub/plain, None', + request.connection.written_data()) + + def test_transfer_data_no_handler(self): + dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) + for resource in ['/blank', '/sub/non_callable', + '/sub/no_wsh_at_the_end', '/does/not/exist']: + request = mock.MockRequest(connection=mock.MockConn('')) + request.ws_resource = resource + request.ws_protocol = 'p2' + try: + dispatcher.transfer_data(request) + self.fail() + except dispatch.DispatchError, e: + self.failUnless(str(e).find('No handler') != -1) + except Exception: + self.fail() + + def test_transfer_data_handler_exception(self): + dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) + request = mock.MockRequest(connection=mock.MockConn('')) + request.ws_resource = '/sub/exception_in_transfer' + request.ws_protocol = 'p3' + try: + dispatcher.transfer_data(request) + self.fail() + except dispatch.DispatchError, e: + self.failUnless(str(e).find('Intentional') != -1) + except Exception: + self.fail() + + def test_scan_dir(self): + disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) + self.assertEqual(3, len(disp._handlers)) + self.failUnless(disp._handlers.has_key('/origin_check')) + self.failUnless(disp._handlers.has_key('/sub/exception_in_transfer')) + self.failUnless(disp._handlers.has_key('/sub/plain')) + + def test_scan_sub_dir(self): + disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, _TEST_HANDLERS_SUB_DIR) + self.assertEqual(2, len(disp._handlers)) + self.failIf(disp._handlers.has_key('/origin_check')) + self.failUnless(disp._handlers.has_key('/sub/exception_in_transfer')) + self.failUnless(disp._handlers.has_key('/sub/plain')) + + def test_scan_sub_dir_as_root(self): + disp = dispatch.Dispatcher(_TEST_HANDLERS_SUB_DIR, + _TEST_HANDLERS_SUB_DIR) + self.assertEqual(2, len(disp._handlers)) + self.failIf(disp._handlers.has_key('/origin_check')) + self.failIf(disp._handlers.has_key('/sub/exception_in_transfer')) + self.failIf(disp._handlers.has_key('/sub/plain')) + self.failUnless(disp._handlers.has_key('/exception_in_transfer')) + self.failUnless(disp._handlers.has_key('/plain')) + + def test_scan_dir_must_under_root(self): + dispatch.Dispatcher('a/b', 'a/b/c') # OK + dispatch.Dispatcher('a/b///', 'a/b') # OK + self.assertRaises(dispatch.DispatchError, + dispatch.Dispatcher, 'a/b/c', 'a/b') + + +if __name__ == '__main__': + unittest.main() + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/test_handshake.py b/WebKitTools/pywebsocket/test/test_handshake.py new file mode 100644 index 0000000..dd1f65c --- /dev/null +++ b/WebKitTools/pywebsocket/test/test_handshake.py @@ -0,0 +1,316 @@ +#!/usr/bin/env python +# +# Copyright 2009, 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. + + +"""Tests for handshake module.""" + + +import unittest + +import config # This must be imported before mod_pywebsocket. +from mod_pywebsocket import handshake + +import mock + + +_GOOD_REQUEST = ( + 80, + '/demo', + { + 'Upgrade':'WebSocket', + 'Connection':'Upgrade', + 'Host':'example.com', + 'Origin':'http://example.com', + 'WebSocket-Protocol':'sample', + } +) + +_GOOD_RESPONSE_DEFAULT_PORT = ( + 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + 'Upgrade: WebSocket\r\n' + 'Connection: Upgrade\r\n' + 'WebSocket-Origin: http://example.com\r\n' + 'WebSocket-Location: ws://example.com/demo\r\n' + 'WebSocket-Protocol: sample\r\n' + '\r\n') + +_GOOD_RESPONSE_SECURE = ( + 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + 'Upgrade: WebSocket\r\n' + 'Connection: Upgrade\r\n' + 'WebSocket-Origin: http://example.com\r\n' + 'WebSocket-Location: wss://example.com/demo\r\n' + 'WebSocket-Protocol: sample\r\n' + '\r\n') + +_GOOD_REQUEST_NONDEFAULT_PORT = ( + 8081, + '/demo', + { + 'Upgrade':'WebSocket', + 'Connection':'Upgrade', + 'Host':'example.com:8081', + 'Origin':'http://example.com', + 'WebSocket-Protocol':'sample', + } +) + +_GOOD_RESPONSE_NONDEFAULT_PORT = ( + 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + 'Upgrade: WebSocket\r\n' + 'Connection: Upgrade\r\n' + 'WebSocket-Origin: http://example.com\r\n' + 'WebSocket-Location: ws://example.com:8081/demo\r\n' + 'WebSocket-Protocol: sample\r\n' + '\r\n') + +_GOOD_RESPONSE_SECURE_NONDEF = ( + 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + 'Upgrade: WebSocket\r\n' + 'Connection: Upgrade\r\n' + 'WebSocket-Origin: http://example.com\r\n' + 'WebSocket-Location: wss://example.com:8081/demo\r\n' + 'WebSocket-Protocol: sample\r\n' + '\r\n') + +_GOOD_REQUEST_NO_PROTOCOL = ( + 80, + '/demo', + { + 'Upgrade':'WebSocket', + 'Connection':'Upgrade', + 'Host':'example.com', + 'Origin':'http://example.com', + } +) + +_GOOD_RESPONSE_NO_PROTOCOL = ( + 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + 'Upgrade: WebSocket\r\n' + 'Connection: Upgrade\r\n' + 'WebSocket-Origin: http://example.com\r\n' + 'WebSocket-Location: ws://example.com/demo\r\n' + '\r\n') + +_GOOD_REQUEST_WITH_OPTIONAL_HEADERS = ( + 80, + '/demo', + { + 'Upgrade':'WebSocket', + 'Connection':'Upgrade', + 'Host':'example.com', + 'Origin':'http://example.com', + 'WebSocket-Protocol':'sample', + 'AKey':'AValue', + 'EmptyValue':'', + } +) + +_BAD_REQUESTS = ( + ( # HTTP request + 80, + '/demo', + { + 'Host':'www.google.com', + 'User-Agent':'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5;' + ' en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3' + ' GTB6 GTBA', + 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,' + '*/*;q=0.8', + 'Accept-Language':'en-us,en;q=0.5', + 'Accept-Encoding':'gzip,deflate', + 'Accept-Charset':'ISO-8859-1,utf-8;q=0.7,*;q=0.7', + 'Keep-Alive':'300', + 'Connection':'keep-alive', + } + ), + ( # Missing Upgrade + 80, + '/demo', + { + 'Connection':'Upgrade', + 'Host':'example.com', + 'Origin':'http://example.com', + 'WebSocket-Protocol':'sample', + } + ), + ( # Wrong Upgrade + 80, + '/demo', + { + 'Upgrade':'NonWebSocket', + 'Connection':'Upgrade', + 'Host':'example.com', + 'Origin':'http://example.com', + 'WebSocket-Protocol':'sample', + } + ), + ( # Empty WebSocket-Protocol + 80, + '/demo', + { + 'Upgrade':'WebSocket', + 'Connection':'Upgrade', + 'Host':'example.com', + 'Origin':'http://example.com', + 'WebSocket-Protocol':'', + } + ), + ( # Wrong port number format + 80, + '/demo', + { + 'Upgrade':'WebSocket', + 'Connection':'Upgrade', + 'Host':'example.com:0x50', + 'Origin':'http://example.com', + 'WebSocket-Protocol':'sample', + } + ), + ( # Header/connection port mismatch + 8080, + '/demo', + { + 'Upgrade':'WebSocket', + 'Connection':'Upgrade', + 'Host':'example.com', + 'Origin':'http://example.com', + 'WebSocket-Protocol':'sample', + } + ), + ( # Illegal WebSocket-Protocol + 80, + '/demo', + { + 'Upgrade':'WebSocket', + 'Connection':'Upgrade', + 'Host':'example.com', + 'Origin':'http://example.com', + 'WebSocket-Protocol':'illegal protocol', + } + ), +) + + +def _create_request(request_def): + conn = mock.MockConn('') + conn.local_addr = ('0.0.0.0', request_def[0]) + return mock.MockRequest( + uri=request_def[1], + headers_in=request_def[2], + connection=conn) + + +class HandshakerTest(unittest.TestCase): + def test_validate_protocol(self): + handshake._validate_protocol('sample') # should succeed. + handshake._validate_protocol('Sample') # should succeed. + self.assertRaises(handshake.HandshakeError, + handshake._validate_protocol, + 'sample protocol') + self.assertRaises(handshake.HandshakeError, + handshake._validate_protocol, + # "Japan" in Japanese + u'\u65e5\u672c') + + def test_good_request_default_port(self): + request = _create_request(_GOOD_REQUEST) + handshaker = handshake.Handshaker(request, + mock.MockDispatcher()) + handshaker.do_handshake() + self.assertEqual(_GOOD_RESPONSE_DEFAULT_PORT, + request.connection.written_data()) + self.assertEqual('/demo', request.ws_resource) + self.assertEqual('http://example.com', request.ws_origin) + self.assertEqual('ws://example.com/demo', request.ws_location) + self.assertEqual('sample', request.ws_protocol) + + def test_good_request_secure_default_port(self): + request = _create_request(_GOOD_REQUEST) + request.connection.local_addr = ('0.0.0.0', 443) + request.is_https_ = True + handshaker = handshake.Handshaker(request, + mock.MockDispatcher()) + handshaker.do_handshake() + self.assertEqual(_GOOD_RESPONSE_SECURE, + request.connection.written_data()) + self.assertEqual('sample', request.ws_protocol) + + def test_good_request_nondefault_port(self): + request = _create_request(_GOOD_REQUEST_NONDEFAULT_PORT) + handshaker = handshake.Handshaker(request, + mock.MockDispatcher()) + handshaker.do_handshake() + self.assertEqual(_GOOD_RESPONSE_NONDEFAULT_PORT, + request.connection.written_data()) + self.assertEqual('sample', request.ws_protocol) + + def test_good_request_secure_non_default_port(self): + request = _create_request(_GOOD_REQUEST_NONDEFAULT_PORT) + request.is_https_ = True + handshaker = handshake.Handshaker(request, + mock.MockDispatcher()) + handshaker.do_handshake() + self.assertEqual(_GOOD_RESPONSE_SECURE_NONDEF, + request.connection.written_data()) + self.assertEqual('sample', request.ws_protocol) + + def test_good_request_default_no_protocol(self): + request = _create_request(_GOOD_REQUEST_NO_PROTOCOL) + handshaker = handshake.Handshaker(request, + mock.MockDispatcher()) + handshaker.do_handshake() + self.assertEqual(_GOOD_RESPONSE_NO_PROTOCOL, + request.connection.written_data()) + self.assertEqual(None, request.ws_protocol) + + def test_good_request_optional_headers(self): + request = _create_request(_GOOD_REQUEST_WITH_OPTIONAL_HEADERS) + handshaker = handshake.Handshaker(request, + mock.MockDispatcher()) + handshaker.do_handshake() + self.assertEqual('AValue', + request.headers_in['AKey']) + self.assertEqual('', + request.headers_in['EmptyValue']) + + def test_bad_requests(self): + for request in map(_create_request, _BAD_REQUESTS): + handshaker = handshake.Handshaker(request, + mock.MockDispatcher()) + self.assertRaises(handshake.HandshakeError, handshaker.do_handshake) + + +if __name__ == '__main__': + unittest.main() + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/test_mock.py b/WebKitTools/pywebsocket/test/test_mock.py new file mode 100644 index 0000000..8b137d1 --- /dev/null +++ b/WebKitTools/pywebsocket/test/test_mock.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +# +# Copyright 2009, 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. + + +"""Tests for mock module.""" + + +import Queue +import threading +import unittest + +import mock + + +class MockConnTest(unittest.TestCase): + def setUp(self): + self._conn = mock.MockConn('ABC\r\nDEFG\r\n\r\nHIJK') + + def test_readline(self): + self.assertEqual('ABC\r\n', self._conn.readline()) + self.assertEqual('DEFG\r\n', self._conn.readline()) + self.assertEqual('\r\n', self._conn.readline()) + self.assertEqual('HIJK', self._conn.readline()) + self.assertEqual('', self._conn.readline()) + + def test_read(self): + self.assertEqual('ABC\r\nD', self._conn.read(6)) + self.assertEqual('EFG\r\n\r\nHI', self._conn.read(9)) + self.assertEqual('JK', self._conn.read(10)) + self.assertEqual('', self._conn.read(10)) + + def test_read_and_readline(self): + self.assertEqual('ABC\r\nD', self._conn.read(6)) + self.assertEqual('EFG\r\n', self._conn.readline()) + self.assertEqual('\r\nHIJK', self._conn.read(9)) + self.assertEqual('', self._conn.readline()) + + def test_write(self): + self._conn.write('Hello\r\n') + self._conn.write('World\r\n') + self.assertEqual('Hello\r\nWorld\r\n', self._conn.written_data()) + + +class MockBlockingConnTest(unittest.TestCase): + def test_read(self): + class LineReader(threading.Thread): + def __init__(self, conn, queue): + threading.Thread.__init__(self) + self._queue = queue + self._conn = conn + self.setDaemon(True) + self.start() + def run(self): + while True: + data = self._conn.readline() + self._queue.put(data) + conn = mock.MockBlockingConn() + queue = Queue.Queue() + reader = LineReader(conn, queue) + self.failUnless(queue.empty()) + conn.put_bytes('Foo bar\r\n') + read = queue.get() + self.assertEqual('Foo bar\r\n', read) + + +class MockTableTest(unittest.TestCase): + def test_create_from_dict(self): + table = mock.MockTable({'Key':'Value'}) + self.assertEqual('Value', table.get('KEY')) + self.assertEqual('Value', table['key']) + + def test_create_from_list(self): + table = mock.MockTable([('Key', 'Value')]) + self.assertEqual('Value', table.get('KEY')) + self.assertEqual('Value', table['key']) + + def test_create_from_tuple(self): + table = mock.MockTable((('Key', 'Value'),)) + self.assertEqual('Value', table.get('KEY')) + self.assertEqual('Value', table['key']) + + def test_set_and_get(self): + table = mock.MockTable() + self.assertEqual(None, table.get('Key')) + table['Key'] = 'Value' + self.assertEqual('Value', table.get('Key')) + self.assertEqual('Value', table.get('key')) + self.assertEqual('Value', table.get('KEY')) + self.assertEqual('Value', table['Key']) + self.assertEqual('Value', table['key']) + self.assertEqual('Value', table['KEY']) + + +if __name__ == '__main__': + unittest.main() + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/test_msgutil.py b/WebKitTools/pywebsocket/test/test_msgutil.py new file mode 100644 index 0000000..b3ba539 --- /dev/null +++ b/WebKitTools/pywebsocket/test/test_msgutil.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# +# Copyright 2009, 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. + + +"""Tests for msgutil module.""" + + +import Queue +import unittest + +import config # This must be imported before mod_pywebsocket. +from mod_pywebsocket import msgutil + +import mock + + +def _create_request(read_data): + return mock.MockRequest(connection=mock.MockConn(read_data)) + +def _create_blocking_request(): + return mock.MockRequest(connection=mock.MockBlockingConn()) + +class MessageTest(unittest.TestCase): + def test_send_message(self): + request = _create_request('') + msgutil.send_message(request, 'Hello') + self.assertEqual('\x00Hello\xff', request.connection.written_data()) + + def test_send_message_unicode(self): + request = _create_request('') + msgutil.send_message(request, u'\u65e5') + # U+65e5 is encoded as e6,97,a5 in UTF-8 + self.assertEqual('\x00\xe6\x97\xa5\xff', + request.connection.written_data()) + + def test_receive_message(self): + request = _create_request('\x00Hello\xff\x00World!\xff') + self.assertEqual('Hello', msgutil.receive_message(request)) + self.assertEqual('World!', msgutil.receive_message(request)) + + def test_receive_message_unicode(self): + request = _create_request('\x00\xe6\x9c\xac\xff') + # U+672c is encoded as e6,9c,ac in UTF-8 + self.assertEqual(u'\u672c', msgutil.receive_message(request)) + + def test_receive_message_discard(self): + request = _create_request('\x80\x06IGNORE\x00Hello\xff' + '\x01DISREGARD\xff\x00World!\xff') + self.assertEqual('Hello', msgutil.receive_message(request)) + self.assertEqual('World!', msgutil.receive_message(request)) + + def test_payload_length(self): + for length, bytes in ((0, '\x00'), (0x7f, '\x7f'), (0x80, '\x81\x00'), + (0x1234, '\x80\xa4\x34')): + self.assertEqual(length, + msgutil._payload_length(_create_request(bytes))) + + def test_receive_bytes(self): + request = _create_request('abcdefg') + self.assertEqual('abc', msgutil._receive_bytes(request, 3)) + self.assertEqual('defg', msgutil._receive_bytes(request, 4)) + + def test_read_until(self): + request = _create_request('abcXdefgX') + self.assertEqual('abc', msgutil._read_until(request, 'X')) + self.assertEqual('defg', msgutil._read_until(request, 'X')) + + +class MessageReceiverTest(unittest.TestCase): + def test_queue(self): + request = _create_blocking_request() + receiver = msgutil.MessageReceiver(request) + + self.assertEqual(None, receiver.receive_nowait()) + + request.connection.put_bytes('\x00Hello!\xff') + self.assertEqual('Hello!', receiver.receive()) + + def test_onmessage(self): + onmessage_queue = Queue.Queue() + def onmessage_handler(message): + onmessage_queue.put(message) + + request = _create_blocking_request() + receiver = msgutil.MessageReceiver(request, onmessage_handler) + + request.connection.put_bytes('\x00Hello!\xff') + self.assertEqual('Hello!', onmessage_queue.get()) + + +class MessageSenderTest(unittest.TestCase): + def test_send(self): + request = _create_blocking_request() + sender = msgutil.MessageSender(request) + + sender.send('World') + self.assertEqual('\x00World\xff', request.connection.written_data()) + + def test_send_nowait(self): + # Use a queue to check the bytes written by MessageSender. + # request.connection.written_data() cannot be used here because + # MessageSender runs in a separate thread. + send_queue = Queue.Queue() + def write(bytes): + send_queue.put(bytes) + request = _create_blocking_request() + request.connection.write = write + + sender = msgutil.MessageSender(request) + + sender.send_nowait('Hello') + sender.send_nowait('World') + self.assertEqual('\x00Hello\xff', send_queue.get()) + self.assertEqual('\x00World\xff', send_queue.get()) + + +if __name__ == '__main__': + unittest.main() + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/test_util.py b/WebKitTools/pywebsocket/test/test_util.py new file mode 100644 index 0000000..8058b6d --- /dev/null +++ b/WebKitTools/pywebsocket/test/test_util.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# +# Copyright 2009, 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. + + +"""Tests for util module.""" + + +import unittest + +import config # This must be imported before mod_pywebsocket. +from mod_pywebsocket import util + + +class UtilTest(unittest.TestCase): + def test_get_stack_trace(self): + self.assertEqual('None\n', util.get_stack_trace()) + try: + a = 1 / 0 # Intentionally raise exception. + except Exception: + trace = util.get_stack_trace() + self.failUnless(trace.startswith('Traceback')) + self.failUnless(trace.find('ZeroDivisionError') != -1) + + +if __name__ == '__main__': + unittest.main() + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/blank_wsh.py b/WebKitTools/pywebsocket/test/testdata/handlers/blank_wsh.py new file mode 100644 index 0000000..7f87c6a --- /dev/null +++ b/WebKitTools/pywebsocket/test/testdata/handlers/blank_wsh.py @@ -0,0 +1,31 @@ +# Copyright 2009, 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. + + +# intentionally left blank diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/origin_check_wsh.py b/WebKitTools/pywebsocket/test/testdata/handlers/origin_check_wsh.py new file mode 100644 index 0000000..2c139fa --- /dev/null +++ b/WebKitTools/pywebsocket/test/testdata/handlers/origin_check_wsh.py @@ -0,0 +1,42 @@ +# Copyright 2009, 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. + + +def web_socket_do_extra_handshake(request): + if request.ws_origin == 'http://example.com': + return + raise ValueError('Unacceptable origin: %r' % request.ws_origin) + + +def web_socket_transfer_data(request): + request.connection.write('origin_check_wsh.py is called for %s, %s' % + (request.ws_resource, request.ws_protocol)) + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py b/WebKitTools/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py new file mode 100644 index 0000000..b982d02 --- /dev/null +++ b/WebKitTools/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py @@ -0,0 +1,44 @@ +# Copyright 2009, 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. + + +"""Exception in web_socket_transfer_data(). +""" + + +def web_socket_do_extra_handshake(request): + pass + + +def web_socket_transfer_data(request): + raise Exception('Intentional Exception for %s, %s' % + (request.ws_resource, request.ws_protocol)) + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py b/WebKitTools/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py new file mode 100644 index 0000000..17e7be1 --- /dev/null +++ b/WebKitTools/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py @@ -0,0 +1,45 @@ +# Copyright 2009, 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. + + +"""Correct signatures, wrong file name. +""" + + +def web_socket_do_extra_handshake(request): + pass + + +def web_socket_transfer_data(request): + request.connection.write( + 'sub/no_wsh_at_the_end.py is called for %s, %s' % + (request.ws_resource, request.ws_protocol)) + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py b/WebKitTools/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py new file mode 100644 index 0000000..26352eb --- /dev/null +++ b/WebKitTools/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py @@ -0,0 +1,39 @@ +# Copyright 2009, 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. + + +"""Non-callable handlers. +""" + + +web_socket_do_extra_handshake = True +web_socket_transfer_data = 1 + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/sub/plain_wsh.py b/WebKitTools/pywebsocket/test/testdata/handlers/sub/plain_wsh.py new file mode 100644 index 0000000..db3ff69 --- /dev/null +++ b/WebKitTools/pywebsocket/test/testdata/handlers/sub/plain_wsh.py @@ -0,0 +1,40 @@ +# Copyright 2009, 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. + + +def web_socket_do_extra_handshake(request): + pass + + +def web_socket_transfer_data(request): + request.connection.write('sub/plain_wsh.py is called for %s, %s' % + (request.ws_resource, request.ws_protocol)) + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py b/WebKitTools/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py new file mode 100644 index 0000000..6bf659b --- /dev/null +++ b/WebKitTools/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py @@ -0,0 +1,45 @@ +# Copyright 2009, 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. + + +"""Wrong web_socket_do_extra_handshake signature. +""" + + +def no_web_socket_do_extra_handshake(request): + pass + + +def web_socket_transfer_data(request): + request.connection.write( + 'sub/wrong_handshake_sig_wsh.py is called for %s, %s' % + (request.ws_resource, request.ws_protocol)) + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py b/WebKitTools/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py new file mode 100644 index 0000000..e0e2e55 --- /dev/null +++ b/WebKitTools/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py @@ -0,0 +1,45 @@ +# Copyright 2009, 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. + + +"""Wrong web_socket_transfer_data() signature. +""" + + +def web_socket_do_extra_handshake(request): + pass + + +def no_web_socket_transfer_data(request): + request.connection.write( + 'sub/wrong_transfer_sig_wsh.py is called for %s, %s' % + (request.ws_resource, request.ws_protocol)) + + +# vi:sts=4 sw=4 et diff --git a/WebKitTools/wx/browser/wscript b/WebKitTools/wx/browser/wscript index 530cd75..d5246c1 100644 --- a/WebKitTools/wx/browser/wscript +++ b/WebKitTools/wx/browser/wscript @@ -45,7 +45,7 @@ def build(bld): includes = ' '.join(include_paths), source = 'browser.cpp', target = 'wxBrowser', - uselib = 'WX CURL ICU SQLITE3 XSLT XML WXWEBKIT ' + get_config(), + uselib = 'WXWEBKIT WX ' + get_config(), libpath = [output_dir], uselib_local = '', install_path = output_dir) diff --git a/WebKitTools/wx/build/build_utils.py b/WebKitTools/wx/build/build_utils.py index 0a795a8..b767d9f 100644 --- a/WebKitTools/wx/build/build_utils.py +++ b/WebKitTools/wx/build/build_utils.py @@ -27,13 +27,12 @@ import commands import glob import os import platform +import re import shutil import sys import urllib import urlparse -import Logs - def get_output(command): """ Windows-compatible function for getting output from a command. @@ -110,6 +109,7 @@ def update_wx_deps(conf, wk_root, msvc_version='msvc2008'): """ Download and update tools needed to build the wx port. """ + import Logs Logs.info('Ensuring wxWebKit dependencies are up-to-date.') wklibs_dir = os.path.join(wk_root, 'WebKitLibraries') @@ -154,3 +154,35 @@ def flattenSources(sources): flat_sources.extend(group) return flat_sources + +def git_branch_name(): + try: + branches = commands.getoutput("git branch --no-color") + match = re.search('^\* (.*)', branches, re.MULTILINE) + if match: + return ".%s" % match.group(1) + except: + pass + + return "" + +def get_config(wk_root): + config_file = os.path.join(wk_root, 'WebKitBuild', 'Configuration') + config = 'Debug' + + if os.path.exists(config_file): + config = open(config_file).read() + + return config + +def svn_revision(): + if os.system("git info") == 0: + info = commands.getoutput("git-svn info ../..") + else: + info = commands.getoutput("svn info") + + for line in info.split("\n"): + if line.startswith("Revision: "): + return line.replace("Revision: ", "").strip() + + return "" diff --git a/WebKitTools/wx/build/settings.py b/WebKitTools/wx/build/settings.py index 4019273..652a880 100644 --- a/WebKitTools/wx/build/settings.py +++ b/WebKitTools/wx/build/settings.py @@ -95,8 +95,9 @@ jscore_dirs = [ ] webcore_dirs = [ - 'accessibility', - 'bindings/js', + 'accessibility', + 'bindings', + 'bindings/js', 'bridge', 'bridge/c', 'css', @@ -139,21 +140,8 @@ webcore_dirs = [ 'xml' ] -config_file = os.path.join(wk_root, 'WebKitBuild', 'Configuration') -config = 'Debug' - -if os.path.exists(config_file): - config = open(config_file).read() - -config_dir = config - -try: - branches = commands.getoutput("git branch --no-color") - match = re.search('^\* (.*)', branches, re.MULTILINE) - if match: - config_dir += ".%s" % match.group(1) -except: - pass +config = get_config(wk_root) +config_dir = config + git_branch_name() output_dir = os.path.join(wk_root, 'WebKitBuild', config_dir) @@ -203,6 +191,7 @@ def common_set_options(opt): opt.add_option('--wxpython', action='store_true', default=False, help='Create the wxPython bindings.') opt.add_option('--wx-compiler-prefix', action='store', default='vc', help='Specify a different compiler prefix (do this if you used COMPILER_PREFIX when building wx itself)') + opt.add_option('--macosx-version', action='store', default='', help="Version of OS X to build for.") def common_configure(conf): """ @@ -245,11 +234,32 @@ def common_configure(conf): msvc_version = 'msvc2005' msvclibs_dir = os.path.join(wklibs_dir, msvc_version, 'win') - conf.env.append_value('CXXFLAGS', ['/wd4291','/wd4344','/wd4396','/wd4800']) + + # Disable several warnings which occur many times during the build. + # Some of them are harmless (4099, 4344, 4396, 4800) and working around + # them in WebKit code is probably just not worth it. We can simply do + # nothing about the others (4503). A couple are possibly valid but + # there are just too many of them in the code so fixing them is + # impossible in practice and just results in tons of distracting output + # (4244, 4291). Finally 4996 is actively harmful as it is given for + # just about any use of standard C/C++ library facilities. + conf.env.append_value('CXXFLAGS', [ + '/wd4099', # type name first seen using 'struct' now seen using 'class' + '/wd4244', # conversion from 'xxx' to 'yyy', possible loss of data: + '/wd4291', # no matching operator delete found (for placement new) + '/wd4344', # behaviour change in template deduction + '/wd4396', # inline can't be used in friend declaration + '/wd4503', # decorated name length exceeded, name was truncated + '/wd4800', # forcing value to bool 'true' or 'false' + '/wd4996', # deprecated function + ]) + + # This one also occurs in C code, so disable it there as well. + conf.env.append_value('CCFLAGS', ['/wd4996']) for use in port_uses[build_port]: conf.env.append_value('CXXDEFINES', ['WTF_USE_%s' % use]) - + if build_port == "wx": update_wx_deps(conf, wk_root, msvc_version) @@ -265,7 +275,7 @@ def common_configure(conf): conf.env['CPPPATH_WX'] = wxincludes conf.env['LIB_WX'] = wxlibs conf.env['LIBPATH_WX'] = wxlibpaths - + if sys.platform.startswith('darwin'): conf.env['LIB_ICU'] = ['icucore'] # Apple does not ship the ICU headers with Mac OS X, so WebKit includes a copy of 3.2 headers @@ -274,15 +284,33 @@ def common_configure(conf): conf.env.append_value('CPPPATH', wklibs_dir) conf.env.append_value('LIBPATH', wklibs_dir) - # WebKit only supports 10.4+ + min_version = None + mac_target = 'MACOSX_DEPLOYMENT_TARGET' - if mac_target in os.environ and os.environ[mac_target] == '10.3': - os.environ[mac_target] = '10.4' + if Options.options.macosx_version != '': + min_version = Options.options.macosx_version - if mac_target in conf.env and conf.env[mac_target] == '10.3': - conf.env[mac_target] = '10.4' - - #conf.env['PREFIX'] = output_dir + # WebKit only supports 10.4+, but ppc systems often set this to earlier systems + if not min_version: + min_version = commands.getoutput('sw_vers -productVersion')[:4] + if min_version in ['10.1','10.2','10.3']: + min_version = '10.4' + + os.environ[mac_target] = conf.env[mac_target] = min_version + + sdk_version = min_version + if min_version == "10.4": + sdk_version += "u" + conf.env.append_value('LIB', ['WebKitSystemInterfaceTiger']) + + sdkroot = '/Developer/SDKs/MacOSX%s.sdk' % sdk_version + sdkflags = ['-arch', 'i386', '-isysroot', sdkroot] + + conf.env.append_value('CPPFLAGS', sdkflags) + conf.env.append_value('LINKFLAGS', sdkflags) + + conf.env.append_value('CPPPATH_SQLITE3', [os.path.join(wklibs_dir, 'WebCoreSQLite3')]) + conf.env.append_value('LIB_SQLITE3', ['WebCoreSQLite3']) libprefix = '' if building_on_win32: @@ -310,7 +338,7 @@ def common_configure(conf): conf.env.append_value('LIB', [ 'kernel32', 'user32','gdi32','comdlg32','winspool','winmm', 'shell32', 'comctl32', 'ole32', 'oleaut32', 'uuid', 'advapi32', - 'wsock32', 'gdiplus']) + 'wsock32', 'gdiplus', 'version']) conf.env['LIB_ICU'] = ['icudt', 'icule', 'iculx', 'icuuc', 'icuin', 'icuio', 'icutu'] @@ -337,9 +365,10 @@ def common_configure(conf): conf.check_cfg(msg='Checking for libxslt', path='xslt-config', args='--cflags --libs', package='', uselib_store='XSLT', mandatory=True) conf.check_cfg(path='xml2-config', args='--cflags --libs', package='', uselib_store='XML', mandatory=True) - conf.check_cfg(path='curl-config', args='--cflags --libs', package='', uselib_store='CURL', mandatory=True) - if sys.platform.startswith('darwin'): - conf.env.append_value('LIB', ['WebCoreSQLite3']) + if sys.platform.startswith('darwin') and min_version and min_version == '10.4': + conf.check_cfg(path=os.path.join(wklibs_dir, 'unix', 'bin', 'curl-config'), args='--cflags --libs', package='', uselib_store='CURL', mandatory=True) + else: + conf.check_cfg(path='curl-config', args='--cflags --libs', package='', uselib_store='CURL', mandatory=True) if not sys.platform.startswith('darwin'): conf.check_cfg(package='cairo', args='--cflags --libs', uselib_store='WX', mandatory=True) diff --git a/WebKitTools/wx/install-unix-extras b/WebKitTools/wx/install-unix-extras index 68d81e4..00c936c 100755 --- a/WebKitTools/wx/install-unix-extras +++ b/WebKitTools/wx/install-unix-extras @@ -74,6 +74,12 @@ LIBPNG_VERSION="1.2.33" LIBPNG_TARBALL="libpng-$LIBPNG_VERSION.tar.gz" LIBPNG_URL="http://wxwebkit.wxcommunity.com/downloads/deps/$LIBPNG_TARBALL" +LIBCURL_VERSION="7.19.6" +LIBCURL_TARBALL="curl-$LIBCURL_VERSION.tar.gz" +LIBCURL_URL="http://curl.haxx.se/download/$LIBCURL_TARBALL" + +export MAC_OS_X_DEPLOYMENT_TARGET=10.4 + cd $DL_DIR # build ICU if [ `which icu-config >/dev/null 2>&1` ]; then @@ -170,3 +176,25 @@ if [ ! -f $DEPS_PREFIX/lib/libpng.a ]; then cd $DL_DIR rm -rf $DL_DIR/libpng-$LIBPNG_VERSION fi + +if [ ! -f $DEPS_PREFIX/lib/libcurl.$DLLEXT ]; then + $DL_CMD -o $DL_DIR/$LIBCURL_TARBALL $LIBCURL_URL + + tar xzvf $DL_DIR/$LIBCURL_TARBALL + cd $DL_DIR/curl-$LIBCURL_VERSION + + if [ "${OSTYPE:0:6}" == "darwin" ]; then + ./configure --prefix=$DEPS_PREFIX --disable-dependency-tracking + make CFLAGS="-O -g -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc" \ + LDFLAGS="-arch i386 -arch ppc" + make install + else + ./configure --prefix=$DEPS_PREFIX + + make + make install + fi + + cd $DL_DIR + rm -rf $DL_DIR/curl-$LIBCURL_VERSION +fi diff --git a/WebKitTools/wx/packaging/build-mac-installer.py b/WebKitTools/wx/packaging/build-mac-installer.py new file mode 100644 index 0000000..a0c1b22 --- /dev/null +++ b/WebKitTools/wx/packaging/build-mac-installer.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python + +# Copyright (C) 2009 Kevin Ollivier 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. +# +# Script for building Mac .pkg installer + +import commands +import distutils.sysconfig +import glob +import optparse +import os +import shutil +import string +import sys +import tempfile + +script_dir = os.path.abspath(os.path.dirname(__file__)) +sys.path.append(os.path.abspath(os.path.join(script_dir, "..", "build"))) + +from build_utils import * + +import wx + +wxwk_root = os.path.abspath(os.path.join(script_dir, "..", "..", "..")) +wxwebkit_dir = os.path.abspath(os.path.join(wxwk_root, "WebKitBuild", get_config(wxwk_root) + git_branch_name())) + +wx_version = wx.__version__[:5] +py_version = sys.version[:3] + +wxwk_version = svn_revision() + +platform = "osx" + +pkgname = "wxWebKit-%s-wx%s-py%s" % (platform, wx_version[:3], py_version) + +tempdir = "/tmp/%s" % (pkgname) + +if os.path.exists(tempdir): + shutil.rmtree(tempdir) + os.makedirs(tempdir) + +installroot = os.path.join(tempdir, "install-root") +installapps = os.path.join(tempdir, "install-apps") + +sp_root = distutils.sysconfig.get_python_lib() +wx_root = sp_root +if sys.platform.startswith("darwin"): + wx_root = "/usr/local/lib/wxPython-unicode-%s" % wx.__version__ + sp_root = "%s/lib/python%s/site-packages" % (wx_root, py_version) +sitepackages = "%s/wx-%s-mac-unicode/wx" % (sp_root, wx_version[:3]) +prefix = sitepackages + +def mac_update_dependencies(dylib, prefix): + """ + Copies any non-system dependencies into the bundle, and + updates the install name path to the new path in the bundle. + """ + global wx_root + system_prefixes = ["/usr/lib", "/System/Library", wx_root] + + output = commands.getoutput("otool -L %s" % dylib).strip() + for line in output.split("\n"): + filename = line.split("(")[0].strip() + if os.path.exists(filename): + print "checking dll %s" % filename + copy = True + for sys_prefix in system_prefixes: + if filename.startswith(sys_prefix): + copy = False + + if copy: + copydir = os.path.dirname(dylib) + + filedir, basename = os.path.split(filename) + dest_filename = os.path.join(prefix, basename) + copyname = os.path.join(copydir, basename) + if not os.path.exists(copyname): + shutil.copy(filename, copydir) + os.system("install_name_tool -id %s %s" % (dest_filename, copyname)) + + os.system("install_name_tool -change %s %s %s" % (filename, dest_filename, dylib)) + +def exitIfError(cmd): + print cmd + retval = os.system(cmd) + if retval != 0: + if os.path.exists(tempdir): + shutil.rmtree(tempdir) + sys.exit(1) + +wxroot = installroot + prefix +wxpythonroot = installroot + sitepackages + +try: + if not os.path.exists(wxroot): + os.makedirs(wxroot) + + if not os.path.exists(wxpythonroot): + os.makedirs(wxpythonroot) + + for wildcard in ["*.py", "*.so", "*.dylib"]: + files = glob.glob(os.path.join(wxwebkit_dir, wildcard)) + for afile in files: + shutil.copy(afile, wxpythonroot) + + if sys.platform.startswith("darwin"): + dylib_path = os.path.join(wxpythonroot, "libwxwebkit.dylib") + os.system("install_name_tool -id %s %s" % (os.path.join(prefix, "libwxwebkit.dylib"), dylib_path)) + mac_update_dependencies(dylib_path, prefix) + mac_update_dependencies(os.path.join(wxpythonroot, "_webview.so"), prefix) + + demodir = installroot + "/Applications/wxWebKit/Demos" + if not os.path.exists(demodir): + os.makedirs(demodir) + + shutil.copy(os.path.join(wxwk_root, "WebKit", "wx", "bindings", "python", "samples", "simple.py"), demodir) + + if os.path.exists(pkgname + ".pkg"): + shutil.rmtree(pkgname + ".pkg") + + pkg_args = ['--title ' + pkgname, + '--out %s.pkg' % pkgname, + '--version ' + wxwk_version.strip(), + '--id org.wxwebkit.wxwebkit', + '--domain system', + '--root-volume-only', + '--root ' + installroot, + '--resources %s/mac/resources' % script_dir, + '--verbose' + ] + + packagemaker = "/Developer/Applications/Utilities/PackageMaker.app/Contents/MacOS/PackageMaker" + exitIfError(packagemaker + " %s" % (string.join(pkg_args, " "))) +finally: + if os.path.exists(tempdir): + shutil.rmtree(tempdir) diff --git a/WebKitTools/wx/packaging/build-win-installer.py b/WebKitTools/wx/packaging/build-win-installer.py index beed705..2bb8034 100644 --- a/WebKitTools/wx/packaging/build-win-installer.py +++ b/WebKitTools/wx/packaging/build-win-installer.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (C) 2008 Kevin Ollivier All rights reserved. # @@ -30,6 +30,14 @@ import commands import glob from subprocess import * +script_dir = os.path.abspath(os.path.dirname(__file__)) +sys.path.append(os.path.abspath(os.path.join(script_dir, "..", "build"))) + +from build_utils import * + +wxwk_root = os.path.abspath(os.path.join(script_dir, "..", "..", "..")) +wxwebkit_dir = os.path.abspath(os.path.join(wxwk_root, "WebKitBuild", get_config(wxwk_root) + git_branch_name())) + # Find InnoSetup executable def getInnoSetupPath(): name = "ISCC.exe" @@ -37,6 +45,7 @@ def getInnoSetupPath(): dirs = os.environ["PATH"].split(":") # Add the default file path dirs.append("C:\\Program Files\\Inno Setup 5") + dirs.append("C:\\Program Files (x86)\\Inno Setup 5") if os.environ.has_key("INNO5"): retval = os.environ["INNO5"] @@ -48,40 +57,19 @@ def getInnoSetupPath(): retval = filepath return retval - -def getWebKitOutputDir(): - retval = "" - if os.environ.has_key("WEBKITOUTPUTDIR"): - retval = os.environ["WEBKITOUTPUTDIR"] - - if retval == "": - retval = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "WebKitBuild")) - - return os.path.join(retval, "Release") - -def getRevisionString(): - pipe = Popen("svnversion", shell=True, stdout=PIPE).stdout - svnrevision = pipe.read().strip() - print "r" + svnrevision - svnrevision = string.split(svnrevision, ":")[0] - svnrevision = svnrevision.replace("M", "") - svnrevision = "r" + svnrevision - - return svnrevision if __name__ == "__main__": innoSetup = getInnoSetupPath() - buildDir = getWebKitOutputDir() os.chdir(sys.path[0]) - svnrevision = getRevisionString() + svnrevision = svn_revision() if not os.path.exists(innoSetup): print "ERROR: Cannot find InnoSetup." #sys.exit(1) - if not os.path.exists(buildDir): - print "ERROR: Build dir %s doesn't exist." % buildDir + if not os.path.exists(wxwebkit_dir): + print "ERROR: Build dir %s doesn't exist." % wxwebkit_dir sys.exit(1) fileList = """ @@ -89,7 +77,7 @@ CopyMode: alwaysoverwrite; Source: *.pyd; DestDir: "{app}" CopyMode: alwaysoverwrite; Source: *.py; DestDir: "{app}" """ - dlls = glob.glob(os.path.join(buildDir, "*.dll")) + dlls = glob.glob(os.path.join(wxwebkit_dir, "*.dll")) for dll in dlls: if dll.find("wxbase") == -1 and dll.find("wxmsw") == -1: fileList += """CopyMode: alwaysoverwrite; Source: %s; DestDir: "{app}" \n""" % dll @@ -97,8 +85,8 @@ CopyMode: alwaysoverwrite; Source: *.py; DestDir: "{app}" installerTemplate = open("wxWebKitInstaller.iss.in", "r").read() installerTemplate = installerTemplate.replace("<<VERSION>>", svnrevision) - installerTemplate = installerTemplate.replace("<<ROOTDIR>>", buildDir ) - installerTemplate = installerTemplate.replace("<<PYTHONVER>>", "2.5" ) + installerTemplate = installerTemplate.replace("<<ROOTDIR>>", wxwebkit_dir ) + installerTemplate = installerTemplate.replace("<<PYTHONVER>>", sys.version[0:3] ) installerTemplate = installerTemplate.replace("<<FILES>>", fileList ) outputFile = open("wxWebKitInstaller.iss", "w") diff --git a/WebKitTools/wx/packaging/wxWebKitInstaller.iss.in b/WebKitTools/wx/packaging/wxWebKitInstaller.iss.in index a2c808c..ebc89d4 100644 --- a/WebKitTools/wx/packaging/wxWebKitInstaller.iss.in +++ b/WebKitTools/wx/packaging/wxWebKitInstaller.iss.in @@ -17,7 +17,7 @@ UninstallFilesDir={app}\Uninstall Compression=bzip/9 SourceDir=<<ROOTDIR>> OutputDir=win-installer -OutputBaseFilename=wxWebKit-Py<<PYTHONVER>>-<<VERSION>> +OutputBaseFilename=wxWebKit-wx2.8-Py<<PYTHONVER>>-<<VERSION>> DisableStartupPrompt=yes AllowNoIcons=yes DisableProgramGroupPage=yes |